2008-12-09 Claudio Saavedra <csaavedra@igalia.com>
[hildon] / src / hildon-window.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-window
27  * @short_description: Widget representing a top-level window in the Hildon framework.
28  *
29  * The HildonWindow is a GTK widget which represents a top-level
30  * window in the Hildon framework. It is derived from the GtkWindow
31  * and provides additional commodities specific to the Hildon
32  * framework.
33  *
34  * Among these windows in the Hildon framework can have a single menu
35  * attached, which is toggled with a hardware key or by tapping
36  * a custom button in the window frame. This menu can be set
37  * by providing a GtkMenu to the hildon_window_set_main_menu() method.
38  *
39  * Similarly a window in the Hildon framework can have several toolbars
40  * attached. These can be added to the HildonWindow with
41  * hildon_window_add_toolbar().
42  *
43  * <example>
44  * <title>Creating a HildonWindow</title>
45  * <programlisting>
46  * HildonWindow *window;
47  * GtkToolbar *toolbar;
48  * GtkMenu *menu;
49  * GdkPixbuf *icon_pixbuf;
50  * <!-- -->
51  * window = HILDON_WINDOW (hildon_window_new());
52  * <!-- -->
53  * toolbar = create_toolbar();
54  * <!-- -->
55  * menu = create_menu();
56  * <!-- -->
57  * icon_pixbuf = create_icon();
58  * <!-- -->
59  * hildon_window_set_main_menu (window, menu);
60  * <!-- -->
61  * hildon_window_add_toolbar (window, toolbar);
62  * <!-- -->
63  * // Can be used to set the window fullscreen
64  * gtk_window_fullscreen (GTK_WINDOW (window));
65  * <!-- -->
66  * // Used to trigger the blinking of the window's icon in the task navigator
67  * gtk_window_set_urgency_hint (GTK_WINDOW (window), TRUE);
68  * <!-- -->
69  * // Change the window's icon in the task navigator
70  * gtk_window_set_icon (GTK_WINDOW (window), icon_pixbuf);
71  * </programlisting>
72  * </example>
73  */
74
75 #undef                                          HILDON_DISABLE_DEPRECATED
76
77 #include                                        <memory.h>
78 #include                                        <string.h>
79 #include                                        <strings.h>
80 #include                                        <stdio.h>
81 #include                                        <libintl.h>
82 #include                                        <X11/X.h>
83 #include                                        <X11/Xatom.h>
84 #include                                        <gdk/gdkkeysyms.h>
85 #include                                        <gdk/gdkx.h>
86 #include                                        <gtk/gtkprivate.h>
87
88 #include                                        "hildon-window.h"
89 #include                                        "hildon-window-private.h"
90 #include                                        "hildon-find-toolbar.h"
91 #include                                        "hildon-defines.h"
92
93 #define                                         _(String) gettext(String)
94
95 #define                                         TOOLBAR_HEIGHT 70
96
97 #define                                         TOOLBAR_MIDDLE 0
98
99 /*FIXME*/
100 #define                                         CAN_HIBERNATE "CANKILL"
101
102 #define                                         CAN_HIBERNATE_LENGTH 7
103
104 #define                                         CAN_HIBERNATE_PROPERTY "_HILDON_ABLE_TO_HIBERNATE"
105
106 #define TITLE_SEPARATOR                         " - "
107
108 typedef void                                    (*HildonWindowSignal) (HildonWindow *, gint, gpointer);
109
110 static void
111 hildon_window_init                              (HildonWindow * self);
112
113 static void
114 hildon_window_class_init                        (HildonWindowClass * window_class);
115
116 static void
117 hildon_window_menu_popup_func                   (GtkMenu *menu, 
118                                                  gint *x, 
119                                                  gint *y,
120                                                  gboolean *push_in,
121                                                  GtkWidget *widget);
122 static void
123 hildon_window_menu_popup_func_full              (GtkMenu *menu, 
124                                                  gint *x, 
125                                                  gint *y,
126                                                  gboolean *push_in,
127                                                  GtkWidget *widget);
128 static gboolean
129 hildon_window_expose                            (GtkWidget *widget, 
130                                                  GdkEventExpose *event);
131 static void 
132 hildon_window_forall                            (GtkContainer *container,
133                                                  gboolean include_internals,
134                                                  GtkCallback callback,
135                                                  gpointer callback_data);
136 static void
137 hildon_window_show_all                          (GtkWidget *widget);
138
139 static void
140 hildon_window_size_allocate                     (GtkWidget * widget,
141                                                  GtkAllocation *allocation);
142 static void
143 hildon_window_size_request                      (GtkWidget * widget,
144                                                  GtkRequisition *requisition);
145 static void
146 hildon_window_finalize                          (GObject *obj_self);
147
148 static void
149 hildon_window_get_property                      (GObject *object,
150                                                  guint property_id,
151                                                  GValue *value, 
152                                                  GParamSpec *pspec);
153
154 static void
155 hildon_window_destroy                           (GtkObject *obj);
156
157 static void
158 hildon_window_realize                           (GtkWidget *widget);
159
160 static void
161 hildon_window_unrealize                         (GtkWidget *widget);
162
163 static void
164 hildon_window_map                               (GtkWidget *widget);
165
166 static void
167 hildon_window_unmap                             (GtkWidget *widget);
168
169 static gboolean
170 hildon_window_key_press_event                   (GtkWidget *widget,
171                                                  GdkEventKey *event);
172
173 static gboolean
174 hildon_window_key_release_event                 (GtkWidget *widget, 
175                                                  GdkEventKey *event);
176 static gboolean
177 hildon_window_window_state_event                (GtkWidget *widget, 
178                                                  GdkEventWindowState *event);
179 static gboolean
180 hildon_window_focus_out_event                   (GtkWidget *widget, 
181                                                  GdkEventFocus *event);
182
183 static void
184 hildon_window_notify                            (GObject *gobject, 
185                                                  GParamSpec *param);
186
187 static void
188 hildon_window_is_topmost_notify                 (HildonWindow *window);
189
190 static gboolean
191 hildon_window_toggle_menu                       (HildonWindow * self,
192                                                  guint button,
193                                                  guint32 time);
194
195 static gboolean
196 hildon_window_toggle_menu_real                  (HildonWindow * self,
197                                                  guint button,
198                                                  guint32 time);
199
200 static gboolean
201 hildon_window_escape_timeout                    (gpointer data);
202
203 static GdkFilterReturn
204 hildon_window_event_filter                      (GdkXEvent *xevent, 
205                                                  GdkEvent *event, 
206                                                  gpointer data);
207
208 static GdkFilterReturn
209 hildon_window_root_window_event_filter          (GdkXEvent *xevent, 
210                                                  GdkEvent *event, 
211                                                  gpointer data );
212
213 static void
214 hildon_window_get_borders                       (HildonWindow *window);
215
216 static void
217 visible_toolbar                                 (gpointer data, 
218                                                  gpointer user_data);
219
220 static void
221 paint_toolbar                                   (GtkWidget *widget, 
222                                                  GtkBox *box, 
223                                                  GdkEventExpose * event, 
224                                                  gboolean fullscreen);
225
226 enum
227 {
228     PROP_0,
229     PROP_IS_TOPMOST
230 };
231
232 enum
233 {
234     WIN_TYPE = 0,
235     WIN_TYPE_MESSAGE,
236     MAX_WIN_MESSAGES
237 };
238
239 G_DEFINE_TYPE (HildonWindow, hildon_window, GTK_TYPE_WINDOW);
240
241 static void 
242 hildon_window_class_init                        (HildonWindowClass * window_class)
243 {
244     /* Get convenience variables */
245     GtkWidgetClass *widget_class        = GTK_WIDGET_CLASS (window_class);
246     GObjectClass *object_class          = G_OBJECT_CLASS (window_class);
247     GtkContainerClass *container_class  = GTK_CONTAINER_CLASS (window_class);
248
249     object_class->get_property          = hildon_window_get_property;
250     object_class->notify                = hildon_window_notify;
251     widget_class->size_allocate         = hildon_window_size_allocate;
252     widget_class->size_request          = hildon_window_size_request;
253     widget_class->expose_event          = hildon_window_expose;
254     widget_class->show_all              = hildon_window_show_all;
255     widget_class->realize               = hildon_window_realize;
256     widget_class->unrealize             = hildon_window_unrealize;
257     widget_class->key_press_event       = hildon_window_key_press_event;
258     widget_class->key_release_event     = hildon_window_key_release_event;
259     widget_class->window_state_event    = hildon_window_window_state_event;
260     widget_class->focus_out_event       = hildon_window_focus_out_event;
261     widget_class->map                   = hildon_window_map;
262     widget_class->unmap                 = hildon_window_unmap;
263
264     /* now the object stuff */
265     object_class->finalize              = hildon_window_finalize;
266
267     /* To the container */
268     container_class->forall             = hildon_window_forall;
269
270     /* To this class */
271     window_class->toggle_menu           = hildon_window_toggle_menu_real;
272
273     /* gtkobject stuff*/
274     GTK_OBJECT_CLASS (window_class)->destroy = hildon_window_destroy; 
275
276     g_type_class_add_private (window_class,
277             sizeof (struct _HildonWindowPrivate));
278
279     /* Install properties */
280
281     g_object_class_install_property (object_class, PROP_IS_TOPMOST,
282             g_param_spec_boolean ("is-topmost",
283                 "Is top-most",
284                 "Whether the window is currently activated by the window "
285                 "manager",
286                 FALSE,
287                 G_PARAM_READABLE));
288
289     gtk_widget_class_install_style_property (widget_class,
290             g_param_spec_boxed ("borders",
291                 "Graphical borders",
292                 "Size of graphical window borders",
293                 GTK_TYPE_BORDER,
294                 G_PARAM_READABLE));
295
296     gtk_widget_class_install_style_property (widget_class,
297             g_param_spec_boxed ("toolbar-borders",
298                 "Graphical toolbar borders",
299                 "Size of graphical toolbar borders",
300                 GTK_TYPE_BORDER,
301                 G_PARAM_READABLE));
302
303     /* opera hack, install clip operation signal */
304     g_signal_new ("clipboard_operation",
305             G_OBJECT_CLASS_TYPE (object_class),
306             G_SIGNAL_RUN_FIRST,
307             G_STRUCT_OFFSET (HildonWindowClass, clipboard_operation),
308             NULL, NULL,
309             g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1,
310             G_TYPE_INT);
311 }
312
313 static void
314 hildon_window_init                              (HildonWindow *self)
315 {
316     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
317     g_assert (priv != NULL);
318
319     priv->vbox = gtk_vbox_new (TRUE, TOOLBAR_MIDDLE);
320     gtk_widget_set_parent (priv->vbox, GTK_WIDGET(self));
321     priv->menu = NULL;
322     priv->visible_toolbars = 0;
323     priv->is_topmost = FALSE;
324     priv->borders = NULL;
325     priv->toolbar_borders = NULL;
326     priv->escape_timeout = 0;
327
328     priv->fullscreen = FALSE;
329
330     priv->program = NULL;
331
332     /* We need to track the root window _MB_CURRENT_APP_WINDOW property */
333     gdk_window_set_events (gdk_get_default_root_window (),
334             gdk_window_get_events (gdk_get_default_root_window ()) | GDK_PROPERTY_CHANGE_MASK);
335
336     gdk_window_add_filter (gdk_get_default_root_window (), 
337             hildon_window_root_window_event_filter, self);
338 }
339
340 static void
341 hildon_window_finalize                          (GObject * obj_self)
342 {
343     HildonWindow *self;
344     HildonWindowPrivate *priv; 
345       
346     g_return_if_fail (HILDON_WINDOW (obj_self));
347
348     priv = HILDON_WINDOW_GET_PRIVATE (obj_self);
349     g_assert (priv != NULL);
350     
351     self = HILDON_WINDOW (obj_self);
352
353     if (priv->escape_timeout) {
354       g_source_remove (priv->escape_timeout);
355       priv->escape_timeout = 0;
356     }
357
358     if (priv->borders)
359         gtk_border_free (priv->borders);
360
361     if (priv->toolbar_borders)
362         gtk_border_free (priv->toolbar_borders);
363
364     if (G_OBJECT_CLASS (hildon_window_parent_class)->finalize)
365         G_OBJECT_CLASS (hildon_window_parent_class)->finalize (obj_self);
366
367 }
368
369 static void
370 hildon_window_realize                           (GtkWidget *widget)
371 {
372     Atom *old_atoms, *new_atoms;
373     Display *disp;
374     Window window;
375     gint atom_count;
376     Window active_window;
377     HildonWindowPrivate *priv;
378
379     GTK_WIDGET_CLASS (hildon_window_parent_class)->realize (widget);
380
381     priv = HILDON_WINDOW_GET_PRIVATE (widget);
382     g_assert (priv != NULL);
383
384     gtk_widget_realize (GTK_WIDGET (priv->vbox));
385
386     /* catch the custom button signal from mb to display the menu */
387     gdk_window_add_filter (widget->window, hildon_window_event_filter, widget);
388
389     window = GDK_WINDOW_XID (widget->window);
390     disp = GDK_WINDOW_XDISPLAY (widget->window);
391
392     /* Enable custom button that is used for menu */
393     XGetWMProtocols (disp, window, &old_atoms, &atom_count);
394     new_atoms = g_new (Atom, atom_count + 1);
395
396     memcpy (new_atoms, old_atoms, sizeof(Atom) * atom_count);
397
398     new_atoms[atom_count++] =
399         XInternAtom (disp, "_NET_WM_CONTEXT_CUSTOM", False);
400
401     XSetWMProtocols (disp, window, new_atoms, atom_count);
402
403     XFree(old_atoms);
404     g_free(new_atoms);
405
406     /* rely on GDK to set the window group to its default */
407     gdk_window_set_group (widget->window, NULL);
408
409     if (priv->program) {
410         gboolean can_hibernate = hildon_program_get_can_hibernate (priv->program);
411
412         hildon_window_set_can_hibernate_property (HILDON_WINDOW (widget),
413                 &can_hibernate);
414     }
415
416     /* Update the topmost status */
417     active_window = hildon_window_get_active_window();
418     hildon_window_update_topmost (HILDON_WINDOW (widget), active_window);
419 }
420
421 static void
422 hildon_window_unrealize                         (GtkWidget *widget)
423 {
424     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
425     g_assert (priv != NULL);
426
427     gdk_window_remove_filter (widget->window, hildon_window_event_filter,
428             widget);
429
430     gtk_widget_unrealize (GTK_WIDGET (priv->vbox));
431     GTK_WIDGET_CLASS(hildon_window_parent_class)->unrealize(widget);
432 }
433
434 static void
435 hildon_window_map                             (GtkWidget *widget)
436 {
437   HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
438   g_assert (priv != NULL);
439
440   if (GTK_WIDGET_CLASS (hildon_window_parent_class)->map)
441     GTK_WIDGET_CLASS (hildon_window_parent_class)->map (widget);
442
443   if (GTK_WIDGET_VISIBLE (priv->vbox))
444     gtk_widget_map (priv->vbox);
445 }
446
447 static void
448 hildon_window_unmap                             (GtkWidget *widget)
449 {
450   HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
451   g_assert (priv != NULL);
452
453   gtk_widget_unmap (priv->vbox);
454
455   if (GTK_WIDGET_CLASS (hildon_window_parent_class)->unmap)
456     GTK_WIDGET_CLASS (hildon_window_parent_class)->unmap (widget);
457 }
458
459 static void
460 hildon_window_get_property                      (GObject *object, 
461                                                  guint property_id,
462                                                  GValue *value, 
463                                                  GParamSpec * pspec)
464 {
465     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (object);
466     g_assert (priv != NULL);
467
468     switch (property_id) {
469
470         case PROP_IS_TOPMOST:
471             g_value_set_boolean (value, priv->is_topmost);
472             break;
473
474         default:
475             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
476             break;
477     }
478 }
479
480 /*
481  * Retrieve the graphical borders size used by the themes
482  */
483 static void
484 hildon_window_get_borders                       (HildonWindow *window)
485 {
486     GtkBorder zero = {0, 0, 0, 0};
487     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (window);
488     g_assert (priv);
489
490     GtkBorder *borders = NULL;
491     GtkBorder *toolbar_borders = NULL;
492
493     if (priv->borders)
494         gtk_border_free (priv->borders);
495     if (priv->toolbar_borders)
496         gtk_border_free (priv->toolbar_borders);
497
498     priv->borders = NULL;
499     priv->toolbar_borders = NULL;
500
501     gtk_widget_style_get (GTK_WIDGET (window), "borders",&borders,
502             "toolbar-borders", &toolbar_borders,
503             NULL);
504
505     // We're doing a copy here instead of reusing the pointer, 
506     // as we don't know where it comes from (has it been allocated using 
507     // malloc or slices... and we want to free it sanely. Blowing on 
508     // cold probbably.
509
510     if (borders) {
511         priv->borders = gtk_border_copy (borders);
512         gtk_border_free (borders);
513     } else
514         priv->borders = g_boxed_copy (GTK_TYPE_BORDER, &zero);
515
516     if (toolbar_borders) {
517         priv->toolbar_borders = gtk_border_copy (toolbar_borders);
518         gtk_border_free (toolbar_borders);
519     } else
520         priv->toolbar_borders = g_boxed_copy (GTK_TYPE_BORDER, &zero);
521 }
522
523 static gboolean
524 hildon_window_expose                            (GtkWidget *widget, 
525                                                  GdkEventExpose * event)
526 {
527     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
528     g_assert (priv);
529
530     GtkWidget *bx = priv->vbox;
531     GtkBox *box = GTK_BOX(bx);
532     GtkBorder *b = priv->borders;
533     GtkBorder *tb = priv->toolbar_borders;
534     gint tb_height = 0;
535
536     if (! priv->borders) {
537         hildon_window_get_borders (HILDON_WINDOW (widget));
538         b = priv->borders;
539         tb = priv->toolbar_borders;
540     }
541
542     tb_height = bx->allocation.height + tb->top + tb->bottom;
543
544     paint_toolbar (widget, box,
545             event, priv->fullscreen);
546
547     if (! priv->fullscreen) {
548
549         /* Draw the left and right window border */
550         gint side_borders_height = widget->allocation.height - b->top;
551
552         if (priv->visible_toolbars)
553             side_borders_height -= tb_height;
554         else
555             side_borders_height -= b->bottom;
556
557         if (b->left > 0) 
558         {
559             gtk_paint_box (widget->style, widget->window,
560                     GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
561                     &event->area, widget, "left-border",
562                     widget->allocation.x, widget->allocation.y +
563                     b->top, b->left, side_borders_height);
564         } 
565
566         if (b->right > 0)
567         {
568             gtk_paint_box (widget->style, widget->window,
569                     GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
570                     &event->area, widget, "right-border",
571                     widget->allocation.x + widget->allocation.width -
572                     b->right, widget->allocation.y + b->top,
573                     b->right, side_borders_height);
574         }
575
576         /* If no toolbar, draw the bottom window border */
577         if (! priv->visible_toolbars && b->bottom > 0)
578         {
579             gtk_paint_box (widget->style, widget->window,
580                     GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
581                     &event->area, widget, "bottom-border",
582                     widget->allocation.x, widget->allocation.y +
583                     (widget->allocation.height - b->bottom),
584                     widget->allocation.width, b->bottom);
585         }
586
587         /* Draw the top border */
588         if (b->top > 0)
589         {
590             gtk_paint_box (widget->style, widget->window,
591                     GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
592                     &event->area, widget, "top-border",
593                     widget->allocation.x, widget->allocation.y,
594                     widget->allocation.width, b->top);
595         } 
596
597
598     }
599
600     /* don't draw the window stuff as it overwrites our borders with a blank
601      * rectangle. Instead start with the drawing of the GtkBin */
602     GTK_WIDGET_CLASS (g_type_class_peek_parent (hildon_window_parent_class))->expose_event (widget, event);
603
604     /* FIXME Not sure why this is commented out 
605      * GTK_WIDGET_CLASS (hildon_window_parent_class))->
606      *  expose_event (widget, event); 
607      */
608
609     return FALSE;
610 }
611
612 static void
613 hildon_window_size_request                      (GtkWidget *widget, 
614                                                  GtkRequisition *requisition)
615 {
616     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
617     g_assert (priv);
618
619     GtkWidget *child = GTK_BIN (widget)->child;
620     GtkRequisition req2;
621     gint border_width = GTK_CONTAINER(widget)->border_width;
622
623     if (! priv->borders)
624     {
625         hildon_window_get_borders (HILDON_WINDOW (widget));
626     }
627
628     if (child)
629         gtk_widget_size_request (child, requisition);
630
631     if (priv->vbox != NULL)
632         gtk_widget_size_request (priv->vbox, &req2);
633
634     requisition->height += req2.height;
635     requisition->width = (requisition->width < req2.width) ? 
636         req2.width : requisition->width;
637
638     requisition->width  += 2 * border_width;
639     requisition->height += 2 * border_width;
640
641     if (! priv->fullscreen)
642     {
643         requisition->height += priv->borders->top;
644         if (req2.height == 0)
645             requisition->height += priv->borders->bottom;
646         requisition->width += priv->borders->left + priv->borders->right;
647     }
648 }
649
650 static void
651 hildon_window_size_allocate                     (GtkWidget *widget, 
652                                                  GtkAllocation *allocation)
653 {
654     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
655     g_assert (priv);
656
657     GtkAllocation box_alloc;
658     GtkAllocation alloc = *allocation;
659     GtkRequisition req;
660     gint border_width = GTK_CONTAINER(widget)->border_width;
661
662     GtkWidget *box = priv->vbox;
663     GtkBin *bin = GTK_BIN(widget);
664     GtkBorder *b = priv->borders;
665     GtkBorder *tb = priv->toolbar_borders;
666
667     if (!priv->borders)
668     {
669         hildon_window_get_borders (HILDON_WINDOW (widget));
670         b = priv->borders;
671         tb = priv->toolbar_borders;
672     }
673
674     widget->allocation = *allocation;
675
676     gtk_widget_get_child_requisition (box, &req);
677
678     box_alloc.width = allocation->width - tb->left - tb->right;
679     box_alloc.height = ( (req.height < allocation->height) ?
680             req.height : allocation->height );
681     box_alloc.x = allocation->x + tb->left;
682     box_alloc.y = allocation->y + allocation->height - box_alloc.height - tb->bottom;
683
684     if (bin->child != NULL && GTK_IS_WIDGET (bin->child)
685             && GTK_WIDGET_VISIBLE (bin->child))
686     {
687         alloc.x += border_width;
688         alloc.y += border_width;
689         alloc.width -= (border_width * 2);
690         alloc.height -= (border_width * 2) + box_alloc.height;
691
692         if (! priv->fullscreen)
693         {
694             alloc.x += b->left;
695             alloc.width -= (b->left + b->right);
696             alloc.y += b->top;
697
698             alloc.height -= b->top;
699
700             if (box_alloc.height <= 0)
701                 alloc.height -= b->bottom;
702             else
703                 alloc.height -= (tb->top + tb->bottom);            
704         }
705         else
706         {
707             if (!(box_alloc.height <= 0))
708                 alloc.height -= (tb->top + tb->bottom);              
709         }
710
711         gtk_widget_size_allocate (bin->child, &alloc);
712     }
713
714     gtk_widget_size_allocate (box, &box_alloc);
715
716     if (priv->previous_vbox_y != box_alloc.y)
717     {
718         /* The size of the VBox has changed, we need to redraw part
719          * of the window borders */
720         gint draw_from_y = priv->previous_vbox_y < box_alloc.y?
721             priv->previous_vbox_y - tb->top:
722             box_alloc.y - tb->top;
723
724         gtk_widget_queue_draw_area (widget, 0, draw_from_y, 
725                 widget->allocation.width,
726                 widget->allocation.height - draw_from_y);
727
728         priv->previous_vbox_y = box_alloc.y;
729     }
730
731 }
732
733 static void
734 hildon_window_forall                            (GtkContainer *container, 
735                                                  gboolean include_internals,
736                                                  GtkCallback callback, 
737                                                  gpointer callback_data)
738 {
739     HildonWindow *self = HILDON_WINDOW (container);
740     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
741
742     g_return_if_fail (callback != NULL);
743     g_assert (priv);
744
745     GTK_CONTAINER_CLASS (hildon_window_parent_class)->forall (container, include_internals,
746             callback, callback_data);
747     if (include_internals && priv->vbox != NULL)
748         (* callback)(GTK_WIDGET (priv->vbox), callback_data);
749 }
750
751 static void
752 hildon_window_show_all                          (GtkWidget *widget)
753 {
754     HildonWindow *self = HILDON_WINDOW (widget);
755     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
756
757     g_assert (priv != NULL);
758
759     GTK_WIDGET_CLASS (hildon_window_parent_class)->show_all (widget);
760     gtk_widget_show_all (priv->vbox);
761 }
762
763 static void
764 hildon_window_destroy                           (GtkObject *obj)
765 {
766     HildonWindow *self = HILDON_WINDOW (obj);
767     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (obj);
768     GList *menu_list = NULL;
769     GList *menu_node = NULL;
770
771     g_assert (priv != NULL);
772
773     if (priv->vbox != NULL)
774     {
775         if (priv->program)
776         {
777             GtkWidget * common_toolbar = 
778                 GTK_WIDGET (hildon_program_get_common_toolbar (priv->program));
779             if (common_toolbar && common_toolbar->parent == priv->vbox)
780             {
781                 gtk_container_remove (GTK_CONTAINER (priv->vbox),
782                         common_toolbar);
783             }
784         }
785
786         gtk_widget_unparent (priv->vbox);
787         priv->vbox = NULL;    
788
789     }
790
791     menu_list = g_list_copy (gtk_menu_get_for_attach_widget (GTK_WIDGET (obj)));
792     menu_node = menu_list;
793
794     while (menu_node)
795     {
796         if (GTK_IS_MENU (menu_node->data))
797         {
798             if (GTK_WIDGET_VISIBLE (GTK_WIDGET (menu_node->data)))
799             {
800                 gtk_menu_popdown (GTK_MENU (menu_node->data));
801                 gtk_menu_shell_deactivate (GTK_MENU_SHELL (menu_node->data));
802             }
803             gtk_menu_detach (GTK_MENU (menu_node->data));
804
805             /* Destroy it, but only if it's not a common menu */
806             if (priv->program && 
807                 hildon_program_get_common_menu (priv->program) != menu_node->data) {
808                     gtk_object_destroy (GTK_OBJECT (menu_node->data));
809                     g_object_unref (menu_node->data);
810             }
811         }
812         menu_node = menu_node->next;
813     }
814
815     g_list_free (menu_list);
816     menu_list = NULL;
817
818     if (priv->program)
819     {
820         hildon_program_remove_window (priv->program, self);
821     }
822
823     gdk_window_remove_filter (gdk_get_default_root_window(), 
824             hildon_window_root_window_event_filter,
825             obj);
826
827     gtk_widget_set_events (GTK_WIDGET(obj), 0);
828
829     GTK_OBJECT_CLASS (hildon_window_parent_class)->destroy (obj);
830 }
831
832 static void
833 hildon_window_notify                            (GObject *gobject, 
834                                                  GParamSpec *param)
835 {
836     HildonWindow *window = HILDON_WINDOW (gobject);
837
838     if (g_str_equal (param->name, "is-topmost"))
839     {
840         hildon_window_is_topmost_notify (window);
841     }
842
843     if (G_OBJECT_CLASS(hildon_window_parent_class)->notify)
844         G_OBJECT_CLASS(hildon_window_parent_class)->notify (gobject, param);
845 }
846
847
848 static void
849 visible_toolbar                                 (gpointer data, 
850                                                  gpointer user_data)
851 {
852     if (GTK_WIDGET_VISIBLE (((GtkBoxChild *)data)->widget))
853         (*((gint *)user_data))++;
854 }
855
856 static void
857 paint_toolbar                                   (GtkWidget *widget, 
858                                                  GtkBox *box, 
859                                                  GdkEventExpose * event, 
860                                                  gboolean fullscreen)
861 {
862     gint toolbar_num = 0; 
863     gint count;
864
865     /* collect info to help on painting the boxes */
866     g_list_foreach (box->children, visible_toolbar, 
867             (gpointer) &toolbar_num);
868
869     if(toolbar_num <= 0)
870         return;
871
872     /*top most toolbar painting*/
873     gtk_paint_box (widget->style, widget->window,
874             GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
875             &event->area, widget, "toolbar-primary",
876             widget->allocation.x,
877             GTK_WIDGET(box)->allocation.y,
878             widget->allocation.width,
879             TOOLBAR_HEIGHT);
880
881     /*multi toolbar painting*/
882     for (count = 0; count < toolbar_num - 1; count++)
883     {
884             gtk_paint_box (widget->style, widget->window,
885                            GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
886                            &event->area, widget, "toolbar-secondary",
887                            widget->allocation.x,
888                            GTK_WIDGET(box)->allocation.y +
889                            (1 + count) * (TOOLBAR_HEIGHT),
890                            widget->allocation.width,
891                            TOOLBAR_HEIGHT);
892     }
893 }
894
895 /*
896  * Checks the root window to know which is the topped window
897  */
898 Window
899 hildon_window_get_active_window                 (void)
900 {
901     Atom realtype;
902     gint xerror;
903     int format;
904     int status;
905     Window ret;
906     unsigned long n;
907     unsigned long extra;
908     union
909     {
910         Window *win;
911         unsigned char *char_pointer;
912     } win;
913     Atom active_app_atom = 
914         XInternAtom (GDK_DISPLAY (), "_MB_CURRENT_APP_WINDOW", False);
915
916     win.win = NULL;
917
918     gdk_error_trap_push ();
919     status = XGetWindowProperty (GDK_DISPLAY(), GDK_ROOT_WINDOW(),
920             active_app_atom, 0L, 16L,
921             0, XA_WINDOW, &realtype, &format,
922             &n, &extra, &win.char_pointer);
923     xerror = gdk_error_trap_pop ();
924     if (xerror || !(status == Success && realtype == XA_WINDOW && format == 32
925                 && n == 1 && win.win != NULL))
926     {
927         if (win.win != NULL)
928             XFree (win.char_pointer);
929         return None;
930     }
931
932     ret = win.win[0];
933
934     if (win.win != NULL)
935         XFree(win.char_pointer);
936
937     return ret;
938 }
939
940 static int
941 xclient_message_type_check                      (XClientMessageEvent *cm, 
942                                                  const gchar *name)
943 {
944     return cm->message_type == XInternAtom(GDK_DISPLAY(), name, FALSE);
945 }
946
947 /*
948  * Handle the window border custom button, which toggles the menu,
949  * and the Hildon input method copy paste messages
950  */
951 static GdkFilterReturn
952 hildon_window_event_filter                      (GdkXEvent *xevent, 
953                                                  GdkEvent *event, 
954                                                  gpointer data)
955 {
956     XAnyEvent *eventti = xevent;
957
958     if (eventti->type == ClientMessage)
959     {
960         XClientMessageEvent *cm = xevent;
961
962         if (xclient_message_type_check (cm, "_MB_GRAB_TRANSFER"))
963         {
964             hildon_window_toggle_menu (HILDON_WINDOW ( data ), cm->data.l[2], cm->data.l[0]);
965             return GDK_FILTER_REMOVE;
966         }
967         /* opera hack clipboard client message */
968         else if (xclient_message_type_check (cm, "_HILDON_IM_CLIPBOARD_COPY"))
969         {
970             g_signal_emit_by_name(G_OBJECT(data), "clipboard_operation",
971                     HILDON_WINDOW_CO_COPY);
972             return GDK_FILTER_REMOVE;
973         }
974         else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_CUT"))
975         {
976             g_signal_emit_by_name(G_OBJECT(data), "clipboard_operation",
977                     HILDON_WINDOW_CO_CUT);
978             return GDK_FILTER_REMOVE;
979         }
980         else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_PASTE"))
981         {
982             g_signal_emit_by_name(G_OBJECT(data), "clipboard_operation",
983                     HILDON_WINDOW_CO_PASTE);
984             return GDK_FILTER_REMOVE;
985         }
986     }
987
988     return GDK_FILTER_CONTINUE;
989 }
990
991 /*
992  * Here we keep track of changes in the _MB_CURRENT_APP_WINDOW,
993  * to know when we acquire/lose topmost status
994  */
995 static GdkFilterReturn
996 hildon_window_root_window_event_filter          (GdkXEvent *xevent, 
997                                                  GdkEvent *event, 
998                                                  gpointer data)
999 {
1000     XAnyEvent *eventti = xevent;
1001     HildonWindow *hwindow = HILDON_WINDOW (data);
1002
1003     if (eventti->type == PropertyNotify)
1004     {
1005         XPropertyEvent *pevent = xevent;
1006         Atom active_app_atom = 
1007             XInternAtom (GDK_DISPLAY (), "_MB_CURRENT_APP_WINDOW", False);
1008
1009         if (pevent->atom == active_app_atom)
1010         {
1011             Window active_window = hildon_window_get_active_window();
1012
1013             hildon_window_update_topmost (hwindow, active_window);
1014         }
1015     }
1016
1017     return GDK_FILTER_CONTINUE;
1018 }
1019
1020 /*
1021  * Handle the menu hardware key here
1022  */
1023 static gboolean
1024 hildon_window_key_press_event                   (GtkWidget *widget, 
1025                                                  GdkEventKey *event)
1026 {
1027     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
1028
1029     g_return_val_if_fail (HILDON_IS_WINDOW (widget),FALSE);
1030     g_assert (priv);
1031
1032     switch (event->keyval)
1033     {
1034         case HILDON_HARDKEY_MENU:
1035             if (hildon_window_toggle_menu (HILDON_WINDOW (widget), 0, GDK_CURRENT_TIME))
1036                 return TRUE;
1037             break;
1038         case HILDON_HARDKEY_ESC:
1039             if (!priv->escape_timeout)
1040             {
1041                 priv->escape_timeout = g_timeout_add 
1042                     (HILDON_WINDOW_LONG_PRESS_TIME,
1043                      hildon_window_escape_timeout, widget);
1044             }
1045             break;
1046     }
1047
1048     return GTK_WIDGET_CLASS (hildon_window_parent_class)->key_press_event (widget, event);
1049 }
1050
1051 static gboolean
1052 hildon_window_key_release_event                 (GtkWidget *widget, 
1053                                                  GdkEventKey *event)
1054 {
1055     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
1056
1057     g_return_val_if_fail (HILDON_IS_WINDOW (widget), FALSE);
1058     g_assert (priv);
1059
1060     switch (event->keyval)
1061     {
1062         case HILDON_HARDKEY_ESC:
1063             if (priv->escape_timeout)
1064             {
1065                 g_source_remove (priv->escape_timeout);
1066                 priv->escape_timeout = 0;
1067             }
1068             break;
1069     }
1070
1071     return GTK_WIDGET_CLASS (hildon_window_parent_class)->key_release_event (widget, event);
1072
1073 }
1074
1075 /*
1076  * We keep track of the window state changes, because the drawing
1077  * (borders) differs whether we are in fullscreen mode or not
1078  */
1079 static gboolean
1080 hildon_window_window_state_event                (GtkWidget *widget, 
1081                                                  GdkEventWindowState *event)
1082 {
1083     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
1084     g_assert (priv != NULL);
1085
1086     if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1087         priv->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
1088
1089     if (GTK_WIDGET_CLASS (hildon_window_parent_class)->window_state_event)
1090     {
1091         return GTK_WIDGET_CLASS (hildon_window_parent_class)->window_state_event (
1092                 widget,
1093                 event);
1094     }
1095     else
1096     {
1097         return FALSE;
1098     }
1099 }
1100
1101 /*
1102  * If the window lost focus while the user started to press the ESC key, we
1103  * won't get the release event. We need to stop the timeout.
1104  */
1105 static gboolean
1106 hildon_window_focus_out_event                   (GtkWidget *widget, 
1107                                                  GdkEventFocus *event)
1108 {
1109   HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (widget);
1110
1111   if (priv->escape_timeout)
1112   {
1113       g_source_remove (priv->escape_timeout);
1114       priv->escape_timeout = 0;
1115   }
1116
1117   return GTK_WIDGET_CLASS (hildon_window_parent_class)->focus_out_event (widget, event);
1118 }
1119
1120 /*
1121  * The menu popuping needs a menu popup-function
1122  */
1123 static void
1124 hildon_window_menu_popup_func                   (GtkMenu *menu, 
1125                                                  gint *x, 
1126                                                  gint *y,
1127                                                  gboolean *push_in, 
1128                                                  GtkWidget *widget)
1129 {
1130     gint window_x = 0;
1131     gint window_y = 0;
1132     GdkWindow *window = GTK_WIDGET(widget)->window;
1133
1134     if (window)
1135     {
1136         gdk_window_get_origin (window, &window_x, &window_y);
1137     }
1138
1139     gtk_widget_style_get (GTK_WIDGET (menu), "horizontal-offset", x,
1140             "vertical-offset", y, NULL);
1141
1142     if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1143     {
1144         *x = GTK_WIDGET (widget)->allocation.width + window_x - GTK_WIDGET (menu)->allocation.width - *x;
1145     }
1146     else
1147         *x += window_x;
1148
1149     *y += window_y;
1150
1151 }
1152
1153 static void
1154 hildon_window_menu_popup_func_full              (GtkMenu *menu, 
1155                                                  gint *x, 
1156                                                  gint *y,
1157                                                  gboolean *push_in, 
1158                                                  GtkWidget *widget)
1159 {
1160     gtk_widget_style_get (GTK_WIDGET (menu), "horizontal-offset", x,
1161             "vertical-offset", y, NULL);
1162
1163     if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1164         *x = GTK_WIDGET (widget)->allocation.width - GTK_WIDGET (menu)->allocation.width - *x;
1165     else
1166         *x = MAX (0, *x);
1167
1168     *y = MAX (0, *y);
1169 }
1170
1171
1172 /*
1173  * Takes the common toolbar when we acquire the top-most status
1174  */
1175 static void
1176 hildon_window_is_topmost_notify                 (HildonWindow *window)
1177 {
1178     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (window);
1179
1180     g_assert (priv);
1181
1182     if (priv->is_topmost)
1183     {
1184         hildon_window_take_common_toolbar (window);
1185     }
1186 }
1187
1188 /*
1189  * Sets the program to which the window belongs. This should only be called
1190  * by hildon_program_add_window
1191  */
1192 void G_GNUC_INTERNAL
1193 hildon_window_set_program                       (HildonWindow *self, 
1194                                                  GObject *program)
1195 {
1196     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
1197
1198     g_return_if_fail (HILDON_IS_WINDOW (self));
1199     g_assert (priv != NULL);
1200
1201     if (priv->program)
1202     {
1203         g_object_unref (priv->program);
1204     }
1205
1206     /* Now that we are bound to a program, we can rely on it to track the
1207      * root window */
1208     gdk_window_remove_filter (gdk_get_default_root_window(), 
1209             hildon_window_root_window_event_filter,
1210             self);
1211
1212     priv->program = HILDON_PROGRAM (program);
1213     g_object_ref (program);
1214 }
1215
1216 /*
1217  * Unsets the program to which the window belongs. This should only be called
1218  * by hildon_program_remove_window
1219  */
1220 void G_GNUC_INTERNAL
1221 hildon_window_unset_program                     (HildonWindow *self)
1222 {
1223     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
1224
1225     g_return_if_fail(HILDON_IS_WINDOW (self));
1226     g_assert (priv != NULL);
1227
1228     if (priv->program)
1229     {
1230         g_object_unref (priv->program);
1231         priv->program = NULL;
1232
1233         /* We need to start tacking the root window again */
1234         gdk_window_set_events (gdk_get_default_root_window (),
1235                 gdk_window_get_events (gdk_get_default_root_window ())
1236                 | GDK_PROPERTY_CHANGE_MASK);
1237
1238         gdk_window_add_filter (gdk_get_default_root_window (),
1239                 hildon_window_root_window_event_filter, self );
1240     }
1241
1242     priv->program = NULL;
1243 }
1244
1245 /*
1246  * Sets whether or not the program to which this window belongs is
1247  * killable. This is used by the HildonProgram to signify to the
1248  * Task Navigator whether or not it can hibernate in memory-low situations
1249  **/    
1250 void G_GNUC_INTERNAL
1251 hildon_window_set_can_hibernate_property        (HildonWindow *self, 
1252                                                  gpointer _can_hibernate)
1253 {
1254     GdkAtom killable_atom;
1255     gboolean can_hibernate;
1256
1257     g_return_if_fail(self && HILDON_IS_WINDOW (self));
1258
1259     if (!GTK_WIDGET_REALIZED ((GTK_WIDGET (self))))
1260     {
1261         return;
1262     }
1263
1264     can_hibernate = * ((gboolean *)_can_hibernate);
1265
1266     killable_atom = gdk_atom_intern (CAN_HIBERNATE_PROPERTY, FALSE);
1267
1268     if (can_hibernate)
1269     {
1270         gdk_property_change (GTK_WIDGET (self)->window, killable_atom,
1271                 (GdkAtom)31/* XA_STRING */, 8,
1272                 GDK_PROP_MODE_REPLACE, (const guchar *)CAN_HIBERNATE,
1273                 CAN_HIBERNATE_LENGTH);
1274     }
1275     else
1276     {
1277         gdk_property_delete (GTK_WIDGET (self)->window, killable_atom);
1278     }
1279
1280 }
1281
1282 /*
1283  * If a common toolbar was set to the program, reparent it to
1284  * us
1285  */
1286 void G_GNUC_INTERNAL
1287 hildon_window_take_common_toolbar               (HildonWindow *self)
1288 {
1289     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
1290
1291     g_return_if_fail(HILDON_IS_WINDOW (self));
1292     g_assert (priv);
1293
1294     if (priv->program)
1295     {
1296         GtkWidget *common_toolbar =  
1297             GTK_WIDGET (hildon_program_get_common_toolbar (priv->program));
1298
1299         if (common_toolbar && common_toolbar->parent != priv->vbox)
1300         {
1301             g_object_ref (common_toolbar);
1302             if (common_toolbar->parent)
1303             {
1304                 gtk_container_remove (GTK_CONTAINER (common_toolbar->parent),
1305                         common_toolbar);
1306             }
1307
1308             gtk_box_pack_end (GTK_BOX(priv->vbox), common_toolbar,
1309                     TRUE, TRUE, 0);
1310             g_object_unref (common_toolbar);
1311
1312             gtk_widget_set_size_request (common_toolbar, -1, TOOLBAR_HEIGHT);
1313
1314             gtk_widget_show  (priv->vbox);
1315
1316         }
1317     }
1318 }
1319
1320 /*
1321  * Compare the window that was last topped, and act consequently
1322  */
1323 void
1324 hildon_window_update_topmost                    (HildonWindow *self, 
1325                                                  Window window_id)
1326 {
1327     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
1328
1329     Window my_window;
1330
1331     g_return_if_fail (HILDON_IS_WINDOW (self));
1332     g_assert (priv);
1333
1334     my_window = GDK_WINDOW_XID (GTK_WIDGET (self)->window);
1335
1336     if (window_id == my_window)
1337     {
1338         if (! priv->is_topmost)
1339         {
1340             priv->is_topmost = TRUE;
1341             hildon_window_is_topmost_notify (self);
1342             g_object_notify (G_OBJECT (self), "is-topmost");
1343         }
1344     }
1345     else if (priv->is_topmost)
1346     {
1347         /* Should this go in the signal handler? */
1348         GtkWidget *focus = gtk_window_get_focus (GTK_WINDOW (self));
1349
1350         if (GTK_IS_ENTRY (focus))
1351             gtk_im_context_focus_out (GTK_ENTRY (focus)->im_context);
1352         if (GTK_IS_TEXT_VIEW (focus))
1353             gtk_im_context_focus_out (GTK_TEXT_VIEW (focus)->im_context);
1354
1355         priv->is_topmost = FALSE;
1356         hildon_window_is_topmost_notify (self);
1357         g_object_notify (G_OBJECT (self), "is-topmost");
1358     }
1359 }
1360
1361 static void
1362 detach_menu_func                                (GtkWidget *attach_widget, 
1363                                                  GtkMenu *menu)
1364 {
1365     /* FIXME Why is this even needed here? */
1366 }
1367
1368 static gboolean
1369 hildon_window_toggle_menu                       (HildonWindow *self,
1370                                                  guint button,
1371                                                  guint32 time)
1372 {
1373     g_return_val_if_fail (HILDON_IS_WINDOW (self), FALSE);
1374
1375     if (HILDON_WINDOW_GET_CLASS (self)->toggle_menu != NULL)
1376     {
1377         return HILDON_WINDOW_GET_CLASS (self)->toggle_menu (self, button, time);
1378     }
1379     else
1380     {
1381         return FALSE;
1382     }
1383 }
1384
1385
1386 /*
1387  * Toggles the display of the HildonWindow menu.
1388  * Returns whether or not something was done (whether or not we had a menu
1389  * to toggle)
1390  */
1391 static gboolean
1392 hildon_window_toggle_menu_real                  (HildonWindow * self,
1393                                                  guint button,
1394                                                  guint32 time)
1395 {
1396     GtkMenu *menu_to_use = NULL;
1397     GList *menu_children = NULL;
1398     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (self);
1399
1400     g_return_val_if_fail (HILDON_IS_WINDOW (self), FALSE);
1401     g_assert (priv != NULL);
1402
1403     /* Select which menu to use, Window specific has highest priority,
1404      * then program specific */
1405     if (priv->menu)
1406     {
1407         menu_to_use = GTK_MENU (priv->menu);
1408     }
1409     else if (priv->program)
1410     {
1411         menu_to_use = hildon_program_get_common_menu (priv->program);
1412         if (menu_to_use && gtk_menu_get_attach_widget (menu_to_use) != 
1413                 GTK_WIDGET (self))
1414         {
1415             g_object_ref (menu_to_use);
1416             if (gtk_menu_get_attach_widget (menu_to_use))
1417             {
1418                 gtk_menu_detach (menu_to_use);
1419             }
1420
1421             gtk_menu_attach_to_widget (menu_to_use, GTK_WIDGET (self), 
1422                     &detach_menu_func);
1423             g_object_unref (menu_to_use);
1424         }
1425     }
1426
1427     if (! menu_to_use)
1428     {
1429         return FALSE;
1430     }
1431
1432
1433     if (GTK_WIDGET_MAPPED (GTK_WIDGET (menu_to_use)))
1434     {
1435         gtk_menu_popdown (menu_to_use);
1436         gtk_menu_shell_deactivate (GTK_MENU_SHELL (menu_to_use));
1437         return TRUE;
1438     }
1439
1440     /* Check if the menu has items */
1441     menu_children = gtk_container_get_children (GTK_CONTAINER (menu_to_use));
1442
1443     if (menu_children)
1444     {
1445         g_list_free (menu_children);
1446
1447         /* Apply right theming */
1448         gtk_widget_set_name (GTK_WIDGET (menu_to_use),
1449                 "menu_force_with_corners");
1450
1451         if (priv->fullscreen) 
1452         {
1453             gtk_menu_popup (menu_to_use, NULL, NULL,
1454                     (GtkMenuPositionFunc)
1455                     hildon_window_menu_popup_func_full,
1456                     self, button, time);
1457         }
1458         else
1459         {
1460             gtk_menu_popup (menu_to_use, NULL, NULL,
1461                     (GtkMenuPositionFunc)
1462                     hildon_window_menu_popup_func,
1463                     self, button, time);
1464         }
1465         gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_to_use), TRUE);
1466         return TRUE;
1467     }
1468
1469     return FALSE;
1470 }
1471
1472 /*
1473  * If the ESC key was not released when the timeout expires,
1474  * close the window
1475  */
1476 static gboolean
1477 hildon_window_escape_timeout                    (gpointer data)
1478 {
1479     HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (data);
1480     GdkEvent *event;
1481
1482     g_assert (priv);
1483
1484     GDK_THREADS_ENTER ();
1485
1486     /* Send fake event, simulation a situation that user
1487        pressed 'x' from the corner */
1488     event = gdk_event_new(GDK_DELETE);
1489     ((GdkEventAny *)event)->window = GDK_WINDOW (g_object_ref (GTK_WIDGET(data)->window));
1490     gtk_main_do_event(event);
1491
1492     /* That unrefs the window, so we're reffing it above */
1493     gdk_event_free(event);
1494
1495     priv->escape_timeout = 0;
1496
1497     GDK_THREADS_LEAVE ();
1498
1499     return FALSE;
1500 }
1501
1502 /**
1503  * hildon_window_new: 
1504  * 
1505  * Creates a new HildonWindow.
1506  * 
1507  * Return value: A @HildonWindow.
1508  **/
1509 GtkWidget*
1510 hildon_window_new                               (void)
1511 {
1512     HildonWindow *newwindow = g_object_new (HILDON_TYPE_WINDOW, NULL);
1513
1514     return GTK_WIDGET (newwindow);
1515 }
1516
1517 /**
1518  * hildon_window_add_with_scrollbar
1519  * @self : A @HildonWindow
1520  * @child : A @GtkWidget
1521  *
1522  * Adds the @child to the HildonWindow and creates a scrollbar
1523  * for it. Similar as adding first a @GtkScrolledWindow and then the
1524  * @child to it.
1525  */
1526 void
1527 hildon_window_add_with_scrollbar                (HildonWindow *self,
1528                                                  GtkWidget *child)
1529 {
1530     GtkScrolledWindow *scrolledw;
1531
1532     g_return_if_fail (HILDON_IS_WINDOW (self));
1533     g_return_if_fail (GTK_IS_WIDGET (child));
1534     g_return_if_fail (child->parent == NULL);
1535
1536     scrolledw = GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (NULL, NULL));
1537     gtk_scrolled_window_set_policy (scrolledw, GTK_POLICY_NEVER,
1538             GTK_POLICY_AUTOMATIC);
1539     gtk_scrolled_window_set_shadow_type (scrolledw, GTK_SHADOW_NONE);
1540
1541     if (GTK_IS_VIEWPORT (child))
1542         gtk_container_add (GTK_CONTAINER (scrolledw), child);
1543     else
1544     {
1545         if (GTK_IS_CONTAINER (child) )
1546             gtk_container_set_focus_vadjustment (GTK_CONTAINER(child),
1547                     gtk_scrolled_window_get_vadjustment (scrolledw) );
1548         gtk_scrolled_window_add_with_viewport (scrolledw, child);
1549     }
1550
1551     gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (scrolledw));
1552 }
1553
1554 static void
1555 calculate_visible_toolbars                      (gpointer data,
1556                                                  gpointer user_data)
1557 {
1558   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (((GtkBoxChild *)data)->widget)))
1559     (*((gint *)user_data)) ++;
1560 }
1561
1562 static void
1563 toolbar_visible_notify                          (GtkWidget *toolbar, GParamSpec *pspec,
1564                                                  HildonWindow *window)
1565 {
1566   HildonWindowPrivate *priv = HILDON_WINDOW_GET_PRIVATE (window);
1567
1568   g_assert (priv);
1569
1570   /* Recalculate from scratch the value just in case */
1571   priv->visible_toolbars = 0;
1572
1573   g_list_foreach (GTK_BOX (priv->vbox)->children, calculate_visible_toolbars, 
1574                   &priv->visible_toolbars);
1575
1576   if (priv->visible_toolbars == 0)
1577     gtk_widget_hide (priv->vbox);
1578   else
1579     gtk_widget_show (priv->vbox);
1580 }
1581
1582 /**
1583  * hildon_window_add_toolbar:
1584  * @self: A @HildonWindow
1585  * @toolbar: A #GtkToolbar to add to the HildonWindow
1586  *
1587  * Adds a toolbar to the window. Note that the toolbar is not automatically
1588  * shown. You need to call #gtk_widget_show_all on it to make it visible. 
1589  * It's also possible to hide the toolbar (without removing it) by calling
1590  * #gtk_widget_hide.
1591  **/
1592 void 
1593 hildon_window_add_toolbar                       (HildonWindow *self, 
1594                                                  GtkToolbar *toolbar)
1595 {
1596     GtkBox *vbox;
1597     HildonWindowPrivate *priv;
1598
1599     g_return_if_fail (HILDON_IS_WINDOW (self));
1600     g_return_if_fail (toolbar && GTK_IS_TOOLBAR (toolbar));
1601
1602     priv = HILDON_WINDOW_GET_PRIVATE (self);
1603
1604     vbox = GTK_BOX (priv->vbox);
1605
1606     gtk_box_pack_start (vbox, GTK_WIDGET (toolbar), TRUE, TRUE, 0);
1607     gtk_box_reorder_child (vbox, GTK_WIDGET (toolbar), 0);
1608     gtk_widget_set_size_request (GTK_WIDGET (toolbar), -1, TOOLBAR_HEIGHT);
1609
1610     g_signal_connect (G_OBJECT (toolbar), "notify::visible",
1611                       G_CALLBACK (toolbar_visible_notify), self);
1612
1613     if (GTK_WIDGET_VISIBLE (toolbar))
1614       {
1615         priv->visible_toolbars++;
1616         gtk_widget_show (priv->vbox);
1617       }
1618
1619     gtk_widget_queue_resize (GTK_WIDGET (self));
1620 }
1621
1622 /**
1623  * hildon_window_remove_toolbar:
1624  * @self: A @HildonWindow
1625  * @toolbar: A #GtkToolbar to remove from the HildonWindow
1626  *
1627  * Removes a toolbar from the window. Note that this decreases the refference
1628  * count on the widget. If you want to keep the toolbar alive call #g_object_ref 
1629  * before calling this function.
1630  **/
1631 void
1632 hildon_window_remove_toolbar                    (HildonWindow *self, 
1633                                                  GtkToolbar *toolbar)
1634 {
1635     HildonWindowPrivate *priv;
1636
1637     g_return_if_fail (HILDON_IS_WINDOW (self));
1638     
1639     priv = HILDON_WINDOW_GET_PRIVATE (self);
1640
1641     if (GTK_WIDGET_VISIBLE (toolbar))
1642       {
1643         if (--(priv->visible_toolbars) == 0)
1644           gtk_widget_hide (priv->vbox);
1645       }
1646
1647     g_signal_handlers_disconnect_by_func (toolbar, toolbar_visible_notify, self);
1648
1649     gtk_container_remove (GTK_CONTAINER (priv->vbox), GTK_WIDGET (toolbar));
1650 }
1651
1652 /**
1653  * hildon_window_get_menu:
1654  * @self : #HildonWindow
1655  * 
1656  * Gets the #GtMenu assigned to the #HildonAppview. Note that the 
1657  * window is still the owner of the menu.
1658  * 
1659  * Return value: The #GtkMenu assigned to this application view. 
1660  **/
1661 GtkMenu*
1662 hildon_window_get_menu                          (HildonWindow * self)
1663 {
1664     HildonWindowPrivate *priv;
1665
1666     g_return_val_if_fail (HILDON_IS_WINDOW (self), NULL);
1667
1668     priv = HILDON_WINDOW_GET_PRIVATE (self);
1669
1670     return GTK_MENU (priv->menu);
1671 }
1672
1673 /* Since we've been asking developers to call gtk_window_add_accel_group()
1674  * themselves, do not trigger criticals by trying it again.
1675  */
1676 static void
1677 hildon_window_add_accel_group (HildonWindow *self,
1678                                GtkAccelGroup *accel_group)
1679 {
1680     GSList *groups, *l;
1681
1682     groups = gtk_accel_groups_from_object (G_OBJECT (self));
1683     for (l = groups; l != NULL; l = l->next)
1684       if (l->data == (gpointer)accel_group)
1685         /* Maybe print a warning here? */
1686         return;
1687
1688     gtk_window_add_accel_group (GTK_WINDOW (self), accel_group);
1689 }
1690
1691 /**
1692  * hildon_window_set_main_menu:
1693  * @self: A #HildonWindow
1694  * @menu: The #GtkMenu to be used for this #HildonWindow
1695  * 
1696  * Sets the menu to be used for this window. This menu overrides
1697  * a program-wide menu that may have been set with
1698  * hildon_program_set_common_menu(). Pass %NULL to remove the current
1699  * menu. #HildonWindow takes ownership of the passed menu and you're
1700  * not supposed to free it yourself anymore.
1701  *
1702  * Note that if you're using a #HildonStackableWindow (and not just a
1703  * standard #HildonWindow) you should use
1704  * hildon_stackable_window_set_main_menu()
1705  * instead. #HildonStackableWindow uses #HildonAppMenu rather than
1706  * #GtkMenu.
1707  *
1708  * Since: 2.2
1709  **/ 
1710 void
1711 hildon_window_set_main_menu (HildonWindow* self,
1712                              GtkMenu     * menu)
1713 {
1714     HildonWindowPrivate *priv;
1715     GtkAccelGroup *accel_group;
1716
1717     g_return_if_fail (HILDON_IS_WINDOW (self));
1718
1719     priv = HILDON_WINDOW_GET_PRIVATE (self);
1720
1721     if (priv->menu != NULL)
1722     {
1723         accel_group = gtk_menu_get_accel_group (GTK_MENU (priv->menu));
1724         if (accel_group != NULL)
1725             gtk_window_remove_accel_group (GTK_WINDOW (self), accel_group);
1726
1727         gtk_menu_detach (GTK_MENU (priv->menu));
1728         g_object_unref (priv->menu);
1729     }
1730
1731     priv->menu = (menu != NULL) ? GTK_WIDGET (menu) : NULL;
1732     if (priv->menu != NULL)
1733     {
1734         gtk_widget_set_name (priv->menu, "menu_force_with_corners");
1735         gtk_menu_attach_to_widget (GTK_MENU (priv->menu), GTK_WIDGET (self), &detach_menu_func);
1736         g_object_ref (GTK_MENU (priv->menu));
1737
1738         accel_group = gtk_menu_get_accel_group (GTK_MENU (priv->menu));
1739         if (accel_group != NULL)
1740             hildon_window_add_accel_group (self, accel_group);
1741     }
1742 }
1743
1744 /**
1745  * hildon_window_set_menu:
1746  * @self: A #HildonWindow
1747  * @menu: The #GtkMenu to be used for this #HildonWindow
1748  * 
1749  * Sets the menu to be used for this window. This menu overrides
1750  * a program-wide menu that may have been set with
1751  * hildon_program_set_common_menu. Pass NULL to remove the current
1752  * menu. HildonWindow takes ownership of the passed menu and you're
1753  * not supposed to free it yourself anymore.
1754  *
1755  * Note: hildon_window_set_menu() calls gtk_widget_show_all() for the
1756  * #GtkMenu. To pass control about visibility to the application
1757  * developer, hildon_window_set_main_menu() was introduced, which
1758  * doesn't do this.
1759  *
1760  * Deprecated: Hildon 2.2: use hildon_window_set_main_menu()
1761  **/ 
1762 void
1763 hildon_window_set_menu                          (HildonWindow *self, 
1764                                                  GtkMenu *menu)
1765 {
1766     HildonWindowPrivate *priv;
1767
1768     g_return_if_fail (HILDON_IS_WINDOW (self));
1769
1770     hildon_window_set_main_menu (self, menu);
1771
1772     priv = HILDON_WINDOW_GET_PRIVATE (self);
1773
1774     if (priv->menu != NULL)
1775         gtk_widget_show_all (GTK_WIDGET (priv->menu));
1776 }
1777
1778 /**
1779  * hildon_window_get_is_topmost:
1780  * @self: A #HildonWindow
1781  * 
1782  * Return value: Whether or not the #HildonWindow is currenltly activated
1783  * by the window manager.
1784  **/
1785 gboolean
1786 hildon_window_get_is_topmost                    (HildonWindow *self)
1787 {
1788     HildonWindowPrivate *priv;
1789
1790     g_return_val_if_fail (HILDON_IS_WINDOW (self), FALSE);
1791
1792     priv = HILDON_WINDOW_GET_PRIVATE (self);
1793     return priv->is_topmost;
1794 }
1795