]> Pileus Git - ~andy/gtk/blob - gtk/gtkoptionmenu.c
Changed LGPL address for FSF in all .h and .c files
[~andy/gtk] / gtk / gtkoptionmenu.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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include "gtkmenu.h"
20 #include "gtkmenuitem.h"
21 #include "gtkoptionmenu.h"
22 #include "gtksignal.h"
23
24
25 #define CHILD_LEFT_SPACING        5
26 #define CHILD_RIGHT_SPACING       1
27 #define CHILD_TOP_SPACING         1
28 #define CHILD_BOTTOM_SPACING      1
29 #define OPTION_INDICATOR_WIDTH    12
30 #define OPTION_INDICATOR_HEIGHT   8
31 #define OPTION_INDICATOR_SPACING  2
32
33
34 static void gtk_option_menu_class_init      (GtkOptionMenuClass *klass);
35 static void gtk_option_menu_init            (GtkOptionMenu      *option_menu);
36 static void gtk_option_menu_destroy         (GtkObject          *object);
37 static void gtk_option_menu_size_request    (GtkWidget          *widget,
38                                              GtkRequisition     *requisition);
39 static void gtk_option_menu_size_allocate   (GtkWidget          *widget,
40                                              GtkAllocation      *allocation);
41 static void gtk_option_menu_paint           (GtkWidget          *widget,
42                                              GdkRectangle       *area);
43 static void gtk_option_menu_draw            (GtkWidget          *widget,
44                                              GdkRectangle       *area);
45 static gint gtk_option_menu_expose          (GtkWidget          *widget,
46                                              GdkEventExpose     *event);
47 static gint gtk_option_menu_button_press    (GtkWidget          *widget,
48                                              GdkEventButton     *event);
49 static void gtk_option_menu_deactivate      (GtkMenuShell       *menu_shell,
50                                              GtkOptionMenu      *option_menu);
51 static void gtk_option_menu_update_contents (GtkOptionMenu      *option_menu);
52 static void gtk_option_menu_remove_contents (GtkOptionMenu      *option_menu);
53 static void gtk_option_menu_calc_size       (GtkOptionMenu      *option_menu);
54 static void gtk_option_menu_position        (GtkMenu            *menu,
55                                              gint               *x,
56                                              gint               *y,
57                                              gpointer            user_data);
58 static void gtk_option_menu_show_all        (GtkWidget          *widget);
59 static void gtk_option_menu_hide_all        (GtkWidget          *widget);
60
61
62 static GtkButtonClass *parent_class = NULL;
63
64
65 guint
66 gtk_option_menu_get_type ()
67 {
68   static guint option_menu_type = 0;
69
70   if (!option_menu_type)
71     {
72       GtkTypeInfo option_menu_info =
73       {
74         "GtkOptionMenu",
75         sizeof (GtkOptionMenu),
76         sizeof (GtkOptionMenuClass),
77         (GtkClassInitFunc) gtk_option_menu_class_init,
78         (GtkObjectInitFunc) gtk_option_menu_init,
79         (GtkArgSetFunc) NULL,
80         (GtkArgGetFunc) NULL,
81       };
82
83       option_menu_type = gtk_type_unique (gtk_button_get_type (), &option_menu_info);
84     }
85
86   return option_menu_type;
87 }
88
89 static void
90 gtk_option_menu_class_init (GtkOptionMenuClass *class)
91 {
92   GtkObjectClass *object_class;
93   GtkWidgetClass *widget_class;
94   GtkButtonClass *button_class;
95
96   object_class = (GtkObjectClass*) class;
97   widget_class = (GtkWidgetClass*) class;
98   button_class = (GtkButtonClass*) class;
99
100   parent_class = gtk_type_class (gtk_button_get_type ());
101
102   object_class->destroy = gtk_option_menu_destroy;
103
104   widget_class->draw = gtk_option_menu_draw;
105   widget_class->draw_focus = NULL;
106   widget_class->size_request = gtk_option_menu_size_request;
107   widget_class->size_allocate = gtk_option_menu_size_allocate;
108   widget_class->expose_event = gtk_option_menu_expose;
109   widget_class->button_press_event = gtk_option_menu_button_press;
110   widget_class->show_all = gtk_option_menu_show_all;
111   widget_class->hide_all = gtk_option_menu_hide_all;
112 }
113
114 static void
115 gtk_option_menu_init (GtkOptionMenu *option_menu)
116 {
117   GTK_WIDGET_UNSET_FLAGS (option_menu, GTK_CAN_FOCUS);
118
119   option_menu->menu = NULL;
120   option_menu->menu_item = NULL;
121   option_menu->width = 0;
122   option_menu->height = 0;
123 }
124
125 GtkWidget*
126 gtk_option_menu_new ()
127 {
128   return GTK_WIDGET (gtk_type_new (gtk_option_menu_get_type ()));
129 }
130
131 GtkWidget*
132 gtk_option_menu_get_menu (GtkOptionMenu *option_menu)
133 {
134   g_return_val_if_fail (option_menu != NULL, NULL);
135   g_return_val_if_fail (GTK_IS_OPTION_MENU (option_menu), NULL);
136
137   return option_menu->menu;
138 }
139
140 static void
141 gtk_option_menu_detacher (GtkWidget     *widget,
142                           GtkMenu       *menu)
143 {
144   GtkOptionMenu *option_menu;
145
146   g_return_if_fail (widget != NULL);
147   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
148
149   option_menu = GTK_OPTION_MENU (widget);
150   g_return_if_fail (option_menu->menu == (GtkWidget*) menu);
151
152   gtk_option_menu_remove_contents (option_menu);
153   gtk_signal_disconnect_by_data (GTK_OBJECT (option_menu->menu),
154                                  option_menu);
155   
156   option_menu->menu = NULL;
157 }
158
159 void
160 gtk_option_menu_set_menu (GtkOptionMenu *option_menu,
161                           GtkWidget     *menu)
162 {
163   g_return_if_fail (option_menu != NULL);
164   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
165   g_return_if_fail (menu != NULL);
166   g_return_if_fail (GTK_IS_MENU (menu));
167
168   if (option_menu->menu != menu)
169     {
170       gtk_option_menu_remove_menu (option_menu);
171
172       option_menu->menu = menu;
173       gtk_menu_attach_to_widget (GTK_MENU (menu),
174                                  GTK_WIDGET (option_menu),
175                                  gtk_option_menu_detacher);
176
177       gtk_option_menu_calc_size (option_menu);
178
179       gtk_signal_connect (GTK_OBJECT (option_menu->menu), "deactivate",
180                           (GtkSignalFunc) gtk_option_menu_deactivate,
181                           option_menu);
182
183       if (GTK_WIDGET (option_menu)->parent)
184         gtk_widget_queue_resize (GTK_WIDGET (option_menu));
185
186       gtk_option_menu_update_contents (option_menu);
187     }
188 }
189
190 void
191 gtk_option_menu_remove_menu (GtkOptionMenu *option_menu)
192 {
193   g_return_if_fail (option_menu != NULL);
194   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
195
196   if (option_menu->menu)
197     gtk_menu_detach (GTK_MENU (option_menu->menu));
198 }
199
200 void
201 gtk_option_menu_set_history (GtkOptionMenu *option_menu,
202                              guint          index)
203 {
204   GtkWidget *menu_item;
205
206   g_return_if_fail (option_menu != NULL);
207   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
208
209   if (option_menu->menu)
210     {
211       gtk_menu_set_active (GTK_MENU (option_menu->menu), index);
212       menu_item = gtk_menu_get_active (GTK_MENU (option_menu->menu));
213
214       if (menu_item != option_menu->menu_item)
215         {
216           gtk_option_menu_remove_contents (option_menu);
217           gtk_option_menu_update_contents (option_menu);
218         }
219     }
220 }
221
222
223 static void
224 gtk_option_menu_destroy (GtkObject *object)
225 {
226   GtkOptionMenu *option_menu;
227
228   g_return_if_fail (object != NULL);
229   g_return_if_fail (GTK_IS_OPTION_MENU (object));
230
231   option_menu = GTK_OPTION_MENU (object);
232
233   if (option_menu->menu)
234     gtk_widget_destroy (option_menu->menu);
235
236   if (GTK_OBJECT_CLASS (parent_class)->destroy)
237     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
238 }
239
240 static void
241 gtk_option_menu_size_request (GtkWidget      *widget,
242                               GtkRequisition *requisition)
243 {
244   GtkOptionMenu *option_menu;
245   gint tmp;
246
247   g_return_if_fail (widget != NULL);
248   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
249   g_return_if_fail (requisition != NULL);
250
251   option_menu = GTK_OPTION_MENU (widget);
252
253   requisition->width = ((GTK_CONTAINER (widget)->border_width +
254                          GTK_WIDGET (widget)->style->klass->xthickness) * 2 +
255                         option_menu->width +
256                         OPTION_INDICATOR_WIDTH +
257                         OPTION_INDICATOR_SPACING * 5 +
258                         CHILD_LEFT_SPACING + CHILD_RIGHT_SPACING);
259   requisition->height = ((GTK_CONTAINER (widget)->border_width +
260                           GTK_WIDGET (widget)->style->klass->ythickness) * 2 +
261                          option_menu->height +
262                          CHILD_TOP_SPACING + CHILD_BOTTOM_SPACING);
263
264   tmp = (requisition->height - option_menu->height +
265          OPTION_INDICATOR_HEIGHT + OPTION_INDICATOR_SPACING * 2);
266   requisition->height = MAX (requisition->height, tmp);
267 }
268
269 static void
270 gtk_option_menu_size_allocate (GtkWidget     *widget,
271                                GtkAllocation *allocation)
272 {
273   GtkWidget *child;
274   GtkAllocation child_allocation;
275
276   g_return_if_fail (widget != NULL);
277   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
278   g_return_if_fail (allocation != NULL);
279
280   widget->allocation = *allocation;
281   if (GTK_WIDGET_REALIZED (widget))
282     gdk_window_move_resize (widget->window,
283                             allocation->x, allocation->y,
284                             allocation->width, allocation->height);
285
286   child = GTK_BUTTON (widget)->child;
287   if (child && GTK_WIDGET_VISIBLE (child))
288     {
289       child_allocation.x = (GTK_CONTAINER (widget)->border_width +
290                             GTK_WIDGET (widget)->style->klass->xthickness);
291       child_allocation.y = (GTK_CONTAINER (widget)->border_width +
292                             GTK_WIDGET (widget)->style->klass->ythickness);
293       child_allocation.width = (allocation->width - child_allocation.x * 2 -
294                                 OPTION_INDICATOR_WIDTH - OPTION_INDICATOR_SPACING * 5 -
295                                 CHILD_LEFT_SPACING - CHILD_RIGHT_SPACING);
296       child_allocation.height = (allocation->height - child_allocation.y * 2 -
297                                  CHILD_TOP_SPACING - CHILD_BOTTOM_SPACING);
298       child_allocation.x += CHILD_LEFT_SPACING;
299       child_allocation.y += CHILD_RIGHT_SPACING;
300
301       gtk_widget_size_allocate (child, &child_allocation);
302     }
303 }
304
305 static void
306 gtk_option_menu_paint (GtkWidget    *widget,
307                        GdkRectangle *area)
308 {
309   GdkRectangle restrict_area;
310   GdkRectangle new_area;
311
312   g_return_if_fail (widget != NULL);
313   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
314   g_return_if_fail (area != NULL);
315
316   if (GTK_WIDGET_DRAWABLE (widget))
317     {
318       restrict_area.x = GTK_CONTAINER (widget)->border_width;
319       restrict_area.y = GTK_CONTAINER (widget)->border_width;
320       restrict_area.width = widget->allocation.width - restrict_area.x * 2;
321       restrict_area.height = widget->allocation.height - restrict_area.y * 2;
322
323       if (gdk_rectangle_intersect (area, &restrict_area, &new_area))
324         {
325           gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (widget));
326           gdk_window_clear_area (widget->window,
327                                  new_area.x, new_area.y,
328                                  new_area.width, new_area.height);
329
330           gtk_draw_shadow (widget->style, widget->window,
331                            GTK_WIDGET_STATE (widget), GTK_SHADOW_OUT,
332                            restrict_area.x, restrict_area.y,
333                            restrict_area.width, restrict_area.height);
334
335           gtk_draw_shadow (widget->style, widget->window,
336                            GTK_WIDGET_STATE (widget), GTK_SHADOW_OUT,
337                            restrict_area.x + restrict_area.width - restrict_area.x -
338                            OPTION_INDICATOR_WIDTH - OPTION_INDICATOR_SPACING * 4,
339                            restrict_area.y + (restrict_area.height - OPTION_INDICATOR_HEIGHT) / 2,
340                            OPTION_INDICATOR_WIDTH, OPTION_INDICATOR_HEIGHT);
341         }
342     }
343 }
344
345 static void
346 gtk_option_menu_draw (GtkWidget    *widget,
347                       GdkRectangle *area)
348 {
349   GtkWidget *child;
350   GdkRectangle child_area;
351
352   g_return_if_fail (widget != NULL);
353   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
354   g_return_if_fail (area != NULL);
355
356   if (GTK_WIDGET_DRAWABLE (widget))
357     {
358       gtk_option_menu_paint (widget, area);
359
360       child = GTK_BUTTON (widget)->child;
361       if (child && gtk_widget_intersect (child, area, &child_area))
362         gtk_widget_draw (child, &child_area);
363     }
364 }
365
366 static gint
367 gtk_option_menu_expose (GtkWidget      *widget,
368                         GdkEventExpose *event)
369 {
370   GtkWidget *child;
371   GdkEventExpose child_event;
372   gint remove_child;
373
374   g_return_val_if_fail (widget != NULL, FALSE);
375   g_return_val_if_fail (GTK_IS_OPTION_MENU (widget), FALSE);
376   g_return_val_if_fail (event != NULL, FALSE);
377
378   if (GTK_WIDGET_DRAWABLE (widget))
379     {
380       gtk_option_menu_paint (widget, &event->area);
381
382       remove_child = FALSE;
383       child = GTK_BUTTON (widget)->child;
384
385       if (!child)
386         {
387           if (!GTK_OPTION_MENU (widget)->menu)
388             return FALSE;
389           gtk_option_menu_update_contents (GTK_OPTION_MENU (widget));
390           child = GTK_BUTTON (widget)->child;
391           if (!child)
392             return FALSE;
393           remove_child = TRUE;
394         }
395
396       child_event = *event;
397
398       if (GTK_WIDGET_NO_WINDOW (child) &&
399           gtk_widget_intersect (child, &event->area, &child_event.area))
400         gtk_widget_event (child, (GdkEvent*) &child_event);
401
402       if (remove_child)
403         gtk_option_menu_remove_contents (GTK_OPTION_MENU (widget));
404     }
405
406   return FALSE;
407 }
408
409 static gint
410 gtk_option_menu_button_press (GtkWidget      *widget,
411                               GdkEventButton *event)
412 {
413   GtkOptionMenu *option_menu;
414
415   g_return_val_if_fail (widget != NULL, FALSE);
416   g_return_val_if_fail (GTK_IS_OPTION_MENU (widget), FALSE);
417   g_return_val_if_fail (event != NULL, FALSE);
418
419   if ((event->type == GDK_BUTTON_PRESS) &&
420       (event->button == 1))
421     {
422       option_menu = GTK_OPTION_MENU (widget);
423       gtk_option_menu_remove_contents (option_menu);
424       gtk_menu_popup (GTK_MENU (option_menu->menu), NULL, NULL,
425                       gtk_option_menu_position, option_menu,
426                       event->button, event->time);
427     }
428
429   return FALSE;
430 }
431
432 static void
433 gtk_option_menu_deactivate (GtkMenuShell  *menu_shell,
434                             GtkOptionMenu *option_menu)
435 {
436   g_return_if_fail (menu_shell != NULL);
437   g_return_if_fail (option_menu != NULL);
438   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
439
440   gtk_option_menu_update_contents (option_menu);
441 }
442
443 static void
444 gtk_option_menu_update_contents (GtkOptionMenu *option_menu)
445 {
446   GtkWidget *child;
447
448   g_return_if_fail (option_menu != NULL);
449   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
450
451   if (option_menu->menu)
452     {
453       gtk_option_menu_remove_contents (option_menu);
454
455       option_menu->menu_item = gtk_menu_get_active (GTK_MENU (option_menu->menu));
456       if (option_menu->menu_item)
457         {
458           gtk_widget_ref (option_menu->menu_item);
459           child = GTK_BIN (option_menu->menu_item)->child;
460           if (child)
461             {
462               gtk_container_block_resize (GTK_CONTAINER (option_menu));
463               if (GTK_BUTTON (option_menu)->child)
464                 gtk_container_remove (GTK_CONTAINER (option_menu),
465                                       GTK_BUTTON (option_menu)->child);
466               if (GTK_WIDGET (option_menu)->state != child->state)
467                 gtk_widget_set_state (child, GTK_WIDGET (option_menu)->state);
468               gtk_widget_reparent (child, GTK_WIDGET (option_menu));
469               gtk_container_unblock_resize (GTK_CONTAINER (option_menu));
470             }
471
472           gtk_widget_size_request (child, &child->requisition);
473           gtk_widget_size_allocate (GTK_WIDGET (option_menu),
474                                     &(GTK_WIDGET (option_menu)->allocation));
475
476           if (GTK_WIDGET_DRAWABLE (option_menu))
477             gtk_widget_queue_draw (GTK_WIDGET (option_menu));
478         }
479     }
480 }
481
482 static void
483 gtk_option_menu_remove_contents (GtkOptionMenu *option_menu)
484 {
485   g_return_if_fail (option_menu != NULL);
486   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
487
488   if (GTK_BUTTON (option_menu)->child)
489     {
490       gtk_container_block_resize (GTK_CONTAINER (option_menu));
491       if (GTK_WIDGET (option_menu->menu_item)->state != GTK_BUTTON (option_menu)->child->state)
492         gtk_widget_set_state (GTK_BUTTON (option_menu)->child,
493                               GTK_WIDGET (option_menu->menu_item)->state);
494       gtk_widget_unrealize (GTK_BUTTON (option_menu)->child);
495       gtk_widget_reparent (GTK_BUTTON (option_menu)->child, option_menu->menu_item);
496       gtk_widget_unref (option_menu->menu_item);
497       option_menu->menu_item = NULL;
498       gtk_container_unblock_resize (GTK_CONTAINER (option_menu));
499     }
500 }
501
502 static void
503 gtk_option_menu_calc_size (GtkOptionMenu *option_menu)
504 {
505   GtkWidget *child;
506   GList *children;
507
508   g_return_if_fail (option_menu != NULL);
509   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
510
511   option_menu->width = 0;
512   option_menu->height = 0;
513
514   if (option_menu->menu)
515     {
516       children = GTK_MENU_SHELL (option_menu->menu)->children;
517       while (children)
518         {
519           child = children->data;
520           children = children->next;
521
522           if (GTK_WIDGET_VISIBLE (child))
523             {
524               gtk_widget_size_request (child, &child->requisition);
525
526               option_menu->width = MAX (option_menu->width, child->requisition.width);
527               option_menu->height = MAX (option_menu->height, child->requisition.height);
528             }
529         }
530     }
531 }
532
533 static void
534 gtk_option_menu_position (GtkMenu  *menu,
535                           gint     *x,
536                           gint     *y,
537                           gpointer  user_data)
538 {
539   GtkOptionMenu *option_menu;
540   GtkWidget *active;
541   GtkWidget *child;
542   GList *children;
543   gint shift_menu;
544   gint screen_width;
545   gint screen_height;
546   gint menu_xpos;
547   gint menu_ypos;
548   gint width;
549   gint height;
550
551   g_return_if_fail (user_data != NULL);
552   g_return_if_fail (GTK_IS_OPTION_MENU (user_data));
553
554   option_menu = GTK_OPTION_MENU (user_data);
555
556   width = GTK_WIDGET (menu)->allocation.width;
557   height = GTK_WIDGET (menu)->allocation.height;
558
559   active = gtk_menu_get_active (GTK_MENU (option_menu->menu));
560   children = GTK_MENU_SHELL (option_menu->menu)->children;
561   gdk_window_get_origin (GTK_WIDGET (option_menu)->window, &menu_xpos, &menu_ypos);
562
563   menu_ypos += GTK_WIDGET (option_menu)->allocation.height / 2 - 2;
564
565   if (active != NULL)
566     menu_ypos -= active->requisition.height / 2;
567
568   while (children)
569     {
570       child = children->data;
571
572       if (active == child)
573         break;
574
575       menu_ypos -= child->allocation.height;
576       children = children->next;
577     }
578
579   screen_width = gdk_screen_width ();
580   screen_height = gdk_screen_height ();
581
582   shift_menu = FALSE;
583   if (menu_ypos < 0)
584     {
585       menu_ypos = 0;
586       shift_menu = TRUE;
587     }
588   else if ((menu_ypos + height) > screen_height)
589     {
590       menu_ypos -= ((menu_ypos + height) - screen_height);
591       shift_menu = TRUE;
592     }
593
594   if (shift_menu)
595     {
596       if ((menu_xpos + GTK_WIDGET (option_menu)->allocation.width + width) <= screen_width)
597         menu_xpos += GTK_WIDGET (option_menu)->allocation.width;
598       else
599         menu_xpos -= width;
600     }
601
602   if (menu_xpos < 0)
603     menu_xpos = 0;
604   else if ((menu_xpos + width) > screen_width)
605     menu_xpos -= ((menu_xpos + width) - screen_width);
606
607   *x = menu_xpos;
608   *y = menu_ypos;
609 }
610
611
612 static void
613 gtk_option_menu_show_all (GtkWidget *widget)
614 {
615   GtkContainer *container;
616   GtkOptionMenu *option_menu;
617   
618   g_return_if_fail (widget != NULL);
619   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
620   container = GTK_CONTAINER (widget);
621   option_menu = GTK_OPTION_MENU (widget);
622
623   gtk_widget_show (widget);
624   gtk_container_foreach (container, (GtkCallback) gtk_widget_show_all, NULL);
625   if (option_menu->menu)
626     gtk_widget_show_all (option_menu->menu);
627   if (option_menu->menu_item)
628     gtk_widget_show_all (option_menu->menu_item);
629 }
630
631
632 static void
633 gtk_option_menu_hide_all (GtkWidget *widget)
634 {
635   GtkContainer *container;
636
637   g_return_if_fail (widget != NULL);
638   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
639   container = GTK_CONTAINER (widget);
640
641   gtk_widget_hide (widget);
642   gtk_container_foreach (container, (GtkCallback) gtk_widget_hide_all, NULL);
643 }
644