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