2006-08-30 Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
[hildon] / hildon-widgets / hildon-window.c
index 6e86829..01d9609 100644 (file)
@@ -3,12 +3,12 @@
  *
  * Copyright (C) 2006 Nokia Corporation.
  *
- * Contact: Luc Pionchon <luc.pionchon@nokia.com>
+ * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -139,22 +139,14 @@ hildon_window_key_release_event (GtkWidget       *widget,
                                  GdkEventKey     *event);
 static gboolean
 hildon_window_window_state_event (GtkWidget *widget, 
-                                  GdkEventWindowState *event,
-                                  gpointer null);
+                                  GdkEventWindowState *event);
+
 
-static void 
-hildon_window_title_notify (GObject *gobject,
-                            GParamSpec *arg1,
-                            gpointer user_data);
 static void
-hildon_window_is_topmost_notify (GObject *self,
-                                 GParamSpec *property_spec,
-                                 gpointer null);
+hildon_window_notify (GObject *gobject, GParamSpec *param);
 
-static gboolean 
-hildon_window_vbox_expose_event (GtkWidget *vbox,
-                                 GdkEventExpose *event,
-                                 gpointer window);
+static void
+hildon_window_is_topmost_notify (HildonWindow *window);
 
 static gboolean
 hildon_window_toggle_menu (HildonWindow * self);
@@ -255,6 +247,7 @@ hildon_window_class_init (HildonWindowClass * window_class)
 
     object_class->set_property = hildon_window_set_property;
     object_class->get_property = hildon_window_get_property;
+    object_class->notify = hildon_window_notify;
 
     /* Set the widgets virtual functions */
     widget_class->size_allocate = hildon_window_size_allocate;
@@ -265,6 +258,7 @@ hildon_window_class_init (HildonWindowClass * window_class)
     widget_class->unrealize = hildon_window_unrealize;
     widget_class->key_press_event = hildon_window_key_press_event;
     widget_class->key_release_event = hildon_window_key_release_event;
+    widget_class->window_state_event = hildon_window_window_state_event;
     
     /* now the object stuff */
     object_class->finalize = hildon_window_finalize;
@@ -300,7 +294,15 @@ hildon_window_class_init (HildonWindowClass * window_class)
                                          "Size of graphical toolbar borders",
                                           GTK_TYPE_BORDER,
                                           G_PARAM_READABLE));
-
+    
+    /* opera hack, install clip operation signal */
+    g_signal_new ("clipboard_operation",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (HildonWindowClass, clipboard_operation),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1,
+                  GTK_TYPE_INT);
 }
 
 static void
@@ -308,7 +310,7 @@ hildon_window_init (HildonWindow * self)
 {
     HildonWindowPrivate *priv = self->priv = HILDON_WINDOW_GET_PRIVATE(self);
 
-    self->priv->vbox = gtk_vbox_new (TRUE, 10);
+    self->priv->vbox = gtk_vbox_new (TRUE, TOOLBAR_MIDDLE);
     gtk_widget_set_parent (self->priv->vbox, GTK_WIDGET(self));
     priv->menu = NULL;
     priv->visible_toolbars = 0;
@@ -320,23 +322,6 @@ hildon_window_init (HildonWindow * self)
     priv->fullscreen = FALSE;
    
     priv->program = NULL;
-    gtk_widget_set_events (GTK_WIDGET(self), GDK_BUTTON_PRESS_MASK |
-             GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
-
-    /* Handle the drawing of the vbox (add the pixmap borders) */
-    g_signal_connect (G_OBJECT (self->priv->vbox), "expose-event",
-                      G_CALLBACK (hildon_window_vbox_expose_event),
-                      self);
-
-    /* Used to keep track of fullscreen / unfullscreen */
-    g_signal_connect (G_OBJECT (self), "window_state_event",
-            G_CALLBACK (hildon_window_window_state_event), self);
-    
-    g_signal_connect (G_OBJECT (self), "notify::title",
-            G_CALLBACK (hildon_window_title_notify), self);
-    
-    g_signal_connect (G_OBJECT (self), "notify::is-topmost",
-            G_CALLBACK (hildon_window_is_topmost_notify), self);
     
     /* We need to track the root window _MB_CURRENT_APP_WINDOW property */
     gdk_window_set_events (gdk_get_default_root_window (),
@@ -506,6 +491,8 @@ hildon_window_expose (GtkWidget * widget, GdkEventExpose * event)
     if (!priv->borders)
     {
         hildon_window_get_borders (HILDON_WINDOW (widget));
+        b = HILDON_WINDOW(widget)->priv->borders;
+        tb = HILDON_WINDOW(widget)->priv->toolbar_borders;
     }
 
     tb_height = bx->allocation.height + tb->top + tb->bottom;
@@ -513,18 +500,8 @@ hildon_window_expose (GtkWidget * widget, GdkEventExpose * event)
     g_list_foreach (box->children, visible_toolbars, 
             &currently_visible_toolbars);
 
-    if (priv->previous_vbox_y != priv->vbox->allocation.y)
-    {
-        gint draw_from_y = priv->previous_vbox_y < priv->vbox->allocation.y?
-            priv->previous_vbox_y - tb->top:
-            priv->vbox->allocation.y - tb->top;
-        
-        gtk_widget_queue_draw_area (widget, 0, draw_from_y, 
-                widget->allocation.width,
-                widget->allocation.height - draw_from_y);
-        
-        priv->previous_vbox_y = priv->vbox->allocation.y;
-    }
+    paint_toolbar (widget, box,
+                   event, priv->fullscreen);
 
     if (!HILDON_WINDOW (widget)->priv->fullscreen)
     {
@@ -557,7 +534,7 @@ hildon_window_expose (GtkWidget * widget, GdkEventExpose * event)
         }
 
         /* If no toolbar, draw the bottom window border */
-        if (!currently_visible_toolbars &&b->bottom > 0)
+        if (!currently_visible_toolbars && b->bottom > 0)
         {
             gtk_paint_box (widget->style, widget->window,
                     GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
@@ -584,6 +561,8 @@ hildon_window_expose (GtkWidget * widget, GdkEventExpose * event)
      * rectangle. Instead start with the drawing of the GtkBin */
     GTK_WIDGET_CLASS (g_type_class_peek_parent (parent_class))->
         expose_event (widget, event);
+    /*GTK_WIDGET_CLASS (parent_class))->
+        expose_event (widget, event);*/
 
     return FALSE;
 
@@ -643,11 +622,13 @@ hildon_window_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
     if (!priv->borders)
     {
         hildon_window_get_borders (HILDON_WINDOW (widget));
+        b = HILDON_WINDOW (widget)->priv->borders;
+        tb = HILDON_WINDOW (widget)->priv->toolbar_borders;
     }
     
     widget->allocation = *allocation;
 
-    gtk_widget_size_request (box, &req);
+    gtk_widget_get_child_requisition (box, &req);
 
     box_alloc.width = allocation->width - tb->left - tb->right;
     box_alloc.height = ( (req.height < allocation->height) ?
@@ -655,8 +636,8 @@ hildon_window_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
     box_alloc.x = allocation->x + tb->left;
     box_alloc.y = allocation->y + allocation->height - box_alloc.height - tb->bottom;
 
-
-    if (bin->child != NULL && GTK_IS_WIDGET (bin->child))
+    if (bin->child != NULL && GTK_IS_WIDGET (bin->child)
+                           && GTK_WIDGET_VISIBLE (bin->child))
     {
         alloc.x += border_width;
         alloc.y += border_width;
@@ -670,19 +651,39 @@ hildon_window_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
             alloc.y += b->top;
 
             alloc.height -= b->top;
-        }
 
-        if (box_alloc.height <= 0 && 
-                !(HILDON_WINDOW (widget)->priv->fullscreen))
-            alloc.height -= b->bottom;
+            if (box_alloc.height <= 0)
+                alloc.height -= b->bottom;
+            else
+                alloc.height -= (tb->top + tb->bottom);            
+        }
         else
-            alloc.height -= (tb->top + tb->bottom);
+        {
+            if (!(box_alloc.height <= 0))
+                alloc.height -= (tb->top + tb->bottom);              
+        }
 
         gtk_widget_size_allocate (bin->child, &alloc);
     }
 
 
     gtk_widget_size_allocate (box, &box_alloc);
+
+    if (priv->previous_vbox_y != box_alloc.y)
+    {
+        /* The size of the VBox has changed, we need to redraw part
+         * of the window borders */
+        gint draw_from_y = priv->previous_vbox_y < box_alloc.y?
+            priv->previous_vbox_y - tb->top:
+            box_alloc.y - tb->top;
+        
+        gtk_widget_queue_draw_area (widget, 0, draw_from_y, 
+                widget->allocation.width,
+                widget->allocation.height - draw_from_y);
+        
+        priv->previous_vbox_y = box_alloc.y;
+    }
+
 }
 
 static void
@@ -765,57 +766,24 @@ hildon_window_destroy (GtkObject *obj)
     GTK_OBJECT_CLASS (parent_class)->destroy (obj);
 }
 
+
 static void
-hildon_window_is_topmost_notify (GObject *self,
-                                 GParamSpec *property_spec,
-                                 gpointer null)
+hildon_window_notify (GObject *gobject, GParamSpec *param)
 {
-    HildonWindow *window = HILDON_WINDOW (self);
-
-    if (window->priv->is_topmost)
+    HildonWindow *window = HILDON_WINDOW (gobject);
+    
+    if (strcmp (param->name, "title") == 0)
     {
-        hildon_window_take_common_toolbar (window);
-    }
 
-    else
+        hildon_window_update_title (window);
+    }
+    else if (strcmp (param->name, "is-topmost"))
     {
-        /* If the window lost focus while the user started to press
-         * the ESC key, we won't get the release event. We need to
-         * stop the timeout*/
-        if (window->priv->escape_timeout)
-        {
-            g_source_remove (window->priv->escape_timeout);
-            window->priv->escape_timeout = 0;
-        }
+        hildon_window_is_topmost_notify (window);
     }
-}
-
-
-static gboolean 
-hildon_window_vbox_expose_event (GtkWidget *vbox,
-                                 GdkEventExpose *event,
-                                 gpointer window)
-{
-    HildonWindowPrivate *priv = HILDON_WINDOW (window)->priv;
-
-    hildon_window_get_borders (HILDON_WINDOW(window));
-
-    event->area.x -= priv->toolbar_borders->left;
-    event->area.y -= priv->toolbar_borders->top;
-    event->area.width += (priv->toolbar_borders->left + 
-                          priv->toolbar_borders->right);
-    event->area.height += (priv->toolbar_borders->top + 
-                           priv->toolbar_borders->bottom);
 
-    paint_toolbar (GTK_WIDGET (window), GTK_BOX (vbox), event, priv->fullscreen);
-    
-    GTK_WIDGET_CLASS (G_TYPE_INSTANCE_GET_CLASS (vbox, GTK_TYPE_VBOX, GtkVBox))
-            ->expose_event (vbox, event);
-    
-    event->area = GTK_WIDGET(window)->allocation;
-
-
-    return TRUE;
+    if (G_OBJECT_CLASS(parent_class)->notify)
+        G_OBJECT_CLASS(parent_class)->notify (gobject, param);
 }
 
 /* Utilities */
@@ -1018,13 +986,19 @@ hildon_window_get_active_window (void)
     return ret;
 }
 
+static int
+xclient_message_type_check (XClientMessageEvent *cm, const gchar *name)
+{
+    return cm->message_type == XInternAtom(GDK_DISPLAY(), name, FALSE);
+}
 
 /*****************/
 /* Event filters */
 /*****************/
 
 /*
- * Handle the window border custom button, which toggles the menu
+ * Handle the window border custom button, which toggles the menu,
+ * and the Hildon input method copy paste messages
  */
 static GdkFilterReturn
 hildon_window_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
@@ -1034,14 +1008,31 @@ hildon_window_event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
     if (eventti->type == ClientMessage)
     {
         XClientMessageEvent *cm = xevent;
-        Atom mb_grab_transfer_atom = 
-            XInternAtom (GDK_DISPLAY(), "_MB_GRAB_TRANSFER", FALSE);
 
-        if (cm->message_type == mb_grab_transfer_atom)
+        if (xclient_message_type_check (cm, "_MB_GRAB_TRANSFER"))
         {
             hildon_window_toggle_menu (HILDON_WINDOW ( data ));
             return GDK_FILTER_REMOVE;
         }
+        /* opera hack clipboard client message */
+        else if (xclient_message_type_check (cm, "_HILDON_IM_CLIPBOARD_COPY"))
+        {
+            g_signal_emit_by_name(G_OBJECT(data), "clipboard_operation",
+                                  HILDON_WINDOW_CO_COPY);
+            return GDK_FILTER_REMOVE;
+        }
+        else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_CUT"))
+        {
+            g_signal_emit_by_name(G_OBJECT(data), "clipboard_operation",
+                                  HILDON_WINDOW_CO_CUT);
+            return GDK_FILTER_REMOVE;
+        }
+        else if (xclient_message_type_check(cm, "_HILDON_IM_CLIPBOARD_PASTE"))
+        {
+            g_signal_emit_by_name(G_OBJECT(data), "clipboard_operation",
+                                  HILDON_WINDOW_CO_PASTE);
+            return GDK_FILTER_REMOVE;
+        }
     }
 
     return GDK_FILTER_CONTINUE;
@@ -1143,8 +1134,7 @@ hildon_window_key_release_event (GtkWidget *widget, GdkEventKey *event)
  */
 static gboolean
 hildon_window_window_state_event (GtkWidget *widget, 
-                                  GdkEventWindowState *event,
-                                  gpointer null)
+                                  GdkEventWindowState *event)
 {
     if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
     {
@@ -1152,7 +1142,16 @@ hildon_window_window_state_event (GtkWidget *widget,
             event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
     }
 
-    return FALSE;
+    if (GTK_WIDGET_CLASS (parent_class)->window_state_event)
+    {
+        return GTK_WIDGET_CLASS (parent_class)->window_state_event (
+                widget,
+                event);
+    }
+    else
+    {
+        return FALSE;
+    }
 }
 
 static void 
@@ -1209,6 +1208,31 @@ hildon_window_menupopupfuncfull ( GtkMenu *menu, gint *x, gint *y,
 /* Private methods  */
 /********************/
 
+
+/*
+ * Takes the common toolbar when we acquire the top-most status
+ */
+static void
+hildon_window_is_topmost_notify (HildonWindow *window)
+{
+    if (window->priv->is_topmost)
+    {
+        hildon_window_take_common_toolbar (window);
+    }
+
+    else
+    {
+        /* If the window lost focus while the user started to press
+         * the ESC key, we won't get the release event. We need to
+         * stop the timeout*/
+        if (window->priv->escape_timeout)
+        {
+            g_source_remove (window->priv->escape_timeout);
+            window->priv->escape_timeout = 0;
+        }
+    }
+}
+
 /*
  * Sets the program to which the window belongs. This should only be called
  * by hildon_program_add_window
@@ -1414,8 +1438,9 @@ static gboolean
 hildon_window_toggle_menu (HildonWindow * self)
 {
     GtkMenu *menu_to_use = NULL;
+    GList *menu_children = NULL;
     
-    g_return_if_fail (self && HILDON_IS_WINDOW (self));
+    g_return_val_if_fail (self && HILDON_IS_WINDOW (self), FALSE);
 
     /* Select which menu to use, Window specific has highest priority,
      * then program specific */
@@ -1447,15 +1472,20 @@ hildon_window_toggle_menu (HildonWindow * self)
     }
     
 
-    if (GTK_WIDGET_VISIBLE (GTK_WIDGET (menu_to_use)))
+    if (GTK_WIDGET_MAPPED (GTK_WIDGET (menu_to_use)))
     {
         gtk_menu_popdown (menu_to_use);
         gtk_menu_shell_deactivate (GTK_MENU_SHELL (menu_to_use));
         return TRUE;
     }
 
-    if (gtk_container_get_children (GTK_CONTAINER (menu_to_use)) != NULL)
+    /* Check if the menu has items */
+    menu_children = gtk_container_get_children (GTK_CONTAINER (menu_to_use));
+
+    if (menu_children)
     {
+        g_list_free (menu_children);
+
         /* Apply right theming */
         gtk_widget_set_name (GTK_WIDGET (menu_to_use),
                 "menu_force_with_corners");
@@ -1477,9 +1507,11 @@ hildon_window_toggle_menu (HildonWindow * self)
                            gtk_get_current_event_time ());
         }
         gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_to_use), TRUE);
+        return TRUE;
     }
 
-    return TRUE;
+    return FALSE;
+
 }
 
 /*
@@ -1499,8 +1531,10 @@ hildon_window_escape_timeout (gpointer data)
     /* Send fake event, simulation a situation that user
        pressed 'x' from the corner */
     event = gdk_event_new(GDK_DELETE);
-    ((GdkEventAny *)event)->window = GTK_WIDGET(data)->window;
+    ((GdkEventAny *)event)->window = GDK_WINDOW (g_object_ref (GTK_WIDGET(data)->window));
     gtk_main_do_event(event);
+
+    /* That unrefs the window, so we're reffing it above */
     gdk_event_free(event);
 
     priv->escape_timeout = 0;
@@ -1607,11 +1641,6 @@ hildon_window_remove_toolbar (HildonWindow *self, GtkToolbar *toolbar)
     g_return_if_fail (self && HILDON_IS_WINDOW (self));
 
     gtk_container_remove (vbox, GTK_WIDGET(toolbar));
-    /* FIXME: As the toolbar border graphics go beyond the VBox, we
-     * need to trigger a manual redraw */
-    gtk_widget_queue_draw_area (GTK_WIDGET (self) , 0, 0, 
-                GTK_WIDGET(self)->allocation.width,
-                GTK_WIDGET(self)->allocation.height);
 }
 
 /**
@@ -1638,25 +1667,27 @@ hildon_window_get_menu (HildonWindow * self)
  * 
  * Sets the menu to be used for this window. This menu overrides
  * a program-wide menu that may have been set with
- * hildon_program_set_common_menu.
+ * hildon_program_set_common_menu. Pass NULL to remove the current
+ * menu.
  **/ 
 void
 hildon_window_set_menu (HildonWindow *self, GtkMenu *menu)
 {
-    g_return_if_fail (self && HILDON_IS_WINDOW (self) && 
-            menu && GTK_IS_MENU (menu));
+    g_return_if_fail (HILDON_IS_WINDOW (self));
 
-    if (self->priv->menu)
-    {
+    if (self->priv->menu != NULL) {
+        gtk_menu_detach (GTK_MENU (self->priv->menu));
         g_object_unref (self->priv->menu);
     }
 
-    self->priv->menu = GTK_WIDGET (menu);
-    gtk_widget_set_name (GTK_WIDGET (self->priv->menu),
-                         "menu_force_with_corners");
-    gtk_widget_show_all (self->priv->menu);
+    self->priv->menu = (menu != NULL) ? GTK_WIDGET (menu) : NULL;
+    if (self->priv->menu != NULL) {
+        gtk_widget_set_name (self->priv->menu, "menu_force_with_corners");
+        gtk_menu_attach_to_widget (GTK_MENU (self->priv->menu), GTK_WIDGET (self), &detach_menu_func);
+        g_object_ref (GTK_MENU (self->priv->menu));
+    }
 
-    gtk_menu_attach_to_widget (menu, GTK_WIDGET (self), &detach_menu_func);
+    gtk_widget_show_all (GTK_WIDGET (self));
 }
 
 /**