]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenu.c
Initial revision
[~andy/gtk] / gtk / gtkmenu.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 <ctype.h>
19 #include "gdk/gdkkeysyms.h"
20 #include "gtkmain.h"
21 #include "gtkmenu.h"
22 #include "gtkmenuitem.h"
23 #include "gtksignal.h"
24
25
26 #define MENU_ITEM_CLASS(w)  GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
27
28
29 static void gtk_menu_class_init     (GtkMenuClass      *klass);
30 static void gtk_menu_init           (GtkMenu           *menu);
31 static void gtk_menu_show           (GtkWidget         *widget);
32 static void gtk_menu_map            (GtkWidget         *widget);
33 static void gtk_menu_unmap          (GtkWidget         *widget);
34 static void gtk_menu_realize        (GtkWidget         *widget);
35 static void gtk_menu_size_request   (GtkWidget         *widget,
36                                      GtkRequisition    *requisition);
37 static void gtk_menu_size_allocate  (GtkWidget         *widget,
38                                      GtkAllocation     *allocation);
39 static void gtk_menu_paint          (GtkWidget         *widget);
40 static void gtk_menu_draw           (GtkWidget         *widget,
41                                      GdkRectangle      *area);
42 static gint gtk_menu_expose         (GtkWidget         *widget,
43                                      GdkEventExpose    *event);
44 static gint gtk_menu_configure      (GtkWidget         *widget,
45                                      GdkEventConfigure *event);
46 static gint gtk_menu_key_press      (GtkWidget         *widget,
47                                      GdkEventKey       *event);
48 static gint gtk_menu_need_resize    (GtkContainer      *container);
49 static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
50
51
52 guint
53 gtk_menu_get_type ()
54 {
55   static guint menu_type = 0;
56
57   if (!menu_type)
58     {
59       GtkTypeInfo menu_info =
60       {
61         "GtkMenu",
62         sizeof (GtkMenu),
63         sizeof (GtkMenuClass),
64         (GtkClassInitFunc) gtk_menu_class_init,
65         (GtkObjectInitFunc) gtk_menu_init,
66         (GtkArgFunc) NULL,
67       };
68
69       menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
70     }
71
72   return menu_type;
73 }
74
75 static void
76 gtk_menu_class_init (GtkMenuClass *class)
77 {
78   GtkWidgetClass *widget_class;
79   GtkContainerClass *container_class;
80   GtkMenuShellClass *menu_shell_class;
81
82   widget_class = (GtkWidgetClass*) class;
83   container_class = (GtkContainerClass*) class;
84   menu_shell_class = (GtkMenuShellClass*) class;
85
86   widget_class->show = gtk_menu_show;
87   widget_class->map = gtk_menu_map;
88   widget_class->unmap = gtk_menu_unmap;
89   widget_class->realize = gtk_menu_realize;
90   widget_class->draw = gtk_menu_draw;
91   widget_class->size_request = gtk_menu_size_request;
92   widget_class->size_allocate = gtk_menu_size_allocate;
93   widget_class->expose_event = gtk_menu_expose;
94   widget_class->configure_event = gtk_menu_configure;
95   widget_class->key_press_event = gtk_menu_key_press;
96
97   container_class->need_resize = gtk_menu_need_resize;
98
99   menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
100   menu_shell_class->deactivate = gtk_menu_deactivate;
101 }
102
103 static void
104 gtk_menu_init (GtkMenu *menu)
105 {
106   GTK_WIDGET_SET_FLAGS (menu, GTK_ANCHORED);
107
108   menu->parent_menu_item = NULL;
109   menu->old_active_menu_item = NULL;
110   menu->accelerator_table = NULL;
111   menu->position_func = NULL;
112   menu->position_func_data = NULL;
113
114   GTK_MENU_SHELL (menu)->menu_flag = TRUE;
115 }
116
117 GtkWidget*
118 gtk_menu_new ()
119 {
120   return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
121 }
122
123 void
124 gtk_menu_append (GtkMenu   *menu,
125                  GtkWidget *child)
126 {
127   gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
128 }
129
130 void
131 gtk_menu_prepend (GtkMenu   *menu,
132                   GtkWidget *child)
133 {
134   gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), child);
135 }
136
137 void
138 gtk_menu_insert (GtkMenu   *menu,
139                  GtkWidget *child,
140                  gint       position)
141 {
142   gtk_menu_shell_insert (GTK_MENU_SHELL (menu), child, position);
143 }
144
145 void
146 gtk_menu_popup (GtkMenu             *menu,
147                 GtkWidget           *parent_menu_shell,
148                 GtkWidget           *parent_menu_item,
149                 GtkMenuPositionFunc  func,
150                 gpointer             data,
151                 gint                 button,
152                 guint32              activate_time)
153 {
154   g_return_if_fail (menu != NULL);
155   g_return_if_fail (GTK_IS_MENU (menu));
156
157   GTK_MENU_SHELL (menu)->parent_menu_shell = parent_menu_shell;
158   GTK_MENU_SHELL (menu)->active = TRUE;
159   GTK_MENU_SHELL (menu)->button = button;
160
161   menu->parent_menu_item = parent_menu_item;
162   menu->position_func = func;
163   menu->position_func_data = data;
164   GTK_MENU_SHELL (menu)->activate_time = activate_time;
165
166   gtk_widget_show (GTK_WIDGET (menu));
167   gtk_grab_add (GTK_WIDGET (menu));
168 }
169
170 void
171 gtk_menu_popdown (GtkMenu *menu)
172 {
173   GtkMenuShell *menu_shell;
174
175   g_return_if_fail (menu != NULL);
176   g_return_if_fail (GTK_IS_MENU (menu));
177
178   menu_shell = GTK_MENU_SHELL (menu);
179
180   menu_shell->parent_menu_shell = NULL;
181   menu_shell->active = FALSE;
182
183   if (menu_shell->active_menu_item)
184     {
185       menu->old_active_menu_item = menu_shell->active_menu_item;
186       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
187       menu_shell->active_menu_item = NULL;
188     }
189
190   gtk_widget_hide (GTK_WIDGET (menu));
191   gtk_grab_remove (GTK_WIDGET (menu));
192 }
193
194 GtkWidget*
195 gtk_menu_get_active (GtkMenu *menu)
196 {
197   GtkWidget *child;
198   GList *children;
199
200   g_return_val_if_fail (menu != NULL, NULL);
201   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
202
203   if (!menu->old_active_menu_item)
204     {
205       child = NULL;
206       children = GTK_MENU_SHELL (menu)->children;
207
208       while (children)
209         {
210           child = children->data;
211           children = children->next;
212
213           if (GTK_BIN (child)->child)
214             break;
215           child = NULL;
216         }
217
218       menu->old_active_menu_item = child;
219     }
220
221   return menu->old_active_menu_item;
222 }
223
224 void
225 gtk_menu_set_active (GtkMenu *menu,
226                      gint     index)
227 {
228   GtkWidget *child;
229   GList *tmp_list;
230
231   g_return_if_fail (menu != NULL);
232   g_return_if_fail (GTK_IS_MENU (menu));
233
234   tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
235   if (tmp_list)
236     {
237       child = tmp_list->data;
238       if (GTK_BIN (child)->child)
239         menu->old_active_menu_item = child;
240     }
241 }
242
243 void
244 gtk_menu_set_accelerator_table (GtkMenu             *menu,
245                                 GtkAcceleratorTable *table)
246 {
247   g_return_if_fail (menu != NULL);
248   g_return_if_fail (GTK_IS_MENU (menu));
249
250   if (menu->accelerator_table)
251     gtk_accelerator_table_unref (menu->accelerator_table);
252
253   menu->accelerator_table = table;
254   if (menu->accelerator_table)
255     gtk_accelerator_table_ref (menu->accelerator_table);
256 }
257
258
259 static void
260 gtk_menu_show (GtkWidget *widget)
261 {
262   g_return_if_fail (widget != NULL);
263   g_return_if_fail (GTK_IS_MENU (widget));
264
265   GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE);
266   gtk_widget_map (widget);
267 }
268
269 static void
270 gtk_menu_map (GtkWidget *widget)
271 {
272   GtkMenu *menu;
273   GtkMenuShell *menu_shell;
274   GtkWidget *child;
275   GList *children;
276   GtkAllocation allocation;
277   gint x, y;
278
279   g_return_if_fail (widget != NULL);
280   g_return_if_fail (GTK_IS_MENU (widget));
281
282   menu = GTK_MENU (widget);
283   menu_shell = GTK_MENU_SHELL (widget);
284   GTK_WIDGET_SET_FLAGS (menu_shell, GTK_MAPPED);
285   GTK_WIDGET_UNSET_FLAGS (menu_shell, GTK_UNMAPPED);
286
287   gtk_widget_size_request (widget, &widget->requisition);
288
289   if (menu_shell->menu_flag)
290     {
291       menu_shell->menu_flag = FALSE;
292
293       allocation.x = widget->allocation.x;
294       allocation.y = widget->allocation.y;
295       allocation.width = widget->requisition.width;
296       allocation.height = widget->requisition.height;
297
298       gtk_widget_size_allocate (widget, &allocation);
299     }
300
301   gdk_window_get_pointer (NULL, &x, &y, NULL);
302
303   if (menu->position_func)
304     (* menu->position_func) (menu, &x, &y, menu->position_func_data);
305   else
306     {
307       gint screen_width;
308       gint screen_height;
309
310       screen_width = gdk_screen_width ();
311       screen_height = gdk_screen_height ();
312
313       x -= 2;
314       y -= 2;
315
316       if ((x + widget->requisition.width) > screen_width)
317         x -= ((x + widget->requisition.width) - screen_width);
318       if (x < 0)
319         x = 0;
320       if ((y + widget->requisition.height) > screen_height)
321         y -= ((y + widget->requisition.height) - screen_height);
322       if (y < 0)
323         y = 0;
324     }
325
326   gdk_window_move_resize (widget->window, x, y,
327                           widget->requisition.width,
328                           widget->requisition.height);
329
330   children = menu_shell->children;
331   while (children)
332     {
333       child = children->data;
334       children = children->next;
335
336       if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
337         gtk_widget_map (child);
338     }
339
340   gdk_window_show (widget->window);
341 }
342
343 static void
344 gtk_menu_unmap (GtkWidget *widget)
345 {
346   g_return_if_fail (widget != NULL);
347   g_return_if_fail (GTK_IS_MENU (widget));
348
349   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
350   GTK_WIDGET_SET_FLAGS (widget, GTK_UNMAPPED);
351   gdk_window_hide (widget->window);
352 }
353
354 static void
355 gtk_menu_realize (GtkWidget *widget)
356 {
357   GdkWindowAttr attributes;
358   gint attributes_mask;
359
360   g_return_if_fail (widget != NULL);
361   g_return_if_fail (GTK_IS_MENU (widget));
362
363   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
364
365   attributes.x = widget->allocation.x;
366   attributes.y = widget->allocation.y;
367   attributes.width = widget->allocation.width;
368   attributes.height = widget->allocation.height;
369   attributes.wclass = GDK_INPUT_OUTPUT;
370   attributes.visual = gtk_widget_get_visual (widget);
371   attributes.colormap = gtk_widget_get_colormap (widget);
372   attributes.window_type = GDK_WINDOW_TEMP;
373   attributes.event_mask = gtk_widget_get_events (widget);
374   attributes.event_mask |= (GDK_EXPOSURE_MASK |
375                             GDK_KEY_PRESS_MASK |
376                             GDK_STRUCTURE_MASK);
377
378   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
379   widget->window = gdk_window_new (NULL, &attributes, attributes_mask);
380   gdk_window_set_user_data (widget->window, widget);
381
382   widget->style = gtk_style_attach (widget->style, widget->window);
383   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
384 }
385
386 static void
387 gtk_menu_size_request (GtkWidget      *widget,
388                        GtkRequisition *requisition)
389 {
390   GtkMenu *menu;
391   GtkMenuShell *menu_shell;
392   GtkWidget *child;
393   GList *children;
394   gint max_accelerator_size;
395   gint max_toggle_size;
396
397   g_return_if_fail (widget != NULL);
398   g_return_if_fail (GTK_IS_MENU (widget));
399   g_return_if_fail (requisition != NULL);
400
401   menu = GTK_MENU (widget);
402   menu_shell = GTK_MENU_SHELL (widget);
403
404   requisition->width = 0;
405   requisition->height = 0;
406
407   max_accelerator_size = 0;
408   max_toggle_size = 0;
409
410   children = menu_shell->children;
411   while (children)
412     {
413       child = children->data;
414       children = children->next;
415
416       if (GTK_WIDGET_VISIBLE (child))
417         {
418           GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
419           gtk_widget_size_request (child, &child->requisition);
420
421           requisition->width = MAX (requisition->width, child->requisition.width);
422           requisition->height += child->requisition.height;
423
424           max_accelerator_size = MAX (max_accelerator_size, GTK_MENU_ITEM (child)->accelerator_size);
425           max_toggle_size = MAX (max_toggle_size, MENU_ITEM_CLASS (child)->toggle_size);
426         }
427     }
428
429   requisition->width += max_toggle_size + max_accelerator_size;
430   requisition->width += (GTK_CONTAINER (menu)->border_width +
431                          widget->style->klass->xthickness) * 2;
432   requisition->height += (GTK_CONTAINER (menu)->border_width +
433                           widget->style->klass->ythickness) * 2;
434
435   children = menu_shell->children;
436   while (children)
437     {
438       child = children->data;
439       children = children->next;
440
441       GTK_MENU_ITEM (child)->accelerator_size = max_accelerator_size;
442       GTK_MENU_ITEM (child)->toggle_size = max_toggle_size;
443     }
444 }
445
446 static void
447 gtk_menu_size_allocate (GtkWidget     *widget,
448                         GtkAllocation *allocation)
449 {
450   GtkMenu *menu;
451   GtkMenuShell *menu_shell;
452   GtkWidget *child;
453   GtkAllocation child_allocation;
454   GList *children;
455
456   g_return_if_fail (widget != NULL);
457   g_return_if_fail (GTK_IS_MENU (widget));
458   g_return_if_fail (allocation != NULL);
459
460   menu = GTK_MENU (widget);
461   menu_shell = GTK_MENU_SHELL (widget);
462
463   widget->allocation = *allocation;
464
465   if (menu_shell->children)
466     {
467       child_allocation.x = (GTK_CONTAINER (menu)->border_width +
468                             widget->style->klass->xthickness);
469       child_allocation.y = (GTK_CONTAINER (menu)->border_width +
470                             widget->style->klass->ythickness);
471       child_allocation.width = allocation->width - child_allocation.x * 2;
472
473       children = menu_shell->children;
474       while (children)
475         {
476           child = children->data;
477           children = children->next;
478
479           if (GTK_WIDGET_VISIBLE (child))
480             {
481               child_allocation.height = child->requisition.height;
482
483               gtk_widget_size_allocate (child, &child_allocation);
484
485               child_allocation.y += child_allocation.height;
486             }
487         }
488     }
489 }
490
491 static void
492 gtk_menu_paint (GtkWidget *widget)
493 {
494   g_return_if_fail (widget != NULL);
495   g_return_if_fail (GTK_IS_MENU (widget));
496
497   if (GTK_WIDGET_DRAWABLE (widget))
498     {
499       gtk_draw_shadow (widget->style,
500                        widget->window,
501                        GTK_STATE_NORMAL,
502                        GTK_SHADOW_OUT,
503                        0, 0,
504                        widget->allocation.width,
505                        widget->allocation.height);
506     }
507 }
508
509 static void
510 gtk_menu_draw (GtkWidget    *widget,
511                GdkRectangle *area)
512 {
513   GtkMenuShell *menu_shell;
514   GtkWidget *child;
515   GdkRectangle child_area;
516   GList *children;
517
518   g_return_if_fail (widget != NULL);
519   g_return_if_fail (GTK_IS_MENU (widget));
520   g_return_if_fail (area != NULL);
521
522   if (GTK_WIDGET_DRAWABLE (widget))
523     {
524       gtk_menu_paint (widget);
525
526       menu_shell = GTK_MENU_SHELL (widget);
527
528       children = menu_shell->children;
529       while (children)
530         {
531           child = children->data;
532           children = children->next;
533
534           if (gtk_widget_intersect (child, area, &child_area))
535             gtk_widget_draw (child, &child_area);
536         }
537     }
538 }
539
540 static gint
541 gtk_menu_expose (GtkWidget      *widget,
542                  GdkEventExpose *event)
543 {
544   GtkMenuShell *menu_shell;
545   GtkWidget *child;
546   GdkEventExpose child_event;
547   GList *children;
548
549   g_return_val_if_fail (widget != NULL, FALSE);
550   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
551   g_return_val_if_fail (event != NULL, FALSE);
552
553   if (!GTK_WIDGET_UNMAPPED (widget))
554     GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
555
556   if (GTK_WIDGET_DRAWABLE (widget))
557     {
558       gtk_menu_paint (widget);
559
560       menu_shell = GTK_MENU_SHELL (widget);
561       child_event = *event;
562
563       children = menu_shell->children;
564       while (children)
565         {
566           child = children->data;
567           children = children->next;
568
569           if (GTK_WIDGET_NO_WINDOW (child) &&
570               gtk_widget_intersect (child, &event->area, &child_event.area))
571             gtk_widget_event (child, (GdkEvent*) &child_event);
572         }
573     }
574
575   return FALSE;
576 }
577
578 static gint
579 gtk_menu_configure (GtkWidget         *widget,
580                     GdkEventConfigure *event)
581 {
582   GtkAllocation allocation;
583
584   g_return_val_if_fail (widget != NULL, FALSE);
585   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
586   g_return_val_if_fail (event != NULL, FALSE);
587
588   if (GTK_MENU_SHELL (widget)->menu_flag)
589     {
590       GTK_MENU_SHELL (widget)->menu_flag = FALSE;
591
592       allocation.x = 0;
593       allocation.y = 0;
594       allocation.width = event->width;
595       allocation.height = event->height;
596
597       gtk_widget_size_allocate (widget, &allocation);
598     }
599
600   return FALSE;
601 }
602
603 static gint
604 gtk_menu_key_press (GtkWidget   *widget,
605                     GdkEventKey *event)
606 {
607   GtkAllocation allocation;
608   GtkAcceleratorTable *table;
609   GtkMenuShell *menu_shell;
610   GtkMenuItem *menu_item;
611   gchar *signame;
612   int delete;
613
614   g_return_val_if_fail (widget != NULL, FALSE);
615   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
616   g_return_val_if_fail (event != NULL, FALSE);
617
618   delete = ((event->keyval == GDK_Delete) ||
619             (event->keyval == GDK_BackSpace));
620
621   if (delete || ((event->keyval >= 0x20) && (event->keyval <= 0xFF)))
622     {
623       menu_shell = GTK_MENU_SHELL (widget);
624       menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
625
626       if (menu_item && GTK_BIN (menu_item)->child)
627         {
628           /* block resizes */
629           gtk_container_block_resize (GTK_CONTAINER (widget));
630
631           table = NULL;
632           /* if the menu item currently has an accelerator then we'll
633            *  remove it before we do anything else.
634            */
635           if (menu_item->accelerator_signal)
636             {
637               signame = gtk_signal_name (menu_item->accelerator_signal);
638               table = gtk_accelerator_table_find (GTK_OBJECT (widget),
639                                                   signame,
640                                                   menu_item->accelerator_key,
641                                                   menu_item->accelerator_mods);
642               if (!table)
643                 table = GTK_MENU (widget)->accelerator_table;
644               gtk_widget_remove_accelerator (GTK_WIDGET (menu_item),
645                                              table, signame);
646             }
647
648           if (!table)
649             table = GTK_MENU (widget)->accelerator_table;
650
651           /* if we aren't simply deleting the accelerator, then we'll install
652            *  the new one now.
653            */
654           if (!delete)
655             gtk_widget_install_accelerator (GTK_WIDGET (menu_item),
656                                             table, "activate",
657                                             toupper (event->keyval),
658                                             event->state);
659
660           /* check and see if the menu has changed size. */
661           gtk_widget_size_request (widget, &widget->requisition);
662
663           allocation.x = widget->allocation.x;
664           allocation.y = widget->allocation.y;
665           allocation.width = widget->requisition.width;
666           allocation.height = widget->requisition.height;
667
668           if ((allocation.width == widget->allocation.width) &&
669               (allocation.height == widget->allocation.height))
670             {
671               gtk_widget_queue_draw (widget);
672             }
673           else
674             {
675               gtk_widget_size_allocate (GTK_WIDGET (widget), &allocation);
676               gtk_menu_map (widget);
677             }
678
679           /* unblock resizes */
680           gtk_container_unblock_resize (GTK_CONTAINER (widget));
681         }
682     }
683
684   return FALSE;
685 }
686
687 static gint
688 gtk_menu_need_resize (GtkContainer *container)
689 {
690   GtkAllocation allocation;
691
692   g_return_val_if_fail (container != NULL, FALSE);
693   g_return_val_if_fail (GTK_IS_MENU (container), FALSE);
694
695   if (GTK_WIDGET_VISIBLE (container))
696     {
697       GTK_MENU_SHELL (container)->menu_flag = FALSE;
698
699       gtk_widget_size_request (GTK_WIDGET (container),
700                                &GTK_WIDGET (container)->requisition);
701
702       allocation.x = GTK_WIDGET (container)->allocation.x;
703       allocation.y = GTK_WIDGET (container)->allocation.y;
704       allocation.width = GTK_WIDGET (container)->requisition.width;
705       allocation.height = GTK_WIDGET (container)->requisition.height;
706
707       gtk_widget_size_allocate (GTK_WIDGET (container), &allocation);
708     }
709   else
710     {
711       GTK_MENU_SHELL (container)->menu_flag = TRUE;
712     }
713
714   return FALSE;
715 }
716
717 static void
718 gtk_menu_deactivate (GtkMenuShell *menu_shell)
719 {
720   GtkMenuShell *parent;
721
722   g_return_if_fail (menu_shell != NULL);
723   g_return_if_fail (GTK_IS_MENU (menu_shell));
724
725   parent = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
726
727   menu_shell->activate_time = 0;
728   gtk_menu_popdown (GTK_MENU (menu_shell));
729
730   if (parent)
731     gtk_menu_shell_deactivate (parent);
732 }