[maemo-commits] [maemo-commits] r9127 - in projects/haf/branches/maemo-af-desktop/hildon-desktop: . data src

From: subversion at stage.maemo.org subversion at stage.maemo.org
Date: Tue Jan 16 16:47:57 EET 2007
Author: lucasr
Date: 2007-01-16 16:47:55 +0200 (Tue, 16 Jan 2007)
New Revision: 9127

Added:
   projects/haf/branches/maemo-af-desktop/hildon-desktop/data/others-button.desktop.in
   projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hn-others-button.c
   projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hn-others-button.h
Modified:
   projects/haf/branches/maemo-af-desktop/hildon-desktop/ChangeLog
   projects/haf/branches/maemo-af-desktop/hildon-desktop/configure.ac
   projects/haf/branches/maemo-af-desktop/hildon-desktop/data/Makefile.am
   projects/haf/branches/maemo-af-desktop/hildon-desktop/src/Makefile.am
   projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hd-plugin-loader-builtin.c
Log:
2007-01-16  Lucas Rocha  <lucas.rocha at nokia.com>

	* configure.ac, src/Makefile.am, src/hn-others-button.[ch]: others menu 
	button ported to hildon-desktop.
	* src/hd-plugin-loader-builtin.c: added others menu support.
	* data/others-button.desktop.in: others menu button plugin desktop
	file.


Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/ChangeLog
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/ChangeLog	2007-01-16 14:39:18 UTC (rev 9126)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/ChangeLog	2007-01-16 14:47:55 UTC (rev 9127)
@@ -1,3 +1,11 @@
+2007-01-16  Lucas Rocha  <lucas.rocha at nokia.com>
+
+	* configure.ac, src/Makefile.am, src/hn-others-button.[ch]: others menu 
+	button ported to hildon-desktop.
+	* src/hd-plugin-loader-builtin.c: added others menu support.
+	* data/others-button.desktop.in: others menu button plugin desktop
+	file.
+
 2007-01-11  Johan Bilien  <johan.bilien at nokia.com>
 
 	* src/hd-home-background.[ch]:

Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/configure.ac
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/configure.ac	2007-01-16 14:39:18 UTC (rev 9126)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/configure.ac	2007-01-16 14:47:55 UTC (rev 9127)
@@ -190,4 +190,5 @@
 background-manager/background-manager-dbus.h 
 data/Makefile 
 data/app-switcher.desktop 
+data/others-button.desktop 
 ])

Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/data/Makefile.am
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/data/Makefile.am	2007-01-16 14:39:18 UTC (rev 9126)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/data/Makefile.am	2007-01-16 14:47:55 UTC (rev 9127)
@@ -6,6 +6,7 @@
 	home-background.conf
 
 hildonnavigatordesktopentry_DATA = \
-	app-switcher.desktop 
+	app-switcher.desktop       \
+        others-button.desktop	
 
 CLEANFILES = *~

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/data/others-button.desktop.in
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/data/others-button.desktop.in	2007-01-16 14:39:18 UTC (rev 9126)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/data/others-button.desktop.in	2007-01-16 14:47:55 UTC (rev 9127)
@@ -0,0 +1,4 @@
+[Desktop Entry]
+Name=Other Menu Button
+Type=builtin
+X-Path=othersbutton

Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/src/Makefile.am
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/src/Makefile.am	2007-01-16 14:39:18 UTC (rev 9126)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/src/Makefile.am	2007-01-16 14:47:55 UTC (rev 9127)
@@ -16,6 +16,7 @@
 	-I$(top_srcdir) 						   \
 	-DLOCALEDIR=\"$(localedir)\" 					   \
 	-DDATADIR=\"$(datadir)\"                                           \
+	-DHD_DESKTOP_ENTRY_PATH=\"$(hildondesktopentrydir)\" 		   \
 	-DHD_DESKTOP_CONFIG_PATH=\"$(hildondesktopconfdir)\" 		   \
 	-DHD_DESKTOP_MODULE_PATH=\"$(hildondesktoplibdir)\"                \
 	-DHD_DESKTOP_BACKGROUNDS_PATH=\"$(hildondesktopbackgroundsdir)\"   \
@@ -52,9 +53,9 @@
 HOME_SOURCES = \
 	hd-home-window.c            \
 	hd-home-window.h            \
-    hd-home-background-dialog.c \
-    hd-home-background-dialog.h \
-	hd-home-background.c 		\
+	hd-home-background-dialog.c \
+	hd-home-background-dialog.h \
+	hd-home-background.c        \
 	hd-home-background.h
 
 APP_SWITCHER_SOURCES  = \
@@ -71,10 +72,15 @@
 	hn-app-tooltip.c              \
 	hn-app-tooltip.h 
 
+OTHERS_BUTTON_SOURCES  = \
+	hn-others-button.c \
+	hn-others-button.h
+
 hildon_desktop_SOURCES = \
 	$(PLUGIN_MANAGER_SOURCES)   \
 	$(HOME_SOURCES)             \
 	$(APP_SWITCHER_SOURCES)     \
+	$(OTHERS_BUTTON_SOURCES)    \
 	hd-desktop.c                \
 	hd-desktop.h                \
 	hd-select-plugins-dialog.c  \

Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hd-plugin-loader-builtin.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hd-plugin-loader-builtin.c	2007-01-16 14:39:18 UTC (rev 9126)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hd-plugin-loader-builtin.c	2007-01-16 14:47:55 UTC (rev 9127)
@@ -29,13 +29,15 @@
 #include "hd-plugin-loader-builtin.h"
 #include "hd-config.h"
 #include "hn-app-switcher.h"
+#include "hn-others-button.h"
 
 #define HD_PLUGIN_LOADER_BUILTIN_GET_PRIVATE(obj) \
         (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HD_TYPE_PLUGIN_LOADER_BUILTIN, HDPluginLoaderBuiltinPrivate))
 
 G_DEFINE_TYPE (HDPluginLoaderBuiltin, hd_plugin_loader_builtin, HD_TYPE_PLUGIN_LOADER);
 
-#define HD_PLUGIN_LOADER_BUILTIN_APP_SWITCHER "appswitcher"
+#define HD_PLUGIN_LOADER_BUILTIN_APP_SWITCHER  "appswitcher"
+#define HD_PLUGIN_LOADER_BUILTIN_OTHERS_BUTTON "othersbutton"
 
 static GList *
 hd_plugin_loader_builtin_load (HDPluginLoader *loader, GError **error)
@@ -64,7 +66,7 @@
                                 HD_PLUGIN_CONFIG_GROUP, 
                                 HD_PLUGIN_CONFIG_KEY_PATH,
                                 &keyfile_error);
-  g_debug ("path %s",path);
+  
   if (keyfile_error)
   {
     g_propagate_error (error, keyfile_error);
@@ -78,7 +80,13 @@
 
     objects = g_list_append (objects, object);
   }
+  else if (!g_ascii_strcasecmp (path, HD_PLUGIN_LOADER_BUILTIN_OTHERS_BUTTON)) 
+  {
+    GObject *object = g_object_new (HN_TYPE_OTHERS_BUTTON, NULL);
 
+    objects = g_list_append (objects, object);
+  }
+
   g_free (path);
   
   return objects;

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hn-others-button.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hn-others-button.c	2007-01-16 14:39:18 UTC (rev 9126)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hn-others-button.c	2007-01-16 14:47:55 UTC (rev 9127)
@@ -0,0 +1,1020 @@
+/* -*- mode:C; c-file-style:"gnu"; -*- */
+/*
+ * This file is part of maemo-af-desktop
+ *
+ * Copyright (C) 2006 Nokia Corporation.
+ *
+ * Contact: Karoliina Salminen <karoliina.t.salminen at 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.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <errno.h>
+#include <sys/resource.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#include <hildon-base-lib/hildon-base-dnotify.h>
+#include <hildon-widgets/hildon-finger.h>
+#include <libhildondesktop/libhildonmenu.h>
+#include <libhildondesktop/hildon-thumb-menu-item.h>
+#include <libhildonwm/hd-wm.h>
+#include <libosso.h>
+
+#include "hn-others-button.h"
+#include "hn-app-switcher.h"
+
+#define HN_OTHERS_BUTTON_GET_PRIVATE(obj) \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HN_TYPE_OTHERS_BUTTON, HNOthersButtonPrivate))
+
+G_DEFINE_TYPE (HNOthersButton, hn_others_button, TASKNAVIGATOR_TYPE_ITEM);
+
+#define NAVIGATOR_BUTTON_THREE "hildon-navigator-button-three"
+#define NAVIGATOR_MENU_NAME    "menu_from_navigator"
+
+#define OTHERS_MENU_ICON_NAME "qgn_grid_tasknavigator_others"
+#define OTHERS_MENU_ICON_SIZE 64
+
+#define MENU_ITEM_DEFAULT_ICON        ""
+#define MENU_ITEM_SUBMENU_ICON        "qgn_list_gene_fldr_cls"
+#define MENU_ITEM_SUBMENU_ICON_DIMMED "qgn_list_gene_nonreadable_fldr"
+#define MENU_ITEM_DEFAULT_APP_ICON    "qgn_list_gene_default_app"
+#define MENU_ITEM_ICON_SIZE           26
+#define MENU_ITEM_THUMB_ICON_SIZE     64
+
+#define MENU_ITEM_EMPTY_SUBMENU_STRING _( "tana_li_of_noapps" )
+
+#define BORDER_WIDTH 7
+#define BUTTON_HEIGHT 90
+
+#define MENU_Y_POS 180
+#define MENU_MAX_WIDTH 360
+
+#define WORKAREA_ATOM "_NET_WORKAREA"
+
+#define MENU_ITEM_N_ITEMS(n) dngettext("hildon-fm", "sfil_li_folder_contents_item", "sfil_li_folder_contents_items", n)
+
+struct _HNOthersButtonPrivate
+{
+  GtkWidget  *button;
+  guint       collapse_id;
+  gboolean    thumb_pressed;
+  guint       dnotify_update_timeout;
+};
+
+static void hn_others_button_create_menu (HNOthersButton *button);
+static void hn_others_button_button_toggled (GtkWidget *widget, HNOthersButton *button);
+static gboolean hn_others_button_key_press (GtkWidget* widget, GdkEventKey *event, HNOthersButton *button);
+static gboolean hn_others_button_button_press (GtkWidget *widget, GdkEventButton *event, HNOthersButton *button);
+static gboolean hn_others_button_button_release(GtkWidget *widget, GdkEventButton *event, HNOthersButton *button);
+
+static void
+hn_others_button_finalize (GObject *gobject)
+{
+  HNOthersButton *button = HN_OTHERS_BUTTON (gobject);
+
+  if (button->priv->collapse_id)
+    g_source_remove (button->priv->collapse_id);
+
+  if (button->priv->dnotify_update_timeout)
+    g_source_remove (button->priv->dnotify_update_timeout);
+  
+  if (TASKNAVIGATOR_ITEM (button)->menu)
+    gtk_widget_destroy (GTK_WIDGET (TASKNAVIGATOR_ITEM (button)->menu));
+  
+  G_OBJECT_CLASS (hn_others_button_parent_class)->finalize (gobject);
+}
+
+static void
+hn_others_button_class_init (HNOthersButtonClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ 
+  gobject_class->finalize = hn_others_button_finalize;
+    
+  g_type_class_add_private (gobject_class, sizeof (HNOthersButtonPrivate));
+}
+
+static void
+hn_others_button_init (HNOthersButton *button)
+{
+  HDWM *hdwm;
+  GtkWidget *icon;
+  
+  HNOthersButtonPrivate *priv = HN_OTHERS_BUTTON_GET_PRIVATE (button);
+
+  button->priv = priv;
+
+  gtk_widget_set_name (GTK_WIDGET(button), NAVIGATOR_BUTTON_THREE);
+
+  gtk_widget_set_extension_events (GTK_WIDGET (button),
+                                   GDK_EXTENSION_EVENTS_ALL);
+
+  priv->button = gtk_toggle_button_new ();
+
+  icon = gtk_image_new_from_pixbuf (get_icon (OTHERS_MENU_ICON_NAME,
+				 	      OTHERS_MENU_ICON_SIZE));
+
+  if (icon)
+  {
+    gtk_container_add (GTK_CONTAINER (priv->button), icon);
+  }
+
+  gtk_widget_set_size_request (priv->button, 80, 80);
+
+  gtk_widget_show_all (priv->button);
+
+  hdwm = hd_wm_get_singleton ();
+  hd_wm_set_others_menu_button (hdwm, priv->button);
+
+  g_signal_connect (G_OBJECT (priv->button), 
+                    "button-press-event",
+      	             G_CALLBACK (hn_others_button_button_press), 
+		     button);
+  
+  g_signal_connect (G_OBJECT (priv->button), 
+		    "button-release-event",
+      	             G_CALLBACK (hn_others_button_button_release), 
+		     button);
+  
+  g_signal_connect (G_OBJECT (priv->button), 
+		    "toggled",
+      	            G_CALLBACK (hn_others_button_button_toggled), 
+		    button);
+  
+  g_signal_connect (G_OBJECT (priv->button), 
+		    "key-press-event",
+ 		     G_CALLBACK (hn_others_button_key_press), 
+		     button);
+
+  priv->collapse_id = 0;
+  priv->thumb_pressed = FALSE;
+  priv->dnotify_update_timeout = 0;
+
+  TASKNAVIGATOR_ITEM (button)->menu = NULL;
+
+  gtk_container_add (GTK_CONTAINER (button), priv->button);
+}
+
+GtkWidget *
+hn_others_button_new ()
+{
+  HNOthersButton *button;
+
+  button = g_object_new (HN_TYPE_OTHERS_BUTTON, NULL);
+
+  return GTK_WIDGET (button);
+}
+
+static void
+hn_others_menu_size_request(GtkWidget *menu,
+			    GtkRequisition *req,
+			    gpointer data)
+{
+  if (req->width > MENU_MAX_WIDTH)
+  {
+    gtk_widget_set_size_request (GTK_WIDGET (menu), MENU_MAX_WIDTH, -1);
+  }
+}
+
+static gboolean
+hn_others_menu_deactivate (GtkWidget *menu, HNOthersButton *button)
+{
+  g_return_val_if_fail (button, FALSE);
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button->priv->button),
+			        FALSE);
+
+  return TRUE;
+}
+
+static gboolean
+hn_others_menu_key_press(GtkWidget *menu,
+			 GdkEventKey *event,
+			 HNOthersButton *button)
+{
+  HDWM *hdwm = hd_wm_get_singleton ();
+
+  if (event->keyval == GDK_Left    ||
+      event->keyval == GDK_KP_Left ||
+      event->keyval == GDK_Escape)
+  {
+    gtk_menu_shell_deactivate (GTK_MENU_SHELL (menu));
+
+    if (event->keyval == GDK_Escape)
+    {
+      /* pass focus to the last active application */
+      hd_wm_focus_active_window (hdwm);
+    }
+    else
+    {
+      hd_wm_activate (HD_TN_ACTIVATE_KEY_FOCUS);
+      gtk_widget_grab_focus (GTK_WIDGET (button->priv->button));
+    }
+    
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static void
+hn_others_menu_activate_item (GtkMenuItem *item, HNOthersButton *button)
+{
+  gchar *service_field;
+  gchar *exec_field;
+  gchar *program = NULL;
+  GError *error = NULL;
+  
+  g_return_if_fail (button);
+
+  if ((service_field = g_object_get_data (G_OBJECT (item),
+      				          DESKTOP_ENTRY_SERVICE_FIELD)))
+  {
+    /* Launch the app or if it's already running move it to the top */
+    hd_wm_top_service (service_field);
+  }
+  else
+  {
+    /* No way to track hibernation (if any), so call the dialog
+       unconditionally when in lowmem state */
+    if (hd_wm_is_lowmem_situation ())
+    {
+      /*
+      if (!tn_close_application_dialog (CAD_ACTION_OPENING))
+      {
+        return;
+      }
+      */
+    }
+    
+    exec_field = g_object_get_data (G_OBJECT (item),
+    			              DESKTOP_ENTRY_EXEC_FIELD);
+
+    if(exec_field)
+    {
+      gchar * space = strchr(exec_field, ' ');
+
+      if(space)
+      {
+        gchar *cmd = g_strdup (exec_field);
+        cmd[space - exec_field] = 0;
+
+        gchar *exc = g_find_program_in_path (cmd);
+
+        program = g_strconcat (exc, space, NULL);
+
+        g_free (exc);
+        g_free (cmd);
+      }
+      else
+      {
+        program = g_find_program_in_path (exec_field);
+      }
+    }
+    
+    if (program)
+    {
+      gint argc;
+      gchar **argv;
+      GPid child_pid;
+      
+      if (g_shell_parse_argv (program, &argc, &argv, &error))
+      {
+        g_spawn_async (
+    	      /* Child's current working directory,
+    	         or NULL to inherit parent's */
+    	      NULL,
+    	      /* Child's argument vector. [0] is the path of
+    	         the program to execute */
+    	      argv,
+    	      /* Child's environment, or NULL to inherit
+    	         parent's */
+    	      NULL,
+    	      /* Flags from GSpawnFlags */
+    	      0,
+    	      /* Function to run in the child just before
+    	         exec() */
+    	      NULL,
+    	      /* User data for child_setup */
+    	      NULL,
+    	      /* Return location for child process ID or NULL */
+    	      &child_pid,
+    	      /* Return location for error */
+    	      &error);
+      }
+      
+      if (error)
+      {
+        g_error ("Others_menu_activate_item: failed to execute %s: %s.",
+                  exec_field, 
+      	    error->message);
+    
+        g_clear_error (&error);
+      }
+      else
+      {
+        int priority;
+        errno = 0;
+
+        /* If the child process inherited desktop's high priority,
+         * give child default priority */
+        priority = getpriority (PRIO_PROCESS, child_pid);
+    
+        if (!errno && priority < 0)
+        {
+          setpriority (PRIO_PROCESS, child_pid, 0);
+        }
+      }
+    }
+    else
+    {
+      /* We don't have the service name and we don't
+       * have a path to the executable so it would be
+       * quite difficult to launch the app.
+       */
+      g_error ("hn_others_menu_activate_item: "
+               "both service name and binary path missing. "
+               "Unable to launch.");
+    }
+  }
+  
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button->priv->button), 
+		                FALSE);
+
+  g_free(program);
+}
+
+static void
+hn_others_menu_get_items (GtkMenu *menu,
+			  HNOthersButton *button,
+			  GtkTreeModel *model,
+			  GtkTreeIter *iter)
+{
+  GtkMenu *submenu = NULL;
+
+  GtkWidget *menu_item = NULL;
+
+  gchar *item_name = NULL;
+  gchar *item_comment = NULL;
+  GdkPixbuf *item_icon  = NULL;
+  GdkPixbuf *item_thumb_icon = NULL;
+  gchar *item_exec = NULL;
+  gchar *item_service = NULL;
+  gchar *item_desktop_id = NULL;
+  gchar *item_text_domain = NULL;
+  GtkTreeIter child_iter;
+  gint children;
+  gboolean my_iterator = FALSE;
+  
+  g_return_if_fail (menu);
+
+  if (!model)
+  {
+    GtkTreeIter iter0;
+    
+    model = get_menu_contents();
+    iter = g_malloc0 (sizeof (GtkTreeIter));
+    my_iterator = TRUE;
+    
+    /* Get the top level iterator. */
+    if (!gtk_tree_model_get_iter_first(model, &iter0) ||
+        !gtk_tree_model_iter_children(model, iter, &iter0))
+    {
+      g_object_unref (G_OBJECT (model));
+      return;
+    }
+  }
+  else
+  {
+    g_object_ref (G_OBJECT (model));
+  }
+    
+  /* Loop! */
+  do  {
+    item_name = NULL;
+    item_icon = NULL;
+    item_thumb_icon = NULL;
+    item_exec = NULL;
+    item_service = NULL;
+    item_desktop_id = NULL;
+    item_text_domain = NULL;
+
+    gtk_tree_model_get (model, iter,
+		        TREE_MODEL_NAME, &item_name,
+		        TREE_MODEL_ICON, &item_icon,
+		        TREE_MODEL_THUMB_ICON, &item_thumb_icon,
+		        TREE_MODEL_EXEC, &item_exec,
+		        TREE_MODEL_SERVICE, &item_service,
+		        TREE_MODEL_DESKTOP_ID, &item_desktop_id,
+		        TREE_MODEL_COMMENT, &item_comment,
+		        TREE_MODEL_TEXT_DOMAIN, &item_text_domain,
+		        -1);
+
+    children = 0;
+
+    /* If the item has children. */
+    if (gtk_tree_model_iter_children (model, &child_iter, iter))
+    {
+      gchar *child_string = NULL;
+      	    
+      /* It's a submenu */
+      submenu = GTK_MENU (gtk_menu_new ());
+      
+      gtk_widget_set_name (GTK_WIDGET (submenu),
+                           NAVIGATOR_MENU_NAME);
+      
+      /* Create a menu item and add it to the menu. */
+      children = gtk_tree_model_iter_n_children (model, iter);
+      child_string = g_strdup_printf(MENU_ITEM_N_ITEMS (children), children);
+ 
+      menu_item = hildon_thumb_menu_item_new_with_labels (
+                                (item_text_domain && *item_text_domain) ?
+                                dgettext(item_text_domain, item_name):
+                                _(item_name),
+                                NULL,
+                                child_string);
+      
+      g_free(child_string);
+      
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+      		             GTK_WIDGET (menu_item));
+      
+      /* Add the submenu icon */
+      if (item_icon && item_thumb_icon)
+      {
+        hildon_thumb_menu_item_set_images (
+            		  HILDON_THUMB_MENU_ITEM (menu_item),
+            		  gtk_image_new_from_pixbuf (item_icon),
+            		  gtk_image_new_from_pixbuf (item_thumb_icon));
+      }
+      	    
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item),
+      			         GTK_WIDGET (submenu));
+      
+      /* Recurse! */
+      hn_others_menu_get_items(submenu, button, model, &child_iter);
+    }
+    else if ( !item_desktop_id || strlen( item_desktop_id ) == 0 )
+    {
+      /* Empty submenu. Skip "Extras" */
+      if (strcmp (item_name, "tana_fi_extras") != 0)
+      {
+        gchar *child_string;
+        submenu = GTK_MENU(gtk_menu_new());
+    
+        gtk_widget_set_name (GTK_WIDGET(submenu),
+      		             NAVIGATOR_MENU_NAME);
+
+        /* Create a menu item and add it to the menu.
+         */
+        child_string = g_strdup_printf(MENU_ITEM_N_ITEMS(children), children);
+        menu_item = hildon_thumb_menu_item_new_with_labels (
+      	        (item_text_domain && *item_text_domain)?
+      	        dgettext(item_text_domain, item_name):
+      	        _(item_name),
+      	        NULL,
+      	        child_string);
+
+	g_free(child_string);
+
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+      		               GTK_WIDGET (menu_item));
+
+        /* Add the submenu icon */
+        if (item_icon)
+        {
+          hildon_thumb_menu_item_set_images(
+      	          HILDON_THUMB_MENU_ITEM(menu_item),
+      	          gtk_image_new_from_pixbuf(item_icon),
+      	          gtk_image_new_from_pixbuf(item_thumb_icon));
+        }
+
+        gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item),
+      		                   GTK_WIDGET (submenu));
+
+        /* Create a menu item and add it to the menu. */
+        GtkWidget *submenu_item =
+                gtk_image_menu_item_new_with_label (MENU_ITEM_EMPTY_SUBMENU_STRING);
+
+        gtk_widget_set_sensitive (submenu_item, FALSE);
+
+        gtk_menu_shell_append (GTK_MENU_SHELL (submenu),
+                               GTK_WIDGET (submenu_item));
+
+      }
+    }
+    else if (strcmp(item_desktop_id, SEPARATOR_STRING) == 0)
+    {
+      /* Separator */
+      menu_item = gtk_separator_menu_item_new();
+
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+      		             GTK_WIDGET (menu_item));
+      	    
+    }
+    else
+    {
+      /* Application */
+      menu_item = hildon_thumb_menu_item_new_with_labels (
+      		(item_text_domain && *item_text_domain)?
+      		dgettext(item_text_domain, item_name):
+      		_(item_name),
+      		NULL,
+      		/* work around strange behaviour of gettext for
+      		 * empty  strings
+      		 */
+      		(item_comment && *item_comment)?_(item_comment):"");
+
+      if (!item_icon)
+      {
+        item_icon = get_icon (MENU_ITEM_DEFAULT_APP_ICON,
+      		              MENU_ITEM_ICON_SIZE);
+      }
+
+      if (!item_thumb_icon)
+      {
+        item_thumb_icon = get_icon (MENU_ITEM_DEFAULT_APP_ICON,
+      		                    MENU_ITEM_THUMB_ICON_SIZE);
+      }
+
+      if (item_icon && item_thumb_icon)
+      {
+        hildon_thumb_menu_item_set_images (
+      		   HILDON_THUMB_MENU_ITEM (menu_item),
+      		   gtk_image_new_from_pixbuf (item_icon),
+      		   gtk_image_new_from_pixbuf (item_thumb_icon));
+      }
+
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+      		             GTK_WIDGET (menu_item));
+
+      g_object_set_data_full (G_OBJECT (menu_item),
+      		              DESKTOP_ENTRY_EXEC_FIELD,
+      		              g_strdup (item_exec), 
+			      g_free);
+
+      g_object_set_data_full (G_OBJECT(menu_item),
+      		              DESKTOP_ENTRY_SERVICE_FIELD,
+      		              g_strdup (item_service), 
+			      g_free);
+
+      /* Connect the signal and callback */
+      g_signal_connect (G_OBJECT (menu_item), 
+		        "activate",
+      		        G_CALLBACK (hn_others_menu_activate_item),
+      		        button);
+    }
+ 
+    g_free (item_name);
+    
+    if (item_icon)
+      g_object_unref (G_OBJECT (item_icon));
+    
+    if (item_thumb_icon)
+      g_object_unref (G_OBJECT (item_thumb_icon));
+    
+    g_free (item_exec);
+    g_free (item_service);
+    g_free (item_desktop_id);
+    g_free (item_comment);
+    g_free (item_text_domain);
+	    
+  } while (gtk_tree_model_iter_next(model, iter));
+
+
+  if (my_iterator)
+  {
+    gtk_tree_iter_free (iter);
+    
+    g_debug ("ref count remaining on model %d (should be 1)",
+             G_OBJECT (model)->ref_count);
+  }
+  
+  g_object_unref (G_OBJECT (model));
+}
+
+static void
+hn_others_menu_populate (HNOthersButton *button)
+{
+  hn_others_menu_get_items (TASKNAVIGATOR_ITEM (button)->menu, 
+		            button, 
+			    NULL, 
+			    NULL);
+  
+  gtk_widget_show_all (GTK_WIDGET (TASKNAVIGATOR_ITEM (button)->menu));
+}
+
+static void
+hn_others_button_create_menu (HNOthersButton *button)
+{
+  GtkWidget * menu;
+    
+  g_return_if_fail (button);
+
+  /* Create the menu shell, and connect callbacks */
+  menu = gtk_menu_new ();
+    
+  gtk_widget_set_name (menu, NAVIGATOR_MENU_NAME);
+
+  g_signal_connect (G_OBJECT (menu), 
+		    "size-request",
+		    G_CALLBACK (hn_others_menu_size_request),
+		    button);
+    
+  g_signal_connect (G_OBJECT (menu), 
+		    "deactivate",
+		    G_CALLBACK (hn_others_menu_deactivate),
+		    button);
+    
+  g_signal_connect (G_OBJECT (menu), 
+		    "key-press-event",
+		    G_CALLBACK (hn_others_menu_key_press),
+		    button);
+    
+  g_signal_connect (G_OBJECT (menu), 
+		    "button-release-event",
+		    G_CALLBACK (hn_app_switcher_menu_button_release_cb),
+		    NULL);
+
+  if (TASKNAVIGATOR_ITEM (button)->menu)
+  {
+    /* Destroy the previous version of the menu */
+    g_debug ("Destroying previous menu... ");
+    gtk_widget_destroy (GTK_WIDGET (TASKNAVIGATOR_ITEM (button)->menu));
+    g_debug ("done.");
+  }
+    
+  TASKNAVIGATOR_ITEM (button)->menu = GTK_MENU (menu);
+
+  /* Now populate the menu */
+  hn_others_menu_populate (button);
+}
+
+static void
+hn_others_button_get_workarea (GtkAllocation *allocation)
+{
+  unsigned long n;
+  unsigned long extra;
+  int format;
+  int status;
+  Atom property = XInternAtom (GDK_DISPLAY (), WORKAREA_ATOM, FALSE);
+  Atom realType;
+  
+  /* This is needed to get rid of the punned type-pointer 
+     breaks strict aliasing warning*/
+  union
+  {
+    unsigned char *char_value;
+    int *int_value;
+  } value;
+    
+  status = XGetWindowProperty (GDK_DISPLAY (), 
+			       GDK_ROOT_WINDOW (), 
+			       property, 
+			       0L, 
+			       4L,
+			       0, 
+			       XA_CARDINAL, 
+			       &realType, 
+			       &format,
+			       &n, 
+			       &extra, 
+			       (unsigned char **) &value.char_value);
+    
+  if (status == Success &&
+      realType == XA_CARDINAL &&
+      format == 32 && 
+      n == 4  &&
+      value.char_value != NULL)
+  {
+    allocation->x = value.int_value[0];
+    allocation->y = value.int_value[1];
+    allocation->width = value.int_value[2];
+    allocation->height = value.int_value[3];
+  }
+  else
+  {
+    allocation->x = 0;
+    allocation->y = 0;
+    allocation->width = 0;
+    allocation->height = 0;
+  }
+    
+  if (value.char_value) 
+  {
+    XFree(value.char_value);  
+  }
+}
+
+static void
+hn_others_button_get_menu_position (GtkMenu *menu,
+                                    gint *x,
+                                    gint *y,
+                                    gboolean *push_in,
+                                    GtkWidget *button)
+{
+  GtkRequisition  req;
+  GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(menu));
+  int menu_height = 0;
+  int main_height = 0;
+  GtkAllocation workarea = { 0, 0, 0, 0 };
+    
+  hn_others_button_get_workarea (&workarea);
+    
+  gtk_widget_size_request (GTK_WIDGET (menu), &req);
+
+  menu_height = req.height;
+  main_height = gdk_screen_get_height (screen);
+
+  *push_in = FALSE;
+  *x =  workarea.x;
+
+  if (main_height - button->allocation.y < menu_height)
+  {
+    *y = MAX(0, ((main_height - menu_height) / 2));
+  }
+  else
+  {
+    *y = button->allocation.y;
+  }
+}
+
+static void
+hn_others_button_menu_show (HNOthersButton * button)
+{
+  g_return_if_fail (button);
+  
+  if (!TASKNAVIGATOR_ITEM (button)->menu)
+    hn_others_button_create_menu (button);
+
+  gtk_menu_popup (TASKNAVIGATOR_ITEM (button)->menu,
+		  NULL,
+		  NULL,
+		  (GtkMenuPositionFunc) hn_others_button_get_menu_position,
+		  button,
+		  1,
+		  gtk_get_current_event_time ());
+  
+  gtk_menu_shell_select_first (GTK_MENU_SHELL (TASKNAVIGATOR_ITEM (button)->menu), 
+		               TRUE);
+}
+
+static void
+hn_others_button_button_toggled (GtkWidget *widget, HNOthersButton *button)
+{
+  if (button->priv->collapse_id)
+    return;
+
+  if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+  {
+    g_debug ("button not active -- not showing menu");
+    return;
+  } 
+	
+  g_debug ("showing menu");
+
+  hn_others_button_menu_show (button);
+}
+
+static gboolean
+hn_others_button_key_press (GtkWidget *widget, 
+		            GdkEventKey *event,
+			    HNOthersButton *button)
+{
+  g_return_val_if_fail (button, FALSE);
+
+  if (event->keyval == GDK_Right ||
+      event->keyval == GDK_KP_Enter)
+  {
+    hn_others_button_menu_show (button);
+    return TRUE;
+  }
+  else if (event->keyval == GDK_Left || 
+           event->keyval == GDK_KP_Left)
+  {
+    hd_wm_activate (HD_TN_ACTIVATE_LAST_APP_WINDOW);
+  }
+	
+  return FALSE;
+}
+
+static gboolean
+hn_others_button_button_press (GtkWidget *widget, 
+		               GdkEventButton *event,
+			       HNOthersButton *button)
+{
+  /* Eat all press events to stabilize the behaviour */
+  g_debug ("swallowing button-press-event");
+
+  return TRUE;
+}
+
+/* Since we'll get hundred press events when thumbing the button, we'll need to
+ * wait for a moment until reacting
+ */
+static gboolean
+hn_others_button_press_collapser (HNOthersButton *button)
+{
+  g_return_val_if_fail (button, FALSE);
+  
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button->priv->button)))
+  {
+    g_debug ("deactivating menu");
+    gtk_menu_shell_deactivate (GTK_MENU_SHELL (TASKNAVIGATOR_ITEM (button)->menu));
+    button->priv->collapse_id = 0;
+    button->priv->thumb_pressed = FALSE;
+    
+    return FALSE;
+  }
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+  
+  if (!TASKNAVIGATOR_ITEM (button)->menu)
+    hn_others_button_create_menu (button);
+
+  if (button->priv->thumb_pressed)
+  {
+    hildon_menu_set_thumb_mode (TASKNAVIGATOR_ITEM (button)->menu, TRUE);
+  }
+  else
+  {
+    hildon_menu_set_thumb_mode(TASKNAVIGATOR_ITEM (button)->menu, FALSE);
+  }
+
+  hn_others_button_menu_show (button);
+  
+  button->priv->collapse_id = 0;
+  button->priv->thumb_pressed = FALSE;
+  
+  return FALSE;
+}
+
+static gboolean
+hn_others_button_button_release (GtkWidget *widget,
+			         GdkEventButton *event,
+			         HNOthersButton *button)
+{
+  /* This has to be done in the release handler, not the press handler,
+   * otherwise the button gets toggled when there is no corresponding release
+   * event, or worse, before the release event arrives (in which case, the
+   * release event is passed to the open menu).
+   */
+  if (button->priv->collapse_id <= 0)
+  {
+    hd_wm_activate (HD_TN_DEACTIVATE_KEY_FOCUS);
+
+    button->priv->collapse_id =
+            g_timeout_add (100,
+            	           (GSourceFunc) hn_others_button_press_collapser,
+            	           button);
+  }
+
+  if (hildon_button_event_is_finger (event))
+    button->priv->thumb_pressed = TRUE;
+
+  return TRUE;
+}
+
+/* Dnotify dispatches large number of events when a file is modified --
+   we use a 1s timeout to wait for the flurry to pass, and then do our stuff */
+static gboolean
+hn_others_button_menu_changed (HNOthersButton *button)
+{
+  g_return_val_if_fail (button, FALSE);
+
+  g_debug ("Creating menu");
+  
+  button->priv->dnotify_update_timeout = 0;
+
+  hn_others_button_create_menu (button);
+  
+  return FALSE;
+}
+
+static void
+hn_others_button_dnotify_handler (char *path, gpointer data)
+{
+  HNOthersButton * button = HN_OTHERS_BUTTON (data);
+  
+  if (!button->priv->dnotify_update_timeout)
+  {
+    button->priv->dnotify_update_timeout =
+            g_timeout_add (1000,
+            	           (GSourceFunc)hn_others_button_menu_changed,
+            	           button);
+  }
+}
+
+void
+hn_others_button_dnotify_register (HNOthersButton * button)
+{
+  const gchar *home_dir;
+  gchar *dir;
+  gchar	*file = NULL;
+  gchar *conf_file;
+
+  home_dir = getenv ("HOME");
+  
+  conf_file = g_build_filename (home_dir, USER_MENU_FILE, NULL);
+ 
+  /* We copy SYSTEMWIDE_MENU_FILE to always track the changes */
+  if (!g_file_test (conf_file, G_FILE_TEST_EXISTS))
+    if (g_file_get_contents (SYSTEMWIDE_MENU_FILE, &file, NULL, NULL))
+    {
+      g_debug ("I couldn't get contents");
+      g_file_set_contents (USER_MENU_FILE, file, -1, NULL);
+    }
+  
+  g_free (file);
+  
+  /* Watch systemwide menu conf */
+  dir = g_path_get_dirname (SYSTEMWIDE_MENU_FILE);
+  
+  if (hildon_dnotify_set_cb (
+		(hildon_dnotify_cb_f *) hn_others_button_dnotify_handler,
+		dir, button) != HILDON_OK)
+  {
+    g_error ("Others_menu_initialize_menu: "
+      	     "failed setting dnotify callback "
+      	     "for systemwide menu conf." );
+  }
+
+  g_free (dir);
+  
+  /* Watch user specific menu conf */
+  if (home_dir && *home_dir)
+  {    
+    /* Have to get the directory from the path because the USER_MENU_FILE
+       define might contain directory (it does, in fact). */
+    dir = g_path_get_dirname (conf_file);
+    
+    g_mkdir (dir, 0755);
+
+    if (dir && *dir)
+    {
+      if (hildon_dnotify_set_cb (
+    	  (hildon_dnotify_cb_f *) hn_others_button_dnotify_handler,
+    	  dir, button) != HILDON_OK)
+      {
+        g_error ("Others_menu_initialize_menu: "
+      	         "failed setting dnotify callback "
+      	         "for user spesific menu conf." );
+      }
+    }
+    else
+    {
+      g_error ("Others_menu_initialize_menu: "
+    	       "failed to create directory '%s'", dir);
+    }
+
+    g_free (dir);
+  }
+      
+  g_free (conf_file);
+  
+  /* Monitor the .desktop directories, so we can regenerate the menu
+   * when a new application is installed */
+  if (hildon_dnotify_set_cb (
+      (hildon_dnotify_cb_f *) hn_others_button_dnotify_handler,
+      HD_DESKTOP_ENTRY_PATH, button) != HILDON_OK)
+  {
+    g_error ("Others_menu_initialize_menu: "
+      	     "failed setting dnotify callback "
+      	     "for .desktop directory." );
+  }
+}
+
+void
+hn_others_button_close_menu (HNOthersButton *button)
+{
+  g_return_if_fail (button && HN_IS_OTHERS_BUTTON (button));
+  g_return_if_fail (TASKNAVIGATOR_ITEM (button)->menu != NULL);
+
+  gtk_menu_popdown (TASKNAVIGATOR_ITEM (button)->menu);
+}

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hn-others-button.h
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hn-others-button.h	2007-01-16 14:39:18 UTC (rev 9126)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/src/hn-others-button.h	2007-01-16 14:47:55 UTC (rev 9127)
@@ -0,0 +1,69 @@
+/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * This file is part of maemo-af-desktop
+ *
+ * Copyright (C) 2006 Nokia Corporation.
+ *
+ * Contact: Karoliina Salminen <karoliina.t.salminen at 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.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __HN_OTHERS_BUTTON_H__
+#define __HN_OTHERS_BUTTON_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libhildonwm/hd-wm.h>
+#include <libhildondesktop/libhildondesktop.h>
+
+G_BEGIN_DECLS
+
+typedef struct _HNOthersButton HNOthersButton;
+typedef struct _HNOthersButtonClass HNOthersButtonClass;
+typedef struct _HNOthersButtonPrivate HNOthersButtonPrivate;
+
+#define HN_TYPE_OTHERS_BUTTON            (hn_others_button_get_type ())
+#define HN_OTHERS_BUTTON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), HN_TYPE_OTHERS_BUTTON, HNOthersButton))
+#define HN_IS_OTHERS_BUTTON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HN_TYPE_OTHERS_BUTTON))
+#define HN_OTHERS_BUTTON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), HN_TYPE_OTHERS_BUTTON, HNOthersButtonClass))
+#define HN_IS_OTHERS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), HN_TYPE_OTHERS_BUTTON))
+#define HN_OTHERS_BUTTON_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), HN_TYPE_OTHERS_BUTTON, HNOthersButtonClass))
+
+struct _HNOthersButton
+{
+  TaskNavigatorItem parent_instance;
+  
+  HNOthersButtonPrivate *priv;
+};
+
+struct _HNOthersButtonClass
+{
+  TaskNavigatorItemClass parent_class;
+};
+
+GType        hn_others_button_get_type             (void);
+
+GtkWidget   *hn_others_button_new                  (void);
+
+void         hn_others_button_dnotify_register     (HNOthersButton *button);
+
+void         hn_others_button_close_menu	   (HNOthersButton *button);
+
+G_END_DECLS
+
+#endif /* __HN_OTHERS_BUTTON_H__ */


More information about the maemo-commits mailing list