[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.orgDate: Mon Nov 27 14:31:17 EET 2006
- Previous message: [maemo-commits] r8339 - projects/haf/trunk/dbus/debian/patches
- Next message: [maemo-commits] r8341 - in projects/haf/trunk/gtkhtml: . debian upstream/patches
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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);
- Previous message: [maemo-commits] r8339 - projects/haf/trunk/dbus/debian/patches
- Next message: [maemo-commits] r8341 - in projects/haf/trunk/gtkhtml: . debian upstream/patches
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]