]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/toolbar-editor/egg-toolbar-editor.c
Make frontends depend on just libev. Rework and group CFLAGS/LIBS
[evince.git] / cut-n-paste / toolbar-editor / egg-toolbar-editor.c
1 /*
2  *  Copyright (C) 2003 Marco Pesenti Gritti
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2, or (at your option)
7  *  any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  *  $Id$
19  */
20
21 #include "config.h"
22
23 #include "egg-toolbar-editor.h"
24 #include "egg-editable-toolbar.h"
25 #include "eggintl.h"
26
27 #include <string.h>
28 #include <libxml/tree.h>
29 #include <gtk/gtkimage.h>
30 #include <gtk/gtkeventbox.h>
31 #include <gtk/gtkdnd.h>
32 #include <gtk/gtkscrolledwindow.h>
33 #include <gtk/gtklabel.h>
34 #include <gtk/gtktable.h>
35 #include <gtk/gtkstock.h>
36 #include <gtk/gtkhbox.h>
37
38 static const GtkTargetEntry dest_drag_types[] = {
39   {EGG_TOOLBAR_ITEM_TYPE, GTK_TARGET_SAME_APP, 0},
40 };
41
42 static const GtkTargetEntry source_drag_types[] = {
43   {EGG_TOOLBAR_ITEM_TYPE, GTK_TARGET_SAME_APP, 0},
44 };
45
46 static void egg_toolbar_editor_class_init       (EggToolbarEditorClass *klass);
47 static void egg_toolbar_editor_init             (EggToolbarEditor *t);
48 static void egg_toolbar_editor_finalize         (GObject *object);
49 static void update_actions_list                 (EggToolbarEditor *editor);
50 static void update_editor_sheet                 (EggToolbarEditor *editor);
51
52 enum
53 {
54   PROP_0,
55   PROP_UI_MANAGER,
56   PROP_TOOLBARS_MODEL
57 };
58
59 static GObjectClass *parent_class = NULL;
60
61 #define EGG_TOOLBAR_EDITOR_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EGG_TYPE_TOOLBAR_EDITOR, EggToolbarEditorPrivate))
62
63 struct EggToolbarEditorPrivate
64 {
65   GtkUIManager *manager;
66   EggToolbarsModel *model;
67
68   GtkWidget *table;
69   GtkWidget *scrolled_window;
70
71   GList *default_actions_list;
72   GList *actions_list;
73 };
74
75 GType
76 egg_toolbar_editor_get_type (void)
77 {
78   static GType type = 0;
79
80   if (G_UNLIKELY (type == 0))
81     {
82       static const GTypeInfo our_info = {
83         sizeof (EggToolbarEditorClass),
84         NULL,                   /* base_init */
85         NULL,                   /* base_finalize */
86         (GClassInitFunc) egg_toolbar_editor_class_init,
87         NULL,
88         NULL,                   /* class_data */
89         sizeof (EggToolbarEditor),
90         0,                      /* n_preallocs */
91         (GInstanceInitFunc) egg_toolbar_editor_init
92       };
93
94       type = g_type_register_static (GTK_TYPE_VBOX,
95                                      "EggToolbarEditor",
96                                      &our_info, 0);
97     }
98
99   return type;
100 }
101
102 static gint
103 compare_actions (gconstpointer a,
104                  gconstpointer b)
105 {
106   GValue value_a = { 0, }, value_b = { 0, };
107   const char *short_label_a, *short_label_b;
108   int ret;
109
110   g_value_init (&value_a, G_TYPE_STRING);
111   g_object_get_property (G_OBJECT (a), "short_label", &value_a);
112   short_label_a = g_value_get_string (&value_a);
113
114   g_value_init (&value_b, G_TYPE_STRING);
115   g_object_get_property (G_OBJECT (b), "short_label", &value_b);
116   short_label_b = g_value_get_string (&value_b);
117
118   ret = g_utf8_collate (short_label_a, short_label_b);
119
120   g_value_unset (&value_a);
121   g_value_unset (&value_b);
122
123   return ret;
124 }
125
126 static GtkAction *
127 find_action (EggToolbarEditor *t,
128              const char       *name)
129 {
130   GList *l;
131   GtkAction *action = NULL;
132
133   l = gtk_ui_manager_get_action_groups (t->priv->manager);
134
135   g_return_val_if_fail (EGG_IS_TOOLBAR_EDITOR (t), NULL);
136   g_return_val_if_fail (name != NULL, NULL);
137
138   for (; l != NULL; l = l->next)
139     {
140       GtkAction *tmp;
141
142       tmp = gtk_action_group_get_action (GTK_ACTION_GROUP (l->data), name);
143       if (tmp)
144         action = tmp;
145     }
146
147   return action;
148 }
149
150 static void
151 egg_toolbar_editor_set_ui_manager (EggToolbarEditor *t,
152                                    GtkUIManager     *manager)
153 {
154   g_return_if_fail (GTK_IS_UI_MANAGER (manager));
155
156   t->priv->manager = g_object_ref (manager);
157 }
158
159 static void
160 toolbar_removed_cb (EggToolbarsModel   *model,
161                     int                 position,
162                     EggToolbarEditor   *editor)
163 {
164   update_actions_list (editor);
165   update_editor_sheet (editor);
166 }
167
168 static void
169 egg_toolbar_editor_set_model (EggToolbarEditor *t,
170                               EggToolbarsModel *model)
171 {
172   g_return_if_fail (EGG_IS_TOOLBAR_EDITOR (t));
173
174   t->priv->model = g_object_ref (model);
175
176   g_signal_connect_object (model, "toolbar_removed",
177                            G_CALLBACK (toolbar_removed_cb), t, 0);
178 }
179
180 static void
181 egg_toolbar_editor_set_property (GObject      *object,
182                                  guint         prop_id,
183                                  const GValue *value,
184                                  GParamSpec   *pspec)
185 {
186   EggToolbarEditor *t = EGG_TOOLBAR_EDITOR (object);
187
188   switch (prop_id)
189     {
190     case PROP_UI_MANAGER:
191       egg_toolbar_editor_set_ui_manager (t, g_value_get_object (value));
192       break;
193     case PROP_TOOLBARS_MODEL:
194       egg_toolbar_editor_set_model (t, g_value_get_object (value));
195       break;
196     }
197 }
198
199 static void
200 egg_toolbar_editor_get_property (GObject    *object,
201                                  guint       prop_id,
202                                  GValue     *value,
203                                  GParamSpec *pspec)
204 {
205   EggToolbarEditor *t = EGG_TOOLBAR_EDITOR (object);
206
207   switch (prop_id)
208     {
209     case PROP_UI_MANAGER:
210       g_value_set_object (value, t->priv->manager);
211       break;
212     case PROP_TOOLBARS_MODEL:
213       g_value_set_object (value, t->priv->model);
214       break;
215     }
216 }
217
218 static void
219 egg_toolbar_editor_class_init (EggToolbarEditorClass *klass)
220 {
221   GObjectClass *object_class = G_OBJECT_CLASS (klass);
222
223   parent_class = g_type_class_peek_parent (klass);
224
225   object_class->finalize = egg_toolbar_editor_finalize;
226   object_class->set_property = egg_toolbar_editor_set_property;
227   object_class->get_property = egg_toolbar_editor_get_property;
228
229   g_object_class_install_property (object_class,
230                                    PROP_UI_MANAGER,
231                                    g_param_spec_object ("ui-manager",
232                                                         "UI-Manager",
233                                                         "UI Manager",
234                                                         GTK_TYPE_UI_MANAGER,
235                                                         G_PARAM_READWRITE |
236                                                         G_PARAM_CONSTRUCT_ONLY));
237  g_object_class_install_property (object_class,
238                                   PROP_TOOLBARS_MODEL,
239                                   g_param_spec_object ("model",
240                                                        "Model",
241                                                        "Toolbars Model",
242                                                        EGG_TYPE_TOOLBARS_MODEL,
243                                                        G_PARAM_READWRITE |
244                                                        G_PARAM_CONSTRUCT_ONLY));
245
246   g_type_class_add_private (object_class, sizeof (EggToolbarEditorPrivate));
247 }
248
249 static void
250 egg_toolbar_editor_finalize (GObject *object)
251 {
252   EggToolbarEditor *editor = EGG_TOOLBAR_EDITOR (object);
253
254   if (editor->priv->manager)
255     {
256       g_object_unref (editor->priv->manager);
257     }
258
259   if (editor->priv->model)
260     {
261       g_object_unref (editor->priv->model);
262     }
263
264   g_list_free (editor->priv->default_actions_list);
265   g_list_free (editor->priv->actions_list);
266
267   G_OBJECT_CLASS (parent_class)->finalize (object);
268 }
269
270 GtkWidget *
271 egg_toolbar_editor_new (GtkUIManager *manager,
272                         EggToolbarsModel *model)
273 {
274   return GTK_WIDGET (g_object_new (EGG_TYPE_TOOLBAR_EDITOR,
275                                    "ui-manager", manager,
276                                    "model", model,
277                                    NULL));
278 }
279
280 static void
281 drag_begin_cb (GtkWidget          *widget,
282                GdkDragContext     *context)
283 {
284         gtk_widget_hide (widget);
285 }
286
287 static void
288 drag_end_cb (GtkWidget          *widget,
289              GdkDragContext     *context)
290 {
291         gtk_widget_show (widget);
292 }
293
294 static void
295 editor_drag_data_received_cb (GtkWidget          *widget,
296                               GdkDragContext     *context,
297                               gint                x,
298                               gint                y,
299                               GtkSelectionData   *selection_data,
300                               guint               info,
301                               guint               time_,
302                               EggToolbarEditor *editor)
303 {
304   GtkAction *action;
305   const char *data;
306
307   g_return_if_fail (EGG_IS_TOOLBAR_EDITOR (editor));
308   g_return_if_fail (selection_data != NULL);
309
310   if (selection_data->length <= 0 || selection_data->data == NULL) return;
311
312   data = (const char *) selection_data->data;
313
314   if (strcmp (data, "separator") == 0) return;
315
316   action = find_action (editor, data);
317   g_return_if_fail (action != NULL);
318
319   if (g_list_find (editor->priv->default_actions_list, action))
320     {
321       editor->priv->actions_list = g_list_insert_sorted
322             (editor->priv->actions_list, action, compare_actions);
323     }
324
325   update_editor_sheet (editor);
326 }
327
328 static void
329 editor_drag_data_delete_cb (GtkWidget          *widget,
330                             GdkDragContext     *context,
331                             EggToolbarEditor *editor)
332 {
333   GtkAction *action;
334   g_return_if_fail (EGG_IS_TOOLBAR_EDITOR (editor));
335
336   action = GTK_ACTION (g_object_get_data (G_OBJECT (widget), "egg-action"));
337   if (action)
338     {
339       editor->priv->actions_list = g_list_remove
340             (editor->priv->actions_list, action);
341     }
342
343   update_editor_sheet (editor);
344 }
345
346 static void
347 drag_data_get_cb (GtkWidget          *widget,
348                   GdkDragContext     *context,
349                   GtkSelectionData   *selection_data,
350                   guint               info,
351                   guint32             time,
352                   EggToolbarEditor   *editor)
353 {
354   GtkAction *action;
355   const char *target;
356
357   action = GTK_ACTION (g_object_get_data (G_OBJECT (widget), "egg-action"));
358
359   if (action)
360     {
361       target = gtk_action_get_name (action);
362     }
363   else
364     {
365       target = "separator";
366     }
367
368   gtk_selection_data_set (selection_data,
369                           selection_data->target, 8,
370                           (const guchar *)target, strlen (target));
371 }
372
373 static gchar *
374 elide_underscores (const gchar *original)
375 {
376   gchar *q, *result;
377   const gchar *p;
378   gboolean last_underscore;
379
380   q = result = g_malloc (strlen (original) + 1);
381   last_underscore = FALSE;
382
383   for (p = original; *p; p++)
384     {
385       if (!last_underscore && *p == '_')
386         last_underscore = TRUE;
387       else
388         {
389           last_underscore = FALSE;
390           *q++ = *p;
391         }
392     }
393
394   *q = '\0';
395
396   return result;
397 }
398
399 static void
400 set_drag_cursor (GtkWidget *widget)
401 {
402   GdkCursor *cursor;
403   GdkPixbuf *pixbuf;
404
405   pixbuf = gdk_pixbuf_new_from_file (CURSOR_DIR "/hand-open.png", NULL);
406   cursor = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), pixbuf, 12, 12);
407
408   gdk_window_set_cursor (widget->window, cursor);
409   gdk_cursor_unref (cursor);
410   g_object_unref (pixbuf);
411 }
412
413 static void
414 event_box_realize_cb (GtkWidget *widget, GtkImage *icon)
415 {
416   GtkImageType type;
417
418   set_drag_cursor (widget);
419
420   type = gtk_image_get_storage_type (icon);
421   if (type == GTK_IMAGE_STOCK)
422     {
423       gchar *stock_id;
424       GdkPixbuf *pixbuf;
425       gtk_image_get_stock (icon, &stock_id, NULL);
426       pixbuf = gtk_widget_render_icon (widget, stock_id,
427                                        GTK_ICON_SIZE_LARGE_TOOLBAR, NULL);
428       gtk_drag_source_set_icon_pixbuf (widget, pixbuf);
429       g_object_unref (pixbuf);
430     }
431   else if (type == GTK_IMAGE_PIXBUF)
432     {
433       GdkPixbuf *pixbuf = gtk_image_get_pixbuf (icon);
434       gtk_drag_source_set_icon_pixbuf (widget, pixbuf);
435     }
436 }
437
438 static GtkWidget *
439 editor_create_item (EggToolbarEditor *editor,
440                     GtkImage         *icon,
441                     const char       *label_text,
442                     GdkDragAction     action)
443 {
444   GtkWidget *event_box;
445   GtkWidget *vbox;
446   GtkWidget *label;
447   gchar *label_no_mnemonic = NULL;
448
449   event_box = gtk_event_box_new ();
450   gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
451   gtk_widget_show (event_box);
452   gtk_drag_source_set (event_box,
453                        GDK_BUTTON1_MASK,
454                        source_drag_types, G_N_ELEMENTS (source_drag_types), action);
455   g_signal_connect (event_box, "drag_data_get",
456                     G_CALLBACK (drag_data_get_cb), editor);
457   g_signal_connect (event_box, "drag_data_delete",
458                     G_CALLBACK (editor_drag_data_delete_cb), editor);
459   g_signal_connect_after (event_box, "realize",
460                           G_CALLBACK (event_box_realize_cb), icon);
461
462   if (action == GDK_ACTION_MOVE)
463     {
464       g_signal_connect (event_box, "drag_begin",
465                         G_CALLBACK (drag_begin_cb), NULL);
466       g_signal_connect (event_box, "drag_end",
467                         G_CALLBACK (drag_end_cb), NULL);
468     }
469
470   vbox = gtk_vbox_new (0, FALSE);
471   gtk_widget_show (vbox);
472   gtk_container_add (GTK_CONTAINER (event_box), vbox);
473
474   gtk_widget_show (GTK_WIDGET (icon));
475   gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (icon), FALSE, TRUE, 0);
476   label_no_mnemonic = elide_underscores (label_text);
477   label = gtk_label_new (label_no_mnemonic);
478   g_free (label_no_mnemonic);
479   gtk_widget_show (label);
480   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
481
482   return event_box;
483 }
484
485 static void
486 update_editor_sheet (EggToolbarEditor *editor)
487 {
488   GList *l;
489   GList *to_drag;
490   int x, y, height, width;
491   GtkWidget *table;
492   GtkWidget *viewport;
493   GtkWidget *item;
494   GtkWidget *icon;
495
496   g_return_if_fail (EGG_IS_TOOLBAR_EDITOR (editor));
497
498   viewport = GTK_BIN (editor->priv->scrolled_window)->child;
499   if (viewport)
500     {
501       table = GTK_BIN (viewport)->child;
502       gtk_container_remove (GTK_CONTAINER (viewport), table);
503     }
504   table = gtk_table_new (0, 0, TRUE);
505   editor->priv->table = table;
506   gtk_container_set_border_width (GTK_CONTAINER (table), 12);
507   gtk_widget_show (table);
508   gtk_scrolled_window_add_with_viewport
509     (GTK_SCROLLED_WINDOW (editor->priv->scrolled_window), table);
510   gtk_drag_dest_set (table, GTK_DEST_DEFAULT_ALL,
511                      dest_drag_types, G_N_ELEMENTS (dest_drag_types), GDK_ACTION_MOVE);
512   g_signal_connect (table, "drag_data_received",
513                     G_CALLBACK (editor_drag_data_received_cb), editor);
514
515   to_drag = editor->priv->actions_list;
516
517   x = y = 0;
518   width = 4;
519   height = (g_list_length (to_drag)) / width + 1;
520   gtk_table_resize (GTK_TABLE (editor->priv->table), height, width);
521
522   for (l = to_drag; l != NULL; l = l->next)
523     {
524       GtkAction *action = (l->data);
525       const char *stock_id, *short_label;
526       GValue value = { 0, };
527
528       g_value_init (&value, G_TYPE_STRING);
529       g_object_get_property (G_OBJECT (action), "stock_id", &value);
530       stock_id = g_value_get_string (&value);
531       icon = gtk_image_new_from_stock
532                 (stock_id ? stock_id : GTK_STOCK_DND,
533                  GTK_ICON_SIZE_LARGE_TOOLBAR);
534       g_value_unset (&value);
535
536       g_value_init (&value, G_TYPE_STRING);
537       g_object_get_property (G_OBJECT (action), "short_label", &value);
538       short_label = g_value_get_string (&value);
539       item = editor_create_item (editor, GTK_IMAGE (icon),
540                                  short_label, GDK_ACTION_MOVE);
541       g_value_unset (&value);
542       g_object_set_data (G_OBJECT (item), "egg-action", action);
543       gtk_table_attach_defaults (GTK_TABLE (editor->priv->table),
544                                  item, x, x + 1, y, y + 1);
545
546       x++;
547       if (x >= width)
548         {
549           x = 0;
550           y++;
551         }
552     }
553
554   icon = _egg_editable_toolbar_new_separator_image ();
555   item = editor_create_item (editor, GTK_IMAGE (icon), _("Separator"),
556                              GDK_ACTION_COPY);
557   gtk_table_attach_defaults (GTK_TABLE (editor->priv->table),
558                              item, x, x + 1, y, y + 1);
559 }
560
561 static void
562 setup_editor (EggToolbarEditor *editor)
563 {
564   GtkWidget *scrolled_window;
565
566   gtk_container_set_border_width (GTK_CONTAINER (editor), 12);
567   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
568   editor->priv->scrolled_window = scrolled_window;
569   gtk_widget_show (scrolled_window);
570   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
571                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
572   gtk_box_pack_start (GTK_BOX (editor), scrolled_window, TRUE, TRUE, 0);
573 }
574
575 static void
576 egg_toolbar_editor_init (EggToolbarEditor *t)
577 {
578   t->priv = EGG_TOOLBAR_EDITOR_GET_PRIVATE (t);
579
580   t->priv->manager = NULL;
581   t->priv->default_actions_list = NULL;
582   t->priv->actions_list = NULL;
583
584   setup_editor (t);
585 }
586
587 void
588 egg_toolbar_editor_add_action (EggToolbarEditor *editor,
589                                const char       *action_name)
590 {
591         GtkAction *action;
592
593         action = find_action (editor, action_name);
594         g_return_if_fail (action != NULL);
595
596         editor->priv->default_actions_list = g_list_insert_sorted
597                 (editor->priv->default_actions_list, action, compare_actions);
598 }
599
600 static void
601 parse_item_list (EggToolbarEditor *t,
602                  xmlNodePtr        child)
603 {
604   while (child)
605     {
606       if (xmlStrEqual (child->name, (const xmlChar*) "toolitem"))
607         {
608           xmlChar *name;
609
610           name = xmlGetProp (child, (const xmlChar*) "name");
611           egg_toolbar_editor_add_action (t, (const char*)name);
612           xmlFree (name);
613         }
614       child = child->next;
615     }
616 }
617
618 static gboolean
619 model_has_action (EggToolbarsModel *model, GtkAction *action)
620 {
621   int i, l, n_items, n_toolbars;
622
623   n_toolbars = egg_toolbars_model_n_toolbars (model);
624   for (i = 0; i < n_toolbars; i++)
625     {
626       n_items = egg_toolbars_model_n_items (model, i);
627       for (l = 0; l < n_items; l++)
628         {
629           const char *name;
630           const char *action_name;
631           gboolean sep;
632
633           egg_toolbars_model_item_nth (model, i, l, &sep, &name, NULL);
634           action_name = gtk_action_get_name (action);
635           if (!sep && strcmp (name, action_name) == 0) return TRUE;
636         }
637     }
638
639   return FALSE;
640 }
641
642 static void
643 update_actions_list (EggToolbarEditor *editor)
644 {
645   GList *l;
646
647   if (editor->priv->actions_list)
648     g_list_free (editor->priv->actions_list);
649
650   /* Remove the already used items */
651   editor->priv->actions_list = NULL;
652
653   for (l = editor->priv->default_actions_list; l != NULL; l = l->next)
654     {
655       GtkAction *action = GTK_ACTION (l->data);
656
657       if (!model_has_action (editor->priv->model, action))
658         {
659           editor->priv->actions_list = g_list_insert_sorted
660                 (editor->priv->actions_list, action, compare_actions);
661         }
662     }
663 }
664
665 void
666 egg_toolbar_editor_load_actions (EggToolbarEditor *editor,
667                                  const char       *xml_file)
668 {
669   xmlDocPtr doc;
670   xmlNodePtr root;
671   xmlNodePtr child;
672
673   doc = xmlParseFile (xml_file);
674   root = xmlDocGetRootElement (doc);
675   child = root->children;
676
677   while (child)
678     {
679       if (xmlStrEqual (child->name, (const xmlChar*) "available"))
680         {
681           parse_item_list (editor, child->children);
682         }
683       child = child->next;
684     }
685
686   xmlFreeDoc (doc);
687
688   update_actions_list (editor);
689   update_editor_sheet (editor);
690 }