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