]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenufactory.c
Initial revision
[~andy/gtk] / gtk / gtkmenufactory.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include <string.h>
19 #include "gtkcheckmenuitem.h"
20 #include "gtkmenu.h"
21 #include "gtkmenubar.h"
22 #include "gtkmenufactory.h"
23 #include "gtkmenuitem.h"
24 #include "gtksignal.h"
25
26
27 enum
28 {
29   CREATE  = 1 << 0,
30   DESTROY = 1 << 1,
31   CHECK   = 1 << 2
32 };
33
34
35 static void         gtk_menu_factory_create            (GtkMenuFactory *factory,
36                                                         GtkMenuEntry   *entry,
37                                                         GtkWidget      *parent,
38                                                         const char     *path);
39 static void         gtk_menu_factory_remove            (GtkMenuFactory *factory,
40                                                         GtkWidget      *parent,
41                                                         const char     *path);
42 static GtkWidget*   gtk_menu_factory_make_widget       (GtkMenuFactory *factory);
43 static GtkMenuPath* gtk_menu_factory_get               (GtkWidget      *parent,
44                                                         const char     *path,
45                                                         int             flags);
46 static GtkMenuPath* gtk_menu_factory_find_recurse      (GtkMenuFactory *factory,
47                                                         GtkWidget      *parent,
48                                                         const char     *path);
49 static void         gtk_menu_factory_parse_accelerator (const char     *accelerator,
50                                                         char     *accelerator_key,
51                                                         guint8         *accelerator_mods);
52
53
54 GtkMenuFactory*
55 gtk_menu_factory_new (GtkMenuFactoryType type)
56 {
57   GtkMenuFactory *factory;
58
59   factory = g_new (GtkMenuFactory, 1);
60   factory->path = NULL;
61   factory->type = type;
62   factory->table = NULL;
63   factory->widget = NULL;
64   factory->subfactories = NULL;
65
66   return factory;
67 }
68
69 void
70 gtk_menu_factory_destroy (GtkMenuFactory *factory)
71 {
72   GtkMenuFactory *subfactory;
73   GList *tmp_list;
74
75   g_return_if_fail (factory != NULL);
76
77   if (factory->path)
78     g_free (factory->path);
79
80   tmp_list = factory->subfactories;
81   while (tmp_list)
82     {
83       subfactory = tmp_list->data;
84       tmp_list = tmp_list->next;
85
86       gtk_menu_factory_destroy (subfactory);
87     }
88 }
89
90 void
91 gtk_menu_factory_add_entries (GtkMenuFactory *factory,
92                               GtkMenuEntry   *entries,
93                               int             nentries)
94 {
95   int i;
96
97   g_return_if_fail (factory != NULL);
98   g_return_if_fail (entries != NULL);
99   g_return_if_fail (nentries > 0);
100
101   if (!factory->widget)
102     factory->widget = gtk_menu_factory_make_widget (factory);
103
104   for (i = 0; i < nentries; i++)
105     gtk_menu_factory_create (factory, &entries[i], factory->widget, entries[i].path);
106 }
107
108 void
109 gtk_menu_factory_add_subfactory (GtkMenuFactory *factory,
110                                  GtkMenuFactory *subfactory,
111                                  const char     *path)
112 {
113   g_return_if_fail (factory != NULL);
114   g_return_if_fail (subfactory != NULL);
115   g_return_if_fail (path != NULL);
116
117   if (subfactory->path)
118     g_free (subfactory->path);
119   subfactory->path = g_strdup (path);
120
121   factory->subfactories = g_list_append (factory->subfactories, subfactory);
122 }
123
124 void
125 gtk_menu_factory_remove_paths (GtkMenuFactory  *factory,
126                                char           **paths,
127                                int              npaths)
128 {
129   int i;
130
131   g_return_if_fail (factory != NULL);
132   g_return_if_fail (paths != NULL);
133   g_return_if_fail (npaths > 0);
134
135   if (factory->widget)
136     {
137       for (i = 0; i < npaths; i++)
138         gtk_menu_factory_remove (factory, factory->widget, paths[i]);
139     }
140 }
141
142 void
143 gtk_menu_factory_remove_entries (GtkMenuFactory *factory,
144                                  GtkMenuEntry   *entries,
145                                  int             nentries)
146 {
147   int i;
148
149   g_return_if_fail (factory != NULL);
150   g_return_if_fail (entries != NULL);
151   g_return_if_fail (nentries > 0);
152
153   if (factory->widget)
154     {
155       for (i = 0; i < nentries; i++)
156         gtk_menu_factory_remove (factory, factory->widget, entries[i].path);
157     }
158 }
159
160 void
161 gtk_menu_factory_remove_subfactory (GtkMenuFactory *factory,
162                                     GtkMenuFactory *subfactory,
163                                     const char     *path)
164 {
165   g_return_if_fail (factory != NULL);
166   g_return_if_fail (subfactory != NULL);
167   g_return_if_fail (path != NULL);
168
169   g_warning ("FIXME: gtk_menu_factory_remove_subfactory");
170 }
171
172 GtkMenuPath*
173 gtk_menu_factory_find (GtkMenuFactory *factory,
174                        const char     *path)
175 {
176   g_return_val_if_fail (factory != NULL, NULL);
177   g_return_val_if_fail (path != NULL, NULL);
178
179   return gtk_menu_factory_find_recurse (factory, factory->widget, path);
180 }
181
182
183 static void
184 gtk_menu_factory_create (GtkMenuFactory *factory,
185                          GtkMenuEntry   *entry,
186                          GtkWidget      *parent,
187                          const char     *path)
188 {
189   GtkMenuFactory *subfactory;
190   GtkMenuPath *menu_path;
191   GtkWidget *menu;
192   GList *tmp_list;
193   char tmp_path[256];
194   char accelerator_key;
195   guint8 accelerator_mods;
196   char *p;
197
198   g_return_if_fail (factory != NULL);
199   g_return_if_fail (entry != NULL);
200
201   /* If 'path' is empty, then simply return.
202    */
203   if (!path || path[0] == '\0')
204     return;
205
206   /* Strip off the next part of the path.
207    */
208   p = strchr (path, '/');
209
210   /* If this is the last part of the path ('p' is
211    *  NULL), then we create an item.
212    */
213   if (!p)
214     {
215       /* Check to see if this item is a separator.
216        */
217       if (strcmp (path, "<separator>") == 0)
218         {
219           entry->widget = gtk_menu_item_new ();
220           gtk_container_add (GTK_CONTAINER (parent), entry->widget);
221           gtk_widget_show (entry->widget);
222         }
223       else
224         {
225           if (strncmp (path, "<check>", 7) == 0)
226             menu_path = gtk_menu_factory_get (parent, path + 7, CREATE | CHECK);
227           else
228             menu_path = gtk_menu_factory_get (parent, path, CREATE);
229           entry->widget = menu_path->widget;
230
231           if (strcmp (path, "<nothing>") == 0)
232             gtk_widget_hide (entry->widget);
233
234           if (entry->accelerator)
235             {
236               gtk_menu_factory_parse_accelerator (entry->accelerator,
237                                                   &accelerator_key,
238                                                   &accelerator_mods);
239               if (!factory->table)
240                 {
241                   factory->table = gtk_accelerator_table_new ();
242                   gtk_accelerator_table_ref (factory->table);
243                 }
244
245               gtk_widget_install_accelerator (menu_path->widget,
246                                               factory->table,
247                                               "activate",
248                                               accelerator_key,
249                                               accelerator_mods);
250             }
251
252           if (entry->callback)
253             gtk_signal_connect (GTK_OBJECT (menu_path->widget), "activate",
254                                 (GtkSignalFunc) entry->callback,
255                                 entry->callback_data);
256         }
257     }
258   else
259     {
260       strncpy (tmp_path, path, (unsigned int) ((long) p - (long) path));
261       tmp_path[(long) p - (long) path] = '\0';
262
263       menu_path = gtk_menu_factory_get (parent, tmp_path, 0);
264       if (!menu_path)
265         {
266           tmp_list = factory->subfactories;
267           while (tmp_list)
268             {
269               subfactory = tmp_list->data;
270               tmp_list = tmp_list->next;
271
272               if (subfactory->path &&
273                   (strcmp (subfactory->path, tmp_path) == 0))
274                 {
275                   if (!subfactory->widget)
276                     subfactory->widget = gtk_menu_factory_make_widget (subfactory);
277                   gtk_menu_factory_create (subfactory, entry, subfactory->widget, p + 1);
278                   return;
279                 }
280             }
281
282           menu_path = gtk_menu_factory_get (parent, tmp_path, CREATE);
283         }
284
285       entry->widget = menu_path->widget;
286       menu = GTK_MENU_ITEM (menu_path->widget)->submenu;
287
288       if (!menu)
289         {
290           menu = gtk_menu_new ();
291           gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_path->widget), menu);
292
293           if (!factory->table)
294             {
295               factory->table = gtk_accelerator_table_new ();
296               gtk_accelerator_table_ref (factory->table);
297             }
298           gtk_menu_set_accelerator_table (GTK_MENU (menu), factory->table);
299         }
300
301       gtk_menu_factory_create (factory, entry, menu, p + 1);
302     }
303 }
304
305 static void
306 gtk_menu_factory_remove (GtkMenuFactory *factory,
307                          GtkWidget      *parent,
308                          const char     *path)
309 {
310   GtkMenuFactory *subfactory;
311   GtkMenuPath *menu_path;
312   GtkWidget *menu;
313   GList *tmp_list;
314   char tmp_path[256];
315   char *p;
316
317   if (!path || path[0] == '\0')
318     return;
319
320   p = strchr (path, '/');
321
322   if (!p)
323     {
324       if (parent)
325         gtk_menu_factory_get (parent, path, DESTROY);
326     }
327   else
328     {
329       strncpy (tmp_path, path, (unsigned int) ((long) p - (long) path));
330       tmp_path[(long) p - (long) path] = '\0';
331
332       menu_path = gtk_menu_factory_get (parent, tmp_path, 0);
333       if (!menu_path)
334         {
335           tmp_list = factory->subfactories;
336           while (tmp_list)
337             {
338               subfactory = tmp_list->data;
339               tmp_list = tmp_list->next;
340
341               if (subfactory->path &&
342                   (strcmp (subfactory->path, tmp_path) == 0))
343                 {
344                   if (!subfactory->widget)
345                     return;
346                   gtk_menu_factory_remove (subfactory, subfactory->widget, p + 1);
347                 }
348             }
349         }
350       else
351         {
352           menu = GTK_MENU_ITEM (menu_path->widget)->submenu;
353           if (menu)
354             gtk_menu_factory_remove (factory, menu, p + 1);
355         }
356     }
357 }
358
359 static GtkWidget*
360 gtk_menu_factory_make_widget (GtkMenuFactory *factory)
361 {
362   GtkWidget *widget;
363
364   g_return_val_if_fail (factory != NULL, NULL);
365
366   switch (factory->type)
367     {
368     case GTK_MENU_FACTORY_MENU:
369       widget = gtk_menu_new ();
370
371       if (!factory->table)
372         {
373           factory->table = gtk_accelerator_table_new ();
374           gtk_accelerator_table_ref (factory->table);
375         }
376       gtk_menu_set_accelerator_table (GTK_MENU (widget), factory->table);
377       return widget;
378     case GTK_MENU_FACTORY_MENU_BAR:
379       return gtk_menu_bar_new ();
380     case GTK_MENU_FACTORY_OPTION_MENU:
381       g_error ("not implemented");
382       break;
383     }
384
385   return NULL;
386 }
387
388 static GtkMenuPath*
389 gtk_menu_factory_get (GtkWidget *parent,
390                       const char *path,
391                       int        flags)
392 {
393   GtkMenuPath *menu_path;
394   GList *tmp_list;
395
396   tmp_list = gtk_object_get_user_data (GTK_OBJECT (parent));
397   while (tmp_list)
398     {
399       menu_path = tmp_list->data;
400       tmp_list = tmp_list->next;
401
402       if (strcmp (menu_path->path, path) == 0)
403         {
404           if (flags & DESTROY)
405             {
406               tmp_list = gtk_object_get_user_data (GTK_OBJECT (parent));
407               tmp_list = g_list_remove (tmp_list, menu_path);
408               gtk_object_set_user_data (GTK_OBJECT (parent), tmp_list);
409
410               gtk_widget_destroy (menu_path->widget);
411               g_free (menu_path->path);
412               g_free (menu_path);
413
414               return NULL;
415             }
416           else
417             {
418               return menu_path;
419             }
420         }
421     }
422
423   if (flags & CREATE)
424     {
425       menu_path = g_new (GtkMenuPath, 1);
426       menu_path->path = g_strdup (path);
427
428       if (flags & CHECK)
429         menu_path->widget = gtk_check_menu_item_new_with_label (path);
430       else
431         menu_path->widget = gtk_menu_item_new_with_label (path);
432
433       gtk_container_add (GTK_CONTAINER (parent), menu_path->widget);
434       gtk_object_set_user_data (GTK_OBJECT (menu_path->widget), NULL);
435       gtk_widget_show (menu_path->widget);
436
437       tmp_list = gtk_object_get_user_data (GTK_OBJECT (parent));
438       tmp_list = g_list_prepend (tmp_list, menu_path);
439       gtk_object_set_user_data (GTK_OBJECT (parent), tmp_list);
440
441       return menu_path;
442     }
443
444   return NULL;
445 }
446
447 static GtkMenuPath*
448 gtk_menu_factory_find_recurse (GtkMenuFactory *factory,
449                                GtkWidget      *parent,
450                                const char     *path)
451 {
452   GtkMenuFactory *subfactory;
453   GtkMenuPath *menu_path;
454   GtkWidget *menu;
455   GList *tmp_list;
456   char tmp_path[256];
457   char *p;
458
459   if (!path || path[0] == '\0')
460     return NULL;
461
462   p = strchr (path, '/');
463
464   if (!p)
465     {
466       if (parent)
467         return gtk_menu_factory_get (parent, path, 0);
468     }
469   else
470     {
471       strncpy (tmp_path, path, (unsigned int) ((long) p - (long) path));
472       tmp_path[(long) p - (long) path] = '\0';
473
474       menu_path = gtk_menu_factory_get (parent, tmp_path, 0);
475       if (!menu_path)
476         {
477           tmp_list = factory->subfactories;
478           while (tmp_list)
479             {
480               subfactory = tmp_list->data;
481               tmp_list = tmp_list->next;
482
483               if (subfactory->path &&
484                   (strcmp (subfactory->path, tmp_path) == 0))
485                 {
486                   if (!subfactory->widget)
487                     return NULL;
488                   return gtk_menu_factory_find_recurse (subfactory, subfactory->widget, p + 1);
489                 }
490             }
491
492           return NULL;
493         }
494
495       menu = GTK_MENU_ITEM (menu_path->widget)->submenu;
496       if (menu)
497         return gtk_menu_factory_find_recurse (factory, menu, p + 1);
498     }
499
500   return NULL;
501 }
502
503 static void
504 gtk_menu_factory_parse_accelerator (const char   *accelerator,
505                                     char   *accelerator_key,
506                                     guint8 *accelerator_mods)
507 {
508   int done;
509
510   g_return_if_fail (accelerator != NULL);
511   g_return_if_fail (accelerator_key != NULL);
512   g_return_if_fail (accelerator_mods != NULL);
513
514   *accelerator_key = 0;
515   *accelerator_mods = 0;
516
517   done = FALSE;
518   while (!done)
519     {
520       if (strncmp (accelerator, "<shift>", 7) == 0)
521         {
522           accelerator += 7;
523           *accelerator_mods |= GDK_SHIFT_MASK;
524         }
525       else if (strncmp (accelerator, "<alt>", 5) == 0)
526         {
527           accelerator += 5;
528           *accelerator_mods |= GDK_MOD1_MASK;
529         }
530       else if (strncmp (accelerator, "<control>", 9) == 0)
531         {
532           accelerator += 9;
533           *accelerator_mods |= GDK_CONTROL_MASK;
534         }
535       else
536         {
537           done = TRUE;
538           *accelerator_key = accelerator[0];
539         }
540     }
541 }