[maemo-commits] [maemo-commits] r8340 - in projects/haf/branches/maemo-af-desktop/hildon-desktop: . libhildondesktop libhildonwm test

From: www-data at stage.maemo.org www-data at stage.maemo.org
Date: Mon Nov 27 14:31:17 EET 2006
Author: moimart
Date: 2006-11-27 14:31:16 +0200 (Mon, 27 Nov 2006)
New Revision: 8340

Added:
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hildon-pixbuf-anim-blinker.c
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hildon-pixbuf-anim-blinker.h
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-button.c
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-button.h
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-menu-item.c
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-menu-item.h
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-sound.c
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-sound.h
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-switcher.c
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-switcher.h
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-tooltip.c
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-tooltip.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/libhildondesktop/Makefile.am
   projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildonwm/hd-wm.c
   projects/haf/branches/maemo-af-desktop/hildon-desktop/test/test1.c
Log:

	* libhildondesktop: Added old app switcher with new interface.
	* ChangeLog updated.



Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/ChangeLog
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/ChangeLog	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/ChangeLog	2006-11-27 12:31:16 UTC (rev 8340)
@@ -1,5 +1,9 @@
 2006-11-27  Moises Martinez  <moises.martinzes at nokia.com>
 
+	* libhildondesktop: Added old app switcher with new interface.
+
+2006-11-27  Moises Martinez  <moises.martinzes at nokia.com>
+
 	* libhildonwm/*: Updated libhildonwm.
 
 2006-11-27  Johan Bilien  <johan.bilien at nokia.com>

Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/configure.ac
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/configure.ac	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/configure.ac	2006-11-27 12:31:16 UTC (rev 8340)
@@ -149,6 +149,4 @@
 	libhildondesktop/Makefile \
 	libhildondesktop/libhildondesktop.pc \
 	data/Makefile \
-	src/Makefile
-	plugins/Makefile \
-	plugins/appswitcher/Makefile)
+	src/Makefile)

Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/Makefile.am
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/Makefile.am	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/Makefile.am	2006-11-27 12:31:16 UTC (rev 8340)
@@ -26,7 +26,8 @@
 		     hildon-home-select-applets-dialog.h \
 		     hildon-home-l10n.h \
 		     hildon-plugin-list.h \
-		     libhildondesktop.h
+		     libhildondesktop.h \
+		     hn-app-switcher.h
 
 EXTRA_DIST = .empty
 
@@ -37,7 +38,9 @@
 	$(OSSO_CFLAGS) \
 	$(LIBXML_CFLAGS) \
 	-DLOCALEDIR=\"$(localedir)\" \
-	-DHILDON_HOME_DESKTOP_ENTRY_DIR=\"$(hildonhomedesktopentrydir)\"
+	-DDATADIR=\"$(datadir)\" \
+	-DHILDON_HOME_DESKTOP_ENTRY_DIR=\"$(hildonhomedesktopentrydir)\" \
+	-I../libhildonwm
 
 hildon-desktop-marshalers.c:
 	echo "#include \"hildon-desktop-marshalers.h\"" >|hildon-desktop-marshalers.c
@@ -49,26 +52,33 @@
 lib_LTLIBRARIES = libhildondesktop.la
 
 
+APP_SWITCHER_SOURCES = \
+  hildon-pixbuf-anim-blinker.c \
+  hn-app-menu-item.c \
+  hn-app-switcher.c \
+  hn-app-button.c \
+  hn-app-sound.c \
+  hn-app-tooltip.c
 
 HILDON_HOME_SOURCES = \
-					  hildon-home-area.c \
-					  hildon-home-area.h \
-					  hildon-home-applet.c \
-					  hildon-home-applet.h \
-					  hildon-plugin-list.c \
-					  hildon-plugin-list.h \
-					  hildon-home-window.c \
-					  hildon-home-window.h \
-					  home-applet-handler.c \
-					  home-applet-handler.h \
-					  hildon-home-titlebar.c \
-					  hildon-home-titlebar.h \
-					  hildon-home-select-applets-dialog.h \
-					  hildon-home-select-applets-dialog.c \
-					  hildon-home-l10n.h
+  hildon-home-area.c \
+  hildon-home-area.h \
+  hildon-home-applet.c \
+  hildon-home-applet.h \
+  hildon-plugin-list.c \
+  hildon-plugin-list.h \
+  hildon-home-window.c \
+  hildon-home-window.h \
+  home-applet-handler.c \
+  home-applet-handler.h \
+  hildon-home-titlebar.c \
+  hildon-home-titlebar.h \
+  hildon-home-select-applets-dialog.h \
+  hildon-home-select-applets-dialog.c \
+  hildon-home-l10n.h
 
 BUILT_SOURCES = hildon-desktop-marshalers.c \
-	  		    hildon-desktop-marshalers.h
+		    hildon-desktop-marshalers.h
 
 
 libhildondesktop_la_SOURCES = \
@@ -111,13 +121,15 @@
 	hildon-thumb-menu-item.h \
 	hildon-thumb-menu-item.c \
 	desktop-multiscreen.h \
-	desktop-multiscreen.c	
+	desktop-multiscreen.c \
+	$(APP_SWITCHER_SOURCES)
 
 
 libhildondesktop_la_LIBADD = \
 	$(HILDON_LIBS) \
 	$(HILDONBASELIB_LIBS) \
 	$(GCONF_LIBS) \
-	$(GNOME_VFS_LIBS)
+	$(GNOME_VFS_LIBS) \
+	../libhildonwm/libhildonwm.la
 
 CLEANFILES = *~

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hildon-pixbuf-anim-blinker.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hildon-pixbuf-anim-blinker.c	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hildon-pixbuf-anim-blinker.c	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,366 @@
+/*
+ * This file is part of maemo-af-desktop
+ *
+ * Copyright (C) 2005 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#define GDK_PIXBUF_ENABLE_BACKEND
+
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk-pixbuf/gdk-pixbuf-animation.h>
+#include "hildon-pixbuf-anim-blinker.h"
+
+struct _HildonPixbufAnimBlinker
+{
+        GdkPixbufAnimation parent_instance;
+
+	gboolean stopped;
+	gint period;
+	gint length;
+	gint frequency;
+	
+	GdkPixbuf *pixbuf;
+	GdkPixbuf *blended;
+};
+
+struct _HildonPixbufAnimBlinkerClass
+{
+        GdkPixbufAnimationClass parent_class;
+};
+
+
+typedef struct _HildonPixbufAnimBlinkerIter HildonPixbufAnimBlinkerIter;
+typedef struct _HildonPixbufAnimBlinkerIterClass HildonPixbufAnimBlinkerIterClass;
+
+#define TYPE_HILDON_PIXBUF_ANIM_BLINKER_ITER              (hildon_pixbuf_anim_blinker_iter_get_type ())
+#define HILDON_PIXBUF_ANIM_BLINKER_ITER(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_HILDON_PIXBUF_ANIM_BLINKER_ITER, HildonPixbufAnimBlinkerIter))
+#define IS_HILDON_PIXBUF_ANIM_BLINKER_ITER(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_HILDON_PIXBUF_ANIM_BLINKER_ITER))
+
+#define HILDON_PIXBUF_ANIM_BLINKER_ITER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_HILDON_PIXBUF_ANIM_BLINKER_ITER, HildonPixbufAnimBlinkerIterClass))
+#define IS_HILDON_PIXBUF_ANIM_BLINKER_ITER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_HILDON_PIXBUF_ANIM_BLINKER_ITER))
+#define HILDON_PIXBUF_ANIM_BLINKER_ITER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_HILDON_PIXBUF_ANIM_BLINKER_ITER, HildonPixbufAnimBlinkerIterClass))
+
+GType hildon_pixbuf_anim_blinker_iter_get_type (void) G_GNUC_CONST;
+
+
+struct _HildonPixbufAnimBlinkerIterClass
+{
+        GdkPixbufAnimationIterClass parent_class;
+};
+
+struct _HildonPixbufAnimBlinkerIter
+{
+        GdkPixbufAnimationIter parent_instance;
+        
+        HildonPixbufAnimBlinker *hildon_pixbuf_anim_blinker;
+        
+        GTimeVal start_time;
+        GTimeVal current_time;
+        gint length;
+};
+
+static void hildon_pixbuf_anim_blinker_finalize (GObject *object);
+
+static gboolean   is_static_image  (GdkPixbufAnimation *animation);
+static GdkPixbuf *get_static_image (GdkPixbufAnimation *animation);
+
+static void       get_size         (GdkPixbufAnimation *anim,
+                                    gint               *width, 
+                                    gint               *height);
+static GdkPixbufAnimationIter *get_iter (GdkPixbufAnimation *anim,
+                                         const GTimeVal     *start_time);
+
+
+G_DEFINE_TYPE(HildonPixbufAnimBlinker, hildon_pixbuf_anim_blinker,
+	      GDK_TYPE_PIXBUF_ANIMATION);
+
+static void
+hildon_pixbuf_anim_blinker_init (HildonPixbufAnimBlinker *anim)
+{
+}
+
+static void
+hildon_pixbuf_anim_blinker_class_init (HildonPixbufAnimBlinkerClass *klass)
+{
+        GObjectClass *object_class;
+        GdkPixbufAnimationClass *anim_class;
+
+        object_class = G_OBJECT_CLASS (klass);
+        anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass);
+        
+        object_class->finalize = hildon_pixbuf_anim_blinker_finalize;
+        
+        anim_class->is_static_image = is_static_image;
+        anim_class->get_static_image = get_static_image;
+        anim_class->get_size = get_size;
+        anim_class->get_iter = get_iter;
+}
+
+static void
+hildon_pixbuf_anim_blinker_finalize (GObject *object)
+{
+        HildonPixbufAnimBlinker *anim;
+        
+        anim = HILDON_PIXBUF_ANIM_BLINKER (object);        
+        
+        g_object_unref (anim->pixbuf);
+        
+        G_OBJECT_CLASS (hildon_pixbuf_anim_blinker_parent_class)->
+        	finalize (object);
+}
+
+static gboolean
+is_static_image (GdkPixbufAnimation *animation)
+{
+        return FALSE;
+}
+
+static GdkPixbuf *
+get_static_image (GdkPixbufAnimation *animation)
+{
+        HildonPixbufAnimBlinker *anim;
+        
+        anim = HILDON_PIXBUF_ANIM_BLINKER (animation);
+ 
+ 	return anim->pixbuf;       
+}
+
+static void
+get_size (GdkPixbufAnimation *animation,
+          gint               *width, 
+          gint               *height)
+{
+        HildonPixbufAnimBlinker *anim;
+
+        anim = HILDON_PIXBUF_ANIM_BLINKER (animation);
+        
+        if (width)
+        	*width = gdk_pixbuf_get_width (anim->pixbuf);
+        if (height)
+        	*height = gdk_pixbuf_get_height (anim->pixbuf);
+}
+
+static GdkPixbufAnimationIter *
+get_iter (GdkPixbufAnimation *anim,
+          const GTimeVal    *start_time)
+{
+	HildonPixbufAnimBlinkerIter *iter;
+	HildonPixbufAnimBlinker *blinker = HILDON_PIXBUF_ANIM_BLINKER (anim);
+	gint i;
+
+	iter = g_object_new (TYPE_HILDON_PIXBUF_ANIM_BLINKER_ITER, NULL);
+
+	iter->hildon_pixbuf_anim_blinker = blinker;
+
+	g_object_ref (iter->hildon_pixbuf_anim_blinker);
+
+	iter->start_time = *start_time;
+	iter->current_time = *start_time;
+
+	/* Find out how many seconds it is before a period repeats */
+	/* (e.g. for 500ms, 1 second, for 333ms, 2 seconds, etc.) */
+	for (i = 1; ((blinker->period * i) % 2000) != 0; i++);
+	i = iter->start_time.tv_sec % ((blinker->period*i)/1000);
+
+	/* Make sure to offset length as well as start time */
+	iter->length = blinker->length + (i * 1000);
+	iter->start_time.tv_sec -= i;
+	iter->start_time.tv_usec = 0;
+
+	return GDK_PIXBUF_ANIMATION_ITER (iter);
+}
+
+static void hildon_pixbuf_anim_blinker_iter_finalize (GObject *object);
+
+static gint       get_delay_time             (GdkPixbufAnimationIter *iter);
+static GdkPixbuf *get_pixbuf                 (GdkPixbufAnimationIter *iter);
+static gboolean   on_currently_loading_frame (GdkPixbufAnimationIter *iter);
+static gboolean   advance                    (GdkPixbufAnimationIter *iter,
+					      const GTimeVal *current_time);
+
+G_DEFINE_TYPE (HildonPixbufAnimBlinkerIter, hildon_pixbuf_anim_blinker_iter,
+	       GDK_TYPE_PIXBUF_ANIMATION_ITER);
+
+static void
+hildon_pixbuf_anim_blinker_iter_init (HildonPixbufAnimBlinkerIter *iter)
+{
+}
+
+static void
+hildon_pixbuf_anim_blinker_iter_class_init (
+	HildonPixbufAnimBlinkerIterClass *klass)
+{
+        GObjectClass *object_class;
+        GdkPixbufAnimationIterClass *anim_iter_class;
+
+        object_class = G_OBJECT_CLASS (klass);
+        anim_iter_class = GDK_PIXBUF_ANIMATION_ITER_CLASS (klass);
+        
+        object_class->finalize = hildon_pixbuf_anim_blinker_iter_finalize;
+        
+        anim_iter_class->get_delay_time = get_delay_time;
+        anim_iter_class->get_pixbuf = get_pixbuf;
+        anim_iter_class->on_currently_loading_frame =
+        	on_currently_loading_frame;
+        anim_iter_class->advance = advance;
+}
+
+static void
+hildon_pixbuf_anim_blinker_iter_finalize (GObject *object)
+{
+        HildonPixbufAnimBlinkerIter *iter;
+        
+        iter = HILDON_PIXBUF_ANIM_BLINKER_ITER (object);
+        
+        g_object_unref (iter->hildon_pixbuf_anim_blinker);
+        
+        G_OBJECT_CLASS (hildon_pixbuf_anim_blinker_iter_parent_class)->
+        	finalize (object);
+}
+
+static gboolean
+advance (GdkPixbufAnimationIter *anim_iter,
+         const GTimeVal         *current_time)
+{
+        HildonPixbufAnimBlinkerIter *iter;
+        gint elapsed;
+        
+        iter = HILDON_PIXBUF_ANIM_BLINKER_ITER (anim_iter);
+        
+        iter->current_time = *current_time;
+        
+        if (iter->hildon_pixbuf_anim_blinker->stopped)
+        	return FALSE;
+        	
+        if (iter->hildon_pixbuf_anim_blinker->length == -1)
+        	return TRUE;
+        
+        /* We use milliseconds for all times */
+        elapsed = (((iter->current_time.tv_sec - iter->start_time.tv_sec) *
+        	G_USEC_PER_SEC + iter->current_time.tv_usec -
+        	iter->start_time.tv_usec)) / 1000;
+        
+        if (elapsed < 0) {
+                /* Try to compensate; probably the system clock
+                 * was set backwards
+                 */
+                iter->start_time = iter->current_time;
+                elapsed = 0;
+        }
+
+	if (elapsed < iter->length)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+static gint
+get_delay_time (GdkPixbufAnimationIter *anim_iter)
+{
+        HildonPixbufAnimBlinkerIter *iter =
+        	HILDON_PIXBUF_ANIM_BLINKER_ITER (anim_iter);
+        gint elapsed;
+        gint period = iter->hildon_pixbuf_anim_blinker->period /
+        	iter->hildon_pixbuf_anim_blinker->frequency;
+        
+        elapsed = (((iter->current_time.tv_sec - iter->start_time.tv_sec) *
+        	G_USEC_PER_SEC + iter->current_time.tv_usec -
+        	iter->start_time.tv_usec)) / 1000;
+
+	if (((elapsed < iter->length) ||
+	    (iter->hildon_pixbuf_anim_blinker->length == -1)) &&
+	    (!iter->hildon_pixbuf_anim_blinker->stopped))
+		return period - (elapsed % period);
+	else
+		return -1;
+}
+
+static GdkPixbuf *
+get_pixbuf (GdkPixbufAnimationIter *anim_iter)
+{
+        HildonPixbufAnimBlinkerIter *iter =
+        	HILDON_PIXBUF_ANIM_BLINKER_ITER (anim_iter);
+        gint elapsed, alpha;
+        gint period = iter->hildon_pixbuf_anim_blinker->period;
+        
+        elapsed = (((iter->current_time.tv_sec - iter->start_time.tv_sec) *
+        	G_USEC_PER_SEC + iter->current_time.tv_usec -
+        	iter->start_time.tv_usec)) / 1000;
+	
+	if ((iter->hildon_pixbuf_anim_blinker->stopped) ||
+	    (elapsed > iter->length &&
+	     iter->hildon_pixbuf_anim_blinker->length != -1))
+	  return iter->hildon_pixbuf_anim_blinker->pixbuf;
+
+	gdk_pixbuf_fill (iter->hildon_pixbuf_anim_blinker->blended,
+		0x00000000);
+	/* Use period * 2 and 512 so that alpha pulses down as well as up */
+	alpha = MIN (((elapsed % (period*2)) * 511) / (period*2), 511);
+	if (alpha > 255) alpha = 511-alpha;
+	gdk_pixbuf_composite (iter->hildon_pixbuf_anim_blinker->pixbuf,
+		iter->hildon_pixbuf_anim_blinker->blended,
+		0, 0,
+		gdk_pixbuf_get_width (iter->hildon_pixbuf_anim_blinker->pixbuf),
+		gdk_pixbuf_get_height (iter->hildon_pixbuf_anim_blinker->pixbuf),
+		0, 0,
+		1, 1,
+		GDK_INTERP_NEAREST,
+		alpha);
+	return iter->hildon_pixbuf_anim_blinker->blended;
+}
+
+static gboolean
+on_currently_loading_frame (GdkPixbufAnimationIter *anim_iter)
+{
+	return FALSE;
+}
+
+
+/* vals in millisecs, length = -1 for infinity */
+GdkPixbufAnimation *
+hildon_pixbuf_anim_blinker_new (GdkPixbuf *pixbuf, gint period, gint length,
+				gint frequency)
+{
+  HildonPixbufAnimBlinker *anim;
+
+  anim = g_object_new (TYPE_HILDON_PIXBUF_ANIM_BLINKER, NULL);
+  anim->pixbuf = g_object_ref (pixbuf);
+  anim->period = period;
+  anim->length = length;
+  anim->frequency = frequency;
+  anim->blended = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+  	gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf));
+
+  return GDK_PIXBUF_ANIMATION (anim);
+}
+
+void
+hildon_pixbuf_anim_blinker_stop (HildonPixbufAnimBlinker *anim)
+{
+	anim->stopped = TRUE;
+}
+
+void
+hildon_pixbuf_anim_blinker_restart (HildonPixbufAnimBlinker *anim)
+{
+	g_warning ("Restarting blinking unimplemented");
+}
+

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hildon-pixbuf-anim-blinker.h
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hildon-pixbuf-anim-blinker.h	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hildon-pixbuf-anim-blinker.h	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,65 @@
+/*
+ * This file is part of maemo-af-desktop
+ *
+ * Copyright (C) 2005 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/* 
+ * Simple blinking pixbuf animation class. 
+ *   Authored by Chris Lord <chris at o-hand.com> 
+ *               Matthew Allum <mallum at o-hand.com>
+ */
+
+#ifndef HILDON_PIXBUF_ANIM_BLINKER_H
+#define HILDON_PIXBUF_ANIM_BLINKER_H
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+typedef struct _HildonPixbufAnimBlinker HildonPixbufAnimBlinker;
+typedef struct _HildonPixbufAnimBlinkerClass HildonPixbufAnimBlinkerClass;
+
+#define TYPE_HILDON_PIXBUF_ANIM_BLINKER              (hildon_pixbuf_anim_blinker_get_type ())
+#define HILDON_PIXBUF_ANIM_BLINKER(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_HILDON_PIXBUF_ANIM_BLINKER, HildonPixbufAnimBlinker))
+#define IS_HILDON_PIXBUF_ANIM_BLINKER(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_HILDON_PIXBUF_ANIM_BLINKER))
+
+#define HILDON_PIXBUF_ANIM_BLINKER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_HILDON_PIXBUF_ANIM_BLINKER, HildonPixbufAnimBlinkerClass))
+#define IS_HILDON_PIXBUF_ANIM_BLINKER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_HILDON_PIXBUF_ANIM_BLINKER))
+#define HILDON_PIXBUF_ANIM_BLINKER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_HILDON_PIXBUF_ANIM_BLINKER, HildonPixbufAnimBlinkerClass))
+
+GType hildon_pixbuf_anim_blinker_get_type (void) G_GNUC_CONST;
+GType hildon_pixbuf_anim_blinker_iter_get_type (void) G_GNUC_CONST;
+
+GdkPixbufAnimation *hildon_pixbuf_anim_blinker_new (GdkPixbuf *pixbuf, 
+						    gint       period,
+						    gint       length,
+						    gint       frequency);
+
+void
+hildon_pixbuf_anim_blinker_stop (HildonPixbufAnimBlinker *anim);
+
+void
+hildon_pixbuf_anim_blinker_restart (HildonPixbufAnimBlinker *anim);
+
+G_END_DECLS
+
+#endif
+

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-button.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-button.c	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-button.c	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,1327 @@
+/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+
+/* hn-app-button.c
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/* Hildon includes */
+#include "hn-app-button.h"
+#include "hn-app-menu-item.h"
+#include "hn-app-tooltip.h"
+#include <libhildonwm/hd-wm.h>
+#include "hildon-pixbuf-anim-blinker.h"
+#include "hn-app-switcher.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* GLib include */
+#include <glib.h>
+#include <glib/gi18n.h>
+
+/* GTK includes */
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkcheckbutton.h>
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtkradiobutton.h>
+#include <gtk/gtkmenu.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkseparatormenuitem.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkmisc.h>
+
+#include <libosso.h>
+
+#include <hildon-widgets/gtk-infoprint.h>
+#include "hildon-pixbuf-anim-blinker.h"
+
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+
+/* GDK includes */
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+
+/* X includes */
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+/* log include */
+#include <log-functions.h>
+
+/* Menu item strings */
+#define AS_HOME_ITEM 		_("tana_fi_home")
+#define AS_HOME_ITEM_ICON 	"qgn_list_home"
+
+/* Defined workarea atom */
+#define WORKAREA_ATOM "_NET_WORKAREA"
+
+#define AS_MENU_BUTTON_ICON      "qgn_list_tasknavigator_appswitcher"
+#define AS_MENU_DEFAULT_APP_ICON "qgn_list_gene_default_app"
+
+#define ANIM_DURATION 5000 	/* 5 Secs for blinking icons */
+#define ANIM_FPS      2
+
+/* application button compose icon names */
+static const gchar *app_group_icons[] = {
+  NULL,                    /* single instance: no icon */
+  "qgn_indi_grouped2",
+  "qgn_indi_grouped3",
+  "qgn_indi_grouped4",
+  "qgn_indi_grouped5",
+  "qgn_indi_grouped6",
+  "qgn_indi_grouped7",
+  "qgn_indi_grouped8",
+  "qgn_indi_grouped9",
+  
+  "qgn_indi_grouped_more", /* 9+ instances: keep last! */
+};
+static guint app_group_n_icons = G_N_ELEMENTS (app_group_icons);
+
+/* fast mnemonic value for the "more" icon name */
+#define APP_GROUP_ICON_MORE 	(app_group_icons[app_group_n_icons - 1])
+#define APP_GROUP_ICON_SIZE     16
+
+/* Hardcoded pixel perfecting values */
+#define BUTTON_HEIGHT      38
+
+#define AS_BUTTON_BORDER_WIDTH  0
+#define AS_MENU_BORDER_WIDTH    20
+#define AS_TIP_BORDER_WIDTH 	20
+#define AS_ROW_HEIGHT 		30
+#define AS_ICON_SIZE            26
+#define AS_TOOLTIP_WIDTH        360
+#define AS_MENU_ITEM_WIDTH      360
+#define AS_INTERNAL_PADDING     10
+#define AS_SEPARATOR_HEIGHT     10
+#define AS_BUTTON_BOX_PADDING   10
+
+enum
+{
+  BUTTON_PROP_0,
+  BUTTON_PROP_ENTRY_INFO,
+  BUTTON_PROP_IS_BLINKING
+};
+
+#define HN_APP_BUTTON_GET_PRIVATE(obj) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((obj), HN_TYPE_APP_BUTTON, HNAppButtonPrivate))
+
+struct _HNAppButtonPrivate
+{
+  HDEntryInfo *info;
+  GtkWidget *icon;
+
+  GtkToggleButton *prev_button;
+  
+  GtkWidget *tooltip;
+  GtkWidget *menu;
+
+  guint is_blinking  : 1;
+  guint is_thumbable : 1;
+};
+
+
+G_DEFINE_TYPE (HNAppButton, hn_app_button, GTK_TYPE_TOGGLE_BUTTON);
+
+
+#if 0
+/* TODO - uncomment to handle keyboard focus */
+static gboolean
+hn_app_button_focus (GtkWidget        *widget,
+		     GtkDirectionType  direction)
+{
+  return TRUE;
+}
+#endif
+
+static void
+hn_app_button_icon_animation (GtkWidget *icon, gboolean   turn_on);
+
+static void
+hn_app_button_finalize (GObject *gobject)
+{
+  HNAppButton *app_button = HN_APP_BUTTON (gobject);
+
+  if (app_button->priv->tooltip)
+    gtk_widget_destroy (app_button->priv->tooltip);
+  
+  G_OBJECT_CLASS (hn_app_button_parent_class)->finalize (gobject);
+}
+
+static void
+hn_app_button_destroy (GtkObject *object)
+{
+  HNAppButton *app_button = HN_APP_BUTTON (object);
+  HNAppButton *tmp_button;
+  GSList *l;
+  
+  app_button->group = g_slist_remove (app_button->group, app_button);
+
+  for (l = app_button->group; l != NULL; l = l->next)
+    {
+      tmp_button = l->data;
+
+      tmp_button->group = app_button->group;
+    }
+  
+  app_button->group = NULL;
+  
+  if (GTK_OBJECT_CLASS (hn_app_button_parent_class)->destroy)
+    GTK_OBJECT_CLASS (hn_app_button_parent_class)->destroy (object);
+}
+
+/* static implementation of the _gtk_button_set_depressed semi-private
+ * function inside GTK+; keep in sync with that
+ */
+static void
+hn_app_button_set_depressed (HNAppButton *app_button,
+			     gboolean     depressed)
+{
+  GtkWidget *widget = GTK_WIDGET (app_button);
+
+  depressed = depressed != FALSE;
+
+  if (depressed != GTK_BUTTON (app_button)->depressed)
+    {
+      GTK_BUTTON (app_button)->depressed = depressed;
+      gtk_widget_queue_resize (widget);
+    }
+}
+
+/* taken from gtkradiobutton.c */
+static void
+hn_app_button_clicked (GtkButton *button)
+{
+  HNAppButton *app_button = HN_APP_BUTTON (button);
+  GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
+  GtkToggleButton *tmp_button;
+  GtkStateType new_state;
+  gboolean toggled = FALSE;
+  gboolean depressed;
+
+  g_object_ref (button);
+
+  if (toggle_button->active)
+    {
+      GSList *l;
+      
+      gboolean found = FALSE;
+      tmp_button = NULL;
+      
+      for (l = app_button->group; l != NULL; l = l->next)
+        {
+          tmp_button = l->data;
+
+	  if (tmp_button->active && tmp_button != toggle_button)
+	    {
+              found = TRUE;
+	      break;
+	    }
+        }
+
+      if (!found && app_button->priv->info != NULL)
+	new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
+      else
+        {
+          toggled = TRUE;
+	  toggle_button->active = !toggle_button->active;
+          new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
+	}
+    }
+  else
+    {
+      if (app_button->priv->info != NULL)
+        {
+          GSList *l;
+	  
+          toggled = TRUE;
+	  toggle_button->active = !toggle_button->active;
+
+	  for (l = app_button->group; l != NULL; l = l->next)
+            {
+              tmp_button = l->data;
+
+	      if (tmp_button->active && (tmp_button != toggle_button))
+	        {
+                  gtk_button_clicked (GTK_BUTTON (tmp_button));
+	          break;
+	        }
+            }
+
+	  new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
+        }
+      else
+        new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
+    }
+
+  if (toggle_button->inconsistent)
+    depressed = FALSE;
+  else if (button->in_button && button->button_down)
+    depressed = !toggle_button->active;
+  else
+    depressed = toggle_button->active;
+
+  if (GTK_WIDGET_STATE (button) != new_state)
+    gtk_widget_set_state (GTK_WIDGET (button), new_state);
+
+  if (toggled)
+    {
+      gtk_toggle_button_toggled (toggle_button);
+
+      g_object_notify (G_OBJECT (toggle_button), "active");
+    }
+
+  hn_app_button_set_depressed (app_button, depressed);
+
+  gtk_widget_queue_draw (GTK_WIDGET (button));
+
+  g_object_unref (button);
+}
+
+static void
+menu_position_func (GtkMenu  *menu,
+		    gint     *x,
+		    gint     *y,
+		    gboolean *push_in,
+		    gpointer  user_data)
+{
+  GtkWidget *widget = GTK_WIDGET (user_data);
+  GdkScreen *screen = gtk_widget_get_screen (widget);
+  GtkRequisition req;
+  gint out_y;
+
+  if (!GTK_WIDGET_REALIZED (widget))
+    return;
+
+  gdk_window_get_origin (widget->window, x, y);
+
+  gtk_widget_size_request (GTK_WIDGET (menu), &req);
+
+  /* align with the current toggle button, but clamp on
+   * the screen size
+   */
+  *x += widget->allocation.width;
+  out_y = widget->allocation.y + req.height;
+  if (out_y > gdk_screen_get_height (screen))
+    *y += widget->allocation.y - (out_y - gdk_screen_get_height (screen));
+  else
+    *y += widget->allocation.y;
+
+  *push_in = FALSE;
+}
+
+static GtkWidget *
+hn_app_button_create_menu (HNAppButton *app_button)
+{
+  GtkWidget *menu;
+  GtkWidget *active_item = NULL;
+  HDEntryInfo *info;
+  const GList *children, *l;
+  gint width;
+  GtkRequisition req;
+
+  info = app_button->priv->info;
+  g_assert (info != NULL);
+
+  menu = gtk_menu_new ();
+
+  children = hd_entry_info_get_children (info);
+  for (l = children; l != NULL; l = l->next)
+    {
+      GtkWidget *menu_item;
+
+      menu_item =
+        hn_app_menu_item_new(l->data, FALSE, app_button->priv->is_thumbable);
+
+      /* the G spec says the first item should be selected */
+      if (!active_item)
+        active_item = menu_item;
+
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+      gtk_widget_show (menu_item);
+    }
+
+  if (active_item)
+    gtk_menu_shell_select_item (GTK_MENU_SHELL (menu), active_item);
+
+  width = MIN (menu->allocation.width, AS_MENU_ITEM_WIDTH);
+  gtk_widget_set_size_request (menu, -1, -1);
+  gtk_widget_size_request (menu, &req);
+  gtk_widget_set_size_request (menu,
+		  	       MAX (width, req.width),
+			       -1);
+
+  return menu;
+}
+
+void
+hn_app_button_make_active (HNAppButton *button)
+{
+  g_return_if_fail (button);
+
+  gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (button), FALSE);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+  gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (button));
+
+  /* reset the previous button to NULL -- this function is called by
+     AS in response to MB stacking order changes
+   */
+  button->priv->prev_button = NULL;
+}
+
+static void
+menu_unmap_cb (GtkWidget   *widget,
+	       HNAppButton *app_button)
+{
+  gtk_widget_destroy (widget);
+  app_button->priv->menu = NULL;
+
+  /* we always deactivate the button when the menu closes, and wait for MB
+   * notification of change in stacking order to ensure that the button
+   * selection always matches the stacking order (i.e., if the app fails to
+   * start or top, we do not want the button selected)
+   */
+  if (app_button->priv->prev_button &&
+      app_button->priv->prev_button != GTK_TOGGLE_BUTTON(app_button))
+    {
+      HN_DBG ("Retoggling previously toggled button");
+	 
+      gtk_toggle_button_set_active (app_button->priv->prev_button, TRUE);
+      gtk_toggle_button_toggled (app_button->priv->prev_button);
+    }
+  
+}
+
+static gboolean
+menu_button_release_cb (GtkWidget      *widget,
+                        GdkEventButton *event,
+                        HNAppButton *app_button)
+{
+  HNAppButtonPrivate *priv = app_button->priv;
+  gint x, y;
+  
+  if (app_button && priv->tooltip)
+    {
+      hn_app_tooltip_remove_show_timer (HN_APP_TOOLTIP (priv->tooltip));
+      
+      gtk_widget_destroy (priv->tooltip);
+      priv->tooltip = NULL;
+    }
+
+  /*
+   * if the button is released outside of the menu, we need to deactivate
+   * the button
+   */
+  gtk_widget_get_pointer(widget, &x, &y);
+
+  HN_DBG ("pointer [%d,%d],\n"
+          "allocation [%d, %d, %d, %d]",
+          x, y,
+          widget->allocation.x,
+          widget->allocation.y,
+          widget->allocation.width,
+          widget->allocation.height);
+
+  if(x > widget->allocation.width  || x < 0 ||
+     y > widget->allocation.height || y < 0)
+    {
+      /* pointer outside the menu, i.e., menu cancelled */
+      gtk_menu_shell_deactivate(GTK_MENU_SHELL(widget));
+
+      if (app_button->priv->prev_button)
+        {
+          HN_DBG ("Retoggling previously toggled button");
+	 
+          gtk_toggle_button_set_active (app_button->priv->prev_button, TRUE);
+          gtk_toggle_button_toggled (app_button->priv->prev_button);
+        }
+
+      g_debug ("%s: %d, hd_wm_activate(HN_TN_ACTIVATE_LAST_APP_WINDOW);",__FILE__,__LINE__);
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+menu_keypress_cb (GtkWidget *menu, GdkEventKey *event, HNAppButton * app_button)
+{
+  g_return_val_if_fail(event, FALSE);
+
+  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 (hd_wm_get_singleton ());
+        }
+      else
+        {
+          /* pointer outside the button, i.e., press canceled */
+          if (app_button->priv->prev_button)
+            {
+              HN_DBG ("Retoggling previously toggled button");
+	 
+              gtk_toggle_button_set_active (app_button->priv->prev_button, TRUE);
+              gtk_toggle_button_toggled (app_button->priv->prev_button);
+            }
+
+		  g_debug ("%s: %d, hd_wm_activate(HN_TN_ACTIVATE_KEY_FOCUS);",__FILE__,__LINE__);
+		  gtk_widget_grab_focus (GTK_WIDGET (app_button));
+        }
+      
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+hn_app_button_pop_menu (HNAppButton *app_button)
+{
+  guint            n_children;
+  HDEntryInfo     *info;
+
+  g_return_val_if_fail(app_button, FALSE);
+
+  info = app_button->priv->info;
+  g_return_val_if_fail(info, FALSE);
+  
+  n_children = hd_entry_info_get_n_children (info);
+  if (n_children == 1)
+    {
+      /* pointer released in the app button, top our app */
+      const GList *child;
+      gboolean was_blinking;
+
+      child = hd_entry_info_get_children (app_button->priv->info);
+
+      /* stop the blinking if needed */
+      was_blinking = hn_app_button_get_is_blinking (app_button);
+      if (was_blinking)
+        {
+          hn_app_button_set_is_blinking (app_button, FALSE);
+	  
+	  if (hd_entry_info_is_urgent (child->data))
+            hd_entry_info_set_ignore_urgent (child->data, TRUE);
+	}
+      
+      hd_wm_top_item (child->data);
+    }
+  else
+    {
+      GtkWidget *menu;
+      gboolean   was_blinking;
+
+      /* multiple instances: show the menu and let the
+       * user choose which one to top; if the user unmaps
+       * the menu, reset the active state to the
+       * previously set button.
+       */
+
+      /* first, stop the button blinking */
+      was_blinking = hn_app_button_get_is_blinking (app_button);
+  
+      if (was_blinking)
+        {
+          const GList *l;
+      
+          hn_app_button_set_is_blinking (app_button, FALSE);
+
+          /*
+           * set the ignore flag on any children that were causing the
+           * blinking
+           */
+          for (l = hd_entry_info_get_children (info); l != NULL; l = l->next)
+            {
+              if (hd_entry_info_is_urgent(l->data))
+                hd_entry_info_set_ignore_urgent(l->data, TRUE);
+            }
+        }
+      
+      menu = hn_app_button_create_menu (app_button);
+      if (!menu)
+        {
+          g_warning ("Unable to create the menu");
+          return FALSE;
+        }
+
+      gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+                      menu_position_func, app_button,
+		      0, GDK_CURRENT_TIME);
+
+      g_signal_connect (menu, "unmap",
+                        G_CALLBACK (menu_unmap_cb),
+                        app_button);
+
+      g_signal_connect (menu, "button-release-event",
+                        G_CALLBACK (menu_button_release_cb),
+                        app_button);
+
+      g_signal_connect (menu, "key-press-event",
+                        G_CALLBACK (menu_keypress_cb),
+                        app_button);
+      
+      app_button->priv->menu = menu;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+hn_app_button_key_press_event (GtkWidget * widget,
+                               GdkEventKey *event)
+{
+  HNAppButton *app_button = HN_APP_BUTTON (widget);
+  GtkToggleButton * toggle_button = GTK_TOGGLE_BUTTON (widget);
+  GSList * l;
+  GtkToggleButton *tmp_button = NULL;
+
+  if (!event)
+    {
+      HN_DBG("no event given !!!");
+      return FALSE;
+    }
+  
+  if (event->keyval == GDK_Right    ||
+      event->keyval == GDK_KP_Right ||
+      event->keyval == GDK_KP_Enter ||
+      event->keyval == GDK_ISO_Enter||
+      event->keyval == GDK_Return)
+    {
+      /* search for the toggled button, so that we can re-toggle
+       * it in case the user didn't top the window/application
+       */
+      
+      app_button->priv->prev_button = NULL;
+      for (l = app_button->group; l != NULL; l = l->next)
+        {
+          tmp_button = l->data;
+
+          if (tmp_button->active && (tmp_button != toggle_button))
+            {
+              app_button->priv->prev_button = tmp_button;
+              break;
+            }
+        }
+  
+      gtk_toggle_button_set_active (toggle_button, TRUE);
+      gtk_toggle_button_toggled (toggle_button);
+  
+      hn_app_button_pop_menu (app_button);
+      return TRUE;
+    }
+  else if(event->keyval == GDK_Left || event->keyval == GDK_KP_Left)
+	{
+      HN_DBG("left keypress -- passing focus to last active app");
+      g_debug ("%s: %d, hd_wm_activate(HN_TN_ACTIVATE_LAST_APP_WINDOW);",__FILE__,__LINE__);
+	}
+
+  return FALSE;
+}
+
+static gboolean
+hn_app_button_release_event (GtkWidget      *widget,
+	                     GdkEventButton *event)
+{
+  HNAppButton *app_button = HN_APP_BUTTON (widget);
+  HNAppButtonPrivate *priv = app_button->priv;
+  gint x,y;
+  gboolean force_untoggle = FALSE;
+  gboolean untoggle = FALSE;
+  
+  HN_DBG("Button released ...");
+
+  if (priv->tooltip)
+    {
+      HN_DBG("removing tooltip timer");
+      
+      /* have to remove the show timer, so that if the tooltip is not
+       * yet showing, it does not get displayed
+       */
+      hn_app_tooltip_remove_show_timer (HN_APP_TOOLTIP (priv->tooltip));
+      
+      gtk_widget_destroy (priv->tooltip);
+      priv->tooltip = NULL;
+    }
+
+  gtk_widget_get_pointer(widget, &x, &y);
+
+  HN_DBG ("pointer [%d,%d],\n"
+          "allocation [%d, %d, %d, %d]",
+          x, y,
+          widget->allocation.x,
+          widget->allocation.y,
+          widget->allocation.width,
+          widget->allocation.height);
+
+  if(x > widget->allocation.width  || x < 0 ||
+     y > widget->allocation.height || y < 0)
+    {
+      untoggle = TRUE;
+    }
+  else if (priv->info && hd_entry_info_get_n_children (priv->info) == 1)
+    {
+      /* single window application, i.e., no submenu -- have to untogle
+       * the button and wait for MB notification of change in stacking order
+       * to make the toggle permanent
+       */
+      force_untoggle = TRUE;
+    }
+    
+  if(untoggle || force_untoggle)
+    {
+      if (priv->prev_button)
+        {
+          HN_DBG ("Retoggling previously toggled button");
+	 
+	  gtk_toggle_button_set_active (priv->prev_button, TRUE);
+	  gtk_toggle_button_toggled (priv->prev_button);
+	}
+
+      if (untoggle)
+        {
+          /* click canceled -- we are done */
+          goto out;
+        }
+    }
+  
+  hn_app_button_pop_menu (app_button);
+
+ out:
+  /* reset the thumbable flag, to avoid problems if the menu is raised
+   * some other way than via pointer (should really only be an issue for
+   * the main menu button, but does us no harm to do it here too)
+   */
+  priv->is_thumbable = FALSE;
+
+#if 0
+  /*
+   * must not reset here, we will need this in case the user cancels the
+   * menu via keypress
+   */
+  priv->prev_button = NULL;
+#endif
+  
+  /* allow the CB chain to continue */
+  return FALSE;
+}
+
+static gboolean
+hn_app_button_press_event (GtkWidget      *widget,
+                           GdkEventButton *event)
+{
+  HNAppButton *app_button;
+  HNAppButtonPrivate *priv;
+  GtkButton *button;
+  GtkToggleButton *toggle_button;
+  GtkToggleButton *tmp_button = NULL;
+  GSList *l;
+  HDEntryInfo *info;
+  
+  g_return_val_if_fail (widget && event, FALSE);
+  
+  app_button = HN_APP_BUTTON (widget);
+  priv = app_button->priv;
+  
+  button = GTK_BUTTON (widget);
+  toggle_button = GTK_TOGGLE_BUTTON (widget);
+
+  /* remember which button was used to press this button */
+  HN_DBG("App button pressed using button %d", event->button);
+
+  g_debug ("%s: %d, hd_wm_activate(HN_TN_DEACTIVATE_KEY_FOCUS);",__FILE__,__LINE__);
+
+  if (event->button == APP_BUTTON_THUMBABLE)
+    priv->is_thumbable = TRUE;
+  else
+    priv->is_thumbable = FALSE;
+  
+  HN_DBG ("choosing");
+
+  /* search for the toggled button, so that we can re-toggle
+   * it in case the user didn't top the window/application
+   */
+  priv->prev_button = NULL;
+  for (l = app_button->group; l != NULL; l = l->next)
+    {
+      tmp_button = l->data;
+
+      if (tmp_button->active && (tmp_button != toggle_button))
+        {
+          priv->prev_button = tmp_button;
+          break;
+        }
+    }
+
+  gtk_toggle_button_set_inconsistent (toggle_button, FALSE);
+  gtk_toggle_button_set_active (toggle_button, TRUE);
+  gtk_toggle_button_toggled (toggle_button);
+  
+  info = priv->info;
+  if (!info)
+    {
+      g_warning ("No entry info bound to this button!");
+      
+      return FALSE;
+    }
+
+  if (!priv->tooltip)
+    priv->tooltip = hn_app_tooltip_new (GTK_WIDGET (app_button));
+
+  hn_app_tooltip_set_text (HN_APP_TOOLTIP(app_button->priv->tooltip),
+                           hd_entry_info_peek_app_name (info));
+
+  hn_app_tooltip_install_timer (HN_APP_TOOLTIP(app_button->priv->tooltip),
+                                NULL,
+                                NULL,
+                                NULL);
+  
+  return FALSE;
+}
+
+static void
+hn_app_button_set_property (GObject      *gobject,
+			    guint         prop_id,
+			    const GValue *value,
+			    GParamSpec   *pspec)
+{
+  HNAppButton *app_button = HN_APP_BUTTON (gobject);
+
+  switch (prop_id)
+    {
+    case BUTTON_PROP_ENTRY_INFO:
+      hn_app_button_set_entry_info (app_button,
+		                    g_value_get_pointer (value));
+      break;
+    case BUTTON_PROP_IS_BLINKING:
+      hn_app_button_set_is_blinking (app_button,
+		      		     g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+hn_app_button_get_property (GObject    *gobject,
+			    guint       prop_id,
+			    GValue     *value,
+			    GParamSpec *pspec)
+{
+
+  HNAppButton *app_button = HN_APP_BUTTON (gobject);
+
+  switch (prop_id)
+    {
+    case BUTTON_PROP_ENTRY_INFO:
+      g_value_set_pointer (value, app_button->priv->info);
+      break;
+    case BUTTON_PROP_IS_BLINKING:
+      g_value_set_boolean (value, app_button->priv->is_blinking);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+hn_app_button_class_init (HNAppButtonClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
+
+  gobject_class->finalize = hn_app_button_finalize;  
+  gobject_class->set_property = hn_app_button_set_property;
+  gobject_class->get_property = hn_app_button_get_property;
+
+  object_class->destroy = hn_app_button_destroy;
+  
+#if 0
+  /* TODO - uncomment to handle keyboard focus */
+  widget_class->focus = hn_app_button_focus;
+#endif
+  widget_class->button_press_event = hn_app_button_press_event;
+  widget_class->button_release_event = hn_app_button_release_event;
+  widget_class->key_press_event = hn_app_button_key_press_event;
+  
+  button_class->clicked = hn_app_button_clicked;
+  
+  g_object_class_install_property (gobject_class,
+		  		   BUTTON_PROP_ENTRY_INFO,
+				   g_param_spec_pointer ("entry-info",
+					   		 "Entry Info",
+							 "The informations about the entry bound to the button",
+							 G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+		  		   BUTTON_PROP_IS_BLINKING,
+				   g_param_spec_boolean ("is-blinking",
+					   		 "Is Blinking",
+							 "Whether the button should be blinking or not",
+							 FALSE,
+							 G_PARAM_READWRITE));
+
+  g_type_class_add_private (gobject_class, sizeof (HNAppButtonPrivate));
+}
+
+static void
+hn_app_button_init (HNAppButton *app_button)
+{
+  HNAppButtonPrivate *priv = HN_APP_BUTTON_GET_PRIVATE (app_button);
+
+  app_button->group = g_slist_prepend (NULL, app_button);
+  app_button->priv = priv;
+
+  gtk_widget_push_composite_child ();
+
+  priv->icon = gtk_image_new ();
+  gtk_widget_set_composite_name (priv->icon, "as-app-button-icon");
+  gtk_container_add (GTK_CONTAINER (app_button), priv->icon);
+  gtk_widget_show (priv->icon);
+
+  gtk_widget_pop_composite_child ();
+
+  priv->info = NULL;
+  
+  priv->is_blinking = FALSE;
+  priv->is_thumbable = FALSE;
+
+  gtk_widget_set_size_request (GTK_WIDGET (app_button), -1, BUTTON_HEIGHT);
+  gtk_widget_set_sensitive (GTK_WIDGET (app_button), FALSE);
+}
+
+/* taken from gtkradiobutton.c */
+void
+hn_app_button_set_group (HNAppButton *app_button,
+			 GSList      *group)
+{
+  g_return_if_fail (HN_IS_APP_BUTTON (app_button));
+
+  if (g_slist_find (group, app_button))
+    return;
+
+  HN_DBG ("setting group for the button");
+
+  if (app_button->group)
+    {
+      GSList *l;
+
+      app_button->group = g_slist_remove (app_button->group, app_button);
+
+      for (l = app_button->group; l != NULL; l = l->next)
+        {
+          HNAppButton *tmp_button = l->data;
+
+	  tmp_button->group = app_button->group;
+        }
+    }
+
+  app_button->group = g_slist_prepend (group, app_button);
+
+  if (group)
+    {
+      GSList *l;
+
+      for (l = group; l != NULL; l = l->next)
+        {
+          HNAppButton *tmp_button = l->data;
+
+	  if (!tmp_button)
+	    continue;
+
+	  tmp_button->group = app_button->group;
+        }
+    }
+
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (app_button), group == NULL);
+}
+
+GSList *
+hn_app_button_get_group (HNAppButton *button)
+{
+  g_return_val_if_fail (HN_IS_APP_BUTTON (button), NULL);
+
+  return button->group;
+}
+
+GtkWidget *
+hn_app_button_new (GSList *group)
+{
+  HNAppButton *app_button;
+
+  app_button = g_object_new (HN_TYPE_APP_BUTTON, NULL);
+
+  if (group)
+    hn_app_button_set_group (app_button, group);
+
+  return GTK_WIDGET (app_button);
+}
+
+void
+hn_app_button_set_icon_from_pixbuf (HNAppButton *button,
+				    GdkPixbuf   *pixbuf)
+{
+  g_return_if_fail (HN_IS_APP_BUTTON (button));
+  g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+
+  gtk_image_set_from_pixbuf (GTK_IMAGE (button->priv->icon), pixbuf);
+}
+
+GdkPixbuf *
+hn_app_button_get_pixbuf_from_icon (HNAppButton *button)
+{
+  g_return_val_if_fail (HN_IS_APP_BUTTON (button), NULL);
+
+  return gtk_image_get_pixbuf (GTK_IMAGE (button->priv->icon));
+}
+
+HDEntryInfo *
+hn_app_button_get_entry_info (HNAppButton *button)
+{
+  g_return_val_if_fail (HN_IS_APP_BUTTON (button), NULL);
+
+  return button->priv->info;
+}
+
+static GdkPixbuf *
+get_pixbuf_for_entry_info (HDEntryInfo *info)
+{
+  GdkPixbuf *retval;
+  GError *error;
+
+  error = NULL;
+  retval = hd_entry_info_get_app_icon (info, AS_ICON_SIZE, &error);
+  if (!error)
+    return retval;
+
+  osso_log (LOG_ERR, "Could not load icon '%s': %s\n",
+            hd_entry_info_get_app_icon_name (info),
+            error->message);
+
+  /* Fallback to default icon */
+  g_clear_error (&error);
+  retval = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+		  		     AS_MENU_DEFAULT_APP_ICON,
+				     AS_ICON_SIZE,
+				     0,
+				     &error);
+  if (!error)
+    return retval;
+
+  osso_log (LOG_ERR, "Could not load icon '%s': %s\n",
+            AS_MENU_DEFAULT_APP_ICON,
+            error->message);
+
+  g_error_free (error);
+
+  return NULL;
+}
+
+static GdkPixbuf *
+compose_app_pixbuf (const GdkPixbuf *src,
+		    HDEntryInfo     *info)
+{
+  GdkPixbuf *retval, *inst_pixbuf;
+  const gchar *inst_name;
+  GError *error;
+  gint dest_width, dest_height;
+  gint off_x, off_y;
+
+  g_return_val_if_fail (GDK_IS_PIXBUF (src), NULL);
+  g_return_val_if_fail (info != NULL, NULL);
+
+  HN_MARK();
+
+  /* first of all, see if this app is hibernated */
+  if (hd_entry_info_is_hibernating (info))
+    {
+      inst_name = "qgn_indi_bkilled";
+    }
+  else if (hd_entry_info_has_extra_icon (info))
+    {
+      inst_name = hd_entry_info_get_extra_icon (info);
+    }
+  else
+    {
+      gint n_instances = hd_entry_info_get_n_children (info);
+      
+      if (!n_instances)
+        {
+          g_warning ("top-level item '%s' has no instances",
+                     hd_entry_info_peek_title (info));
+          return NULL;
+        }
+
+      if (n_instances == 1)
+        return NULL;
+
+      if (G_LIKELY (n_instances <= app_group_n_icons - 1))
+        inst_name = app_group_icons[n_instances - 1];
+      else
+        inst_name = APP_GROUP_ICON_MORE;
+    }
+  
+  HN_DBG ("Compositing icon for '%s' with icon name '%s'",
+	  hd_entry_info_peek_title (info),
+	  inst_name);
+
+  error = NULL;
+  inst_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+		  			  inst_name,
+					  APP_GROUP_ICON_SIZE,
+					  0,
+					  &error);
+  if (error)
+    {
+      g_warning ("unable to find icon '%s' in current theme: %s",
+		 inst_name,
+		 error->message);
+
+      g_error_free (error);
+
+      return NULL;
+    }
+
+  /* make a copy of the source pixbuf, and also make
+   * sure that it has an alpha channel
+   */
+  retval = gdk_pixbuf_add_alpha (src, FALSE, 255, 255, 255);
+
+  dest_width = gdk_pixbuf_get_width (retval);
+  dest_height = gdk_pixbuf_get_height (retval);
+
+  off_x = dest_width - gdk_pixbuf_get_width (inst_pixbuf);
+  off_y = dest_height - gdk_pixbuf_get_height (inst_pixbuf);
+
+  gdk_pixbuf_composite (inst_pixbuf, retval,
+		        0, 0,
+			dest_width, dest_height,
+			off_x, off_y,
+			1.0, 1.0,
+			GDK_INTERP_BILINEAR,
+			0xFF);
+
+  g_object_unref (inst_pixbuf);
+
+  return retval;
+}
+
+void
+hn_app_button_set_entry_info (HNAppButton *button,
+			      HDEntryInfo *info)
+{
+  g_return_if_fail (HN_IS_APP_BUTTON (button));
+
+  button->priv->info = NULL;
+
+  HN_MARK();
+
+  if (info)
+    {
+      GdkPixbuf *app_pixbuf;
+      
+      app_pixbuf = get_pixbuf_for_entry_info (info);
+      if (app_pixbuf)
+	{
+          GdkPixbuf *pixbuf;
+
+	  /* compose the application icon with the number of
+	   * instances running
+	   */
+	  pixbuf = compose_app_pixbuf (app_pixbuf, info);
+	  if (pixbuf)
+            {
+              gtk_image_set_from_pixbuf (GTK_IMAGE (button->priv->icon),
+			                 pixbuf);
+	      g_object_unref (pixbuf);
+	    }
+	  else
+            gtk_image_set_from_pixbuf (GTK_IMAGE (button->priv->icon),
+			               app_pixbuf);
+
+	  g_object_unref (app_pixbuf);
+	}
+      else
+	HN_DBG ("Unable to find the icon (even the default one)");
+
+      /* the newly composed image is static */
+      if(button->priv->is_blinking &&
+         !hn_app_switcher_get_system_inactivity(HN_APP_SWITCHER (hn_app_switcher_new ())))
+        hn_app_button_icon_animation (button->priv->icon,
+                                      button->priv->is_blinking);
+      
+      gtk_widget_show (button->priv->icon);
+      gtk_widget_set_sensitive (GTK_WIDGET (button), TRUE);
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+		                    hd_entry_info_is_active (info));
+      
+      g_object_set (G_OBJECT (button), "can-focus", TRUE, NULL);
+      
+      button->priv->info = info;
+    }
+  else
+    {
+      gtk_widget_hide (button->priv->icon);
+      gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
+
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
+      gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (button));
+
+      g_object_set (G_OBJECT (button), "can-focus", FALSE, NULL);
+    }
+}
+
+void
+hn_app_button_force_update_icon (HNAppButton *button)
+{
+  HDEntryInfo *info;
+  
+  g_return_if_fail (HN_IS_APP_BUTTON (button));
+
+  HN_MARK ();
+
+  info = button->priv->info;
+
+  if (info)
+    {
+      GdkPixbuf *app_pixbuf;
+      
+      app_pixbuf = get_pixbuf_for_entry_info (info);
+      if (app_pixbuf)
+	{
+          GdkPixbuf *pixbuf;
+
+	  /* compose the application icon with the number of
+	   * instances running
+	   */
+	  pixbuf = compose_app_pixbuf (app_pixbuf, info);
+	  if (pixbuf)
+            {
+              gtk_image_set_from_pixbuf (GTK_IMAGE (button->priv->icon),
+			                 pixbuf);
+	      g_object_unref (pixbuf);
+	    }
+	  else
+            gtk_image_set_from_pixbuf (GTK_IMAGE (button->priv->icon),
+			               app_pixbuf);
+
+	  g_object_unref (app_pixbuf);
+	}
+      else
+	HN_DBG ("Unable to find the icon (even the default one)");
+
+      /* the newly composed image is static */
+      if(button->priv->is_blinking &&
+         !hn_app_switcher_get_system_inactivity(HN_APP_SWITCHER (hn_app_switcher_new())))
+        hn_app_button_icon_animation (button->priv->icon,
+                                      button->priv->is_blinking);
+      
+      gtk_widget_show (button->priv->icon);
+      gtk_widget_set_sensitive (GTK_WIDGET (button), TRUE);
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+		                    hd_entry_info_is_active (info));
+      
+      g_object_set (G_OBJECT (button), "can-focus", TRUE, NULL);
+      
+      button->priv->info = info;
+    }
+}
+
+static void
+hn_app_button_icon_animation (GtkWidget *icon,
+			      gboolean   turn_on)
+{
+  GdkPixbuf *pixbuf;
+  GdkPixbufAnimation *pixbuf_anim;
+
+  g_return_if_fail (GTK_IS_IMAGE (icon));
+
+  if (turn_on &&
+      gtk_image_get_storage_type(GTK_IMAGE(icon)) != GTK_IMAGE_ANIMATION)
+    {
+      pixbuf = gtk_image_get_pixbuf (GTK_IMAGE (icon));
+      pixbuf_anim = hildon_pixbuf_anim_blinker_new (pixbuf,
+		     				    1000 / ANIM_FPS,
+						    -1,
+						    10);
+     
+      gtk_image_set_from_animation (GTK_IMAGE(icon), pixbuf_anim);
+      g_object_unref (pixbuf_anim);
+    }
+  else if(!turn_on &&
+          gtk_image_get_storage_type(GTK_IMAGE(icon)) == GTK_IMAGE_ANIMATION)
+    {
+      pixbuf_anim = gtk_image_get_animation (GTK_IMAGE (icon));
+
+      /* grab static image from menu item and reset */
+      pixbuf = gdk_pixbuf_animation_get_static_image (pixbuf_anim);
+
+      gtk_image_set_from_pixbuf (GTK_IMAGE (icon), pixbuf);
+
+      /*
+       * unrefing the pixbuf here causes SIGSEGV
+       */
+      
+      /*g_object_unref (pixbuf);*/
+    }
+}
+
+gboolean
+hn_app_button_get_is_blinking (HNAppButton *button)
+{
+  g_return_val_if_fail (HN_IS_APP_BUTTON (button), FALSE);
+
+  return button->priv->is_blinking;
+}
+
+void
+hn_app_button_set_is_blinking (HNAppButton *button,
+			       gboolean     is_blinking)
+{
+  HN_DBG("Setting is_blinking %d -> %d",
+         button->priv->is_blinking,
+         is_blinking);
+  
+  g_return_if_fail (HN_IS_APP_BUTTON (button));
+
+  if (button->priv->is_blinking != is_blinking)
+    {
+      hn_app_button_icon_animation (button->priv->icon,
+		      		    is_blinking);
+
+      button->priv->is_blinking = is_blinking;
+    }
+}

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-button.h
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-button.h	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-button.h	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,90 @@
+/* hn-app-button.h
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/**
+ * @file hn-app-button.h
+ *
+ * @brief Definitions of the application button used
+ *        by the Application Switcher
+ *
+ */
+
+#ifndef HN_APP_BUTTON_H
+#define HN_APP_BUTTON_H
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtktogglebutton.h>
+
+#include <libhildonwm/hd-wm.h>
+
+#define APP_BUTTON_THUMBABLE 8
+#define APP_BUTTON_NORMAL    1
+
+G_BEGIN_DECLS
+
+#define HN_TYPE_APP_BUTTON              (hn_app_button_get_type ())
+#define HN_APP_BUTTON(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), HN_TYPE_APP_BUTTON, HNAppButton))
+#define HN_IS_APP_BUTTON(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HN_TYPE_APP_BUTTON))
+#define HN_APP_BUTTON_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), HN_TYPE_APP_BUTTON, HNAppButtonClass))
+#define HN_IS_APP_BUTTON_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), HN_TYPE_APP_BUTTON))
+#define HN_APP_BUTTON_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), HN_TYPE_APP_BUTTON, HNAppButtonClass))
+
+typedef struct _HNAppButton        HNAppButton;
+typedef struct _HNAppButtonPrivate HNAppButtonPrivate;
+typedef struct _HNAppButtonClass   HNAppButtonClass;
+
+struct _HNAppButton
+{
+  GtkToggleButton parent_instance;
+
+  GSList *group;
+
+  HNAppButtonPrivate *priv;
+};
+
+struct _HNAppButtonClass
+{
+  GtkToggleButtonClass parent_class;
+};
+
+GType hn_app_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget *  hn_app_button_new                  (GSList      *group);
+void         hn_app_button_set_group            (HNAppButton *button,
+						 GSList      *group);
+GSList *     hn_app_button_get_group            (HNAppButton *button);
+
+void         hn_app_button_set_icon_from_pixbuf (HNAppButton *button,
+						 GdkPixbuf   *pixbuf);
+GdkPixbuf *  hn_app_button_get_pixbuf_from_icon (HNAppButton *button);
+HDEntryInfo *hn_app_button_get_entry_info       (HNAppButton *button);
+void         hn_app_button_set_entry_info       (HNAppButton *button,
+					         HDEntryInfo *info);
+gboolean     hn_app_button_get_is_blinking      (HNAppButton *button);
+void         hn_app_button_set_is_blinking      (HNAppButton *button,
+					         gboolean     is_blinking);
+void         hn_app_button_force_update_icon    (HNAppButton *button);
+void         hn_app_button_make_active          (HNAppButton *button);
+G_END_DECLS
+
+#endif /* HN_APP_BUTTON_H */

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-menu-item.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-menu-item.c	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-menu-item.c	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,757 @@
+/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+
+/* hn-app-switcher.c
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/* Hildon includes */
+#include "hn-app-menu-item.h"
+#include "hildon-pixbuf-anim-blinker.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* GLib include */
+#include <glib.h>
+#include <glib/gi18n.h>
+
+/* GTK includes */
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtkmenu.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkseparatormenuitem.h>
+#include <gtk/gtkeventbox.h>
+#include <gtk/gtkalignment.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkmisc.h>
+
+#include <libosso.h>
+
+#include <hildon-widgets/gtk-infoprint.h>
+#include "hildon-pixbuf-anim-blinker.h"
+
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+
+/* GDK includes */
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+
+/* X includes */
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+/* log include */
+#include <log-functions.h>
+
+/* Menu item strings */
+#define AS_HOME_ITEM 		_("tana_fi_home")
+#define AS_HOME_ITEM_ICON 	"qgn_list_home"
+
+#define AS_MENU_DEFAULT_APP_ICON "qgn_list_gene_default_app"
+
+#define ANIM_DURATION 5000 	/* 5 Secs for blinking icons */
+#define ANIM_FPS      2
+
+/* Hardcoded pixel perfecting values */
+#define AS_BUTTON_BORDER_WIDTH  0
+#define AS_MENU_BORDER_WIDTH    20
+#define AS_TIP_BORDER_WIDTH 	20
+#define AS_BUTTON_HEIGHT        38
+#define AS_ROW_HEIGHT 		30
+#define AS_ICON_SIZE            26
+#define AS_ICON_THUMB_SIZE      64
+#define AS_CLOSE_BUTTON_SIZE    16
+#define AS_CLOSE_BUTTON_THUMB_SIZE 40
+#define AS_TOOLTIP_WIDTH        360
+#define AS_MENU_ITEM_WIDTH      360
+#define AS_INTERNAL_PADDING     10
+#define AS_SEPARATOR_HEIGHT     10
+#define AS_BUTTON_BOX_PADDING   10
+
+/*
+ * HNAppMenuItem
+ */
+
+enum
+{
+  MENU_PROP_0,
+  MENU_PROP_ENTRY_INFO,
+  MENU_PROP_SHOW_CLOSE,
+  MENU_PROP_IS_BLINKING,
+  MENU_PROP_THUMBABLE
+};
+
+#define HN_APP_MENU_ITEM_GET_PRIVATE(obj) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((obj), HN_TYPE_APP_MENU_ITEM, HNAppMenuItemPrivate))
+
+struct _HNAppMenuItemPrivate
+{
+  GtkWidget *icon;
+  GtkWidget *label;
+  GtkWidget *label2;
+  GtkWidget *close;
+
+  GtkIconTheme *icon_theme;
+  
+  guint show_close  : 1;
+  guint thumbable    : 1;
+  guint is_blinking : 1;
+
+  GdkPixbufAnimation *pixbuf_anim;
+  
+  HDEntryInfo *info;
+
+  HildonPixbufAnimBlinker *blinker;
+};
+
+G_DEFINE_TYPE (HNAppMenuItem, hn_app_menu_item, GTK_TYPE_IMAGE_MENU_ITEM);
+
+static void
+hn_app_menu_item_finalize (GObject *gobject)
+{
+  HNAppMenuItemPrivate *priv = HN_APP_MENU_ITEM (gobject)->priv;
+  
+  if (priv->pixbuf_anim)
+    g_object_unref (priv->pixbuf_anim); 
+
+  G_OBJECT_CLASS (hn_app_menu_item_parent_class)->finalize (gobject);
+}
+
+static void
+hn_app_menu_item_set_property (GObject      *gobject,
+			       guint         prop_id,
+			       const GValue *value,
+			       GParamSpec   *pspec)
+{
+  HNAppMenuItemPrivate *priv = HN_APP_MENU_ITEM (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case MENU_PROP_ENTRY_INFO:
+      priv->info = g_value_get_pointer (value);
+      break;
+    case MENU_PROP_SHOW_CLOSE:
+      priv->show_close = g_value_get_boolean (value);
+      break;
+    case MENU_PROP_THUMBABLE:
+      priv->thumbable = g_value_get_boolean (value);
+      break;
+    case MENU_PROP_IS_BLINKING:
+      priv->is_blinking = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+hn_app_menu_item_get_property (GObject    *gobject,
+			       guint       prop_id,
+			       GValue     *value,
+			       GParamSpec *pspec)
+{
+  HNAppMenuItemPrivate *priv = HN_APP_MENU_ITEM (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case MENU_PROP_ENTRY_INFO:
+      g_value_set_pointer (value, priv->info);
+      break;
+    case MENU_PROP_SHOW_CLOSE:
+      g_value_set_boolean (value, priv->show_close);
+      break;
+    case MENU_PROP_THUMBABLE:
+      g_value_set_boolean (value, priv->thumbable);
+      break;
+    case MENU_PROP_IS_BLINKING:
+      g_value_set_boolean (value, priv->is_blinking);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+hn_app_menu_item_icon_animation (GtkWidget *icon,
+	       			 gboolean   is_on)
+{
+  GdkPixbuf *pixbuf;
+  GdkPixbufAnimation *pixbuf_anim;
+  
+  g_return_if_fail (GTK_IS_IMAGE (icon));
+
+  if (is_on)
+    {
+      pixbuf = gtk_image_get_pixbuf (GTK_IMAGE (icon));
+              
+      pixbuf_anim = hildon_pixbuf_anim_blinker_new (pixbuf,
+                                                    1000 / ANIM_FPS,
+						    -1,
+						    10);
+
+      gtk_image_set_from_animation (GTK_IMAGE(icon), pixbuf_anim);
+
+      g_object_unref (pixbuf_anim);
+    }
+  else
+    {
+      pixbuf_anim = gtk_image_get_animation (GTK_IMAGE (icon));
+      
+      /* grab static image from menu item and reset */
+      pixbuf = gdk_pixbuf_animation_get_static_image (pixbuf_anim);
+
+      gtk_image_set_from_pixbuf (GTK_IMAGE (icon), pixbuf);
+      g_object_unref (pixbuf);
+    }
+}
+
+static GObject *
+hn_app_menu_item_constructor (GType                  type,
+			      guint                  n_construct_params,
+			      GObjectConstructParam *construct_params)
+{
+  GObject *gobject;
+  HNAppMenuItem *menuitem;
+  HNAppMenuItemPrivate *priv;
+  GtkWidget *hbox, *vbox = NULL;
+  
+  gobject = G_OBJECT_CLASS (hn_app_menu_item_parent_class)->constructor (type,
+		  							 n_construct_params,
+									 construct_params);
+
+  menuitem = HN_APP_MENU_ITEM (gobject);
+  priv = menuitem->priv;
+  g_assert (priv->info != NULL);
+
+  gtk_widget_push_composite_child ();
+
+  priv->icon = gtk_image_new ();
+  gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menuitem), priv->icon);
+  gtk_widget_show (priv->icon);
+  
+  hbox = gtk_hbox_new (FALSE, 0);
+  gtk_widget_set_composite_name (GTK_WIDGET (hbox), "as-app-menu-hbox");
+  gtk_container_add (GTK_CONTAINER (menuitem), hbox);
+  gtk_widget_show (hbox);
+
+  if(priv->thumbable)
+    {
+      vbox = gtk_vbox_new (FALSE, 0);
+      gtk_widget_set_composite_name (GTK_WIDGET (vbox), "as-app-menu-vbox");
+      gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+      gtk_widget_show (vbox);
+      
+      priv->label2 = gtk_label_new (NULL);
+      gtk_widget_set_composite_name (GTK_WIDGET (priv->label2),
+                                     "as-app-menu-label-desc");
+      gtk_misc_set_alignment (GTK_MISC (priv->label2), 0.0, 0.5);
+      gtk_box_pack_end (GTK_BOX (vbox), priv->label2, TRUE, TRUE, 0);
+      gtk_widget_show (priv->label2);
+    }
+
+  priv->label = gtk_label_new (NULL);
+  gtk_widget_set_composite_name (GTK_WIDGET (priv->label), "as-app-menu-label");
+  gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
+
+  if(priv->thumbable)
+    gtk_box_pack_end (GTK_BOX (vbox), priv->label, TRUE, TRUE, 0);
+  else
+    gtk_box_pack_start (GTK_BOX (hbox), priv->label, TRUE, TRUE, 0);
+  
+  gtk_widget_show (priv->label);
+
+  if (priv->info)
+    {
+      GdkPixbuf *app_pixbuf;
+      
+      priv->is_blinking = hd_entry_info_is_urgent (priv->info);
+      app_pixbuf = hd_entry_info_get_icon (priv->info);
+      if (!app_pixbuf)
+        {
+          gint icon_size;
+          GError *error;
+
+          icon_size = priv->thumbable ? AS_ICON_THUMB_SIZE : AS_ICON_SIZE;
+          error = NULL;
+          app_pixbuf = hd_entry_info_get_app_icon (priv->info,
+                                                   icon_size,
+                                                   &error);
+          if (error)
+            {
+              g_warning ("Could not load icon %s from theme: %s.",
+                         hd_entry_info_get_app_icon_name (priv->info),
+                         error->message);
+              g_error_free (error);
+              error = NULL;
+              app_pixbuf = gtk_icon_theme_load_icon (priv->icon_theme,
+                                                     AS_MENU_DEFAULT_APP_ICON,
+                                                     priv->thumbable ?
+                                                     AS_ICON_THUMB_SIZE : 
+                                                     AS_ICON_SIZE,
+                                                     GTK_ICON_LOOKUP_NO_SVG,
+                                                     &error);
+
+              if (error)
+                {
+                  g_warning ("Could not load icon %s from theme: %s.",
+                             AS_MENU_DEFAULT_APP_ICON,
+                             error->message);
+                  g_error_free (error);
+                }
+            }
+        }
+      
+      if (app_pixbuf)
+        {
+          GdkPixbuf *compose;
+
+	  compose = NULL;
+	  if (hd_entry_info_is_hibernating (priv->info))
+            {
+              compose = gtk_icon_theme_load_icon (priv->icon_theme,
+			      			  "qgn_indi_bkilled",
+						  16,
+						  0,
+						  NULL);
+	    }
+
+      if (compose)
+        {
+          GdkPixbuf *tmp;
+          gint dest_width = gdk_pixbuf_get_width (app_pixbuf);
+          gint dest_height = gdk_pixbuf_get_height (app_pixbuf);
+          gint off_x, off_y;
+
+          off_x = dest_width - gdk_pixbuf_get_width (compose);
+          off_y = dest_height - gdk_pixbuf_get_height (compose);
+
+          /* Copy the pixbuf and make sure we have an alpha channel */
+          tmp = gdk_pixbuf_add_alpha (app_pixbuf, FALSE, 255, 255, 255);
+          if (tmp)
+            {
+              g_object_unref (app_pixbuf);
+              app_pixbuf = tmp;
+            }
+
+          gdk_pixbuf_composite (compose, app_pixbuf,
+                                0, 0,
+                                dest_width, dest_height,
+                                off_x, off_y,
+                                1.0, 1.0,
+                                GDK_INTERP_BILINEAR,
+                                0xff);
+          g_object_unref (compose);
+        }
+
+      gtk_image_set_from_pixbuf (GTK_IMAGE (priv->icon), app_pixbuf);
+      g_object_unref (app_pixbuf);
+
+      if (priv->is_blinking)
+        hn_app_menu_item_icon_animation (priv->icon, TRUE);
+        }
+
+      if (!priv->thumbable)
+	{
+          gchar *app_name = hd_entry_info_get_title (priv->info);
+
+	  gtk_label_set_text (GTK_LABEL (priv->label), app_name);
+	  g_free (app_name);
+	}
+      else
+	{
+          gchar *app_name = hd_entry_info_get_app_name (priv->info);
+	  gchar *win_name = hd_entry_info_get_window_name (priv->info);
+
+	  if (win_name)
+            gtk_label_set_text (GTK_LABEL (priv->label2), win_name);
+	  
+	  gtk_label_set_text (GTK_LABEL (priv->label), app_name);
+
+	  g_free (app_name);
+	  g_free (win_name);
+	}
+    }
+
+  if (priv->show_close)
+    {
+      HNAppMenuItemClass *klass = HN_APP_MENU_ITEM_CLASS (G_OBJECT_GET_CLASS (gobject));
+      priv->close = gtk_image_new ();
+
+      if (priv->thumbable && klass->thumb_close_button)
+          gtk_image_set_from_pixbuf (GTK_IMAGE (priv->close),
+                                     klass->thumb_close_button);
+      else if (!priv->thumbable && klass->close_button)
+          gtk_image_set_from_pixbuf (GTK_IMAGE (priv->close),
+                                     klass->close_button);
+      else
+        g_warning ("Icon missing for close button");
+
+      gtk_box_pack_end (GTK_BOX (hbox), priv->close, FALSE, FALSE, 0);
+      gtk_widget_show (priv->close);
+    }
+  
+  return gobject;
+}
+
+static void
+hn_app_menu_item_size_request (GtkWidget      *widget,
+			       GtkRequisition *requisition)
+{
+  HNAppMenuItemPrivate *priv = HN_APP_MENU_ITEM (widget)->priv;
+  GtkRequisition child_req = {0};
+  GtkRequisition child_req2 = {0};
+  gint child_width, child_height;
+
+  /* if the width of icon + label (+ close) is too big,
+   * we clamp it to AS_MENU_ITEM_WIDTH and ellipsize the
+   * label text; the height is set by the icons
+   */
+
+  gtk_widget_size_request (priv->icon, &child_req);
+  child_width = child_req.width;
+  child_height = child_req.height;  
+
+  child_req.width = child_req.height = 0;
+  gtk_widget_size_request (priv->label,  &child_req);    
+  if (priv->thumbable)
+    {
+    gtk_widget_size_request (priv->label2, &child_req2);    
+    }
+  child_width += MAX (child_req.width, child_req2.width);
+
+  if (priv->show_close)
+    {
+      child_req.width = child_req.height = 0;
+      gtk_widget_size_request (priv->close, &child_req);
+      child_width += child_req.width;
+      child_height = child_req.height;
+    }
+
+  child_width = MAX (child_width, requisition->width);
+  
+  GTK_WIDGET_CLASS (hn_app_menu_item_parent_class)->size_request (widget,
+		 						  requisition);
+
+  if (child_width > AS_MENU_ITEM_WIDTH)
+    {
+      requisition->width = AS_MENU_ITEM_WIDTH;
+
+      gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
+      if (priv->thumbable)
+        gtk_label_set_ellipsize (GTK_LABEL (priv->label2), PANGO_ELLIPSIZE_END);
+    }
+  else
+    requisition->width = MAX (requisition->width, child_width);
+
+  requisition->height = MAX (requisition->height, child_height);
+
+  HN_DBG ("menu-item requisition: (%d, %d)",
+	  requisition->width, requisition->height);
+}
+
+static void
+hn_app_menu_item_activate (GtkMenuItem *menu_item)
+{
+  HDEntryInfo *info;
+
+  info = hn_app_menu_item_get_entry_info (HN_APP_MENU_ITEM (menu_item));
+  g_assert (info != NULL);
+  
+  HN_DBG ("Raising application '%s'", hd_entry_info_peek_title (info));
+  
+  hd_wm_top_item (info);
+}
+
+static gboolean
+hn_app_menu_item_button_release_event (GtkWidget      *widget,
+                                     GdkEventButton *event)
+{
+  HNAppMenuItem *menuitem = HN_APP_MENU_ITEM(widget);
+  gint x, y;
+  
+  HN_DBG ("menu item clicked ended");
+  
+  g_return_val_if_fail (menuitem && menuitem->priv && menuitem->priv->info,
+                        FALSE);
+
+  if(!menuitem->priv->show_close ||
+     !menuitem->priv->close)
+    return FALSE;
+
+  /*
+   * the pointer value is relative to the menuitem, but for some reason,
+   * the close button allocation is relative to the menu as whole
+   */
+  
+  gtk_widget_get_pointer(widget, &x, &y);
+
+  HN_DBG ("pointer [%d,%d],\n"
+          "close allocation [%d, %d, %d, %d]",
+          x, y,
+          menuitem->priv->close->allocation.x,
+          menuitem->priv->close->allocation.y,
+          menuitem->priv->close->allocation.width,
+          menuitem->priv->close->allocation.height);
+
+  /* only test x here; y is always withing the button range */
+  if(x >  menuitem->priv->close->allocation.x &&
+     x <= menuitem->priv->close->allocation.x +
+          menuitem->priv->close->allocation.width)
+    {
+      hd_entry_info_close (menuitem->priv->info);
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+hn_app_menu_item_button_press_event (GtkWidget      *widget,
+                                     GdkEventButton *event)
+{
+  HNAppMenuItem *menuitem = HN_APP_MENU_ITEM(widget);
+  gint x, y;
+  
+  HN_DBG ("menu item clicked");
+  
+  g_return_val_if_fail (menuitem && menuitem->priv && menuitem->priv->info,
+                        FALSE);
+
+  if(!menuitem->priv->show_close ||
+     !menuitem->priv->close)
+    return FALSE;
+
+  /*
+   * the pointer value is relative to the menuitem, but for some reason,
+   * the close button allocation is relative to the menu as whole
+   */
+  
+  gtk_widget_get_pointer(widget, &x, &y);
+
+  HN_DBG ("pointer [%d,%d],\n"
+          "close allocation [%d, %d, %d, %d]",
+          x, y,
+          menuitem->priv->close->allocation.x,
+          menuitem->priv->close->allocation.y,
+          menuitem->priv->close->allocation.width,
+          menuitem->priv->close->allocation.height);
+
+  /* only test x here; y is always withing the button range */
+  if(x >  menuitem->priv->close->allocation.x &&
+     x <= menuitem->priv->close->allocation.x +
+          menuitem->priv->close->allocation.width)
+    {
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+
+static void
+hn_app_menu_item_class_init (HNAppMenuItemClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkMenuItemClass *menuitem_class = GTK_MENU_ITEM_CLASS (klass);
+  GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
+  
+  gobject_class->finalize = hn_app_menu_item_finalize;
+  gobject_class->set_property = hn_app_menu_item_set_property;
+  gobject_class->get_property = hn_app_menu_item_get_property;
+  gobject_class->constructor = hn_app_menu_item_constructor;
+
+  widget_class->size_request = hn_app_menu_item_size_request;
+  widget_class->button_press_event = hn_app_menu_item_button_press_event;
+  widget_class->button_release_event = hn_app_menu_item_button_release_event;
+  
+  menuitem_class->activate = hn_app_menu_item_activate;
+
+  klass->close_button = gtk_icon_theme_load_icon (icon_theme,
+                                                  "qgn_list_app_close",
+                                                  AS_CLOSE_BUTTON_SIZE,
+                                                  0,
+                                                  NULL);
+
+  klass->thumb_close_button = gtk_icon_theme_load_icon (icon_theme,
+                                                        "qgn_list_app_close",
+                                                        AS_CLOSE_BUTTON_THUMB_SIZE,
+                                                        0,
+                                                        NULL);
+  
+  g_object_class_install_property (gobject_class,
+		  		   MENU_PROP_SHOW_CLOSE,
+				   g_param_spec_boolean ("show-close",
+					   		 "Show Close",
+							 "Whether to show a close button",
+							 FALSE,
+							 (G_PARAM_CONSTRUCT | G_PARAM_READWRITE)));
+  g_object_class_install_property (gobject_class,
+		  		   MENU_PROP_THUMBABLE,
+				   g_param_spec_boolean ("thumbable",
+					   		 "Thumbable",
+							 "Whether the menu item should be thumbable",
+							 FALSE,
+							 (G_PARAM_CONSTRUCT | G_PARAM_READWRITE)));
+  g_object_class_install_property (gobject_class,
+		  		   MENU_PROP_IS_BLINKING,
+				   g_param_spec_boolean ("is-blinking",
+					   		 "Is blinking",
+							 "Whether the menu item should blink",
+							 FALSE,
+							 (G_PARAM_CONSTRUCT | G_PARAM_READWRITE)));
+  g_object_class_install_property (gobject_class,
+		  		   MENU_PROP_ENTRY_INFO,
+				   g_param_spec_pointer ("entry-info",
+					   		 "Entry Info",
+							 "The entry info object used to build the menu",
+							 (G_PARAM_CONSTRUCT | G_PARAM_READWRITE)));
+
+  g_type_class_add_private (klass, sizeof (HNAppMenuItemPrivate));
+}
+
+static void
+hn_app_menu_item_init (HNAppMenuItem *menuitem)
+{
+  HNAppMenuItemPrivate *priv = HN_APP_MENU_ITEM_GET_PRIVATE (menuitem);
+
+  menuitem->priv = priv;
+
+  priv->icon_theme = gtk_icon_theme_get_default ();
+  
+  priv->show_close = FALSE;
+  priv->is_blinking = FALSE;
+  priv->thumbable = FALSE;
+  priv->pixbuf_anim = NULL;
+  priv->info = NULL;
+}
+
+GtkWidget *
+hn_app_menu_item_new (HDEntryInfo *info,
+                      gboolean     show_close,
+                      gboolean     thumbable)
+{
+  g_return_val_if_fail (info != NULL, NULL);
+  g_return_val_if_fail (HD_ENTRY_INFO_IS_VALID_TYPE (info->type), NULL);
+  
+  return g_object_new (HN_TYPE_APP_MENU_ITEM,
+                       "entry-info", info,
+                       "show-close", show_close,
+                       "thumbable", thumbable,
+                       NULL);
+}
+
+void
+hn_app_menu_item_set_entry_info (HNAppMenuItem *menuitem,
+		                 HDEntryInfo   *info)
+{
+  HNAppMenuItemPrivate *priv;
+  GdkPixbuf *pixbuf;
+  
+  g_return_if_fail (HN_IS_APP_MENU_ITEM (menuitem));
+  g_return_if_fail (info != NULL);
+
+  priv = menuitem->priv;
+
+  priv->info = info;
+  
+  pixbuf = hd_entry_info_get_icon (priv->info);
+  if (!pixbuf)
+    {
+      GError *error = NULL;
+      gint icon_size;
+
+      icon_size = priv->thumbable ? AS_ICON_THUMB_SIZE : AS_ICON_SIZE;
+
+      pixbuf = hd_entry_info_get_app_icon (priv->info,
+                                           icon_size,
+                                           &error);
+      if (error)
+        {
+          g_warning ("Could not load icon %s from theme: %s.",
+                     hd_entry_info_get_app_icon_name (priv->info),
+                     error->message);
+          
+          g_clear_error (&error);
+          pixbuf = gtk_icon_theme_load_icon (priv->icon_theme,
+                                             AS_MENU_DEFAULT_APP_ICON,
+                                             priv->thumbable ?
+                                             AS_ICON_THUMB_SIZE : AS_ICON_SIZE,
+                                             GTK_ICON_LOOKUP_NO_SVG,
+                                             &error);
+          if (error)
+            {
+              g_warning ("Could not load icon %s from theme: %s.",
+                         AS_MENU_DEFAULT_APP_ICON,
+                         error->message);
+              g_error_free (error);
+            }
+        }
+
+    }
+  
+  if (pixbuf)
+    {
+      gtk_image_set_from_pixbuf (GTK_IMAGE (priv->icon), pixbuf);
+      g_object_unref (pixbuf);
+    }
+  
+  gtk_label_set_text (GTK_LABEL (priv->label),
+		      hd_entry_info_peek_title (priv->info));
+
+  g_object_notify (G_OBJECT (menuitem), "entry-info");
+}
+
+HDEntryInfo *
+hn_app_menu_item_get_entry_info (HNAppMenuItem *menuitem)
+{
+  g_return_val_if_fail (HN_IS_APP_MENU_ITEM (menuitem), NULL);
+
+  return menuitem->priv->info;
+}
+
+void
+hn_app_menu_item_set_is_blinking (HNAppMenuItem *menuitem,
+				  gboolean       is_blinking)
+{
+  g_return_if_fail (HN_IS_APP_MENU_ITEM (menuitem));
+
+  hn_app_menu_item_icon_animation (menuitem->priv->icon, is_blinking);
+}
+
+gboolean
+hn_app_menu_item_get_is_blinking (HNAppMenuItem *menuitem)
+{
+  g_return_val_if_fail (HN_IS_APP_MENU_ITEM (menuitem), FALSE);
+
+  return menuitem->priv->is_blinking;
+}

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-menu-item.h
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-menu-item.h	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-menu-item.h	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,81 @@
+/* hn-app-menu-item.h
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/**
+ * @file hn-app-menu-item.h
+ *
+ * @brief Definitions of the application menu item used
+ *        by the Application Switcher
+ *
+ */
+
+#ifndef HN_APP_MENU_ITEM_H
+#define HN_APP_MENU_ITEM_H
+
+#include <gtk/gtkimagemenuitem.h>
+#include <libhildonwm/hd-wm.h>
+
+G_BEGIN_DECLS
+
+#define HN_TYPE_APP_MENU_ITEM		 (hn_app_menu_item_get_type ())
+#define HN_APP_MENU_ITEM(obj)		 (G_TYPE_CHECK_INSTANCE_CAST ((obj), HN_TYPE_APP_MENU_ITEM, HNAppMenuItem))
+#define HN_IS_APP_MENU_ITEM(obj)	 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HN_TYPE_APP_MENU_ITEM))
+#define HN_APP_MENU_ITEM_CLASS(klass)	 (G_TYPE_CHECK_CLASS_CAST ((klass), HN_TYPE_APP_MENU_ITEM, HNAppMenuItemClass))
+#define HN_IS_APP_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), HN_TYPE_APP_MENU_ITEM))
+#define HN_APP_MENU_ITEM_GET_CLASS(obj)	 (G_TYPE_INSTANCE_GET_CLASS ((obj), HN_TYPE_APP_MENU_ITEM, HNAppMenuItemClass))
+
+typedef struct _HNAppMenuItem        HNAppMenuItem;
+typedef struct _HNAppMenuItemPrivate HNAppMenuItemPrivate;
+typedef struct _HNAppMenuItemClass   HNAppMenuItemClass;
+
+struct _HNAppMenuItem
+{
+  GtkImageMenuItem parent_instance;
+
+  HNAppMenuItemPrivate *priv;
+};
+
+struct _HNAppMenuItemClass
+{
+  GtkImageMenuItemClass parent_class;
+
+  GdkPixbuf *close_button;
+  GdkPixbuf *thumb_close_button;
+};
+
+GType        hn_app_menu_item_get_type        (void) G_GNUC_CONST;
+
+GtkWidget *  hn_app_menu_item_new             (HDEntryInfo   *info,
+                                               gboolean       show_close,
+                                               gboolean       thumbable);
+
+void         hn_app_menu_item_set_entry_info  (HNAppMenuItem *menuitem,
+					       HDEntryInfo   *info);
+HDEntryInfo *hn_app_menu_item_get_entry_info  (HNAppMenuItem *menuitem);
+void         hn_app_menu_item_set_is_blinking (HNAppMenuItem *menuitem,
+					       gboolean       is_blinking);
+gboolean     hn_app_menu_item_get_is_blinking (HNAppMenuItem *menuitem);
+
+G_END_DECLS
+
+#endif /* HN_APP_MENU_ITEM_H */

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-sound.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-sound.c	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-sound.c	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,117 @@
+/*
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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 <unistd.h> /* close() */
+#include <glib/gtypes.h>
+#include <gconf/gconf-client.h>
+#include <esd.h>
+#include "hn-app-sound.h"
+
+#define PROGRAM_NAME        "MaemoApplicationSwitcher"
+#define ALARM_GCONF_PATH    "/apps/osso/sound/system_alert_volume"
+
+static gint
+hn_as_sound_get_volume_scale (void);
+
+gint
+hn_as_sound_init()
+{
+    gint sock;
+   
+    sock = esd_open_sound(NULL);
+
+    return sock;
+}
+
+
+gint
+hn_as_sound_register_sample (gint esd_socket, const gchar *sample)
+{
+    gint sample_id;
+    
+    sample_id = esd_file_cache (esd_socket, PROGRAM_NAME, sample);
+
+    return sample_id;
+}
+
+void
+hn_as_sound_deregister_sample (gint esd_socket, gint sample_id)
+{
+    esd_sample_free (esd_socket, sample_id);
+}
+
+
+gint
+hn_as_sound_play_sample (gint esd_socket, gint sample_id)
+{
+    gint scale = hn_as_sound_get_volume_scale ();
+
+    if (esd_set_default_sample_pan (esd_socket, sample_id, scale, scale) == -1)
+        return -1;
+    
+    if (esd_sample_play (esd_socket, sample_id) == -1)
+        return -1;
+
+    return 0;
+}
+
+
+void
+hn_as_sound_deinit (gint socket)
+{
+    close(socket);
+}
+
+static gint
+hn_as_sound_get_volume_scale ()
+{
+    GConfClient *client = NULL;
+    GError *error = NULL;
+    gint volume;
+
+    client = gconf_client_get_default();
+
+    if (!client)
+        return 0;
+
+    volume = gconf_client_get_int (client, ALARM_GCONF_PATH, &error);
+
+    g_object_unref (G_OBJECT (client));
+
+    if (error)
+    {
+        g_error_free (error);
+        return 0;
+    }
+
+
+    switch (volume)
+    {
+        case 1:
+            return 0x80;
+        case 2:
+            return 0xff;
+    }
+
+    return 0;
+}

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-sound.h
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-sound.h	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-sound.h	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,39 @@
+/*
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+
+gint
+hn_as_sound_init(void);
+
+gint
+hn_as_sound_register_sample (gint esd_socket, const gchar *sample);
+
+void
+hn_as_sound_deregister_sample (gint esd_socket, gint sample_id);
+
+gint
+hn_as_sound_play_sample (gint esd_socket, gint sample_id);
+
+void
+hn_as_sound_deinit (gint socket);
+

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-switcher.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-switcher.c	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-switcher.c	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,2295 @@
+/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+
+/* hn-app-switcher.c
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/* Hildon includes */
+#include "hn-app-switcher.h"
+#include "hn-app-button.h"
+#include "hn-app-tooltip.h"
+#include "hn-app-menu-item.h"
+#include "hn-app-sound.h"
+#include "hildon-pixbuf-anim-blinker.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* X include */
+#include <X11/keysymdef.h>
+#include <X11/keysym.h>
+#include <X11/extensions/XTest.h>
+
+/* GLib include */
+#include <glib.h>
+#include <glib/gi18n.h>
+
+/* GTK includes */
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtkmenu.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkseparatormenuitem.h>
+#include <gtk/gtkeventbox.h>
+#include <gtk/gtkalignment.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkmisc.h>
+
+#include <libosso.h>
+
+#include <hildon-widgets/gtk-infoprint.h>
+#include "hildon-pixbuf-anim-blinker.h"
+
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+
+/* GDK includes */
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+
+/* X includes */
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+/* log include */
+#include <log-functions.h>
+
+/* Menu item strings */
+#define AS_HOME_ITEM 		_("tana_fi_home")
+#define AS_HOME_ITEM_ICON 	"qgn_list_home"
+
+/* Defined workarea atom */
+#define WORKAREA_ATOM "_NET_WORKAREA"
+
+#define AS_MENU_BUTTON_ICON      "qgn_list_tasknavigator_appswitcher"
+#define AS_MENU_DEFAULT_APP_ICON "qgn_list_gene_default_app"
+
+#define ANIM_DURATION 5000 	/* 5 Secs for blinking icons */
+#define ANIM_FPS      2
+
+/* Themed widget names */
+static const gchar *as_button_names[] = {
+  "hildon-navigator-small-button1",
+  "hildon-navigator-small-button2",
+  "hildon-navigator-small-button3",
+  "hildon-navigator-small-button4",
+  
+  NULL,
+};
+
+static const gchar *as_button_pressed_names[] = {
+  "hildon-navigator-small-button1-pressed",
+  "hildon-navigator-small-button2-pressed",
+  "hildon-navigator-small-button3-pressed",
+  "hildon-navigator-small-button4-pressed",
+  NULL,
+};
+
+#define AS_MENU_BUTTON_NAME "hildon-navigator-small-button5"
+
+#define AS_UPPER_SEPARATOR "hildon-navigator-upper-separator"
+#define AS_LOWER_SEPARATOR "hildon-navigator-lower-separator"
+
+#define AS_BOX_NAME         "hildon-navigator-app-switcher"
+
+/* Hardcoded pixel perfecting values */
+#define AS_BUTTON_BORDER_WIDTH  0
+#define AS_MENU_BORDER_WIDTH    20
+#define AS_TIP_BORDER_WIDTH 	20
+#define AS_BUTTON_HEIGHT        38
+#define AS_MENU_BUTTON_HEIGHT   58
+#define AS_ROW_HEIGHT 		    30
+#define AS_ICON_SIZE            26
+#define AS_TOOLTIP_WIDTH        360
+#define AS_MENU_ITEM_WIDTH      360
+#define AS_INTERNAL_PADDING     10
+#define AS_SEPARATOR_HEIGHT     10
+
+/* Needed for catching the MCE D-BUS messages */
+#define MCE_SERVICE          "com.nokia.mce"
+#define MCE_SIGNAL_INTERFACE "com.nokia.mce.signal"
+#define MCE_SIGNAL_PATH      "/com/nokia/mce/signal"
+
+/* hardware signals */
+#define HOME_LONG_PRESS "sig_home_key_pressed_long_ind"
+#define HOME_PRESS      "sig_home_key_pressed_ind"
+
+/* lowmem signals */
+#define LOWMEM_ON_SIGNAL_INTERFACE  "com.nokia.ke_recv.lowmem_on"
+#define LOWMEM_ON_SIGNAL_PATH       "/com/nokia/ke_recv/lowmem_on"
+#define LOWMEM_ON_SIGNAL_NAME       "lowmem_on"
+
+#define LOWMEM_OFF_SIGNAL_INTERFACE "com.nokia.ke_recv.lowmem_off"
+#define LOWMEM_OFF_SIGNAL_PATH      "/com/nokia/ke_recv/lowmem_off"
+#define LOWMEM_OFF_SIGNAL_NAME      "lowmem_off"
+
+/* bgkill signals */
+#define BGKILL_ON_SIGNAL_INTERFACE  "com.nokia.ke_recv.bgkill_on"
+#define BGKILL_ON_SIGNAL_PATH       "/com/nokia/ke_recv/bgkill_on"
+#define BGKILL_ON_SIGNAL_NAME       "bgkill_on"
+
+#define BGKILL_OFF_SIGNAL_INTERFACE "com.nokia.ke_recv.bgkill_off"
+#define BGKILL_OFF_SIGNAL_PATH      "/com/nokia/ke_recv/bgkill_off"
+#define BGKILL_OFF_SIGNAL_NAME      "bgkill_off"
+
+/* sound samples */
+#define HN_WINDOW_OPEN_SOUND        DATADIR"/sounds/ui-window_open.wav"
+#define HN_WINDOW_CLOSE_SOUND       DATADIR"/sounds/ui-window_close.wav"
+
+#define TOOLTIP_SHOW_TIMEOUT 500
+#define TOOLTIP_HIDE_TIMEOUT 1500
+
+static void 
+hn_app_switcher_add_info_cb (HDWM *hdwm, HDEntryInfo *entry_info, gpointer data);
+
+static void 
+hn_app_switcher_remove_info_cb (HDWM *hdwm, HDEntryInfo *entry_info, gpointer data);
+
+static void 
+hn_app_switcher_changed_info_cb (HDWM *hdwm, HDEntryInfo *entry_info, gpointer data);
+
+static void 
+hn_app_switcher_changed_stack_cb (HDWM *hdwm, HDEntryInfo *entry_info, gpointer data);
+
+
+static void
+hn_app_image_animation (GtkWidget *icon,
+		       gboolean   is_on)
+{
+  GdkPixbuf *pixbuf;
+  GdkPixbufAnimation *pixbuf_anim;
+
+  g_return_if_fail (GTK_IS_IMAGE (icon));
+
+  if (is_on)
+    {
+      pixbuf = gtk_image_get_pixbuf (GTK_IMAGE (icon));
+      pixbuf_anim = hildon_pixbuf_anim_blinker_new (pixbuf,
+		     				    1000 / ANIM_FPS,
+						    -1,
+						    10);
+     
+      gtk_image_set_from_animation (GTK_IMAGE(icon), pixbuf_anim);
+      g_object_unref (pixbuf_anim);
+    }
+  else
+    {
+      pixbuf_anim = gtk_image_get_animation (GTK_IMAGE (icon));
+
+      /* grab static image from menu item and reset */
+      pixbuf = gdk_pixbuf_animation_get_static_image (pixbuf_anim);
+
+      gtk_image_set_from_pixbuf (GTK_IMAGE (icon), pixbuf);
+
+      /*
+       * unrefing the pixbuf here causes SIGSEGV
+       */
+      
+      /*g_object_unref (pixbuf);*/
+    }
+}
+
+static void
+refresh_app_button (HNAppSwitcher *app_switcher, HDEntryInfo *entry, gint pos);
+
+static void
+queue_refresh_buttons (HNAppSwitcher *app_switcher);
+
+/*
+ * HNAppSwitcher
+ */
+
+enum
+{
+  AS_APP1_BUTTON,
+  AS_APP2_BUTTON,
+  AS_APP3_BUTTON,
+  AS_APP4_BUTTON,
+
+  N_BUTTONS
+};
+
+enum
+{
+  ADD_INFO,
+  REMOVE_INFO,
+  CHANGED_INFO,
+  CHANGED_STACK,
+  LOWMEM,
+  BGKILL,
+
+  LAST_SIGNAL
+};
+
+#define HN_APP_SWITCHER_GET_PRIVATE(obj) \
+(G_TYPE_INSTANCE_GET_PRIVATE ((obj), HN_TYPE_APP_SWITCHER, HNAppSwitcherPrivate))
+
+struct _HNAppSwitcherPrivate
+{
+  GtkWidget *buttons[N_BUTTONS];
+  GSList *buttons_group;
+
+  GtkWidget *main_button;
+  GtkWidget *main_menu;
+  GtkWidget *main_home_item;
+  GtkWidget *active_menu_item;
+  HDEntryInfo *home_info;
+
+  GtkWidget *tooltip;
+  
+  GtkWidget *current_toggle;
+  GtkWidget *app_menu;
+
+  /* flags */
+  guint menu_icon_is_blinking : 1;
+  guint tooltip_visible : 1;
+  guint switched_to_desktop : 1;
+  guint is_dimming_on : 1;
+  guint system_inactivity : 1;
+  
+  /* pointer location */
+  guint pointer_on_button : 1;
+  guint is_thumbable : 1;
+  guint was_thumbable : 1;
+
+  guint menu_button_timeout;
+
+  GList *applications;
+  
+  GtkIconTheme *icon_theme;
+  osso_context_t *osso;
+
+  guint queue_refresh_id;
+
+  /* sound samples data */
+  gint esd_socket;
+  gint start_sample;
+  gint end_sample;
+};
+
+G_DEFINE_TYPE (HNAppSwitcher, hn_app_switcher, TASKNAVIGATOR_TYPE_ITEM);
+
+static HNAppSwitcher *singleton = NULL;
+
+static guint app_switcher_signals[LAST_SIGNAL] = { 0 };
+
+
+/* MCE signals handler: listens to HOME events  */
+static DBusHandlerResult
+mce_handler (DBusConnection *conn,
+             DBusMessage    *msg,
+             void           *data)
+{
+  HNAppSwitcher *app_switcher = HN_APP_SWITCHER (data);
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  const gchar *member;
+  
+  if (dbus_message_get_type (msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  
+  member = dbus_message_get_member (msg);
+  if (!member || member[0] == '\0')
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  
+  if (strcmp (HOME_LONG_PRESS, member) == 0 && !hd_wm_modal_windows_present())
+    {
+      if (GTK_WIDGET_IS_SENSITIVE (priv->main_button))
+        {
+          GtkToggleButton *button;
+          gboolean is_active;
+
+          /* if the AS menu is up, close it first */
+          button = GTK_TOGGLE_BUTTON (priv->main_button);
+          is_active = gtk_toggle_button_get_active (button);
+
+          if (is_active)
+            {
+              gtk_toggle_button_set_active (button, FALSE);
+              gtk_toggle_button_toggled (button);
+            }
+
+          hd_wm_toggle_desktop ();
+        }
+      
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  
+  if (strcmp (HOME_PRESS, member) == 0 && !hd_wm_modal_windows_present())
+    {
+      if (GTK_WIDGET_IS_SENSITIVE (priv->main_button))
+        {
+          GtkToggleButton *button;
+          gboolean is_active;
+
+          button = GTK_TOGGLE_BUTTON (priv->main_button);
+
+          is_active = gtk_toggle_button_get_active (button);
+          is_active = !is_active;
+          gtk_toggle_button_set_active (button, is_active);
+          gtk_toggle_button_toggled (button);
+
+          if (is_active != ((priv->main_menu) && 
+                            GTK_WIDGET_VISIBLE (priv->main_menu)))
+            {
+              /* XXX */
+              /* Something went wrong. Most probably a menu
+               * is opened, send a fake ESC key event to attempt
+               * to close it
+               */
+              KeyCode keycode;
+              KeySym  keysym;
+
+              keysym = XK_Escape;
+              keycode = XKeysymToKeycode (GDK_DISPLAY(), keysym);
+              
+              XTestFakeKeyEvent (GDK_DISPLAY(), keycode, TRUE, CurrentTime);
+              XTestFakeKeyEvent (GDK_DISPLAY(), keycode, FALSE, CurrentTime);
+              XSync(GDK_DISPLAY(), False);
+          
+              /* Try again */
+              gtk_toggle_button_toggled (button);
+            }
+        }
+
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/* LOWMEM signals handler: listen to LOWMEM_ON and LOWMEM_OFF events,
+ * and relays them through the HNAppSwitcher "lowmem" signal.
+ */
+static DBusHandlerResult
+lowmem_handler (DBusConnection *conn,
+                DBusMessage    *msg,
+                void           *data)
+{
+  HNAppSwitcher *app_switcher = HN_APP_SWITCHER (data);
+  const gchar *member;
+  
+  if (dbus_message_get_type (msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  
+  member = dbus_message_get_member (msg);
+  if (!member || member[0] == '\0')
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  if (strcmp (LOWMEM_ON_SIGNAL_NAME, member) == 0)
+    {
+      g_signal_emit (app_switcher, app_switcher_signals[LOWMEM], 0, TRUE);
+      
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  
+  if (strcmp (LOWMEM_OFF_SIGNAL_NAME, member) == 0)
+    {
+      g_signal_emit (app_switcher, app_switcher_signals[LOWMEM], 0, FALSE);
+      
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/* BGKILL signals handler: listens to the BGKILL_ON and BGKILL_OFF
+ * events, and relays them through the HNAppSwitcher "bgkill"
+ * signal
+ */
+static DBusHandlerResult
+bgkill_handler (DBusConnection *conn,
+                DBusMessage    *msg,
+                void           *data)
+{
+  HNAppSwitcher *app_switcher = HN_APP_SWITCHER (data);
+  const gchar *member;
+
+  if (dbus_message_get_type (msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  
+  member = dbus_message_get_member (msg);
+  if (!member || member[0] == '\0')
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  if (strcmp (BGKILL_ON_SIGNAL_NAME, member) == 0)
+    {
+      g_signal_emit (app_switcher, app_switcher_signals[BGKILL], 0, TRUE);
+      
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  
+  if (strcmp (BGKILL_OFF_SIGNAL_NAME, member) == 0)
+    {
+      g_signal_emit (app_switcher, app_switcher_signals[BGKILL], 0, FALSE);
+      
+      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+  
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gint
+get_app_button_pos (GtkWidget *button)
+{
+  gint pos;
+  
+  g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (button), AS_APP1_BUTTON);
+
+  pos = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
+					    "app-button-pos"));
+
+  return pos;  
+}
+
+static void
+set_app_button_pos (GtkWidget *button,
+		    gint       pos)
+{
+  g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
+  g_return_if_fail (pos >= AS_APP1_BUTTON && pos < N_BUTTONS);
+
+  g_object_set_data (G_OBJECT (button),
+		     "app-button-pos",
+		     GINT_TO_POINTER (pos));
+}
+
+static gboolean
+get_main_button_is_blinking (GtkWidget *button)
+{
+  gint is_blinking;
+
+  g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (button), FALSE);
+
+  is_blinking = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
+			  			    "button-is-blinking"));
+
+  return is_blinking;
+}
+
+static void
+set_main_button_is_blinking (GtkWidget *button,
+			     gboolean   is_blinking)
+{
+  g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
+
+  g_object_set_data (G_OBJECT (button),
+                     "button-is-blinking",
+                     GINT_TO_POINTER (is_blinking));
+}
+
+static GdkPixbuf *
+hn_app_switcher_get_icon_from_theme (HNAppSwitcher *app_switcher,
+				     const gchar   *icon_name,
+				     gint           size)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  GError *error;
+  GdkPixbuf *retval;
+
+  if (!icon_name)
+    return NULL;
+
+  if (!priv->icon_theme)
+    priv->icon_theme = gtk_icon_theme_get_default ();
+
+  g_return_val_if_fail (priv->icon_theme, NULL);
+
+  if (!icon_name || icon_name[0] == '\0')
+    return NULL;
+
+  error = NULL;
+  retval = gtk_icon_theme_load_icon (priv->icon_theme,
+		  		     icon_name,
+				     size == -1 ? AS_ICON_SIZE : size,
+				     0,
+				     &error);
+  if (error)
+    {
+      osso_log (LOG_ERR, "Could not load icon '%s': %s\n",
+		icon_name,
+		error->message);
+
+      g_error_free (error);
+
+      return NULL;
+    }
+
+  return retval;  
+}
+
+#if 0
+static GdkPixbuf *
+hn_app_switcher_get_app_icon_for_entry_info (HNAppSwitcher *app_switcher,
+                                             HDEntryInfo   *info)
+{
+  GdkPixbuf *retval;
+  const gchar *icon_name;
+ 
+  g_debug("New object does not have an icon -- using default");
+  
+  icon_name = hd_entry_info_get_app_icon_name (info);
+  if (!icon_name)
+    icon_name = AS_MENU_DEFAULT_APP_ICON;
+
+  retval = hn_app_switcher_get_icon_from_theme (app_switcher, icon_name, -1);
+
+  if(!retval)
+    {
+      /*perhaps the specified icon is missing -- try the default */
+      retval = hn_app_switcher_get_icon_from_theme (app_switcher,
+                                                    AS_MENU_DEFAULT_APP_ICON,
+                                                    -1);
+    }
+  
+  return retval;
+}
+#endif
+
+static void
+#if 0
+main_menu_detach  (HNAppSwitcher *app_switcher,
+#else
+main_menu_destroy (HNAppSwitcher *app_switcher,
+#endif
+                  GtkMenu   *menu)
+{
+  app_switcher->priv->main_menu = NULL;
+  app_switcher->priv->main_home_item = NULL;
+  app_switcher->priv->active_menu_item = NULL;
+
+  GtkToggleButton *button = GTK_TOGGLE_BUTTON(app_switcher->priv->main_button);
+  gtk_toggle_button_set_active (button, FALSE);
+}
+
+static void
+main_home_item_activate_cb (GtkMenuItem   *menuitem,
+			    HNAppSwitcher *app_switcher)
+{
+  g_debug ("Raising desktop");
+
+  hd_wm_top_desktop ();
+}
+
+static void
+main_menu_ensure_state (HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  GList *menu_children, *l;
+  GtkWidget *separator;
+  
+  /* we must dispose the old contents of the menu first */
+  menu_children = gtk_container_get_children (GTK_CONTAINER (priv->main_menu));
+  for (l = menu_children; l != NULL; l = l->next)
+    {
+      GtkWidget *child = GTK_WIDGET (l->data);
+      
+      /* remove the home menu item too, but keep a reference so
+       * that it stays around until we can re-add it later
+       */
+      if (child == priv->main_home_item)
+        g_object_ref (child);	
+      
+      gtk_container_remove (GTK_CONTAINER (priv->main_menu), child);
+    }
+  g_list_free (menu_children);
+
+  priv->active_menu_item = NULL;
+  /* rebuild the menu */
+  for (l = priv->applications; l != NULL; l = l->next)
+    {
+      GtkWidget *menu_item;
+      const GList * children = hd_entry_info_get_children(l->data);
+      const GList * child;
+
+      for(child = children; child != NULL; child = child->next)
+        {
+          HDEntryInfo *entry = child->data;
+	  
+          g_debug ("Creating new app menu item (thumb:%s) for %s",
+	          priv->is_thumbable ? "on" : "off",
+		  hd_entry_info_peek_title (entry));
+
+          menu_item = hn_app_menu_item_new (entry,
+			 		    TRUE,
+					    priv->is_thumbable);
+
+          if (hd_entry_info_is_active (entry))
+            priv->active_menu_item = menu_item;
+          
+          gtk_menu_shell_append (GTK_MENU_SHELL (priv->main_menu), menu_item);
+          gtk_widget_show (menu_item);
+        }
+
+      /* append the separator for this app*/
+      separator = gtk_separator_menu_item_new ();
+      gtk_menu_shell_append (GTK_MENU_SHELL (priv->main_menu),
+                             separator);
+      gtk_widget_show (separator);
+    }
+
+  /* append home menu item */
+  if (priv->main_home_item)
+    {
+      gtk_widget_destroy (priv->main_home_item);
+      priv->main_home_item = NULL;
+    }
+  
+  g_assert (priv->home_info); 
+  priv->main_home_item = hn_app_menu_item_new (priv->home_info,
+		                               FALSE,
+					       priv->is_thumbable);
+  g_assert (HN_IS_APP_MENU_ITEM (priv->main_home_item));
+      
+  g_signal_connect (priv->main_home_item, "activate",
+		    G_CALLBACK (main_home_item_activate_cb),
+		    app_switcher);
+      
+  gtk_menu_shell_append (GTK_MENU_SHELL (priv->main_menu),
+		 	 priv->main_home_item);
+  gtk_widget_show (priv->main_home_item);
+  
+  g_object_ref (priv->main_home_item);
+
+
+  priv->was_thumbable = priv->is_thumbable;
+  g_object_unref (priv->main_home_item);
+}
+
+static void
+main_menu_unmap_cb (GtkWidget     *menu,
+		    HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  GtkToggleButton *button = GTK_TOGGLE_BUTTON (priv->main_button);
+  
+  gtk_toggle_button_set_active (button, FALSE);
+}
+
+static gboolean
+main_menu_keypress_cb (GtkWidget * menu, GdkEventKey *event,
+                       HNAppSwitcher *app_switcher)
+{
+  g_return_val_if_fail(event && menu && app_switcher, FALSE);
+
+  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 (hd_wm_get_singleton ());
+        }
+      else
+        {
+	  g_debug ("%s: %d, hd_wm_activate (HN_TN_ACTIVATE_KEY_FOCUS);",__FILE__,__LINE__);
+	  gtk_widget_grab_focus (app_switcher->priv->main_button);
+        }
+        
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+gboolean
+hn_app_switcher_menu_button_release_cb (GtkWidget      *widget,
+                                        GdkEventButton *event)
+{
+  gint x, y;
+  
+  gtk_widget_get_pointer(widget, &x, &y);
+
+  g_debug ("Main menu button released\n"
+	  "\tpointer    [%d, %d],\n"
+          "\tallocation [%d, %d, %d, %d]",
+          x, y,
+          widget->allocation.x,
+          widget->allocation.y,
+          widget->allocation.width,
+          widget->allocation.height);
+
+#if 0
+  /* XXX - Is this really necessary? It breaks thumb menu support. EB */
+  if ((x > widget->allocation.width)  || (x < 0) ||
+      (y > widget->allocation.height) || (y < 0))
+    {
+      g_debug ("deactivating menu");
+      
+      hd_wm_activate (HN_TN_ACTIVATE_LAST_APP_WINDOW);
+      gtk_menu_shell_deactivate(GTK_MENU_SHELL(widget));
+      
+      return TRUE;
+    }
+#endif
+
+  return FALSE;
+}
+
+static void
+main_menu_build (HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  
+  if (priv->main_menu)
+    {
+      return;
+    }
+
+  priv->main_menu = gtk_menu_new ();
+  g_signal_connect (priv->main_menu, "unmap",
+		    G_CALLBACK (main_menu_unmap_cb),
+		    app_switcher);
+  g_signal_connect (priv->main_menu, "key-press-event",
+		    G_CALLBACK (main_menu_keypress_cb),
+		    app_switcher);
+  g_signal_connect (priv->main_menu, "button-release-event",
+		    G_CALLBACK (hn_app_switcher_menu_button_release_cb),
+		    NULL);
+#if 0
+  gtk_menu_attach_to_widget (GTK_MENU (priv->main_menu),
+                             GTK_WIDGET (app_switcher),
+                             (GtkMenuDetachFunc)main_menu_detach);
+#else
+  g_signal_connect_swapped (priv->main_menu, "destroy",
+                            G_CALLBACK (main_menu_destroy),
+                            app_switcher);
+#endif
+
+  main_menu_ensure_state (app_switcher);
+}
+
+static void
+main_menu_position_func (GtkMenu  *menu,
+			 gint     *x,
+			 gint     *y,
+			 gboolean *push_in,
+			 gpointer  data)
+{
+  GtkWidget *widget = GTK_WIDGET (data);
+  GdkScreen *screen = gtk_widget_get_screen (widget);
+  GtkRequisition req;
+
+  if (!GTK_WIDGET_REALIZED (widget))
+    return;
+
+  if (hd_wm_fullscreen_mode())
+    {
+      *x = 0;
+    }
+  else
+    {
+      gdk_window_get_origin (widget->window, x, y);
+      *x += widget->allocation.width;
+    }
+
+  gtk_widget_size_request (GTK_WIDGET (menu), &req);
+  *y = gdk_screen_get_height (screen) - req.height;
+  
+  *push_in = FALSE;
+}
+
+
+static void
+main_menu_pop (HNAppSwitcher *app_switcher,
+	       GtkWidget     *toggle)
+{
+  HNAppSwitcherPrivate *priv;
+  gboolean was_blinking;
+
+  g_return_if_fail (HN_IS_APP_SWITCHER (app_switcher));
+  g_return_if_fail (GTK_IS_WIDGET (toggle));
+
+  priv = app_switcher->priv;
+  
+  was_blinking = get_main_button_is_blinking (toggle);
+  if (was_blinking)
+    {
+      /* need to stop the button blinking */
+      const GList       *k, *l;
+      HDEntryInfo       *child;
+      GtkWidget         *app_image = gtk_bin_get_child (GTK_BIN (toggle));
+      int                i;
+      
+      hn_app_image_animation (app_image, FALSE);
+      set_main_button_is_blinking (toggle, FALSE);
+
+      /*
+       * set the ignore flag on any children that were causing the blinking
+       * we skip the first four apps, which cause blinking of the app buttons,
+       * not the menu button.
+       */
+      for (k = priv->applications, i = 0; k != NULL; k = k->next, ++i)
+        {
+          if (i < N_BUTTONS)
+            continue;
+          
+          for (l = hd_entry_info_get_children (k->data); l != NULL; l = l->next)
+            {
+              child = l->data;
+              if (hd_entry_info_is_urgent(child))
+                {
+                  hd_entry_info_set_ignore_urgent(child, TRUE);
+                }
+            }
+        }
+    }
+  
+  main_menu_build (app_switcher);
+
+  gtk_widget_realize (priv->main_menu);
+
+  if (priv->is_thumbable)
+    gtk_widget_set_name (gtk_widget_get_toplevel (priv->main_menu),
+                         "hildon-menu-window-thumb");
+  else
+    gtk_widget_set_name (gtk_widget_get_toplevel (priv->main_menu),
+                         "hildon-menu-window-normal");
+  
+  if (priv->active_menu_item)
+    gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->main_menu),
+                                priv->active_menu_item);
+  else
+    gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->main_menu),
+                                priv->main_home_item);
+
+
+  gtk_menu_popup (GTK_MENU (priv->main_menu), NULL, NULL,
+                  main_menu_position_func, app_switcher,
+                  0, GDK_CURRENT_TIME);
+}
+
+static gboolean
+main_menu_button_keypress_cb (GtkWidget     *toggle,
+	       		      GdkEventKey   *event,
+                              HNAppSwitcher *app_switcher)
+{
+  if (event->keyval == GDK_Right ||
+      event->keyval == GDK_KP_Right ||
+      event->keyval == GDK_KP_Enter)
+    {
+      hn_app_switcher_toggle_menu_button (app_switcher);
+      
+      return TRUE;
+    }
+  else if (event->keyval == GDK_Left ||
+           event->keyval == GDK_KP_Left)
+    {
+      g_debug ("%s: %d, hd_wm_activate (HN_TN_ACTIVATE_LAST_APP_WINDOW);",__FILE__,__LINE__);
+    }
+  
+  return FALSE;
+}
+
+static void
+main_menu_button_toggled_cb (HNAppSwitcher *app_switcher,
+			     GtkWidget     *toggle)
+{
+  HNAppSwitcherPrivate *priv;
+
+  g_return_if_fail (app_switcher);
+
+  priv = app_switcher->priv;
+
+  if (priv->menu_button_timeout)
+    {
+      g_source_remove (priv->menu_button_timeout);
+      priv->menu_button_timeout = 0;
+    }
+
+  if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
+    {
+      if (app_switcher->priv->main_menu)
+        gtk_menu_popdown (GTK_MENU (app_switcher->priv->main_menu));
+      return;
+    }
+
+  g_debug("Main menu button toggled");
+
+  g_debug ("%s: %d, hd_wm_others_open ();",__FILE__,__LINE__);
+ 
+  main_menu_pop (app_switcher, toggle);
+}
+
+static gboolean
+menu_button_release_cb (GtkWidget      *widget,
+                        GdkEventButton *event,
+                        HNAppSwitcher  *app_switcher)
+{
+/*  gint x,y;*/
+  HNAppSwitcherPrivate *priv;
+
+  g_return_val_if_fail (event && app_switcher, FALSE);
+
+  priv = app_switcher->priv;
+#if 0
+  
+  /* if the relase is outside the button, reset the thumbable flag, to avoid
+   * problems if the menu is raised some other way than via pointer
+   */
+  gtk_widget_get_pointer (widget, &x, &y);
+
+  g_debug ("Main menu button released:\n"
+          "\tpointer    [%d, %d],\n"
+          "\tallocation [%d, %d, %d, %d]",
+          x, y,
+          widget->allocation.x,
+          widget->allocation.y,
+          widget->allocation.width,
+          widget->allocation.height);
+
+  if (((x > widget->allocation.width)  || (x < 0)) ||
+      ((y > widget->allocation.height) || (y < 0)))
+    {
+      g_debug ("Release outside button -- resetting thumbable flag");
+      priv->is_thumbable = FALSE;
+
+      hd_wm_activate (HN_TN_ACTIVATE_LAST_APP_WINDOW);
+    }
+#endif
+
+  return FALSE;
+}
+
+static gboolean
+menu_button_pressed_timeout (HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+
+  g_source_remove (priv->menu_button_timeout);
+  priv->menu_button_timeout = 0;
+
+  /* If the thumbable status has changed since last time, we need to
+   * rebuild the menu :( */
+  if (priv->main_menu && priv->is_thumbable != priv->was_thumbable)
+    {
+      gtk_widget_destroy (priv->main_menu);
+      priv->main_menu = NULL;
+    }
+  
+  hn_app_switcher_toggle_menu_button (app_switcher);
+  
+  return FALSE;
+
+}
+
+static gboolean
+menu_button_pressed_cb (GtkWidget      *widget,
+                        GdkEventButton *event,
+                        HNAppSwitcher  *app_switcher)
+{
+  g_return_val_if_fail (event && app_switcher, FALSE);
+  
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+
+  /* remember which button was used to press this button */
+  g_debug("Main menu button pressed using button %d", event->button);
+
+/*  hd_wm_activate (HN_TN_DEACTIVATE_KEY_FOCUS);*/
+
+  if (event->button == APP_BUTTON_THUMBABLE || event->button == 2)
+    {
+      priv->is_thumbable = TRUE;
+    }
+  else if (!priv->menu_button_timeout)
+    priv->is_thumbable = FALSE;
+
+  if (!priv->menu_button_timeout)
+    priv->menu_button_timeout = g_timeout_add (100,
+                                               (GSourceFunc)
+                                                 menu_button_pressed_timeout,
+                                               app_switcher);
+                                               
+  
+  return TRUE;
+}
+
+static GtkWidget *
+create_menu_button (HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  GdkPixbuf *pixbuf;
+  GtkWidget *button;
+  GtkWidget *icon;
+  
+  button = gtk_toggle_button_new ();
+  
+  gtk_widget_set_name (button, AS_MENU_BUTTON_NAME);
+  gtk_widget_set_sensitive (button, FALSE);
+  gtk_widget_set_size_request (button, -1, AS_MENU_BUTTON_HEIGHT);
+
+  g_object_set (G_OBJECT (button),
+                "can-focus", TN_DEFAULT_FOCUS,
+                NULL);
+  
+  pixbuf = hn_app_switcher_get_icon_from_theme (app_switcher, AS_MENU_BUTTON_ICON, -1);
+  icon = gtk_image_new_from_pixbuf (pixbuf);
+  gtk_container_add (GTK_CONTAINER (button), icon);
+  g_object_unref (pixbuf);
+
+  g_signal_connect_swapped (button, "toggled",
+		  	    G_CALLBACK (main_menu_button_toggled_cb),
+			    app_switcher);
+
+  g_signal_connect (button, "key-press-event",
+                    G_CALLBACK (main_menu_button_keypress_cb),
+                    app_switcher);
+ 
+  g_signal_connect (button, "button-press-event",
+                    G_CALLBACK (menu_button_pressed_cb),
+                    app_switcher);
+
+  g_signal_connect (button, "button-release-event",
+                    G_CALLBACK (menu_button_release_cb),
+                    app_switcher);
+  
+  priv->main_button = button;
+  
+  return priv->main_button;
+}
+
+static void
+app_button_toggled_cb (GtkToggleButton *toggle,
+		       gpointer         user_data)
+{
+  GtkWidget *widget = GTK_WIDGET (toggle);
+  gint pos = get_app_button_pos (widget);
+  gboolean is_active = gtk_toggle_button_get_active (toggle);
+  gboolean is_inconsistent = gtk_toggle_button_get_inconsistent (toggle);
+
+  if (is_inconsistent)
+    gtk_widget_set_name (widget, as_button_names[pos]);
+  else
+    {
+      if (is_active)
+	gtk_widget_set_name (widget, as_button_pressed_names[pos]);
+      else
+	gtk_widget_set_name (widget, as_button_names[pos]);
+    }
+
+  g_debug ("setting button (pos=%d) (inconsistent='<%s>', active='<%s>') name: %s",
+	  pos,
+	  is_inconsistent ? "true" : "false",
+	  is_active ? "true" : "false",
+	  gtk_widget_get_name (widget));
+}
+
+static GtkWidget *
+create_app_button (HNAppSwitcher *app_switcher,
+	           gint           pos)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  GtkWidget *retval;
+
+  g_assert (priv->buttons[pos] == NULL);
+
+  g_debug ("Creating app button at pos %d (name %s)",
+	  pos, as_button_names[pos]);
+ 
+  retval = hn_app_button_new (NULL);
+
+  set_app_button_pos (retval, pos);
+  hn_app_button_set_is_blinking (HN_APP_BUTTON (retval), FALSE);
+
+  gtk_widget_set_name (retval, as_button_names[pos]);
+
+  g_signal_connect (retval, "toggled",
+		    G_CALLBACK (app_button_toggled_cb),
+		    NULL);
+
+  priv->buttons[pos] = retval;
+  
+  return priv->buttons[pos];
+}
+
+static void
+hn_app_switcher_build (HNAppSwitcher *app_switcher)
+{
+  GtkWidget *button;
+
+  g_assert (app_switcher);
+
+  app_switcher->hdwm = hd_wm_get_singleton ();
+
+  if (!app_switcher->priv->home_info)
+     app_switcher->priv->home_info = hd_entry_info_new (HD_ENTRY_DESKTOP);
+
+  gtk_widget_push_composite_child ();
+
+  /* inner box, used for padding */
+  g_debug ("Adding inner VBox");
+ 
+  app_switcher->vbox = GTK_VBOX (gtk_vbox_new (FALSE, 0));
+
+  gtk_widget_set_composite_name (GTK_WIDGET (app_switcher->vbox), "application-switcher-button-box");
+  
+  gtk_container_add (GTK_CONTAINER (app_switcher),GTK_WIDGET (app_switcher->vbox));
+ 
+  gtk_widget_show (GTK_WIDGET (app_switcher->vbox));
+
+  /* most recent applications buttons */
+  g_debug ("Adding buttons");
+  button = create_app_button (app_switcher, AS_APP1_BUTTON);
+  gtk_box_pack_start (GTK_BOX (app_switcher->vbox), button, TRUE, TRUE, 0);
+  gtk_widget_show (button);
+  
+  button = create_app_button (app_switcher, AS_APP2_BUTTON);
+  gtk_box_pack_start (GTK_BOX (app_switcher->vbox), button, TRUE, TRUE, 0);
+  gtk_widget_show (button);
+  
+  button = create_app_button (app_switcher, AS_APP3_BUTTON);
+  gtk_box_pack_start (GTK_BOX (app_switcher->vbox), button, TRUE, TRUE, 0);
+  gtk_widget_show (button);
+  
+  button = create_app_button (app_switcher, AS_APP4_BUTTON);
+  gtk_box_pack_start (GTK_BOX (app_switcher->vbox), button, TRUE, TRUE, 0);  
+  gtk_widget_show (button);
+  
+  /* menu button */
+  g_debug ("Adding menu button");
+  button = create_menu_button (app_switcher);
+  gtk_box_pack_start (GTK_BOX (app_switcher->vbox), button, TRUE, TRUE, 0);
+  gtk_widget_show (button);
+
+  g_signal_connect (app_switcher->hdwm,
+		    "entry_info_added",
+		    G_CALLBACK (hn_app_switcher_add_info_cb),
+		    (gpointer)app_switcher);
+
+  g_signal_connect (app_switcher->hdwm,
+		    "entry_info_removed",
+		    G_CALLBACK (hn_app_switcher_remove_info_cb),
+		    (gpointer)app_switcher);
+ 
+  g_signal_connect (app_switcher->hdwm,
+		    "entry_info_changed",
+		    G_CALLBACK (hn_app_switcher_changed_info_cb),
+		    (gpointer)app_switcher);
+ 
+  g_signal_connect (app_switcher->hdwm,
+		    "entry_info_stack_changed",
+		    G_CALLBACK (hn_app_switcher_changed_stack_cb),
+		    (gpointer)app_switcher);
+ 
+  gtk_widget_pop_composite_child ();
+}
+
+/*
+ * initializes esd cache of sound samples
+ */
+static void
+hn_app_switcher_init_sound_samples (HNAppSwitcher *app_switcher)
+{
+  /* initialize the sound samples data */
+  app_switcher->priv->esd_socket = hn_as_sound_init ();
+  app_switcher->priv->start_sample = hn_as_sound_register_sample (
+          app_switcher->priv->esd_socket,
+          HN_WINDOW_OPEN_SOUND);
+  app_switcher->priv->end_sample = hn_as_sound_register_sample (
+          app_switcher->priv->esd_socket,
+          HN_WINDOW_CLOSE_SOUND);
+}
+
+/*
+ * callback for system HW events
+ * -- currently we are only interested in the system inactivity event
+ */
+static
+void hn_app_osso_hw_cb (osso_hw_state_t *state, gpointer data)
+{
+  g_return_if_fail(state && data);
+  HNAppSwitcher *app_switcher = HN_APP_SWITCHER (data);
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+
+  if (state->system_inactivity_ind != priv->system_inactivity)
+    {
+      priv->system_inactivity = state->system_inactivity_ind;
+      queue_refresh_buttons (app_switcher);
+    }
+}
+
+static void
+hn_app_switcher_osso_initialize (HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  DBusConnection *conn = NULL;
+  DBusObjectPathVTable mce_vtable, lowmem_vtable, bgkill_vtable;
+  gboolean res;
+  osso_hw_state_t hs = {0};
+  
+  priv->osso = osso_initialize ("AS_DIMMED_infoprint", "0.1", FALSE, NULL);
+  if (!priv->osso)
+    {
+      osso_log(LOG_ERR, "Failed to initialize libOSSO");
+      
+      return;
+    }
+
+  /* register stystem inactivity handler */
+  hs.system_inactivity_ind = TRUE;
+  osso_hw_set_event_cb(priv->osso, &hs,
+                       hn_app_osso_hw_cb, app_switcher);
+  
+  
+  /* Set up the monitoring of MCE events in order to be able
+   * to top Home or open the menu
+   */
+  conn = osso_get_sys_dbus_connection (priv->osso);
+  if (!conn)
+    {
+      osso_log (LOG_ERR, "Failed getting connection to system bus");
+      
+      return;
+    }
+  
+  mce_vtable.message_function = mce_handler;
+  mce_vtable.unregister_function = NULL;
+
+#define MAKE_SIGNAL_RULE(_iface)	"type=\'signal\', interface=\'" _iface "\'"
+  
+  res = dbus_connection_register_object_path (conn, MCE_SIGNAL_PATH,
+                                              &mce_vtable,
+                                              app_switcher);
+  if (res)
+    {
+      dbus_bus_add_match (conn, MAKE_SIGNAL_RULE (MCE_SIGNAL_INTERFACE), NULL);
+      dbus_connection_flush (conn);
+    }
+  else
+    osso_log (LOG_ERR, "Failed registering MCE handler");
+
+  lowmem_vtable.message_function = lowmem_handler;
+  lowmem_vtable.unregister_function = NULL;
+  
+  res = dbus_connection_register_object_path (conn, LOWMEM_ON_SIGNAL_PATH,
+                                              &lowmem_vtable,
+                                              app_switcher);
+  if (res)
+    {
+      dbus_bus_add_match (conn, MAKE_SIGNAL_RULE (LOWMEM_ON_SIGNAL_INTERFACE), NULL);
+      dbus_connection_flush (conn);
+    }
+  else
+    osso_log(LOG_ERR, "Failed registering LOWMEM_ON handler");
+
+  res = dbus_connection_register_object_path (conn, LOWMEM_OFF_SIGNAL_PATH,
+                                              &lowmem_vtable,
+                                              app_switcher);
+  if (res)
+    {
+      dbus_bus_add_match (conn, MAKE_SIGNAL_RULE (LOWMEM_OFF_SIGNAL_INTERFACE), NULL);
+      dbus_connection_flush (conn);
+    }
+  else
+    osso_log(LOG_ERR, "Failed registering LOWMEM_OFF handler");
+
+  bgkill_vtable.message_function = bgkill_handler;
+  bgkill_vtable.unregister_function = NULL;
+  
+  res = dbus_connection_register_object_path (conn, BGKILL_ON_SIGNAL_PATH,
+                                              &bgkill_vtable,
+                                              app_switcher);
+  if (res)
+    {
+      dbus_bus_add_match (conn, MAKE_SIGNAL_RULE (BGKILL_ON_SIGNAL_INTERFACE), NULL);
+      dbus_connection_flush (conn);
+    }
+  else
+    osso_log(LOG_ERR, "Failed registering BGKILL_ON handler");
+
+  res = dbus_connection_register_object_path (conn, BGKILL_OFF_SIGNAL_PATH,
+                                              &bgkill_vtable,
+                                              app_switcher);
+  if (res)
+    {
+      dbus_bus_add_match (conn, MAKE_SIGNAL_RULE (BGKILL_OFF_SIGNAL_INTERFACE), NULL);
+      dbus_connection_flush (conn);
+    }
+  else
+    osso_log(LOG_ERR, "Failed registering BGKILL_OFF handler");
+}
+
+/* We must override the "show-all" method, as we may have stuff we don't want
+ * to show, like the main button or the application buttons
+ */
+static void
+hn_app_switcher_show_all (GtkWidget *widget)
+{
+  HNAppSwitcherPrivate *priv = HN_APP_SWITCHER (widget)->priv;
+  gint i;
+
+  gtk_widget_show (widget);
+
+  /* show the main menu button only if there is at least
+   * one application on the switcher
+   */
+  if (priv->applications)
+    gtk_widget_show (priv->main_button);
+
+  /* show only the buttons linked to an application */
+  for (i = AS_APP1_BUTTON; i < N_BUTTONS; i++)
+    {
+      GtkWidget *button = priv->buttons[i];
+      HDEntryInfo *info;
+     
+      info = hn_app_button_get_entry_info (HN_APP_BUTTON (button));
+      if (info)
+        gtk_widget_show (button);
+    }
+}
+
+static HDEntryInfo *
+hn_app_switcher_find_app_for_child(HNAppSwitcher *app_switcher,
+                                   HDEntryInfo *entry_info)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  GList * l = priv->applications;
+  HDWMWatchableApp *app = hd_entry_info_get_app(entry_info);
+        
+  while(l)
+    {
+      HDEntryInfo *e = (HDEntryInfo *)l->data;
+            
+      if(app == hd_entry_info_get_app(e))
+          return e;
+      l = g_list_next(l);
+    }
+
+  return NULL;
+}
+
+static void
+remove_entry_from_app_button (HNAppSwitcher *app_switcher,
+			      HDEntryInfo   *entry_info)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  gint pos;
+
+  for (pos = AS_APP1_BUTTON; pos < N_BUTTONS; pos++)
+    {
+      GtkWidget *button = priv->buttons[pos];
+      HDEntryInfo *e;
+
+      e = hn_app_button_get_entry_info (HN_APP_BUTTON (button));
+      if (e == entry_info)
+        {
+          hn_app_button_set_group (HN_APP_BUTTON (button), NULL);
+          hn_app_button_set_entry_info (HN_APP_BUTTON (button), NULL);
+          priv->buttons_group = hn_app_button_get_group (HN_APP_BUTTON (button));
+	  break;
+	}
+    }
+}
+
+static void
+refresh_app_button (HNAppSwitcher *app_switcher,
+                    HDEntryInfo   *entry,
+                    gint           pos)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  const GList          *l, *children = hd_entry_info_get_children(entry);
+  gboolean              urgent = FALSE;
+  HNAppButton          *app_button = HN_APP_BUTTON (priv->buttons[pos]);
+  
+  /* deal with urgency flags */
+  for (l = children; l != NULL; l = l->next)
+    {
+      /*
+       * If the entry is urgent and the ignore flag is not set, the button
+       * should blink
+       */
+      if (hd_entry_info_is_urgent(l->data) &&
+          !hd_entry_info_get_ignore_urgent(l->data))
+        {
+          g_debug("Found an urgent button");
+          urgent = TRUE;
+        }
+
+      /*
+       * if the info is not urgent, we need to clear any leftover
+       * ignore_urgent flag
+       */
+      if(!hd_entry_info_is_urgent(l->data) &&
+         hd_entry_info_get_ignore_urgent(l->data))
+        {
+          hd_entry_info_set_ignore_urgent(l->data, FALSE);
+        }
+    }
+
+  /* bind the entry info to the widget, so that we can
+   * use it later when the user toggles the button
+   *
+   * NB: this recreates the icon respecting system inactivity
+   */
+  hn_app_button_set_entry_info (app_button, entry);
+  hn_app_button_set_group (app_button, priv->buttons_group);
+  priv->buttons_group = hn_app_button_get_group (app_button);
+
+  g_debug ("buttons_group.size := %d",
+	  g_slist_length (priv->buttons_group));
+
+  hn_app_button_set_is_blinking (app_button, urgent);
+
+  if (hd_entry_info_is_active (entry))
+    {
+      GtkToggleButton *button;
+
+      button = GTK_TOGGLE_BUTTON (priv->buttons[pos]);
+      gtk_toggle_button_set_inconsistent (button, FALSE);
+      gtk_toggle_button_set_active (button, TRUE);
+      gtk_toggle_button_toggled (button);
+    }
+  
+  gtk_widget_set_sensitive (priv->buttons[pos], TRUE);
+  g_object_set (G_OBJECT (priv->buttons[pos]),
+                "can-focus", TRUE,
+                NULL);
+}
+
+static gboolean
+refresh_buttons (gpointer user_data)
+{
+  HNAppSwitcher        *app_switcher = HN_APP_SWITCHER (user_data);
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  GList                *l;
+  gint                  pos;
+  gint                  active_button = -1;
+  gboolean              was_blinking;
+  gboolean              is_urgent = FALSE;
+  GtkWidget            *app_image;
+
+  /* first we reset all the buttons icons */
+  for (pos = AS_APP1_BUTTON; pos < N_BUTTONS; pos++)
+    {
+      HNAppButton *button = HN_APP_BUTTON (priv->buttons[pos]);
+
+      hn_app_button_set_group (button, NULL);
+      hn_app_button_set_entry_info (button, NULL);
+    }
+
+  priv->buttons_group = NULL;
+
+  /* then refresh the icons of the application buttons */
+  for (l = priv->applications, pos = AS_APP1_BUTTON;
+       l != NULL && pos < N_BUTTONS;
+       l = l->next, pos++)
+    {
+      HDEntryInfo *entry = l->data;
+
+      /* we just want the most recently used top-level applications */
+      if (entry->type != HD_ENTRY_WATCHED_APP)
+        {
+          g_debug("Object is not an application");
+          continue;
+        }
+
+      if (active_button < 0 &&
+          gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->buttons[pos]))&&
+          !hd_entry_info_is_active (entry))
+        active_button = pos;
+      
+      refresh_app_button (app_switcher, entry, pos);
+
+      g_debug ("Showing object");
+    }
+
+  if (active_button >= 0)
+    {
+      GtkToggleButton *button;
+      
+          g_debug ("Unsetting the previously active button %d",
+                  active_button);
+
+          button = GTK_TOGGLE_BUTTON (priv->buttons[active_button]);
+          gtk_toggle_button_set_inconsistent (button, TRUE);
+          gtk_toggle_button_set_active (button, FALSE);
+          gtk_toggle_button_toggled (button);
+    }
+  
+  /*
+   * now we need to process the menu button to ensure that it blinks
+   * if needed
+   */
+  was_blinking = get_main_button_is_blinking (priv->main_button);
+
+  is_urgent = FALSE;
+  for (l = priv->applications, pos = 0;
+       l != NULL;
+       l = l->next, ++pos)
+    {
+      const GList       *k;
+      HDEntryInfo       *child;
+
+      if (pos < N_BUTTONS)
+        continue;
+
+      /* set the ignore flag on any children that were causing the blinking
+       * we skip the first four apps, which cause blinking of the app buttons,
+       * not the menu button.
+       */
+      for (k = hd_entry_info_get_children (l->data); k != NULL; k = k->next)
+        {
+          child = k->data;
+
+          /* If the entry is urgent and the ignore flag is not set, the
+           * button should blink
+           */
+          if(hd_entry_info_is_urgent(child) &&
+             !hd_entry_info_get_ignore_urgent(child))
+            {
+              is_urgent = TRUE;
+            }
+
+          /*
+           * if the info is not urgent, we need to clear any leftover
+           * ignore_urgent flag
+           */
+          if(!hd_entry_info_is_urgent(child) &&
+             hd_entry_info_get_ignore_urgent(child))
+            {
+              hd_entry_info_set_ignore_urgent(child, FALSE);
+            }
+        }
+    }
+
+  app_image = gtk_bin_get_child (GTK_BIN (priv->main_button));
+  g_assert (GTK_IS_IMAGE (app_image));
+
+  if (is_urgent && !was_blinking && !priv->system_inactivity)
+    {
+      g_debug("Setting menu button urgency to '%s'",
+             is_urgent ? "true" : "false");
+      hn_app_image_animation (app_image, is_urgent);
+      set_main_button_is_blinking (priv->main_button, is_urgent);
+    }
+  else if (was_blinking && priv->system_inactivity)
+    {
+      g_debug("Setting menu button urgency to 'false'", FALSE);
+
+      hn_app_image_animation (app_image, FALSE);
+      set_main_button_is_blinking (priv->main_button, FALSE);
+    }
+
+  /* hide the main button if no more applications are left */
+  if (!priv->applications)
+    {
+      g_debug ("Hiding main button icon");
+
+      gtk_widget_hide (app_image);
+      gtk_widget_set_sensitive (priv->main_button, FALSE);
+    }
+  else
+    {
+      g_debug ("Showing main button icon");
+
+      gtk_widget_set_sensitive (priv->main_button, TRUE);
+      gtk_widget_show (app_image);
+    }
+
+  return FALSE;
+}
+
+static void
+queue_refresh_done (gpointer user_data)
+{
+  HNAppSwitcher *app_switcher = HN_APP_SWITCHER (user_data);
+
+  app_switcher->priv->queue_refresh_id = 0;
+}
+
+static void
+queue_refresh_buttons (HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+
+  if (!priv->queue_refresh_id)
+    priv->queue_refresh_id = g_timeout_add_full (G_PRIORITY_HIGH,
+		    				 200,
+		                                 refresh_buttons,
+					         app_switcher,
+					         queue_refresh_done);
+}
+
+/* Class closure for the "add" signal; this is called each time an
+ * entry has been added to the applications list.
+ */
+static void
+hn_app_switcher_add_info_cb (HDWM *hdwm, HDEntryInfo *entry_info, gpointer data)
+{
+  HNAppSwitcher *app_switcher = HN_APP_SWITCHER (data);
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  HDWMWatchableApp *app;
+  HDEntryInfo * e;
+  
+  g_debug ("In hn_app_switcher_real_add_info");
+
+  if (!entry_info)
+    {
+      g_warning ("No entry info provided!");
+
+      return;
+    }
+
+  switch(entry_info->type)
+    {
+      case HD_ENTRY_WATCHED_WINDOW:
+      case HD_ENTRY_WATCHED_VIEW:
+        /*
+         * because initial windows get created before we have a chance to add
+         * the application item, we have to store orphan windows in temporary
+         * list and process them when the application item is added
+         */
+	g_debug ("Adding new child to AS ...");
+        app = hd_entry_info_get_app (entry_info);
+        e = hn_app_switcher_find_app_for_child (app_switcher, entry_info);
+
+        if (!e)
+          {
+            e = hd_wm_watchable_app_get_info(app);
+            if(!e)
+              {
+                g_warning ("Could not create HDEntryInfo for app.");
+                return;
+              }
+            
+            priv->applications = g_list_prepend (priv->applications, e);
+            
+          }
+        
+        hd_entry_info_add_child (e, entry_info);
+        break;
+      case HD_ENTRY_WATCHED_APP:
+        /* we handle adding of applications internally in AS */
+        g_warning("asked to append HD_ENTRY_WATCHED_APP "
+                  "-- this should not happen");
+        return;
+      default:
+        g_warning("Unknown info type");
+        return;
+    }
+
+  /* Play a sound */
+  if (hn_as_sound_play_sample (priv->esd_socket, priv->start_sample) == -1)
+    {
+      /* Connection to esd was probably closed */
+      hn_as_sound_deinit (priv->esd_socket);
+      hn_app_switcher_init_sound_samples (app_switcher);
+      hn_as_sound_play_sample (priv->esd_socket, priv->start_sample);
+    }
+
+  queue_refresh_buttons (app_switcher);
+  
+  /* we must explicitely ensure that the main menu is rebuilt,
+   * now that the application list is tainted by a new entry;
+   * destroying the menu widget will call the detach function
+   */
+  if (priv->main_menu)
+    {
+      gtk_widget_destroy (priv->main_menu);
+      priv->main_menu = NULL;
+    }
+}
+
+/* Class closure for the "remove" signal; this is called each time
+ * an entry has been remove from the application list
+ */
+static void
+hn_app_switcher_remove_info_cb (HDWM *hdwm, HDEntryInfo *entry_info, gpointer data)
+{
+  HNAppSwitcher *app_switcher = HN_APP_SWITCHER (data);
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  HDEntryInfo * info_parent = NULL;
+  gboolean removed_app = FALSE;
+  
+  switch (entry_info->type)
+    {
+      case HD_ENTRY_WATCHED_WINDOW:
+      case HD_ENTRY_WATCHED_VIEW:
+        g_debug ("removing child from AS ...");
+        info_parent = hd_entry_info_get_parent(entry_info);
+
+        if(!info_parent)
+          {
+            g_warning("An orphan HDEntryInfo !!!");
+            return;
+          }
+
+        if(!hd_entry_info_remove_child(info_parent, entry_info))
+          {
+            g_debug ("... no more children, removing app.");
+            priv->applications = g_list_remove (priv->applications,
+                                                info_parent);
+            removed_app = TRUE;
+          }
+        
+        break;
+      case HD_ENTRY_WATCHED_APP:
+        /* we handle adding/removing of applications internally in AS */
+        g_warning("asked to remove HD_ENTRY_WATCHED_APP "
+                  "-- this should not happen");
+        return;
+      default:
+        g_warning("Unknown info type");
+        return;
+    }
+  
+  /* Play a sound */
+  if (hn_as_sound_play_sample (priv->esd_socket, priv->end_sample) == -1)
+    {
+      /* Connection to esd was probably closed */
+      hn_as_sound_deinit (priv->esd_socket);
+      hn_app_switcher_init_sound_samples (app_switcher);
+      hn_as_sound_play_sample (priv->esd_socket, priv->start_sample);
+    }
+  
+  if (removed_app)
+    remove_entry_from_app_button (app_switcher, info_parent);
+
+  queue_refresh_buttons (app_switcher);
+
+  /* we must explicitely ensure that the main menu is rebuilt,
+   * now that the application list is tainted by delete entry;
+   * destroying the menu widget will call the detach function
+   */
+  if (priv->main_menu)
+    {
+      gtk_widget_destroy (priv->main_menu);
+      priv->main_menu = NULL;
+    }
+
+  if (removed_app)
+    {
+      /* we need to check that not all of the remaining apps are
+       * hibernating, and if they are, wake one of them up, because
+       * we will not receive current window msg from MB
+       */
+      GList * l;
+      gboolean all_asleep = TRUE;
+      
+      for(l = priv->applications; l != NULL; l = l->next)
+        {
+          HDEntryInfo * entry = l->data;
+          HDWMWatchableApp * app = hd_entry_info_get_app (entry);
+
+          if (app && !hd_wm_watchable_app_is_hibernating (app))
+            {
+              all_asleep = FALSE;
+              break;
+            }
+        }
+
+      if(all_asleep && priv->applications)
+        {
+          /*
+           * Unfortunately, we do not know which application is the
+           * most recently used one, so we just wake up the first one
+           */
+          hd_wm_top_item((HDEntryInfo*)priv->applications->data);
+        }
+    }
+}
+
+
+/* Class closure for the "changed" signal; this is called each time
+ * an entry inside the applications list has been changed by the WM,
+ * for instance when the icon or the title changes.
+ *
+ * NB: entry_info can be NULL for global update
+ */
+static void
+hn_app_switcher_changed_info_cb (HDWM *hdwm, HDEntryInfo *entry_info, gpointer data)
+{
+  HNAppSwitcher        *app_switcher = HN_APP_SWITCHER (data);
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  g_debug ("In hn_app_switcher_real_changed_info");
+
+  /* all changes have potential impact on the the main menu; app menus are
+   * created on the fly, so we do not have to worry about menu changes there
+   */
+  if (priv->main_menu)
+    {
+      gtk_widget_destroy (priv->main_menu);
+      priv->main_menu = NULL;
+    }
+
+  /*
+   * If we are given an entry info and it of the app type, we just need to
+   * update at most one button
+   */
+  if(entry_info && entry_info->type == HD_ENTRY_WATCHED_APP)
+    {
+      gint                  pos;
+      GList *               l;
+
+      g_debug ("HDEntryInfo present and of type WATCHED_APP");
+      
+      for (l = priv->applications, pos = AS_APP1_BUTTON;
+           l != NULL && pos < N_BUTTONS;
+           l = l->next, pos++)
+        {
+          HDEntryInfo *entry = l->data;
+          
+          if (entry->type != HD_ENTRY_WATCHED_APP)
+            {
+              g_debug("Object is not an application");
+              continue;
+            }
+
+          if (entry_info == entry)
+            {
+              refresh_app_button (app_switcher, entry_info, pos);
+              return;
+            }
+        }
+      
+      return;
+    }
+  else 
+  if (entry_info)
+    {
+      /* this is a change in a child entry; in addition to the main menu impact
+       * we already dealt with, child changes affect the blinking of the
+       * associated buttons
+       */
+      HDEntryInfo *parent;
+      GtkWidget   *button = NULL;
+      GList       *l;
+      gint         pos;
+      
+      g_return_if_fail (entry_info->type != HD_ENTRY_INVALID);
+      g_debug ("HDEntryInfo present, with child entry");
+
+      parent = hd_entry_info_get_parent (entry_info);
+
+      if (!parent)
+        {
+          g_critical ("Attempting to change orphan child item.");
+          return;
+        }
+
+      /* if the info is urgent but has an ignore flag, we know that it was not
+       * causing the button to blink, and will not cause the button to blink,
+       * so we do not need to update the buttons
+       */
+      if (hd_entry_info_is_urgent (entry_info) &&
+          hd_entry_info_get_ignore_urgent (entry_info))
+        {
+          return;
+        }
+
+      /* if the info is not urgent, but has the ignore flag set, we have to
+       * clear that flag; we also know that it was not causing the associated
+       * button to blink (it was being ingored), so we do not need to update
+       * the button.
+       */
+      if (!hd_entry_info_is_urgent (entry_info) &&
+          hd_entry_info_get_ignore_urgent (entry_info))
+        {
+          hd_entry_info_set_ignore_urgent (entry_info, FALSE);
+          return;
+        }
+
+      /* the remaining cases are more complicated and require that we know
+       * the state of the associated button
+       */
+      for (l = priv->applications, pos = AS_APP1_BUTTON;
+           l != NULL && pos < N_BUTTONS;
+           l = l->next, pos++)
+        {
+          HDEntryInfo *entry = l->data;
+      
+          /* we just want the most recently used top-level applications */
+          if (entry->type != HD_ENTRY_WATCHED_APP)
+            {
+              g_debug("Object is not an application");
+              continue;
+            }
+
+          if (entry == parent)
+            {
+              button = priv->buttons[pos];
+              break;
+            }
+        }
+
+      if (button)
+        {
+          g_debug ("Force the button's icon to update itself");
+          hn_app_button_force_update_icon (HN_APP_BUTTON (button));
+        }
+
+      HN_MARK();
+
+      /* if the current info is urgent and not to be ignored, we know the app
+       * button should blink; make sure it does
+       */
+      if (hd_entry_info_is_urgent (entry_info) &&
+          !hd_entry_info_get_ignore_urgent (entry_info))
+        {
+          if (button)
+            {
+              /* child of one of the app buttons */
+              if (!hn_app_button_get_is_blinking (HN_APP_BUTTON (button)))
+                hn_app_button_set_is_blinking (HN_APP_BUTTON (button), TRUE);
+              
+            }
+          else
+            {
+              /* the child belongs to the main menu button */
+              if (!get_main_button_is_blinking (priv->main_button))
+                {
+                  GtkWidget *app_image =
+                    gtk_bin_get_child (GTK_BIN (priv->main_button));
+
+                  g_debug("Setting menu button urgency to %d", TRUE);
+                  
+                  hn_app_image_animation (app_image, TRUE);
+                  set_main_button_is_blinking (priv->main_button, TRUE);
+                }
+            }
+          
+          return;
+        }
+
+      /* if the info is not urgent and it is not being ignored and the
+       * associated button is not blinking, we know that it was not urgent
+       * previously, so we do not need to update the button
+       */
+      if (!hd_entry_info_is_urgent (entry_info) &&
+          !hd_entry_info_get_ignore_urgent (entry_info))
+        {
+          if((button && !hn_app_button_get_is_blinking (HN_APP_BUTTON (button))) ||
+             (!button && !get_main_button_is_blinking (priv->main_button)))
+            {
+              return;
+            }
+        }
+      
+      HN_MARK();
+
+      /* we are left with the case where the info is not urgent, is not being
+       * ignored, and the associated button is blinking -- this is probably a
+       * change urgent -> !urgent, but we do not know if this button was the
+       * sole cause of the blinking, so we have to update the whole shebang
+       * -- fall through to refresh_buttons();
+       */
+    }
+  
+  g_debug("Queuing a refresh cycle of the buttons (info: %s)",
+         entry_info != NULL ? "yes" : "no");
+
+  /* either global update (no entry_info) or a more complicated case that
+   * was not handled above
+   */
+  queue_refresh_buttons (app_switcher);
+}
+
+
+/* Class closure for the "changed-stack" signal; this is called each time
+ * The TN receives notification from MB about window/view being topped. 
+ */
+static void
+hn_app_switcher_changed_stack_cb (HDWM *hdwm, HDEntryInfo *entry_info, gpointer data)
+{
+  HNAppSwitcher	       * app_switcher = HN_APP_SWITCHER (data);
+  gint                   pos, active_pos;
+  GList                * l;
+  HNAppSwitcherPrivate * priv = app_switcher->priv;
+  HDEntryInfo          * parent;
+  gboolean               active_found = FALSE;
+  
+  g_debug ("In hn_app_switcher_real_changed_stack");
+
+  if (priv->main_menu)
+    {
+      gtk_widget_destroy (priv->main_menu);
+      priv->main_menu = NULL;
+    }
+  
+  if (!entry_info || !hd_entry_info_is_active (entry_info))
+    {
+      /* rebuild everything, since we were not told what has been topped
+       * issue warning, as this is not how this function is meant to be
+       * called
+       */
+      g_warning ("No entry_info provided");
+      
+      queue_refresh_buttons (app_switcher);
+      return;
+    }
+  
+
+  if(entry_info->type == HD_ENTRY_WATCHED_APP)
+    {
+      /* we only accept entries for windows and views */
+      g_warning ("Cannot handle HD_ENTRY_WATCHED_APP");
+      return;
+    }
+  
+
+  if(entry_info->type != HD_ENTRY_DESKTOP)  
+    {
+      parent = hd_entry_info_get_parent (entry_info);
+     
+      if (!parent)
+	{
+	  g_warning ("Orphan entry info");
+	  return;
+	} 
+
+      /* locate the associated button, and toggle it */
+      active_pos = 0;
+      for (l = priv->applications, pos = AS_APP1_BUTTON;
+           l != NULL && pos < N_BUTTONS;
+           l = l->next, pos++)
+        {
+          HDEntryInfo *entry = l->data;
+          
+          if (parent == entry)
+	    {
+	      hn_app_button_make_active (HN_APP_BUTTON(priv->buttons[pos]));
+	      active_pos = pos;
+	      active_found = TRUE;
+	      break;
+	    }
+        }
+    }
+
+  /* no active window in the buttons, make sure that
+   * no button is active
+   */  
+  if (!active_found)
+    {
+      for (pos = AS_APP1_BUTTON; pos < N_BUTTONS; pos++)
+        {
+          GtkToggleButton *app_button;
+
+	  g_debug ("Setting inconsistent state for pos %d", pos);
+
+	  app_button = GTK_TOGGLE_BUTTON (priv->buttons[pos]);
+	  gtk_toggle_button_set_inconsistent (app_button, TRUE);
+	  gtk_toggle_button_set_active (app_button, FALSE);
+	  gtk_toggle_button_toggled (app_button);
+	}
+    }  
+  
+
+  /* we do not worry about the urgency hint here, as we will receive a
+   * notification when it is cleared from the WM
+   */
+}
+
+static void
+hn_app_switcher_real_bgkill (HNAppSwitcher *app_switcher,
+			     gboolean       is_on)
+{
+  hn_app_switcher_changed_info_cb (app_switcher->hdwm, NULL, (gpointer)app_switcher);  
+}
+
+static void
+hn_app_switcher_real_lowmem (HNAppSwitcher *app_switcher,
+                             gboolean       is_on)
+{
+  HNAppSwitcherPrivate *priv = app_switcher->priv;
+  priv->is_dimming_on = is_on;
+
+  g_debug ("Received lowmem signal");
+
+  /* TODO - update the sensitivity of the items depending on the
+   * lowmem state
+   */
+  hn_app_switcher_changed_info_cb (app_switcher->hdwm, NULL, (gpointer)app_switcher);  
+}
+
+static void
+hn_app_switcher_finalize (GObject *gobject)
+{
+  HNAppSwitcher *app_switch = HN_APP_SWITCHER (gobject);
+  HNAppSwitcherPrivate *priv = app_switch->priv;
+  
+  osso_deinitialize (priv->osso);
+
+  g_list_free (priv->applications);
+
+  if (priv->home_info)
+    hd_entry_info_free (priv->home_info);
+
+  g_debug ("Destroying HNAppSwitcher");
+
+  G_OBJECT_CLASS (hn_app_switcher_parent_class)->finalize (gobject);
+}
+
+static GObject *
+hn_app_switcher_constructor (GType                  type,
+			     guint                  n_construct_params,
+			     GObjectConstructParam *construct_params)
+{
+  GObject *object;
+
+  g_debug ("inside hn_app_switcher_constructor...");
+
+  if (!singleton)
+    {
+      object = G_OBJECT_CLASS (hn_app_switcher_parent_class)->constructor (type,
+		      n_construct_params, construct_params);
+      singleton = HN_APP_SWITCHER (object);
+  
+      g_debug ("building HNAppSwitcher widget");
+      
+      hn_app_switcher_build (singleton);
+      hn_app_switcher_osso_initialize (singleton);
+    }
+  else
+    {
+      /* is this even possible? anyway, let's play along. EB */
+      osso_log (LOG_WARNING, "Requested another HNAppSwitcher instance");
+      
+      object = g_object_ref (singleton);
+    }
+  
+  return object;
+}
+
+static void
+hn_app_switcher_class_init (HNAppSwitcherClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  
+  gobject_class->finalize = hn_app_switcher_finalize;
+  gobject_class->constructor = hn_app_switcher_constructor;
+
+  widget_class->show_all = hn_app_switcher_show_all;
+ 
+  klass->bgkill = hn_app_switcher_real_bgkill; 
+  klass->lowmem = hn_app_switcher_real_lowmem;
+  
+  g_type_class_add_private (klass, sizeof (HNAppSwitcherPrivate));
+}
+
+static void
+hn_app_switcher_init (HNAppSwitcher *app_switcher)
+{
+  app_switcher->priv = HN_APP_SWITCHER_GET_PRIVATE (app_switcher);
+  
+  app_switcher->priv->buttons_group = NULL;
+
+  gtk_widget_set_name (GTK_WIDGET (app_switcher), AS_BOX_NAME);
+  
+  /* set base properties of the app-switcher widget */
+  
+  gtk_container_set_border_width (GTK_CONTAINER (app_switcher->vbox), 0);
+
+  hn_app_switcher_init_sound_samples (app_switcher);
+}
+
+
+/* Public API */
+
+GtkWidget *
+hn_app_switcher_new (void)
+{
+  return g_object_new (HN_TYPE_APP_SWITCHER, NULL);
+}
+
+GList *
+hn_app_switcher_get_entries (HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv;
+  GList *retlist, *l;
+
+  g_return_val_if_fail (HN_IS_APP_SWITCHER (app_switcher), NULL);
+  priv = app_switcher->priv;
+
+  retlist = NULL;
+  for (l = priv->applications; l != NULL; l = l->next)
+    retlist = g_list_prepend (retlist, l->data);
+
+  return g_list_reverse (retlist);
+}
+
+void
+hn_app_switcher_foreach_entry (HNAppSwitcher            *app_switcher,
+			       HNAppSwitcherForeachFunc  func,
+			       gpointer                  data)
+{
+  HNAppSwitcherPrivate *priv;
+  GList *entries, *l;
+
+  g_return_if_fail (HN_IS_APP_SWITCHER (app_switcher));
+  g_return_if_fail (func != NULL);
+
+  priv = app_switcher->priv;
+  entries = priv->applications;
+
+  for (l = entries; l != NULL; l = l->next)
+    {
+      HDEntryInfo *info = l->data;
+
+      if (!(* func) (info, data))
+	break;
+    }
+}
+
+
+void
+hn_app_switcher_toggle_menu_button (HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv;
+
+  g_return_if_fail (HN_IS_APP_SWITCHER (app_switcher));
+  priv = app_switcher->priv;
+
+  if (!priv->applications)
+    return;
+  
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->main_button), TRUE);
+  g_signal_emit_by_name (priv->main_button, "toggled");
+}
+
+gboolean
+hn_app_switcher_get_system_inactivity (HNAppSwitcher *app_switcher)
+{
+  HNAppSwitcherPrivate *priv;
+
+  g_return_val_if_fail (HN_IS_APP_SWITCHER (app_switcher), FALSE);
+  priv = app_switcher->priv;
+
+  return priv->system_inactivity;
+}
+
+HDEntryInfo *
+hn_app_switcher_get_home_entry_info (HNAppSwitcher *app_switcher)
+{
+  g_return_val_if_fail (app_switcher, NULL);
+
+  return app_switcher->priv->home_info;
+}
+

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-switcher.h
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-switcher.h	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-switcher.h	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,98 @@
+/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+
+/* hn-app-switcher.h
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/**
+ * @file hn-app-switcher.h
+ *
+ * @brief Definitions of Application Switcher
+ *
+ */
+
+#ifndef __HN_APP_SWITCHER_H__
+#define __HN_APP_SWITCHER_H__
+
+#include <libhildondesktop/libhildondesktop.h>
+#include <libhildonwm/hd-wm.h>
+
+G_BEGIN_DECLS
+
+#define HN_TYPE_APP_SWITCHER            (hn_app_switcher_get_type ())
+#define HN_APP_SWITCHER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), HN_TYPE_APP_SWITCHER, HNAppSwitcher))
+#define HN_IS_APP_SWITCHER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HN_TYPE_APP_SWITCHER))
+#define HN_APP_SWITCHER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), HN_TYPE_APP_SWITCHER, HNAppSwitcherClass))
+#define HN_IS_APP_SWITCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), HN_TYPE_APP_SWITCHER))
+#define HN_APP_SWITCHER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), HN_TYPE_APP_SWITCHER, HNAppSwitcherClass))
+
+typedef struct _HNAppSwitcher HNAppSwitcher;
+typedef struct _HNAppSwitcherPrivate HNAppSwitcherPrivate;
+typedef struct _HNAppSwitcherClass   HNAppSwitcherClass;
+
+typedef gboolean (*HNAppSwitcherForeachFunc) (HDEntryInfo *info,
+					      gpointer     data);
+
+struct _HNAppSwitcher
+{
+  TaskNavigatorItem parent_instance;
+
+  GtkVBox *vbox;
+
+  HDWM *hdwm;
+  
+  HNAppSwitcherPrivate *priv;
+};
+
+struct _HNAppSwitcherClass
+{
+  TaskNavigatorItemClass parent_class;
+  
+   
+  /* relay signals from the bus */
+  void (*lowmem)   (HNAppSwitcher *app_switcher,
+                    gboolean       is_active);
+  void (*bgkill)   (HNAppSwitcher *app_switcher,
+                    gboolean       is_active);
+};
+
+GType      hn_app_switcher_get_type      (void) G_GNUC_CONST;
+
+GtkWidget *hn_app_switcher_new           (void);
+
+GList *    hn_app_switcher_get_entries   (HNAppSwitcher            *app_switcher);
+void       hn_app_switcher_foreach_entry (HNAppSwitcher            *app_switcher,
+					  HNAppSwitcherForeachFunc  func,
+					  gpointer                  data);
+
+void       hn_app_switcher_toggle_menu_button (HNAppSwitcher *app_switcher);
+
+gboolean   hn_app_switcher_get_system_inactivity (HNAppSwitcher *app_switcher);
+
+gboolean   hn_app_switcher_menu_button_release_cb (GtkWidget      *widget,
+                                                 GdkEventButton *event);
+
+HDEntryInfo * hn_app_switcher_get_home_entry_info (HNAppSwitcher *as);
+
+G_END_DECLS
+
+#endif/*__HN_APP_SWITCHER_H__*/

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-tooltip.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-tooltip.c	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-tooltip.c	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,420 @@
+/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+
+/* hn-app-tooltip.c
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/* Hildon includes */
+#include "hn-app-tooltip.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* GLib include */
+#include <glib.h>
+#include <glib/gi18n.h>
+
+/* GTK includes */
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtkeventbox.h>
+#include <gtk/gtkalignment.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkmisc.h>
+#include <gtk/gtkwindow.h>
+
+#include <libosso.h>
+
+#include <hildon-widgets/gtk-infoprint.h>
+#include "hildon-pixbuf-anim-blinker.h"
+
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+
+/* GDK includes */
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+
+/* X includes */
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+/* log include */
+#include <log-functions.h>
+
+#define TOOLTIP_NAME "hildon-task-navigator-tooltip"
+
+#define TOOLTIP_WIDTH 360
+#define TOOLTIP_BORDER_WIDTH 20
+
+#define TOOLTIP_SHOW_TIMEOUT 500
+#define TOOLTIP_HIDE_TIMEOUT 1500
+
+
+/*
+ * HNAppTooltip
+ */
+
+enum
+{
+  TIP_PROP_0,
+  
+  TIP_PROP_WIDGET,
+  TIP_PROP_TEXT
+};
+
+#define HN_APP_TOOLTIP_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), HN_TYPE_APP_TOOLTIP, HNAppTooltipClass))
+#define HN_IS_APP_TOOLTIP_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), HN_TYPE_APP_TOOLTIP))
+#define HN_APP_TOOLTIP_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), HN_TYPE_APP_TOOLTIP, HNAppTooltipClass))
+
+typedef struct _HNAppTooltipClass HNAppTooltipClass;
+
+struct _HNAppTooltip
+{
+  GtkWindow parent_instance;
+
+  GtkWidget *widget;
+  GtkWidget *label;
+
+  guint show_timer_id;
+  guint hide_timer_id;
+  
+  gchar *text;
+
+  GtkCallback show_cb;
+  GtkCallback hide_cb;
+  gpointer cb_data;
+};
+
+struct _HNAppTooltipClass
+{
+  GtkWindowClass parent_class;
+};
+
+G_DEFINE_TYPE (HNAppTooltip, hn_app_tooltip, GTK_TYPE_WINDOW);
+
+static void
+hn_app_tooltip_finalize (GObject *gobject)
+{
+  HNAppTooltip *tip = HN_APP_TOOLTIP (gobject);
+
+  hn_app_tooltip_remove_timers (tip);
+
+  if (tip->text)
+    g_free (tip->text);
+
+  G_OBJECT_CLASS (hn_app_tooltip_parent_class)->finalize (gobject);
+}
+
+static void
+hn_app_tooltip_set_property (GObject      *gobject,
+			     guint         prop_id,
+			     const GValue *value,
+			     GParamSpec   *pspec)
+{
+  HNAppTooltip *tip = HN_APP_TOOLTIP (gobject);
+  GtkWidget *widget = GTK_WIDGET (gobject);
+
+  switch (prop_id)
+    {
+    case TIP_PROP_WIDGET:
+      {
+      gboolean was_visible = GTK_WIDGET_VISIBLE (widget);
+      
+      gtk_widget_hide (widget);
+      
+      tip->widget = g_value_get_object (value);
+      
+      if (was_visible)
+        gtk_widget_show (widget);
+      }
+      break;
+    case TIP_PROP_TEXT:
+      g_free (tip->text);
+      tip->text = g_strdup (g_value_get_string (value));
+      gtk_label_set_text (GTK_LABEL (tip->label), tip->text);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+hn_app_tooltip_get_property (GObject    *gobject,
+			     guint       prop_id,
+			     GValue     *value,
+			     GParamSpec *pspec)
+{
+  switch (prop_id)
+    {
+    case TIP_PROP_WIDGET:
+      break;
+    case TIP_PROP_TEXT:
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+hn_app_tooltip_position (HNAppTooltip *tip)
+{
+  gint x, y;
+  GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET(tip));
+  gint screen_height = gdk_screen_get_height (screen);
+  GtkRequisition req;
+
+  gdk_flush ();
+
+  gtk_widget_realize (tip->widget);
+  gdk_window_get_origin (tip->widget->window, &x, &y);
+
+  x += tip->widget->allocation.width + 1;
+  y += tip->widget->allocation.y;
+
+
+  gtk_widget_size_request (GTK_WIDGET (tip), &req);
+  if (y + req.height > screen_height)
+    y = screen_height - req.height;
+  
+  gtk_window_move (GTK_WINDOW (tip), x, y);
+}
+
+static void
+hn_app_tooltip_show (GtkWidget *widget)
+{
+  hn_app_tooltip_position (HN_APP_TOOLTIP (widget));
+
+  /* chain up */
+  GTK_WIDGET_CLASS (hn_app_tooltip_parent_class)->show (widget);
+}
+
+static void
+hn_app_tooltip_class_init (HNAppTooltipClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  gobject_class->finalize = hn_app_tooltip_finalize;
+  gobject_class->set_property = hn_app_tooltip_set_property;
+  gobject_class->get_property = hn_app_tooltip_get_property;
+
+  widget_class->show = hn_app_tooltip_show;
+
+  g_object_class_install_property (gobject_class,
+		  		   TIP_PROP_WIDGET,
+				   g_param_spec_object ("widget",
+					   		"Widget",
+							"The widget to which we align to",
+							GTK_TYPE_WIDGET,
+							(G_PARAM_READABLE | G_PARAM_WRITABLE)));
+  g_object_class_install_property (gobject_class,
+		  		   TIP_PROP_TEXT,
+				   g_param_spec_string ("text",
+					   		"Text",
+							"The text of the tooltip",
+							NULL,
+							(G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+hn_app_tooltip_init (HNAppTooltip *tip)
+{
+  GtkWindow *window = GTK_WINDOW (tip);
+
+  tip->widget = NULL;
+  tip->text = g_strdup ("Invalid text");
+  
+  gtk_window_set_resizable (window, FALSE);
+  gtk_window_set_decorated (window, FALSE);
+  gtk_window_set_type_hint (window,
+		  	    GDK_WINDOW_TYPE_HINT_DIALOG);
+  
+  gtk_widget_set_size_request (GTK_WIDGET (tip), TOOLTIP_WIDTH, -1);
+  gtk_widget_set_name (GTK_WIDGET (tip), TOOLTIP_NAME);
+  gtk_container_set_border_width (GTK_CONTAINER (tip), TOOLTIP_BORDER_WIDTH);
+
+  tip->label = gtk_label_new (tip->text);
+  gtk_label_set_line_wrap (GTK_LABEL (tip->label), TRUE);
+  gtk_misc_set_alignment (GTK_MISC (tip->label), 0.5, 0.5);
+  gtk_container_add (GTK_CONTAINER (tip), tip->label);
+  gtk_widget_show (tip->label);
+}
+
+GtkWidget *
+hn_app_tooltip_new (GtkWidget *widget)
+{
+  return g_object_new (HN_TYPE_APP_TOOLTIP,
+		       "widget", widget,
+		       NULL);
+}
+
+void
+hn_app_tooltip_set_widget (HNAppTooltip *tip,
+			   GtkWidget    *widget)
+{
+  gboolean was_visible;
+  
+  g_return_if_fail (HN_IS_APP_TOOLTIP (tip));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  was_visible = GTK_WIDGET_VISIBLE (GTK_WIDGET (tip));
+  gtk_widget_hide (GTK_WIDGET (tip));
+
+  tip->widget = widget;
+  
+  if (was_visible)
+    gtk_widget_show (GTK_WIDGET (tip));
+
+  g_object_notify (G_OBJECT (tip), "widget");
+}
+
+GtkWidget *
+hn_app_tooltip_get_widget (HNAppTooltip *tip)
+{
+  g_return_val_if_fail (HN_IS_APP_TOOLTIP (tip), NULL);
+
+  return tip->widget;
+}
+
+void
+hn_app_tooltip_set_text (HNAppTooltip *tip,
+                         const gchar  *text)
+{
+  g_return_if_fail (HN_IS_APP_TOOLTIP (tip));
+
+  g_free (tip->text);
+  tip->text = g_strdup (text);
+  
+  gtk_label_set_text (GTK_LABEL (tip->label), tip->text);
+
+  g_object_notify (G_OBJECT (tip), "text");
+}
+
+const gchar *
+hn_app_tooltip_get_text (HNAppTooltip *tip)
+{
+  g_return_val_if_fail (HN_IS_APP_TOOLTIP (tip), NULL);
+
+  return tip->text;
+}
+
+static gboolean
+hn_app_tooltip_hide_timeout_cb (gpointer data)
+{
+  HNAppTooltip *tip = HN_APP_TOOLTIP (data);
+
+  gtk_widget_hide (GTK_WIDGET (tip));
+
+  if (tip->hide_cb)
+    tip->hide_cb (GTK_WIDGET (tip), tip->cb_data);
+
+  return FALSE;
+}
+
+static gboolean
+hn_app_tooltip_show_timeout_cb (gpointer data)
+{
+  HNAppTooltip *tip = HN_APP_TOOLTIP (data);
+
+  HN_DBG ("Showing tooltip");
+  
+  gtk_widget_show (GTK_WIDGET (tip));
+
+  if (tip->show_cb)
+    tip->show_cb (GTK_WIDGET (tip), tip->cb_data);
+
+  HN_DBG ("Installing tooltip hide timer");
+  
+  tip->show_timer_id = 0;
+  tip->hide_timer_id = g_timeout_add (TOOLTIP_HIDE_TIMEOUT,
+		  		      hn_app_tooltip_hide_timeout_cb,
+				      tip);				      
+
+  return FALSE;
+}
+
+void
+hn_app_tooltip_install_timer (HNAppTooltip *tip,
+			      GtkCallback   show_callback,
+			      GtkCallback   hide_callback,
+			      gpointer      callback_data)
+{
+  g_return_if_fail (HN_IS_APP_TOOLTIP (tip));
+
+  if (tip->show_timer_id)
+    return;
+
+  tip->show_cb = show_callback;
+  tip->hide_cb = hide_callback;
+  tip->cb_data = callback_data;
+
+  HN_DBG ("Installing tooltip show timer");
+
+  tip->show_timer_id = g_timeout_add (TOOLTIP_SHOW_TIMEOUT,
+		  		      hn_app_tooltip_show_timeout_cb,
+				      tip);
+}
+
+void
+hn_app_tooltip_remove_show_timer(HNAppTooltip *tip)
+{
+  g_return_if_fail (HN_IS_APP_TOOLTIP (tip));
+
+  if (tip->show_timer_id)
+    {
+      HN_DBG ("Removing tooltip show timer");
+
+      g_source_remove (tip->show_timer_id);
+      tip->show_timer_id = 0;
+    }
+}
+
+
+void
+hn_app_tooltip_remove_timers (HNAppTooltip *tip)
+{
+  g_return_if_fail (HN_IS_APP_TOOLTIP (tip));
+
+  if (tip->show_timer_id)
+    {
+      HN_DBG ("Removing tooltip show timer");
+
+      g_source_remove (tip->show_timer_id);
+      tip->show_timer_id = 0;
+    }
+
+  if (tip->hide_timer_id)
+    {
+      HN_DBG ("Removing tooltip hide timer");
+
+      gtk_widget_hide(GTK_WIDGET(tip));
+      
+      g_source_remove (tip->hide_timer_id);
+      tip->hide_timer_id = 0;
+    }
+}

Added: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-tooltip.h
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-tooltip.h	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildondesktop/hn-app-tooltip.h	2006-11-27 12:31:16 UTC (rev 8340)
@@ -0,0 +1,65 @@
+/* hn-app-tooltip.h
+ * 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
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/**
+ * @file hn-app-tooltip.h
+ *
+ * @brief Definitions of the application tooltip
+ *        used by the Application Switcher
+ *
+ */
+
+#ifndef HN_APP_TOOLTIP_H
+#define HN_APP_TOOLTIP_H
+
+#include <gtk/gtkwidget.h>
+#include <libhildonwm/hd-wm.h>
+
+G_BEGIN_DECLS
+
+#define HN_TYPE_APP_TOOLTIP		(hn_app_tooltip_get_type ())
+#define HN_APP_TOOLTIP(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), HN_TYPE_APP_TOOLTIP, HNAppTooltip))
+#define HN_IS_APP_TOOLTIP(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), HN_TYPE_APP_TOOLTIP))
+
+typedef struct _HNAppTooltip HNAppTooltip;
+
+GType        hn_app_tooltip_get_type      (void) G_GNUC_CONST;
+
+GtkWidget *  hn_app_tooltip_new           (GtkWidget    *widget);
+void         hn_app_tooltip_set_widget    (HNAppTooltip *tip,
+					   GtkWidget    *widget);
+GtkWidget *  hn_app_tooltip_get_widget    (HNAppTooltip *tip);
+void         hn_app_tooltip_set_text      (HNAppTooltip *tip,
+					   const gchar  *text);
+const gchar *hn_app_tooltip_get_text      (HNAppTooltip *tip);
+
+void         hn_app_tooltip_install_timer (HNAppTooltip *tip,
+					   GtkCallback   show_cb,
+					   GtkCallback   hide_cb,
+					   gpointer      data);
+void         hn_app_tooltip_remove_timers  (HNAppTooltip *tip);
+void         hn_app_tooltip_remove_show_timer  (HNAppTooltip *tip);
+
+G_END_DECLS
+
+#endif /* HN_APP_TOOLTIP_H */

Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildonwm/hd-wm.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildonwm/hd-wm.c	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/libhildonwm/hd-wm.c	2006-11-27 12:31:16 UTC (rev 8340)
@@ -670,7 +670,7 @@
 
     dbus_bus_add_match( connection, match_rule, NULL );
 
-    dbus_connection_add_filter( connection, hd_wm_dbus_method_call_handler,/*model */ NULL, NULL );
+    dbus_connection_add_filter( connection, hd_wm_dbus_method_call_handler,/*model */ hdwm, NULL );
     g_free(match_rule);
 
       /* Setup match rule for Maemo Launcher */

Modified: projects/haf/branches/maemo-af-desktop/hildon-desktop/test/test1.c
===================================================================
--- projects/haf/branches/maemo-af-desktop/hildon-desktop/test/test1.c	2006-11-27 12:10:04 UTC (rev 8339)
+++ projects/haf/branches/maemo-af-desktop/hildon-desktop/test/test1.c	2006-11-27 12:31:16 UTC (rev 8340)
@@ -1,5 +1,6 @@
 #include <gtk/gtk.h>
 #include <libhildondesktop/libhildondesktop.h>
+#include <libhildondesktop/hn-app-switcher.h>
 #include <gdk/gdkwindow.h>
 #include <gdk/gdkx.h>
 #include <gdk/gdkkeysyms.h>
@@ -54,9 +55,9 @@
    
   button  = gtk_button_new_with_label("Testing");
   button2 = GTK_WIDGET (statusbar_item_wrapper_new ("load","/usr/lib/hildon-status-bar/libload.so",FALSE));
-  sbold   = GTK_WIDGET (statusbar_item_wrapper_new ("helloworld_sb","/usr/lib/hildon-status-bar/libhelloworld_sb.so",FALSE));
+  button4 = GTK_WIDGET (hn_app_switcher_new ());
   button3 = GTK_WIDGET (tasknavigator_item_wrapper_new ("contacts","/usr/lib/hildon-navigator/libosso-contact-plugin.so"));
-  button4 = gtk_button_new_with_label("Testing 4");
+  sbold   = gtk_button_new_with_label("Testing 4");
  
   gtk_box_pack_start (GTK_BOX (panel),button,  FALSE,FALSE,0);
   gtk_box_pack_start (GTK_BOX (panel),socket,  FALSE,FALSE,0);


More information about the maemo-commits mailing list