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