1 /*
2     TiMidity++ -- MIDI to WAVE converter and player
3     Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
4     Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 
20     gtk_i.c - Glenn Trigg 29 Oct 1998
21 
22 */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif /* HAVE_CONFIG_H*/
27 
28 #include <string.h>
29 #ifdef HAVE_GLOB_H
30 #include <glob.h>
31 #endif
32 #if HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
35 #include <gtk/gtk.h>
36 
37 #include "timidity.h"
38 #include "common.h"
39 #include "output.h"
40 #include "gtk_h.h"
41 
42 #include "pixmaps/playpaus.xpm"
43 #include "pixmaps/prevtrk.xpm"
44 #include "pixmaps/nexttrk.xpm"
45 #include "pixmaps/rew.xpm"
46 #include "pixmaps/ff.xpm"
47 #include "pixmaps/restart.xpm"
48 #include "pixmaps/quit.xpm"
49 #include "pixmaps/quiet.xpm"
50 #include "pixmaps/loud.xpm"
51 #include "pixmaps/open.xpm"
52 #include "pixmaps/keyup.xpm"
53 #include "pixmaps/keydown.xpm"
54 #include "pixmaps/slow.xpm"
55 #include "pixmaps/fast.xpm"
56 #include "pixmaps/timidity.xpm"
57 
58 static GtkWidget *create_menubar(void);
59 static GtkWidget *create_button_with_pixmap(GtkWidget *, gchar **, gint, gchar *);
60 static GtkWidget *create_pixmap_label(GtkWidget *, gchar **);
61 static gint delete_event(GtkWidget *, GdkEvent *, gpointer);
62 static void destroy (GtkWidget *, gpointer);
63 static GtkTooltips *create_yellow_tooltips(void);
64 static void handle_input(gpointer, gint, GdkInputCondition);
65 static void generic_cb(GtkWidget *, gpointer);
66 static void generic_scale_cb(GtkAdjustment *, gpointer);
67 static void open_file_cb(GtkWidget *, gpointer);
68 static void playlist_cb(GtkWidget *, guint);
69 static void playlist_op(GtkWidget *, guint);
70 static void file_list_cb(GtkWidget *, gint, gint, GdkEventButton *, gpointer);
71 static void clear_all_cb(GtkWidget *, gpointer);
72 static void filer_cb(GtkWidget *, gpointer);
73 static void tt_toggle_cb(GtkWidget *, gpointer);
74 static void locate_update_cb(GtkWidget *, GdkEventButton *, gpointer);
75 static void my_adjustment_set_value(GtkAdjustment *, gint);
76 static void set_icon_pixmap(GtkWidget *, gchar **);
77 
78 static GtkWidget *window, *clist, *text, *vol_scale, *locator;
79 static GtkWidget *filesel = NULL, *plfilesel = NULL;
80 static GtkWidget *tot_lbl, *cnt_lbl, *auto_next, *ttshow;
81 static GtkTooltips *ttip;
82 static int file_number_to_play; /* Number of the file we're playing in the list */
83 static int max_sec, is_quitting = 0, locating = 0, local_adjust = 0;
84 
85 static GtkItemFactoryEntry ife[] = {
86     {"/File/Open", "<control>O", open_file_cb, 0, NULL},
87     {"/File/sep", NULL, NULL, 0, "<Separator>"},
88     {"/File/Load Playlist", "<control>L", playlist_cb,
89      'l', NULL},
90     {"/File/Save Playlist", "<control>S", playlist_cb,
91      's', NULL},
92     {"/File/sep", NULL, NULL, 0, "<Separator>"},
93     {"/File/Quit", "<control>Q", generic_cb, GTK_QUIT, NULL},
94     {"/Options/Auto next", "<control>A", NULL, 0, "<CheckItem>"},
95     {"/Options/Show tooltips", "<control>T", tt_toggle_cb, 0, "<CheckItem>"},
96     {"/Options/Clear All", "<control>C", clear_all_cb, 0, NULL}
97 };
98 
99 #ifdef HAVE_GTK_2
100 static GtkTextBuffer *textbuf;
101 static GtkTextIter iter, start_iter, end_iter;
102 static GtkTextMark *mark;
103 #endif
104 
105 /*----------------------------------------------------------------------*/
106 
107 static void
generic_cb(GtkWidget * widget,gpointer data)108 generic_cb(GtkWidget *widget, gpointer data)
109 {
110     gtk_pipe_int_write(GPOINTER_TO_INT(data));
111     if(GPOINTER_TO_INT(data) == GTK_PAUSE) {
112 	gtk_label_set(GTK_LABEL(cnt_lbl), "Pause");
113     }
114 }
115 
116 static void
tt_toggle_cb(GtkWidget * widget,gpointer data)117 tt_toggle_cb(GtkWidget *widget, gpointer data)
118 {
119     if( GTK_CHECK_MENU_ITEM(ttshow)->active ) {
120 	gtk_tooltips_enable(ttip);
121     }
122     else {
123 	gtk_tooltips_disable(ttip);
124     }
125 }
126 
127 static void
open_file_cb(GtkWidget * widget,gpointer data)128 open_file_cb(GtkWidget *widget, gpointer data)
129 {
130     if( ! filesel ) {
131 	filesel = gtk_file_selection_new("Open File");
132 	gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(filesel));
133 
134 #ifdef HAVE_GTK_2
135 	gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button),
136 			   "clicked",
137 			   G_CALLBACK (filer_cb), GINT_TO_POINTER(1));
138 #else
139 	gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button),
140 			   "clicked",
141 			   GTK_SIGNAL_FUNC (filer_cb), GINT_TO_POINTER(1));
142 #endif
143 #ifdef HAVE_GTK_2
144 	gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button),
145 			   "clicked",
146 			   G_CALLBACK (filer_cb), GINT_TO_POINTER(0));
147 #else
148 	gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button),
149 			   "clicked",
150 			   GTK_SIGNAL_FUNC (filer_cb), GINT_TO_POINTER(0));
151 #endif
152     }
153 
154     gtk_widget_show(GTK_WIDGET(filesel));
155 }
156 
157 static void
filer_cb(GtkWidget * widget,gpointer data)158 filer_cb(GtkWidget *widget, gpointer data)
159 {
160     gchar *filenames[2];
161 #ifdef GLOB_BRACE
162     int i;
163 #ifdef HAVE_GTK_2
164     const gchar *patt;
165 #else
166     gchar *patt;
167 #endif
168     glob_t pglob;
169 
170     if(GPOINTER_TO_INT(data) == 1) {
171 	patt = gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
172 	if(glob(patt, GLOB_BRACE|GLOB_NOMAGIC|GLOB_TILDE, NULL, &pglob))
173 	    return;
174 	for( i = 0; i < pglob.gl_pathc; i++) {
175 	    filenames[0] = pglob.gl_pathv[i];
176 	    filenames[1] = NULL;
177 	    gtk_clist_append(GTK_CLIST(clist), filenames);
178 	}
179 	globfree(&pglob);
180     }
181 #else
182     if((int)data == 1) {
183 	filenames[0] = gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
184 	filenames[1] = NULL;
185 	gtk_clist_append(GTK_CLIST(clist), filenames);
186     }
187 #endif
188     gtk_widget_hide(filesel);
189     gtk_clist_columns_autosize(GTK_CLIST(clist));
190 }
191 
192 static void
generic_scale_cb(GtkAdjustment * adj,gpointer data)193 generic_scale_cb(GtkAdjustment *adj, gpointer data)
194 {
195     if(local_adjust)
196 	return;
197 
198     gtk_pipe_int_write(GPOINTER_TO_INT(data));
199 
200     /* This is a bit of a hack as the volume scale (a GtkVScale) seems
201        to have it's minimum at the top which is counter-intuitive. */
202     if(GPOINTER_TO_INT(data) == GTK_CHANGE_VOLUME) {
203 	gtk_pipe_int_write(MAX_AMPLIFICATION - adj->value);
204     }
205     else {
206 	gtk_pipe_int_write((int)adj->value*100);
207     }
208 }
209 
210 static void
file_list_cb(GtkWidget * widget,gint row,gint column,GdkEventButton * event,gpointer data)211 file_list_cb(GtkWidget *widget, gint row, gint column,
212 	     GdkEventButton *event, gpointer data)
213 {
214     gint	retval;
215     gchar	*fname;
216 
217     if(event && (event->button == 3)) {
218 	if(event->type == GDK_2BUTTON_PRESS) {
219 	    gtk_clist_remove(GTK_CLIST(clist), row);
220 	}
221 	else {
222 	    return;
223 	}
224     }
225     retval = gtk_clist_get_text(GTK_CLIST(widget), row, 0, &fname);
226     if(retval) {
227 	gtk_pipe_int_write(GTK_PLAY_FILE);
228 	gtk_pipe_string_write(fname);
229 	file_number_to_play=row;
230     }
231 }
232 
233 static void
playlist_cb(GtkWidget * widget,guint data)234 playlist_cb(GtkWidget *widget, guint data)
235 {
236     const gchar	*pldir;
237     gchar	*plpatt;
238 
239     if( ! plfilesel ) {
240 	plfilesel = gtk_file_selection_new("");
241 	gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(plfilesel));
242 
243 	pldir = g_getenv("TIMIDITY_PLAYLIST_DIR");
244 	if(pldir != NULL) {
245 	    plpatt = g_strconcat(pldir, "/*.tpl", NULL);
246 	    gtk_file_selection_set_filename(GTK_FILE_SELECTION(plfilesel),
247 					    plpatt);
248 	    g_free(plpatt);
249 	}
250 
251 	gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(plfilesel)->ok_button),
252 			   "clicked",
253 			   GTK_SIGNAL_FUNC (playlist_op), (gpointer)1);
254 	gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(plfilesel)->cancel_button),
255 			   "clicked",
256 			   GTK_SIGNAL_FUNC (playlist_op), NULL);
257     }
258 
259     gtk_window_set_title(GTK_WINDOW(plfilesel), ((char)data == 'l')?
260 			 "Load Playlist":
261 			 "Save Playlist");
262     gtk_object_set_user_data(GTK_OBJECT(plfilesel), GINT_TO_POINTER(data));
263     gtk_file_selection_complete(GTK_FILE_SELECTION(plfilesel), "*.tpl");
264 
265     gtk_widget_show(plfilesel);
266 } /* playlist_cb */
267 
268 static void
playlist_op(GtkWidget * widget,guint data)269 playlist_op(GtkWidget *widget, guint data)
270 {
271     int		i;
272     const gchar	*filename[2];
273     gchar	action, *rowdata, fname[BUFSIZ], *tmp;
274     FILE	*plfp;
275 
276     gtk_widget_hide(plfilesel);
277 
278     if(!data)
279 	return;
280 
281     action = GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(plfilesel)));
282     filename[0] = gtk_file_selection_get_filename(GTK_FILE_SELECTION(plfilesel));
283 
284     if(action == 'l') {
285 	if((plfp = fopen(filename[0], "r")) == NULL) {
286 	    g_error("Can't open %s for reading.", filename[0]);
287 	    return;
288 	}
289 	while(fgets(fname, BUFSIZ, plfp) != NULL) {
290             gchar *filename[2];
291 	    if(fname[strlen(fname) - 1] == '\n')
292 		fname[strlen(fname) - 1] = '\0';
293 	    filename[0] = fname;
294 	    filename[1] = NULL;
295 	    gtk_clist_append(GTK_CLIST(clist), filename);
296 	}
297 	fclose(plfp);
298 	gtk_clist_columns_autosize(GTK_CLIST(clist));
299     }
300     else if(action == 's') {
301 	if((plfp = fopen(filename[0], "w")) == NULL) {
302 	    g_error("Can't open %s for writing.", filename[0]);
303 	    return;
304 	}
305 	for(i = 0; i < GTK_CLIST(clist)->rows; i++) {
306 	    gtk_clist_get_text(GTK_CLIST(clist), i, 0, &rowdata);
307 	    /* Make sure we have an absolute path. */
308 	    if(*rowdata != '/') {
309 		tmp = g_get_current_dir();
310 		rowdata = g_strconcat(tmp, "/", rowdata, NULL);
311 		fprintf(plfp, "%s\n", rowdata);
312 		g_free(rowdata);
313 		g_free(tmp);
314 	    }
315 	    else {
316 		fprintf(plfp, "%s\n", rowdata);
317 	    }
318 	}
319 	fclose(plfp);
320     }
321     else {
322 	g_error("Invalid playlist action!.");
323     }
324 } /* playlist_op */
325 
326 static void
clear_all_cb(GtkWidget * widget,gpointer data)327 clear_all_cb(GtkWidget *widget, gpointer data)
328 {
329     gtk_clist_clear(GTK_CLIST(clist));
330 } /* clear_all_cb */
331 
332 static gint
delete_event(GtkWidget * widget,GdkEvent * event,gpointer data)333 delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
334 {
335     return (FALSE);
336 }
337 
338 static void
destroy(GtkWidget * widget,gpointer data)339 destroy (GtkWidget *widget, gpointer data)
340 {
341     is_quitting = 1;
342     gtk_pipe_int_write(GTK_QUIT);
343 }
344 
345 static void
locate_update_cb(GtkWidget * widget,GdkEventButton * ev,gpointer data)346 locate_update_cb (GtkWidget *widget, GdkEventButton *ev, gpointer data)
347 {
348     if( (ev->button == 1) || (ev->button == 2)) {
349 	if( ev->type == GDK_BUTTON_RELEASE ) {
350 	    locating = 0;
351 	}
352 	else {
353 	    locating = 1;
354 	}
355     }
356 }
357 
358 static void
my_adjustment_set_value(GtkAdjustment * adj,gint value)359 my_adjustment_set_value(GtkAdjustment *adj, gint value)
360 {
361     local_adjust = 1;
362     gtk_adjustment_set_value(adj, (gfloat)value);
363     local_adjust = 0;
364 }
365 
366 void
Launch_Gtk_Process(int pipe_number)367 Launch_Gtk_Process(int pipe_number)
368 {
369     int	argc = 0;
370     gchar **argv = NULL;
371     GtkWidget *button, *mbar, *swin;
372     GtkWidget *table, *align, *handlebox;
373     GtkWidget *vbox, *hbox, *vbox2, *scrolled_win;
374     GtkObject *adj;
375 
376     /* enable locale */
377     gtk_set_locale ();
378 
379     gtk_init (&argc, &argv);
380 
381     ttip = create_yellow_tooltips();
382     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
383     gtk_widget_set_name(window, "TiMidity");
384     gtk_window_set_title(GTK_WINDOW(window), "TiMidity - MIDI Player");
385     gtk_window_set_wmclass(GTK_WINDOW(window), "timidity", "TiMidity");
386 
387     gtk_signal_connect(GTK_OBJECT(window), "delete_event",
388 		       GTK_SIGNAL_FUNC (delete_event), NULL);
389 
390     gtk_signal_connect(GTK_OBJECT(window), "destroy",
391 		       GTK_SIGNAL_FUNC (destroy), NULL);
392 
393     vbox = gtk_vbox_new(FALSE, 0);
394     gtk_container_add(GTK_CONTAINER(window), vbox);
395 
396     mbar = create_menubar();
397     gtk_box_pack_start(GTK_BOX(vbox), mbar, FALSE, FALSE, 0);
398 
399     scrolled_win = gtk_scrolled_window_new(NULL, NULL);
400     gtk_box_pack_start(GTK_BOX(vbox), scrolled_win, TRUE, TRUE ,0);
401     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
402     gtk_widget_show(scrolled_win);
403 
404 #ifdef HAVE_GTK_2
405     text = gtk_text_view_new();
406     gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
407     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
408 #else
409     text = gtk_text_new(NULL, NULL);
410 #endif
411     gtk_widget_show(text);
412     gtk_container_add(GTK_CONTAINER(scrolled_win), text);
413 
414     hbox = gtk_hbox_new(FALSE, 4);
415     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 4);
416     gtk_widget_show(hbox);
417 
418     adj = gtk_adjustment_new(0., 0., 100., 1., 20., 0.);
419     locator = gtk_hscale_new(GTK_ADJUSTMENT(adj));
420     gtk_scale_set_draw_value(GTK_SCALE(locator), TRUE);
421     gtk_signal_connect(GTK_OBJECT(adj), "value_changed",
422 			GTK_SIGNAL_FUNC(generic_scale_cb),
423 			(gpointer)GTK_CHANGE_LOCATOR);
424     gtk_signal_connect(GTK_OBJECT(locator), "button_press_event",
425 			GTK_SIGNAL_FUNC(locate_update_cb),
426 			NULL);
427     gtk_signal_connect(GTK_OBJECT(locator), "button_release_event",
428 			GTK_SIGNAL_FUNC(locate_update_cb),
429 			NULL);
430     gtk_range_set_update_policy(GTK_RANGE(locator),
431 				GTK_UPDATE_DISCONTINUOUS);
432     gtk_scale_set_digits(GTK_SCALE(locator), 0);
433     gtk_widget_show(locator);
434     gtk_box_pack_start(GTK_BOX(hbox), locator, TRUE, TRUE, 4);
435 
436     align = gtk_alignment_new(0., 1., 1., 0.);
437     gtk_widget_show(align);
438     cnt_lbl = gtk_label_new("00:00");
439     gtk_widget_show(cnt_lbl);
440     gtk_container_add(GTK_CONTAINER(align), cnt_lbl);
441     gtk_box_pack_start(GTK_BOX(hbox), align, FALSE, TRUE, 0);
442 
443     align = gtk_alignment_new(0., 1., 1., 0.);
444     gtk_widget_show(align);
445     tot_lbl = gtk_label_new("/00:00");
446     gtk_widget_show(tot_lbl);
447     gtk_container_add(GTK_CONTAINER(align), tot_lbl);
448     gtk_box_pack_start(GTK_BOX(hbox), align, FALSE, TRUE, 0);
449 
450     hbox = gtk_hbox_new(FALSE, 4);
451     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 4);
452 
453     swin = gtk_scrolled_window_new(NULL, NULL);
454     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
455 				   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
456     clist = gtk_clist_new(1);
457     gtk_container_add(GTK_CONTAINER(swin), clist);
458     gtk_widget_show(swin);
459     gtk_widget_show(clist);
460     gtk_widget_set_usize(clist, 200, 10);
461     gtk_clist_set_reorderable(GTK_CLIST(clist), TRUE);
462     gtk_clist_set_button_actions(GTK_CLIST(clist), 0, GTK_BUTTON_SELECTS);
463     gtk_clist_set_button_actions(GTK_CLIST(clist), 1, GTK_BUTTON_DRAGS);
464     gtk_clist_set_button_actions(GTK_CLIST(clist), 2, GTK_BUTTON_SELECTS);
465     gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_SINGLE);
466     gtk_clist_set_column_auto_resize(GTK_CLIST(clist), 1, TRUE);
467     gtk_signal_connect(GTK_OBJECT(clist), "select_row",
468 		       GTK_SIGNAL_FUNC(file_list_cb), NULL);
469 
470     gtk_box_pack_start(GTK_BOX(hbox), swin, TRUE, TRUE, 0);
471 
472     vbox2 = gtk_vbox_new(FALSE, 0);
473     gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 0);
474     gtk_widget_show(vbox2);
475 
476     /* This is so the pixmap creation works properly. */
477     gtk_widget_realize(window);
478     set_icon_pixmap(window, timidity_xpm);
479 
480     gtk_box_pack_start(GTK_BOX(vbox2),
481 		       create_pixmap_label(window, loud_xpm),
482 		       FALSE, FALSE, 0);
483 
484     adj = gtk_adjustment_new(30., 0., (gfloat)MAX_AMPLIFICATION, 1., 20., 0.);
485     vol_scale = gtk_vscale_new(GTK_ADJUSTMENT(adj));
486     gtk_scale_set_draw_value(GTK_SCALE(vol_scale), FALSE);
487     gtk_signal_connect (GTK_OBJECT(adj), "value_changed",
488 			GTK_SIGNAL_FUNC(generic_scale_cb),
489 			(gpointer)GTK_CHANGE_VOLUME);
490     gtk_range_set_update_policy(GTK_RANGE(vol_scale),
491 				GTK_UPDATE_DELAYED);
492     gtk_widget_show(vol_scale);
493     gtk_tooltips_set_tip(ttip, vol_scale, "Volume control", NULL);
494 
495     gtk_box_pack_start(GTK_BOX(vbox2), vol_scale, TRUE, TRUE, 0);
496 
497     gtk_box_pack_start(GTK_BOX(vbox2),
498 		       create_pixmap_label(window, quiet_xpm),
499 		       FALSE, FALSE, 0);
500 
501     handlebox = gtk_handle_box_new();
502     gtk_box_pack_start(GTK_BOX(hbox), handlebox, FALSE, FALSE, 0);
503 
504     table = gtk_table_new(7, 2, TRUE);
505     gtk_container_add(GTK_CONTAINER(handlebox), table);
506 
507     button = create_button_with_pixmap(window, playpaus_xpm, GTK_PAUSE,
508 				       "Play/Pause");
509     gtk_table_attach_defaults(GTK_TABLE(table), button,
510 			      0, 2, 0, 1);
511 
512     button = create_button_with_pixmap(window, prevtrk_xpm, GTK_PREV,
513 				       "Previous file");
514     gtk_table_attach_defaults(GTK_TABLE(table), button,
515 			      0, 1, 1, 2);
516 
517     button = create_button_with_pixmap(window, nexttrk_xpm, GTK_NEXT,
518 				       "Next file");
519     gtk_table_attach_defaults(GTK_TABLE(table), button,
520 			      1, 2, 1, 2);
521 
522     button = create_button_with_pixmap(window, rew_xpm, GTK_RWD,
523 				       "Rewind");
524     gtk_table_attach_defaults(GTK_TABLE(table), button,
525 			      0, 1, 2, 3);
526 
527     button = create_button_with_pixmap(window, ff_xpm, GTK_FWD,
528 				       "Fast forward");
529     gtk_table_attach_defaults(GTK_TABLE(table), button,
530 			      1, 2, 2, 3);
531 
532     button = create_button_with_pixmap(window, keydown_xpm, GTK_KEYDOWN,
533 				       "Lower pitch");
534     gtk_table_attach_defaults(GTK_TABLE(table), button,
535 			      0, 1, 3, 4);
536 
537     button = create_button_with_pixmap(window, keyup_xpm, GTK_KEYUP,
538 				       "Raise pitch");
539     gtk_table_attach_defaults(GTK_TABLE(table), button,
540 			      1, 2, 3, 4);
541 
542     button = create_button_with_pixmap(window, slow_xpm, GTK_SLOWER,
543 				       "Decrease tempo");
544     gtk_table_attach_defaults(GTK_TABLE(table), button,
545 			      0, 1, 4, 5);
546 
547     button = create_button_with_pixmap(window, fast_xpm, GTK_FASTER,
548 				       "Increase tempo");
549     gtk_table_attach_defaults(GTK_TABLE(table), button,
550 			      1, 2, 4, 5);
551 
552     button = create_button_with_pixmap(window, restart_xpm, GTK_RESTART,
553 				       "Restart");
554     gtk_table_attach_defaults(GTK_TABLE(table), button,
555 			      0, 1, 5, 6);
556 
557     button = create_button_with_pixmap(window, open_xpm, 0,
558 				       "Open");
559 #ifdef HAVE_GTK_2
560     gtk_signal_disconnect_by_func(GTK_OBJECT(button), G_CALLBACK(generic_cb), 0);
561 #else
562     gtk_signal_disconnect_by_func(GTK_OBJECT(button), generic_cb, 0);
563 #endif
564     gtk_signal_connect(GTK_OBJECT(button), "clicked",
565 			      GTK_SIGNAL_FUNC(open_file_cb), 0);
566     gtk_table_attach_defaults(GTK_TABLE(table), button,
567 			      1, 2, 5, 6);
568 
569     button = create_button_with_pixmap(window, quit_xpm, GTK_QUIT,
570 				       "Quit");
571     gtk_table_attach_defaults(GTK_TABLE(table), button,
572 			      0, 2, 6, 7);
573 
574     gtk_widget_show(hbox);
575     gtk_widget_show(vbox);
576     gtk_widget_show(table);
577     gtk_widget_show(handlebox);
578     gtk_widget_show(window);
579 
580     gdk_input_add(pipe_number, GDK_INPUT_READ, handle_input, NULL);
581 
582     gtk_main();
583 }
584 
585 static GtkWidget *
create_button_with_pixmap(GtkWidget * window,gchar ** bits,gint data,gchar * thelp)586 create_button_with_pixmap(GtkWidget *window, gchar **bits, gint data, gchar *thelp)
587 {
588     GtkWidget	*pw, *button;
589     GdkPixmap	*pixmap;
590     GdkBitmap	*mask;
591     GtkStyle	*style;
592 
593     style = gtk_widget_get_style(window);
594     pixmap = gdk_pixmap_create_from_xpm_d(window->window,
595 					  &mask,
596 					  &style->bg[GTK_STATE_NORMAL],
597 					  bits);
598     pw = gtk_pixmap_new(pixmap, mask);
599     gtk_widget_show(pw);
600 
601     button = gtk_button_new();
602     gtk_container_add(GTK_CONTAINER(button), pw);
603     gtk_signal_connect(GTK_OBJECT(button), "clicked",
604 			      GTK_SIGNAL_FUNC(generic_cb),
605 			      GINT_TO_POINTER(data));
606     gtk_widget_show(button);
607     gtk_tooltips_set_tip(ttip, button, thelp, NULL);
608 
609     return button;
610 }
611 
612 static GtkWidget *
create_pixmap_label(GtkWidget * window,gchar ** bits)613 create_pixmap_label(GtkWidget *window, gchar **bits)
614 {
615     GtkWidget	*pw;
616     GdkPixmap	*pixmap;
617     GdkBitmap	*mask;
618     GtkStyle	*style;
619 
620     style = gtk_widget_get_style(window);
621     pixmap = gdk_pixmap_create_from_xpm_d(window->window,
622 					  &mask,
623 					  &style->bg[GTK_STATE_NORMAL],
624 					  bits);
625     pw = gtk_pixmap_new(pixmap, mask);
626     gtk_widget_show(pw);
627 
628     return pw;
629 }
630 
631 static void
set_icon_pixmap(GtkWidget * window,gchar ** bits)632 set_icon_pixmap(GtkWidget *window, gchar **bits)
633 {
634     GdkPixmap	*pixmap;
635     GdkBitmap	*mask;
636     GtkStyle	*style;
637 
638     style = gtk_widget_get_style(window);
639     pixmap = gdk_pixmap_create_from_xpm_d(window->window,
640 					  &mask,
641 					  &style->bg[GTK_STATE_NORMAL],
642 					  bits);
643     gdk_window_set_icon(window->window, NULL, pixmap, mask);
644     gdk_window_set_icon_name(window->window, "TiMidity");
645 }
646 
647 static GtkWidget *
create_menubar(void)648 create_menubar(void)
649 {
650     GtkItemFactory	*ifactory;
651     GtkAccelGroup	*ag;
652 
653 #ifdef HAVE_GTK_2
654     ag = gtk_accel_group_new();
655 #else
656     ag = gtk_accel_group_get_default();
657 #endif
658     ifactory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<Main>", ag);
659     gtk_item_factory_create_items(ifactory,
660 				  sizeof(ife) / sizeof(GtkItemFactoryEntry),
661 				  ife, NULL);
662     gtk_widget_show(ifactory->widget);
663 
664     auto_next = gtk_item_factory_get_widget(ifactory, "/Options/Auto next");
665     gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(auto_next), TRUE);
666     ttshow = gtk_item_factory_get_widget(ifactory, "/Options/Show tooltips");
667     gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(ttshow), TRUE);
668 
669     return ifactory->widget;
670 }
671 
672 /* Following function curtesy of the gtk mailing list. */
673 
674 static GtkTooltips *
create_yellow_tooltips()675 create_yellow_tooltips()
676 {
677     GtkTooltips	*tip;
678 
679     /* First create a default Tooltip */
680     tip = gtk_tooltips_new();
681 
682 #ifndef HAVE_GTK_2
683     GdkColor	*t_back;
684 
685     t_back = (GdkColor*)g_malloc( sizeof(GdkColor));
686 
687     /* Try to get the colors */
688     if ( gdk_color_parse("linen", t_back)){
689 	if(gdk_colormap_alloc_color(gdk_colormap_get_system(),
690 				    t_back,
691 				    FALSE, TRUE)) {
692 	    gtk_tooltips_set_colors(tip, t_back, NULL);
693 	}
694     }
695 #endif
696 
697     return tip;
698 }
699 
700 /* Receive DATA sent by the application on the pipe     */
701 
702 static void
handle_input(gpointer client_data,gint source,GdkInputCondition ic)703 handle_input(gpointer client_data, gint source, GdkInputCondition ic)
704 {
705     int message;
706 
707     gtk_pipe_int_read(&message);
708 
709     switch (message) {
710     case REFRESH_MESSAGE:
711 	g_warning("REFRESH MESSAGE IS OBSOLETE !!!");
712 	break;
713 
714     case TOTALTIME_MESSAGE:
715 	{
716 	    int tt;
717 	    int minutes,seconds;
718 	    char local_string[20];
719 	    GtkObject *adj;
720 
721 	    gtk_pipe_int_read(&tt);
722 
723 	    seconds=max_sec=tt/play_mode->rate;
724 	    minutes=seconds/60;
725 	    seconds-=minutes*60;
726 	    sprintf(local_string,"/ %i:%02i",minutes,seconds);
727 	    gtk_label_set(GTK_LABEL(tot_lbl), local_string);
728 
729 	    /* Readjust the time scale */
730 	    adj = gtk_adjustment_new(0., 0., (gfloat)max_sec,
731 				     1., 10., 0.);
732 	    gtk_signal_connect(GTK_OBJECT(adj), "value_changed",
733 			       GTK_SIGNAL_FUNC(generic_scale_cb),
734 			       (gpointer)GTK_CHANGE_LOCATOR);
735 	    gtk_range_set_adjustment(GTK_RANGE(locator),
736 				     GTK_ADJUSTMENT(adj));
737 	}
738 	break;
739 
740     case MASTERVOL_MESSAGE:
741 	{
742 	    int volume;
743 	    GtkAdjustment *adj;
744 
745 	    gtk_pipe_int_read(&volume);
746 	    adj = gtk_range_get_adjustment(GTK_RANGE(vol_scale));
747 	    my_adjustment_set_value(adj, MAX_AMPLIFICATION - volume);
748 	}
749 	break;
750 
751     case FILENAME_MESSAGE:
752 	{
753 	    char filename[255], title[255];
754 	    char *pc;
755 
756 	    gtk_pipe_string_read(filename);
757 
758 	    /* Extract basename of the file */
759 	    pc = strrchr(filename, '/');
760 	    if (pc == NULL)
761 		pc = filename;
762 	    else
763 		pc++;
764 
765 	    sprintf(title, "Timidity %s - %s", timidity_version, pc);
766 	    gtk_window_set_title(GTK_WINDOW(window), title);
767 
768 	    /* Clear the text area. */
769 #ifdef HAVE_GTK_2
770 	    textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
771 	    gtk_text_buffer_get_start_iter(textbuf, &start_iter);
772 	    gtk_text_buffer_get_end_iter(textbuf, &end_iter);
773 	    iter = start_iter;
774 #else
775 	    gtk_text_freeze(GTK_TEXT(text));
776 	    gtk_text_set_point(GTK_TEXT(text), 0);
777 	    gtk_text_forward_delete(GTK_TEXT(text),
778 				    gtk_text_get_length(GTK_TEXT(text)));
779 	    gtk_text_thaw(GTK_TEXT(text));
780 #endif
781 	}
782 	break;
783 
784     case FILE_LIST_MESSAGE:
785 	{
786 	    gchar filename[255], *fnames[2];
787 	    gint i, number_of_files;
788 
789 	    /* reset the playing list : play from the start */
790 	    file_number_to_play = -1;
791 
792 	    gtk_pipe_int_read(&number_of_files);
793 	    for (i = 0; i < number_of_files; i++)
794 	    {
795 		gtk_pipe_string_read(filename);
796 		fnames[0] = filename;
797 		fnames[1] = NULL;
798 		gtk_clist_append(GTK_CLIST(clist), fnames);
799 	    }
800 	    gtk_clist_columns_autosize(GTK_CLIST(clist));
801 	}
802 	break;
803 
804     case NEXT_FILE_MESSAGE:
805     case PREV_FILE_MESSAGE:
806     case TUNE_END_MESSAGE:
807 	{
808 	    int nbfile;
809 
810 	    /* When a file ends, launch next if auto_next toggle */
811 	    if ( (message==TUNE_END_MESSAGE) &&
812 		 !GTK_CHECK_MENU_ITEM(auto_next)->active )
813 		return;
814 
815 	    /* Total number of file to play in the list */
816 	    nbfile = GTK_CLIST(clist)->rows;
817 
818 	    if (message == PREV_FILE_MESSAGE)
819 		file_number_to_play--;
820 	    else
821 		file_number_to_play++;
822 
823 	    /* Do nothing if requested file is before first one */
824 	    if (file_number_to_play < 0) {
825 		file_number_to_play = 0;
826 		return;
827 	    }
828 
829 	    /* Stop after playing the last file */
830 	    if (file_number_to_play >= nbfile) {
831 		file_number_to_play = nbfile - 1;
832 		return;
833 	    }
834 
835 	    if(gtk_clist_row_is_visible(GTK_CLIST(clist),
836 					file_number_to_play) !=
837 	       GTK_VISIBILITY_FULL) {
838 		gtk_clist_moveto(GTK_CLIST(clist), file_number_to_play,
839 				 -1, 1.0, 0.0);
840 	    }
841 	    gtk_clist_select_row(GTK_CLIST(clist), file_number_to_play, 0);
842 	}
843 	break;
844 
845     case CURTIME_MESSAGE:
846 	{
847 	    int		seconds, minutes;
848 	    int		nbvoice;
849 	    char	local_string[20];
850 
851 	    gtk_pipe_int_read(&seconds);
852 	    gtk_pipe_int_read(&nbvoice);
853 
854 	    if( is_quitting )
855 		return;
856 
857 	    minutes=seconds/60;
858 
859 	    sprintf(local_string,"%2d:%02d", minutes, (int)(seconds % 60));
860 
861 	    gtk_label_set(GTK_LABEL(cnt_lbl), local_string);
862 
863 	    /* Readjust the time scale if not dragging the scale */
864 	    if( !locating && (seconds <= max_sec)) {
865 		GtkAdjustment *adj;
866 
867 		adj = gtk_range_get_adjustment(GTK_RANGE(locator));
868 		my_adjustment_set_value(adj, (gfloat)seconds);
869 	    }
870 	}
871 	break;
872 
873     case NOTE_MESSAGE:
874 	{
875 	    int channel;
876 	    int note;
877 
878 	    gtk_pipe_int_read(&channel);
879 	    gtk_pipe_int_read(&note);
880 	    g_warning("NOTE chn%i %i", channel, note);
881 	}
882 	break;
883 
884     case PROGRAM_MESSAGE:
885 	{
886 	    int channel;
887 	    int pgm;
888 
889 	    gtk_pipe_int_read(&channel);
890 	    gtk_pipe_int_read(&pgm);
891 	    g_warning("NOTE chn%i %i", channel, pgm);
892 	}
893 	break;
894 
895     case VOLUME_MESSAGE:
896 	{
897 	    int channel;
898 	    int volume;
899 
900 	    gtk_pipe_int_read(&channel);
901 	    gtk_pipe_int_read(&volume);
902 	    g_warning("VOLUME= chn%i %i", channel, volume);
903 	}
904 	break;
905 
906 
907     case EXPRESSION_MESSAGE:
908 	{
909 	    int channel;
910 	    int express;
911 
912 	    gtk_pipe_int_read(&channel);
913 	    gtk_pipe_int_read(&express);
914 	    g_warning("EXPRESSION= chn%i %i", channel, express);
915 	}
916 	break;
917 
918     case PANNING_MESSAGE:
919 	{
920 	    int channel;
921 	    int pan;
922 
923 	    gtk_pipe_int_read(&channel);
924 	    gtk_pipe_int_read(&pan);
925 	    g_warning("PANNING= chn%i %i", channel, pan);
926 	}
927 	break;
928 
929     case SUSTAIN_MESSAGE:
930 	{
931 	    int channel;
932 	    int sust;
933 
934 	    gtk_pipe_int_read(&channel);
935 	    gtk_pipe_int_read(&sust);
936 	    g_warning("SUSTAIN= chn%i %i", channel, sust);
937 	}
938 	break;
939 
940     case PITCH_MESSAGE:
941 	{
942 	    int channel;
943 	    int bend;
944 
945 	    gtk_pipe_int_read(&channel);
946 	    gtk_pipe_int_read(&bend);
947 	    g_warning("PITCH BEND= chn%i %i", channel, bend);
948 	}
949 	break;
950 
951     case RESET_MESSAGE:
952 	g_warning("RESET_MESSAGE");
953 	break;
954 
955     case CLOSE_MESSAGE:
956 	gtk_exit(0);
957 	break;
958 
959     case CMSG_MESSAGE:
960 	{
961 	    int type;
962 	    char message[1000];
963 #ifdef HAVE_GTK_2
964 	    gchar *message_u8;
965 #endif
966 
967 	    gtk_pipe_int_read(&type);
968 	    gtk_pipe_string_read(message);
969 #ifdef HAVE_GTK_2
970 	    message_u8 = g_locale_to_utf8( message, -1, NULL, NULL, NULL );
971 	    gtk_text_buffer_get_bounds(textbuf, &start_iter, &end_iter);
972 	    gtk_text_buffer_insert(textbuf, &end_iter,
973 			    message_u8, -1);
974 	    gtk_text_buffer_insert(textbuf, &end_iter,
975 			    "\n", 1);
976 	    gtk_text_buffer_get_bounds(textbuf, &start_iter, &end_iter);
977 
978 	    mark = gtk_text_buffer_create_mark(textbuf, NULL, &end_iter, 1);
979 	    gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text), mark, 0.0, 0, 0.0, 1.0);
980 	    gtk_text_buffer_delete_mark(textbuf, mark);
981 	    g_free( message_u8 );
982 #else
983 	    gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
984 			    message, -1);
985 	    gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
986 			    "\n", 1);
987 #endif
988 	}
989 	break;
990     case LYRIC_MESSAGE:
991 	{
992 	    char message[1000];
993 #ifdef HAVE_GTK_2
994 	    gchar *message_u8;
995 #endif
996 
997 	    gtk_pipe_string_read(message);
998 
999 #ifdef HAVE_GTK_2
1000 	    message_u8 = g_locale_to_utf8( message, -1, NULL, NULL, NULL );
1001 	    gtk_text_buffer_get_bounds(textbuf, &start_iter, &end_iter);
1002 	    gtk_text_buffer_insert(textbuf, &iter,
1003 			    message_u8, -1);
1004 	    gtk_text_buffer_get_bounds(textbuf, &start_iter, &end_iter);
1005 
1006 	    mark = gtk_text_buffer_create_mark(textbuf, NULL, &end_iter, 1);
1007 	    gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text), mark, 0.0, 0, 0.0, 1.0);
1008 	    gtk_text_buffer_delete_mark(textbuf, mark);
1009 #else
1010 	    gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
1011 			    message, -1);
1012 #endif
1013 	}
1014 	break;
1015     default:
1016 	g_warning("UNKNOWN Gtk+ MESSAGE %i", message);
1017     }
1018 }
1019