1 /*
2  * Copyright (C) 2003 2004 2007 2009 2011, Magnus Hjorth
3  *
4  * This file is part of mhWaveEdit.
5  *
6  * mhWaveEdit 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  * mhWaveEdit 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 mhWaveEdit; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 
22 #include <config.h>
23 #include <gdk/gdkkeysyms.h>
24 
25 #include <string.h>
26 #include "effectbrowser.h"
27 #include "volumedialog.h"
28 #include "speeddialog.h"
29 #include "sampleratedialog.h"
30 #include "samplesizedialog.h"
31 #include "combinechannelsdialog.h"
32 #include "pipedialog.h"
33 #include "inifile.h"
34 #include "documentlist.h"
35 #include "um.h"
36 #include "gettext.h"
37 #include "mapchannelsdialog.h"
38 #include "sandwichdialog.h"
39 
40 struct source {
41      gchar tag;
42      gchar *name;
43      effect_register_rebuild_func rebuild_func;
44      effect_register_get_func get_func;
45      gpointer rebuild_func_data,get_func_data;
46      int is_new;
47 };
48 
49 struct effect {
50      gchar *name,*title,*location,*author,source_tag;
51      gboolean process_tag;
52 };
53 
54 static ListObject *effect_list = NULL;
55 static GSList *sources = NULL;
56 
57 static GSList *geometry_stack = NULL;
58 static gboolean geometry_stack_inited = FALSE;
59 
60 static GtkObjectClass *parent_class;
61 
62 static void list_widget_rebuild(gpointer dummy, gpointer dummy2,
63 				EffectBrowser *eb);
64 
effect_register_add_source(gchar * name,gchar tag,effect_register_rebuild_func rebuild_func,gpointer rebuild_func_data,effect_register_get_func get_func,gpointer get_func_data)65 void effect_register_add_source(gchar *name, gchar tag,
66 				effect_register_rebuild_func rebuild_func,
67 				gpointer rebuild_func_data,
68 				effect_register_get_func get_func,
69 				gpointer get_func_data)
70 {
71      struct source *s;
72      s = g_malloc(sizeof(*s));
73      s->tag = tag;
74      s->name = name;
75      s->rebuild_func = rebuild_func;
76      s->rebuild_func_data = rebuild_func_data;
77      s->get_func = get_func;
78      s->get_func_data = get_func_data;
79      s->is_new = TRUE;
80      sources = g_slist_append(sources,s);
81 }
82 
effect_register_add_effect(gchar source_tag,const gchar * name,const gchar * title,const gchar * author,const gchar * location)83 void effect_register_add_effect(gchar source_tag, const gchar *name,
84 				const gchar *title, const gchar *author,
85 				const gchar *location)
86 {
87      struct effect *e;
88      e = g_malloc(sizeof(*e));
89      e->source_tag = source_tag;
90      e->name = g_strdup(name);
91      e->title = g_strdup(title);
92      e->author = g_strdup(author);
93      e->location = g_strdup(location);
94      list_object_add(effect_list, e);
95 }
96 
builtin_rebuild_func(gchar source_tag,gpointer user_data)97 static void builtin_rebuild_func(gchar source_tag, gpointer user_data)
98 {
99      gchar *author = _("Built-in");
100      static const gchar loc[] = "";
101 
102      effect_register_add_effect(source_tag,"volume",_("Volume adjust/fade"),
103 				author,loc);
104      effect_register_add_effect(source_tag,"srate",_("Convert samplerate"),
105 				author,loc);
106      effect_register_add_effect(source_tag,"ssize",_("Convert sample format"),
107 				author,loc);
108      effect_register_add_effect(source_tag,"mapchannels",_("Map channels"),
109 				author,loc);
110      effect_register_add_effect(source_tag,"combine",_("Combine channels"),
111 				author,loc);
112      effect_register_add_effect(source_tag,"sandwich",
113 				_("Add channels from other file"),author,loc);
114      effect_register_add_effect(source_tag,"speed",_("Speed"),author,loc);
115      effect_register_add_effect(source_tag,"pipe",_("Pipe through program"),
116 				author,loc);
117 }
118 
builtin_get_func(gchar * name,gchar source_tag,gpointer user_data)119 static EffectDialog *builtin_get_func(gchar *name, gchar source_tag,
120 				      gpointer user_data)
121 {
122      GtkType type = -1;
123      if (!strcmp(name,"volume")) type = volume_dialog_get_type();
124      else if (!strcmp(name,"srate")) type = samplerate_dialog_get_type();
125      else if (!strcmp(name,"ssize")) type = samplesize_dialog_get_type();
126      else if (!strcmp(name,"mapchannels"))
127 	  type = map_channels_dialog_get_type();
128      else if (!strcmp(name,"combine"))
129 	  type = combine_channels_dialog_get_type();
130      else if (!strcmp(name,"speed")) type = speed_dialog_get_type();
131      else if (!strcmp(name,"pipe")) type = pipe_dialog_get_type();
132      else if (!strcmp(name,"sandwich")) type = sandwich_dialog_get_type();
133      if (type >= 0)
134 	  return EFFECT_DIALOG(gtk_type_new(type));
135      else
136 	  return NULL;
137 }
138 
effect_register_init(void)139 void effect_register_init(void)
140 {
141      /* Add built-in effects source */
142      effect_register_add_source("Built-in",'B',builtin_rebuild_func,NULL,
143 				builtin_get_func,NULL);
144 }
145 
effect_register_update_list(void)146 static void effect_register_update_list(void)
147 {
148      GSList *s;
149      struct source *src;
150      gboolean b = FALSE;
151 
152      if (effect_list == NULL)
153 	  effect_list = list_object_new(FALSE);
154 
155      for (s=sources; s!=NULL; s=s->next) {
156 	  src = (struct source *)s->data;
157 	  if (src -> is_new) {
158 	       /* TODO: Cache instead of requesting from source each time */
159 	       src->rebuild_func(src->tag, src->rebuild_func_data);
160 	       src->is_new = FALSE;
161 	       b = TRUE;
162 	  }
163      }
164 
165      if (b) list_object_notify(effect_list,NULL);
166 }
167 
free_effect(struct effect * e)168 void free_effect(struct effect *e)
169 {
170      g_free(e->name);
171      g_free(e->title);
172      g_free(e->location);
173      g_free(e->author);
174      g_free(e);
175 }
176 
effect_register_rebuild(void)177 void effect_register_rebuild(void)
178 {
179      GSList *s;
180      struct source *src;
181      list_object_foreach(effect_list,(GFunc)free_effect,NULL);
182      list_object_clear(effect_list,FALSE);
183      for (s=sources; s!=NULL; s=s->next) {
184 	  src = (struct source *)s->data;
185 	  src->is_new = TRUE;
186      }
187      effect_register_update_list();
188 }
189 
effect_browser_remove_effect(EffectBrowser * eb)190 static void effect_browser_remove_effect(EffectBrowser *eb)
191 {
192      if (eb->current_dialog >= 0)
193 	  gtk_container_remove
194 	       (GTK_CONTAINER(eb->dialog_container),
195 		GTK_WIDGET(eb->dialogs[eb->current_dialog]));
196      eb->current_dialog = -1;
197 }
198 
effect_browser_destroy(GtkObject * obj)199 static void effect_browser_destroy(GtkObject *obj)
200 {
201      EffectBrowser *eb = EFFECT_BROWSER(obj);
202      guint i;
203      effect_browser_remove_effect(eb);
204      for (i=0; i<EFFECT_BROWSER_CACHE_SIZE; i++) {
205 	  if (eb->dialogs[i] != NULL) {
206 	       gtk_widget_unref(GTK_WIDGET(eb->dialogs[i]));
207 	       eb->dialogs[i] = NULL;
208 	  }
209      }
210      if (parent_class->destroy) parent_class->destroy(obj);
211 }
212 
geom_push(EffectBrowser * eb)213 static void geom_push(EffectBrowser *eb)
214 {
215      gchar *c;
216      guint pos;
217      /* This seems to be the only way to find out handle position */
218      pos = GTK_WIDGET(eb->effect_list_container)->allocation.width;
219      c = g_strdup_printf("%d",pos);
220      geometry_stack_push(GTK_WINDOW(eb),c,&geometry_stack);
221      g_free(c);
222 }
223 
effect_browser_delete_event(GtkWidget * widget,GdkEventAny * event)224 static gint effect_browser_delete_event(GtkWidget *widget, GdkEventAny *event)
225 {
226      geom_push(EFFECT_BROWSER(widget));
227      if (GTK_WIDGET_CLASS(parent_class)->delete_event)
228 	  return GTK_WIDGET_CLASS(parent_class)->delete_event(widget,event);
229      else
230 	  return FALSE;
231 }
232 
effect_browser_class_init(GtkObjectClass * klass)233 static void effect_browser_class_init(GtkObjectClass *klass)
234 {
235      parent_class = gtk_type_class(gtk_window_get_type());
236      klass->destroy = effect_browser_destroy;
237      GTK_WIDGET_CLASS(klass)->delete_event = effect_browser_delete_event;
238 }
239 
effect_browser_close(EffectBrowser * eb)240 static void effect_browser_close(EffectBrowser *eb)
241 {
242      geom_push(eb);
243      gtk_widget_destroy(GTK_WIDGET(eb));
244 }
245 
apply_click(GtkWidget * widget,EffectBrowser * eb)246 static void apply_click(GtkWidget *widget, EffectBrowser *eb)
247 {
248      gboolean ca,b,mwf;
249      if (eb->dl->selected == NULL) {
250 	  user_error(_("You have no open file to apply the effect to!"));
251 	  return;
252      }
253 
254      ca = gtk_toggle_button_get_active(eb->close_after);
255      mwf = inifile_get_gboolean("mainwinFront",TRUE);
256 
257      if (ca) gtk_widget_hide(GTK_WIDGET(eb));
258 
259      b = effect_dialog_apply(eb->dialogs[eb->current_dialog]);
260 
261      if (ca) {
262 	  if (b)
263 	       gtk_widget_show(GTK_WIDGET(eb));
264 	  else
265 	       effect_browser_close(eb);
266      } else if (!mwf)
267 	  gdk_window_raise(GTK_WIDGET(eb)->window);
268 }
269 
get_effect_missing_dialog(gchar * name,gchar source_tag)270 static EffectDialog *get_effect_missing_dialog(gchar *name, gchar source_tag)
271 {
272      EffectDialog *ed;
273      GtkWidget *w;
274      ed = gtk_type_new(effect_dialog_get_type());
275      w = gtk_label_new(_("This effect could not be loaded."));
276      gtk_container_add(ed->input_area,w);
277      gtk_widget_show(w);
278      return ed;
279 }
280 
effect_browser_set_effect_main(EffectBrowser * eb,struct effect * e)281 static void effect_browser_set_effect_main(EffectBrowser *eb, struct effect *e)
282 {
283      int i;
284      EffectDialog *ed;
285      GSList *s;
286      struct source *src;
287      gchar *c;
288 
289      effect_browser_remove_effect(eb);
290 
291      /* Check dialog cache */
292      for (i=0; i<EFFECT_BROWSER_CACHE_SIZE; i++) {
293 	  if (eb->dialog_effects[i] == e) break;
294      }
295 
296      if (i >= EFFECT_BROWSER_CACHE_SIZE) {
297 	  /* Dialog not in cache */
298 
299 	  /* Make room in top of cache */
300 	  for (i=0; i<EFFECT_BROWSER_CACHE_SIZE; i++) {
301 	       if (eb->dialog_effects[i] == NULL) break;
302 	  }
303 	  if (i >= EFFECT_BROWSER_CACHE_SIZE) {
304 	       /* No room in cache, throw out last element */
305 	       i = EFFECT_BROWSER_CACHE_SIZE-1;
306 	       gtk_object_unref(GTK_OBJECT(eb->dialogs[i]));
307 	       eb->dialogs[i] = NULL;
308 	       eb->dialog_effects[i] = NULL;
309 	  }
310 	  for (; i>0; i--) {
311 	       eb->dialogs[i] = eb->dialogs[i-1];
312 	       eb->dialog_effects[i] = eb->dialog_effects[i-1];
313 	  }
314 
315 	  /* Get the new dialog */
316 
317 	  ed = NULL;
318 	  for (s=sources; s!=NULL; s=s->next) {
319 	       src = (struct source *)s->data;
320 	       if (src->tag == e->source_tag) {
321 		    ed = src->get_func(e->name, e->source_tag,
322 				       src->get_func_data);
323 		    effect_dialog_setup(ed, e->name, eb);
324 		    break;
325 	       }
326 	  }
327 
328 	  if (ed == NULL)
329 	       ed = get_effect_missing_dialog(e->name,e->source_tag);
330 
331 	  g_assert(i == 0);
332 	  eb->dialogs[i] = ed;
333 	  gtk_object_ref(GTK_OBJECT(ed));
334 	  gtk_object_sink(GTK_OBJECT(ed));
335 	  eb->dialog_effects[i] = e;
336      }
337 
338      eb->current_dialog = i;
339 
340      gtk_container_add(eb->dialog_container,
341 		       GTK_WIDGET(eb->dialogs[i]));
342      gtk_widget_show(GTK_WIDGET(eb->dialogs[i]));
343 
344      c = g_strdup_printf("%c%s",e->source_tag,e->name);
345      inifile_set("lastEffect",c);
346      g_free(c);
347 }
348 
effect_browser_invalidate_effect(EffectBrowser * eb,gchar * effect_name,gchar source_tag)349 void effect_browser_invalidate_effect(EffectBrowser *eb, gchar *effect_name,
350 				      gchar source_tag)
351 {
352      gboolean displayed = FALSE;
353      struct effect *e;
354      gint i=0;
355 
356      /* Search the cache for the effect */
357      for (i=0; i<EFFECT_BROWSER_CACHE_SIZE; i++) {
358 	  e = eb->dialog_effects[i];
359 	  if (e != NULL && e->source_tag == source_tag &&
360 	      !strcmp(e->name, effect_name))
361 	       break;
362      }
363 
364      if (i >= EFFECT_BROWSER_CACHE_SIZE) return; /* Not found */
365 
366      displayed = (i == eb->current_dialog);
367      if (displayed) effect_browser_remove_effect(eb);
368      gtk_object_unref(GTK_OBJECT(eb->dialogs[i]));
369      eb->dialogs[i] = NULL;
370      eb->dialog_effects[i] = NULL;
371      if (displayed) effect_browser_set_effect_main(eb,e);
372 }
373 
effect_browser_select_child(GtkList * list,GtkWidget * widget,gpointer user_data)374 static void effect_browser_select_child(GtkList *list, GtkWidget *widget,
375 					gpointer user_data)
376 {
377      EffectBrowser *eb = EFFECT_BROWSER(user_data);
378      struct effect *effect;
379 
380      effect = gtk_object_get_data(GTK_OBJECT(widget),"effectptr");
381      g_assert(effect != NULL);
382      effect_browser_set_effect_main(eb,effect);
383      eb->list_widget_sel = GTK_LIST_ITEM(widget);
384 }
385 
save_effect_order(EffectBrowser * eb)386 static void save_effect_order(EffectBrowser *eb)
387 {
388      GList *l;
389      gint i;
390      gchar *c,*d;
391      struct effect *effect;
392      l = gtk_container_get_children(GTK_CONTAINER(eb->list_widget));
393      for (i=0; l!=NULL; l=l->next,i++) {
394 	  c = g_strdup_printf("effectBrowserOrder%d",i);
395 	  effect = gtk_object_get_data(GTK_OBJECT(l->data),"effectptr");
396 	  d = g_strdup_printf("%c%s",effect->source_tag,effect->name);
397 	  inifile_set(c,d);
398 	  g_free(c);
399 	  g_free(d);
400      }
401      c = g_strdup_printf("effectBrowserOrder%d",i);
402      inifile_set(c,NULL);
403      g_free(c);
404      g_list_free(l);
405 }
406 
moveup_main(EffectBrowser * eb,GtkListItem * item)407 static void moveup_main(EffectBrowser *eb, GtkListItem *item)
408 {
409      gint i;
410      GList *l;
411      i = gtk_list_child_position(GTK_LIST(eb->list_widget),
412 				 GTK_WIDGET(item));
413      if (i <= 0) return;
414      l = g_list_append(NULL, item);
415      gtk_list_remove_items_no_unref(GTK_LIST(eb->list_widget),l);
416      gtk_list_insert_items(GTK_LIST(eb->list_widget),l,i-1);
417      gtk_list_item_select(GTK_LIST_ITEM(eb->list_widget_sel));
418      save_effect_order(eb);
419 }
420 
movedown_main(EffectBrowser * eb,GtkListItem * item)421 static void movedown_main(EffectBrowser *eb, GtkListItem *item)
422 {
423      gint i;
424      GList *l;
425      i = gtk_list_child_position(GTK_LIST(eb->list_widget),
426 				 GTK_WIDGET(item));
427      l = g_list_append(NULL, item);
428      gtk_list_remove_items_no_unref(GTK_LIST(eb->list_widget),l);
429      gtk_list_insert_items(GTK_LIST(eb->list_widget),l,i+1);
430      gtk_list_item_select(GTK_LIST_ITEM(eb->list_widget_sel));
431      save_effect_order(eb);
432 }
433 
movetop_main(EffectBrowser * eb,GtkListItem * item)434 static void movetop_main(EffectBrowser *eb, GtkListItem *item)
435 {
436      GList *l;
437      l = g_list_append(NULL, item);
438      gtk_list_remove_items_no_unref(GTK_LIST(eb->list_widget),l);
439      gtk_list_prepend_items(GTK_LIST(eb->list_widget),l);
440      gtk_list_item_select(GTK_LIST_ITEM(eb->list_widget_sel));
441      save_effect_order(eb);
442 }
443 
movebot_main(EffectBrowser * eb,GtkListItem * item)444 static void movebot_main(EffectBrowser *eb, GtkListItem *item)
445 {
446      GList *l;
447      l = g_list_append(NULL, item);
448      gtk_list_remove_items_no_unref(GTK_LIST(eb->list_widget),l);
449      gtk_list_append_items(GTK_LIST(eb->list_widget),l);
450      gtk_list_item_select(GTK_LIST_ITEM(eb->list_widget_sel));
451      save_effect_order(eb);
452 }
453 
454 static EffectBrowser *clicked_eb;
455 
list_item_moveup(GtkMenuItem * menuitem,gpointer user_data)456 static void list_item_moveup(GtkMenuItem *menuitem, gpointer user_data)
457 {
458      moveup_main(clicked_eb, clicked_eb->list_widget_clicked);
459 }
460 
list_item_movedown(GtkMenuItem * menuitem,gpointer user_data)461 static void list_item_movedown(GtkMenuItem *menuitem, gpointer user_data)
462 {
463      movedown_main(clicked_eb, clicked_eb->list_widget_clicked);
464 }
465 
list_item_movetotop(GtkMenuItem * menuitem,gpointer user_data)466 static void list_item_movetotop(GtkMenuItem *menuitem, gpointer user_data)
467 {
468      movetop_main(clicked_eb, clicked_eb->list_widget_clicked);
469 }
470 
list_item_movetobottom(GtkMenuItem * menuitem,gpointer user_data)471 static void list_item_movetobottom(GtkMenuItem *menuitem, gpointer user_data)
472 {
473      movebot_main(clicked_eb, clicked_eb->list_widget_clicked);
474 }
475 
list_item_sort_main(EffectBrowser * eb,GCompareFunc compfunc)476 static void list_item_sort_main(EffectBrowser *eb, GCompareFunc compfunc)
477 {
478      /* Not the quickest way, but preserves original order if compfunc
479 	returns >0 when objects are equal */
480      GList *k,*l,*m=NULL;
481      gint i;
482      struct effect *e;
483      gchar *c,*d;
484      k = gtk_container_get_children(GTK_CONTAINER(eb->list_widget));
485      for (l=k; l!=NULL; l=l->next) {
486 	  e = gtk_object_get_data(GTK_OBJECT(l->data),"effectptr");
487 	  g_assert(e != NULL);
488 	  m = g_list_insert_sorted(m,e,compfunc);
489      }
490      g_list_free(k);
491      for (l=m,i=0; l!=NULL; l=l->next,i++) {
492 	  e = (struct effect *)l->data;
493 	  c = g_strdup_printf("effectBrowserOrder%d",i);
494 	  d = g_strdup_printf("%c%s",e->source_tag,e->name);
495 	  inifile_set(c,d);
496 	  g_free(d);
497 	  g_free(c);
498      }
499      c = g_strdup_printf("effectBrowserOrder%d",i);
500      inifile_set(c,NULL);
501      g_free(c);
502      g_list_free(m);
503      list_widget_rebuild(NULL,NULL,eb);
504 }
505 
title_sort_func(gconstpointer a,gconstpointer b)506 gint title_sort_func(gconstpointer a, gconstpointer b)
507 {
508      struct effect const *ae = a, *be = b;
509      int i;
510      i = strcmp(ae->title,be->title);
511      if (i==0) return 1; else return i;
512 }
513 
auth_sort_func(gconstpointer a,gconstpointer b)514 gint auth_sort_func(gconstpointer a, gconstpointer b)
515 {
516      struct effect const *ae = a, *be = b;
517      int i;
518      i = strcmp(ae->author,be->author);
519      if (i==0) return 1; else return i;
520 }
521 
type_sort_func(gconstpointer a,gconstpointer b)522 gint type_sort_func(gconstpointer a, gconstpointer b)
523 {
524      struct effect const *ae = a, *be = b;
525      int i;
526      i = ae->source_tag - be->source_tag;
527      if (i==0) return 1; else return i;
528 }
529 
loc_sort_func(gconstpointer a,gconstpointer b)530 gint loc_sort_func(gconstpointer a, gconstpointer b)
531 {
532      struct effect const *ae = a, *be = b;
533      int i;
534      i = strcmp(ae->location,be->location);
535      if (i==0) return 1; else return i;
536 }
537 
538 
list_item_sortbytitle(GtkMenuItem * menuitem,gpointer user_data)539 static void list_item_sortbytitle(GtkMenuItem *menuitem, gpointer user_data)
540 {
541      list_item_sort_main(clicked_eb, title_sort_func);
542 }
543 
list_item_sortbytype(GtkMenuItem * menuitem,gpointer user_data)544 static void list_item_sortbytype(GtkMenuItem *menuitem, gpointer user_data)
545 {
546      list_item_sort_main(clicked_eb, type_sort_func);
547 }
548 
list_item_sortbyloc(GtkMenuItem * menuitem,gpointer user_data)549 static void list_item_sortbyloc(GtkMenuItem *menuitem, gpointer user_data)
550 {
551      list_item_sort_main(clicked_eb, loc_sort_func);
552 }
553 
list_item_sortbyauth(GtkMenuItem * menuitem,gpointer user_data)554 static void list_item_sortbyauth(GtkMenuItem *menuitem, gpointer user_data)
555 {
556      list_item_sort_main(clicked_eb, auth_sort_func);
557 }
558 
list_item_unsort(GtkMenuItem * menuitem,gpointer user_data)559 static void list_item_unsort(GtkMenuItem *menuitem, gpointer user_data)
560 {
561      inifile_set("effectBrowserOrder0",NULL);
562      list_widget_rebuild(NULL,NULL,clicked_eb);
563 }
564 
list_item_rebuild(GtkMenuItem * menuitem,gpointer user_data)565 static void list_item_rebuild(GtkMenuItem *menuitem, gpointer user_data)
566 {
567      effect_register_rebuild();
568 }
569 
translate_menu_path(const gchar * path,gpointer func_data)570 static gchar *translate_menu_path(const gchar *path, gpointer func_data)
571 {
572     return _(path);
573 }
574 
list_item_button_press(GtkWidget * widget,GdkEventButton * event,gpointer user_data)575 static gint list_item_button_press(GtkWidget *widget, GdkEventButton *event,
576 				   gpointer user_data)
577 {
578      EffectBrowser *eb = EFFECT_BROWSER(user_data);
579      static GtkItemFactory *item_factory = NULL;
580      GtkWidget *w;
581      static GtkItemFactoryEntry menu_items[] = {
582 	  { N_("/Move Up"),        NULL, list_item_moveup,      0, NULL },
583 	  { N_("/Move Down"),      NULL, list_item_movedown,    0, NULL },
584 	  { N_("/Move to Top"),    NULL, list_item_movetotop,   0, NULL },
585 	  { N_("/Move to Bottom"), NULL, list_item_movetobottom,0, NULL },
586 	  { "/sep1",           NULL, NULL,                  0, "<Separator>" },
587 	  { N_("/Sort by Name"), NULL, list_item_sortbytitle, 0, NULL },
588 	  { N_("/Sort by Type"), NULL, list_item_sortbytype, 0, NULL },
589 	  { N_("/Sort by Location"), NULL, list_item_sortbyloc,0,NULL },
590 	  { N_("/Sort by Author"), NULL, list_item_sortbyauth,0, NULL },
591 	  { "/sep2", NULL, NULL, 0, "<Separator>" },
592 	  { N_("/Restore Order"), NULL, list_item_unsort, 0, NULL },
593 	  { N_("/Rebuild Effect List"), NULL, list_item_rebuild,0,NULL }
594      };
595 
596      if (event->button == 3) {
597 	  if (item_factory == NULL) {
598 	       item_factory = gtk_item_factory_new(GTK_TYPE_MENU,"<popup>",NULL);
599 #ifdef ENABLE_NLS
600 	       gtk_item_factory_set_translate_func(item_factory,
601 						   translate_menu_path, NULL, NULL);
602 #endif
603 	       gtk_item_factory_create_items(item_factory,
604 					     ARRAY_LENGTH(menu_items),
605 					     menu_items,NULL);
606 	  }
607 	  clicked_eb = eb;
608 	  eb->list_widget_clicked = GTK_LIST_ITEM(widget);
609 	  w = gtk_item_factory_get_widget(item_factory,"<popup>");
610 	  gtk_menu_popup(GTK_MENU(w),NULL,NULL,NULL,NULL,event->button,
611 			 event->time);
612      }
613      return FALSE;
614 }
615 
add_list_item_main(struct effect * e,GtkList * l,EffectBrowser * eb)616 static void add_list_item_main(struct effect *e, GtkList *l, EffectBrowser *eb)
617 {
618      gchar *c,*d;
619      GtkWidget *w;
620      c = g_strdup_printf("[%c] %s",e->source_tag,e->title);
621 
622      /* Translate here for keeping compatibility with old translations */
623      /* New translations should translate the title without the prefix */
624      if (e->source_tag == 'B' || e->source_tag == 'S') d = _(c); else d = c;
625 
626      w = gtk_list_item_new_with_label(d);
627      g_free(c);
628      gtk_object_set_data(GTK_OBJECT(w),"effectptr",e);
629      gtk_signal_connect(GTK_OBJECT(w),"button_press_event",
630 			GTK_SIGNAL_FUNC(list_item_button_press),eb);
631      gtk_container_add(GTK_CONTAINER(l),w);
632      gtk_widget_show(w);
633 }
634 
add_list_item(gpointer item,gpointer user_data)635 static void add_list_item(gpointer item, gpointer user_data)
636 {
637      EffectBrowser *eb = EFFECT_BROWSER(user_data);
638      struct effect *e = (struct effect *)item;
639      add_list_item_main(e,eb->list_widget,eb);
640 }
641 
top_click(GtkButton * button,gpointer user_data)642 static void top_click(GtkButton *button, gpointer user_data)
643 {
644      EffectBrowser *eb = EFFECT_BROWSER(user_data);
645      movetop_main(eb,eb->list_widget_sel);
646 }
647 
bottom_click(GtkButton * button,gpointer user_data)648 static void bottom_click(GtkButton *button, gpointer user_data)
649 {
650      EffectBrowser *eb = EFFECT_BROWSER(user_data);
651      movebot_main(eb,eb->list_widget_sel);
652 }
653 
up_click(GtkButton * button,gpointer user_data)654 static void up_click(GtkButton *button, gpointer user_data)
655 {
656      EffectBrowser *eb = EFFECT_BROWSER(user_data);
657      moveup_main(eb, eb->list_widget_sel);
658 }
659 
down_click(GtkButton * button,gpointer user_data)660 static void down_click(GtkButton *button, gpointer user_data)
661 {
662      EffectBrowser *eb = EFFECT_BROWSER(user_data);
663      movedown_main(eb, eb->list_widget_sel);
664 }
665 
add_list_widget_items(GtkList * list,EffectBrowser * eb)666 static void add_list_widget_items(GtkList *list, EffectBrowser *eb)
667 {
668      gint i;
669      gchar *c,*d;
670      GList *l;
671      struct effect *e;
672      if (inifile_get("effectBrowserOrder0",NULL) == NULL) {
673 	  list_object_foreach(effect_list,add_list_item,eb);
674      } else {
675 	  for (l=effect_list->list; l!=NULL; l=l->next) {
676 	       e = (struct effect *)l->data;
677 	       e->process_tag = FALSE;
678 	  }
679 	  for (i=0; ; i++) {
680 	       c = g_strdup_printf("effectBrowserOrder%d",i);
681 	       d = inifile_get(c,NULL);
682 	       g_free(c);
683 	       if (d == NULL) break;
684 	       for (l=effect_list->list; l!=NULL; l=l->next) {
685 		    e = (struct effect *)l->data;
686 		    if (e->process_tag) continue;
687 		    if (e->source_tag != d[0] || strcmp(e->name,d+1)) continue;
688 		    add_list_item_main(e,list,eb);
689 		    e->process_tag = TRUE;
690 		    break;
691 	       }
692 	  }
693 	  for (l=effect_list->list; l!=NULL; l=l->next) {
694 	       e = (struct effect *)l->data;
695 	       if (!e->process_tag)
696 		    add_list_item_main(e,list,eb);
697 	  }
698      }
699 }
700 
list_widget_rebuild(gpointer dummy,gpointer dummy2,EffectBrowser * eb)701 static void list_widget_rebuild(gpointer dummy, gpointer dummy2,
702 				EffectBrowser *eb)
703 {
704      gtk_list_clear_items(eb->list_widget,0,-1);
705      add_list_widget_items(eb->list_widget, eb);
706 }
707 
effect_browser_init(EffectBrowser * eb)708 static void effect_browser_init(EffectBrowser *eb)
709 {
710      GtkWidget *b,*b1,*b11,*b11w,*b12,*b121,*b122,*b123,*b124,*b2,*b21;
711      GtkWidget *b21w,*b22,*b23,*b24,*b25,*b251,*b252;
712      GtkAccelGroup* ag;
713      gchar *c,*d;
714      gint x;
715 
716      eb->list_widget_sel = NULL;
717 
718      ag = gtk_accel_group_new();
719 
720      memset(eb->dialogs,0,sizeof(eb->dialogs));
721      memset(eb->dialog_effects,0,sizeof(eb->dialog_effects));
722      eb->current_dialog = -1;
723 
724      b11w = gtk_list_new();
725      eb->list_widget = GTK_LIST(b11w);
726      gtk_list_set_selection_mode(GTK_LIST(b11w),GTK_SELECTION_SINGLE);
727 
728      effect_register_update_list();
729      add_list_widget_items(eb->list_widget,eb);
730 
731      gtk_signal_connect(GTK_OBJECT(effect_list),"item-notify",
732 			GTK_SIGNAL_FUNC(list_widget_rebuild),eb);
733 
734      b11 = gtk_scrolled_window_new(NULL,NULL);
735      gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(b11),
736 				    GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
737      gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(b11),b11w);
738      gtk_widget_set_usize(GTK_WIDGET(b11),150,150);
739      eb->effect_list_container = GTK_CONTAINER(b11);
740 
741 #ifdef GTK_STOCK_GOTO_TOP
742      b121 = gtk_button_new_from_stock(GTK_STOCK_GOTO_TOP);
743 #else
744      b121 = gtk_button_new_with_label(_("Top"));
745 #endif
746      gtk_signal_connect(GTK_OBJECT(b121),"clicked",
747 			GTK_SIGNAL_FUNC(top_click),eb);
748 #ifdef GTK_STOCK_GO_UP
749      b122 = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
750 #else
751      b122 = gtk_button_new_with_label(_("Up"));
752 #endif
753      gtk_signal_connect(GTK_OBJECT(b122),"clicked",
754 			GTK_SIGNAL_FUNC(up_click),eb);
755 #ifdef GTK_STOCK_GO_DOWN
756      b123 = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
757 #else
758      b123 = gtk_button_new_with_label(_("Down"));
759 #endif
760      gtk_signal_connect(GTK_OBJECT(b123),"clicked",
761 			GTK_SIGNAL_FUNC(down_click),eb);
762 #ifdef GTK_STOCK_GOTO_BOTTOM
763      b124 = gtk_button_new_from_stock(GTK_STOCK_GOTO_BOTTOM);
764 #else
765      b124 = gtk_button_new_with_label(_("Bottom"));
766 #endif
767      gtk_signal_connect(GTK_OBJECT(b124),"clicked",
768 			GTK_SIGNAL_FUNC(bottom_click),eb);
769 
770      b12 = gtk_hbox_new(FALSE,5);
771      gtk_box_pack_start(GTK_BOX(b12),b121,FALSE,FALSE,0);
772      gtk_box_pack_start(GTK_BOX(b12),b122,FALSE,FALSE,0);
773      gtk_box_pack_start(GTK_BOX(b12),b123,FALSE,FALSE,0);
774      gtk_box_pack_start(GTK_BOX(b12),b124,FALSE,FALSE,0);
775 
776      b1 = gtk_vbox_new(FALSE,5);
777      gtk_box_pack_start(GTK_BOX(b1),b11,TRUE,TRUE,0);
778      gtk_box_pack_start(GTK_BOX(b1),b12,FALSE,FALSE,0);
779 
780      b21w = gtk_alignment_new(0.5,0.5,1.0,1.0);
781      eb->dialog_container = GTK_CONTAINER(b21w);
782 
783      b21 = gtk_scrolled_window_new(NULL,NULL);
784      gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(b21),
785 				    GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
786      gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(b21),b21w);
787 
788      b22 = gtk_hseparator_new();
789 
790      b23 = gtk_hbox_new(FALSE,3);
791      eb->mw_list_box = GTK_BOX(b23);
792 
793      b24 = gtk_check_button_new_with_label(_("Close dialog after applying "
794 					     "effect"));
795      eb->close_after = GTK_TOGGLE_BUTTON(b24);
796 
797 #ifdef GTK_STOCK_APPLY
798      b251 = gtk_button_new_from_stock(GTK_STOCK_APPLY);
799 #else
800      b251 = gtk_button_new_with_label(_("Apply"));
801 #endif
802      gtk_signal_connect(GTK_OBJECT(b251),"clicked",(GtkSignalFunc)apply_click,
803 			eb);
804 
805 #ifdef GTK_STOCK_CLOSE
806      b252 = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
807 #else
808      b252 = gtk_button_new_with_label(_("Close"));
809 #endif
810      gtk_widget_add_accelerator (b252, "clicked", ag, GDK_Escape, 0, (GtkAccelFlags) 0);
811      gtk_signal_connect_object(GTK_OBJECT(b252),"clicked",
812 			       (GtkSignalFunc)effect_browser_close,
813 			       GTK_OBJECT(eb));
814 
815      b25 = gtk_hbutton_box_new();
816      gtk_box_pack_start(GTK_BOX(b25),b251,FALSE,TRUE,3);
817      gtk_box_pack_start(GTK_BOX(b25),b252,FALSE,TRUE,3);
818 
819      b2 = gtk_vbox_new(FALSE,5);
820      gtk_box_pack_start(GTK_BOX(b2),b21,TRUE,TRUE,0);
821      gtk_box_pack_end(GTK_BOX(b2),b25,FALSE,FALSE,0);
822      gtk_box_pack_end(GTK_BOX(b2),b24,FALSE,FALSE,0);
823      gtk_box_pack_end(GTK_BOX(b2),b23,FALSE,TRUE,0);
824      gtk_box_pack_end(GTK_BOX(b2),b22,FALSE,TRUE,0);
825 
826      b = gtk_hpaned_new();
827      gtk_paned_pack1(GTK_PANED(b),b1,FALSE,TRUE);
828      gtk_paned_pack2(GTK_PANED(b),b2,TRUE,TRUE);
829 
830      gtk_window_set_title(GTK_WINDOW(eb),_("Effects"));
831      gtk_window_add_accel_group(GTK_WINDOW (eb), ag);
832      gtk_window_set_policy(GTK_WINDOW(eb),FALSE,TRUE,FALSE);
833 
834      if (!geometry_stack_inited) {
835 	  if (inifile_get_gboolean("useGeometry",FALSE))
836 	       geometry_stack = geometry_stack_from_inifile("effectGeometry");
837 	  geometry_stack_inited = TRUE;
838      }
839      if (!geometry_stack_pop(&geometry_stack,&c,GTK_WINDOW(eb))) {
840 	 gtk_window_set_position (GTK_WINDOW (eb), GTK_WIN_POS_CENTER);
841 	 gtk_window_set_default_size(GTK_WINDOW(eb),600,300);
842      } else {
843 	  if (c != NULL) {
844 	       x = strtoul(c,&d,10);
845 	       if (*d == 0 && *c != 0)
846 		    gtk_paned_set_position(GTK_PANED(b),x);
847 	       g_free(c);
848 	  }
849      }
850      gtk_container_set_border_width(GTK_CONTAINER(eb),5);
851      gtk_container_add(GTK_CONTAINER(eb),b);
852      GTK_WIDGET_SET_FLAGS(GTK_WIDGET(b251),GTK_CAN_DEFAULT);
853      gtk_widget_grab_default(GTK_WIDGET(b251));
854      gtk_widget_show_all(b);
855 }
856 
effect_browser_get_type(void)857 GtkType effect_browser_get_type(void)
858 {
859      static GtkType id=0;
860      if (!id) {
861 	  GtkTypeInfo info = {
862 	       "EffectBrowser",
863 	       sizeof(EffectBrowser),
864 	       sizeof(EffectBrowserClass),
865 	       (GtkClassInitFunc)effect_browser_class_init,
866 	       (GtkObjectInitFunc)effect_browser_init
867 	  };
868 	  id = gtk_type_unique(gtk_window_get_type(),&info);
869      }
870      return id;
871 }
872 
effect_browser_new(Document * doc)873 GtkWidget *effect_browser_new(Document *doc)
874 {
875      return effect_browser_new_with_effect(doc,"volume",'B',FALSE);
876 }
877 
effect_browser_new_with_effect(Document * doc,gchar * effect,gchar source_tag,gboolean close_after)878 GtkWidget *effect_browser_new_with_effect(Document *doc, gchar *effect,
879 					  gchar source_tag,
880 					  gboolean close_after)
881 {
882      GtkWidget *w;
883      EffectBrowser *eb =
884 	  EFFECT_BROWSER(gtk_type_new(effect_browser_get_type()));
885      gtk_signal_connect(GTK_OBJECT(eb->list_widget),"select_child",
886 			(GtkSignalFunc)effect_browser_select_child,eb);
887 
888      w = document_list_new(doc);
889      gtk_box_pack_end(GTK_BOX(eb->mw_list_box),w,TRUE,TRUE,0);
890      gtk_widget_show(w);
891      eb->dl = DOCUMENT_LIST(w);
892      w = gtk_label_new(_("Apply to: "));
893      gtk_box_pack_end(GTK_BOX(eb->mw_list_box),w,FALSE,FALSE,0);
894      gtk_widget_show(w);
895 
896      if (effect == NULL) {
897 	  effect = inifile_get("lastEffect","Bvolume");
898 	  source_tag = effect[0];
899 	  effect++;
900      }
901      effect_browser_set_effect(eb,effect,source_tag);
902      if (eb->current_dialog < 0) effect_browser_set_effect(eb,"volume",'B');
903      g_assert(eb->current_dialog >= 0);
904      gtk_toggle_button_set_active(eb->close_after,close_after);
905      return GTK_WIDGET(eb);
906 }
907 
effect_browser_set_effect(EffectBrowser * eb,gchar * effect,gchar source_tag)908 void effect_browser_set_effect(EffectBrowser *eb, gchar *effect,
909 			       gchar source_tag)
910 {
911      struct effect *e;
912      GList *l,*w;
913      gpointer p;
914 
915      for (l=effect_list->list; l!=NULL; l=l->next) {
916 	  e = (struct effect *)l->data;
917 	  if (e->source_tag == source_tag && !strcmp(e->name, effect)) {
918 	       /* Find the list item which points to this effect */
919 	       w = gtk_container_get_children(GTK_CONTAINER(eb->list_widget));
920 	       for (; w!=NULL; w=w->next) {
921 		    p = gtk_object_get_data(GTK_OBJECT(w->data),"effectptr");
922 		    g_assert(p != NULL);
923 		    if (p == e) {
924 			 gtk_list_select_child(eb->list_widget,
925 					       GTK_WIDGET(w->data));
926 			 return;
927 		    }
928 	       }
929 	       /* Effect exists but not in list, shouldn't happen */
930 	       g_assert_not_reached();
931 	  }
932      }
933      /* Effect doesn't exist - do nothing */
934 }
935 
effect_browser_shutdown(void)936 void effect_browser_shutdown(void)
937 {
938      if (inifile_get_gboolean("useGeometry",FALSE))
939 	  geometry_stack_save_to_inifile("effectGeometry",geometry_stack);
940 }
941 
942