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