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