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