r2573 - in trunk/src/target/OM-2007.2/applications/openmoko-today2: . src
chris at sita.openmoko.org
chris at sita.openmoko.org
Mon Jul 30 18:07:52 CEST 2007
Author: chris
Date: 2007-07-30 18:07:50 +0200 (Mon, 30 Jul 2007)
New Revision: 2573
Modified:
trunk/src/target/OM-2007.2/applications/openmoko-today2/ChangeLog
trunk/src/target/OM-2007.2/applications/openmoko-today2/src/today-task-manager.c
trunk/src/target/OM-2007.2/applications/openmoko-today2/src/today.h
Log:
Add a tasks list (mostly stolen from matchbox-panel-2)
Modified: trunk/src/target/OM-2007.2/applications/openmoko-today2/ChangeLog
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-today2/ChangeLog 2007-07-30 15:50:16 UTC (rev 2572)
+++ trunk/src/target/OM-2007.2/applications/openmoko-today2/ChangeLog 2007-07-30 16:07:50 UTC (rev 2573)
@@ -1,5 +1,18 @@
2007-07-30 Chris Lord,,, <chris at openedhand.com>
+ * src/today-task-manager.c: (get_utf8_property),
+ (get_text_property), (window_get_name), (window_get_icon),
+ (today_task_manager_free_tasks),
+ (today_task_manager_populate_tasks),
+ (today_task_manager_notify_visible_cb),
+ (today_task_manager_visibility_notify_event_cb),
+ (today_task_manager_unmap_cb), (screen_changed_cb), (filter_func),
+ (today_task_manager_page_create):
+ * src/today.h:
+ Add a tasks list (mostly stolen from matchbox-panel-2)
+
+2007-07-30 Chris Lord,,, <chris at openedhand.com>
+
* src/today-events-store.c: (today_events_store_comp_get_desc),
(today_events_store_objects_added),
(today_events_store_objects_modified),
Modified: trunk/src/target/OM-2007.2/applications/openmoko-today2/src/today-task-manager.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-today2/src/today-task-manager.c 2007-07-30 15:50:16 UTC (rev 2572)
+++ trunk/src/target/OM-2007.2/applications/openmoko-today2/src/today-task-manager.c 2007-07-30 16:07:50 UTC (rev 2573)
@@ -2,13 +2,542 @@
#include <moko-stock.h>
#include <moko-finger-scroll.h>
#include <libtaku/taku-table.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+#include <glib/gi18n.h>
#include "today-task-manager.h"
#include "today-utils.h"
+#define DEFAULT_WINDOW_ICON_NAME "gnome-fs-executable"
+
+/* NOTE: Lots of this code taken from windowselector applet in
+ * matchbox-panel-2. As such, this only works with matchbox as the
+ * window-manager (due to custom window atoms).
+ */
+
+enum {
+ _MB_APP_WINDOW_LIST_STACKING,
+ _MB_CURRENT_APP_WINDOW,
+ UTF8_STRING,
+ _NET_WM_VISIBLE_NAME,
+ _NET_WM_NAME,
+ _NET_ACTIVE_WINDOW,
+ _NET_WM_ICON,
+ N_ATOMS
+};
+
+static gboolean hidden = TRUE;
+static Atom atoms[N_ATOMS];
+
+static GdkFilterReturn
+filter_func (GdkXEvent *xevent, GdkEvent *event, TodayData *data);
+
+/* Retrieves the UTF-8 property @atom from @window */
+static char *
+get_utf8_property (TodayData *data, Window window, Atom atom)
+{
+ GdkDisplay *display;
+ Atom type;
+ int format, result;
+ gulong nitems, bytes_after;
+ guchar *val;
+ char *ret;
+
+ display = gtk_widget_get_display (GTK_WIDGET (data->tasks_table));
+
+ type = None;
+ val = NULL;
+
+ gdk_error_trap_push ();
+ result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ window,
+ atom,
+ 0,
+ G_MAXLONG,
+ False,
+ atoms[UTF8_STRING],
+ &type,
+ &format,
+ &nitems,
+ &bytes_after,
+ (gpointer) &val);
+ if (gdk_error_trap_pop () || result != Success)
+ return NULL;
+
+ if (type != atoms[UTF8_STRING] || format != 8 || nitems == 0) {
+ if (val)
+ XFree (val);
+
+ return NULL;
+ }
+
+ if (!g_utf8_validate ((char *) val, nitems, NULL)) {
+ g_warning ("Invalid UTF-8 in window title");
+
+ XFree (val);
+
+ return NULL;
+ }
+
+ ret = g_strndup ((char *) val, nitems);
+
+ XFree (val);
+
+ return ret;
+}
+
+/* Retrieves the text property @atom from @window */
+static char *
+get_text_property (TodayData *data, Window window, Atom atom)
+{
+ GdkDisplay *display;
+ XTextProperty text;
+ char *ret, **list;
+ int result, count;
+
+ display = gtk_widget_get_display (GTK_WIDGET (data->tasks_table));
+
+ gdk_error_trap_push ();
+ result = XGetTextProperty (GDK_DISPLAY_XDISPLAY (display),
+ window,
+ &text,
+ atom);
+ if (gdk_error_trap_pop () || result == 0)
+ return NULL;
+
+ count = gdk_text_property_to_utf8_list
+ (gdk_x11_xatom_to_atom (text.encoding),
+ text.format,
+ text.value,
+ text.nitems,
+ &list);
+ if (count > 0) {
+ int i;
+
+ ret = list[0];
+
+ for (i = 1; i < count; i++)
+ g_free (list[i]);
+ g_free (list);
+ } else
+ ret = NULL;
+
+ if (text.value)
+ XFree (text.value);
+
+ return ret;
+}
+
+/* Retrieves the name for @window */
+static char *
+window_get_name (TodayData *data, Window window)
+{
+ char *name;
+
+ name = get_utf8_property (data,
+ window,
+ atoms[_NET_WM_VISIBLE_NAME]);
+ if (name == NULL) {
+ name = get_utf8_property (data,
+ window,
+ atoms[_NET_WM_NAME]);
+ } if (name == NULL) {
+ name = get_text_property (data,
+ window,
+ XA_WM_NAME);
+ } if (name == NULL) {
+ name = g_strdup (_("(untitled)"));
+ }
+
+ return name;
+}
+
+/* Retrieves the icon for @window */
+static GdkPixbuf *
+window_get_icon (TodayData *tdata, Window window)
+{
+ GdkPixbuf *pixbuf;
+ GdkDisplay *display;
+ Atom type;
+ int format, result;
+ int ideal_width, ideal_height, ideal_size;
+ int best_width, best_height, best_size;
+ int i, npixels, ip;
+ gulong nitems, bytes_after, *data, *datap, *best_data;
+ GtkSettings *settings;
+ guchar *pixdata;
+
+ /* First, we read the contents of the _NET_WM_ICON property */
+ display = gtk_widget_get_display (GTK_WIDGET (tdata->tasks_table));
+
+ type = 0;
+
+ gdk_error_trap_push ();
+ result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ window,
+ atoms[_NET_WM_ICON],
+ 0,
+ G_MAXLONG,
+ False,
+ XA_CARDINAL,
+ &type,
+ &format,
+ &nitems,
+ &bytes_after,
+ (gpointer) &data);
+ if (gdk_error_trap_pop () || result != Success)
+ return NULL;
+
+ if (type != XA_CARDINAL || nitems < 3) {
+ XFree (data);
+
+ return NULL;
+ }
+
+ /* Got it. Now what size icon are we looking for? */
+ settings = gtk_widget_get_settings (GTK_WIDGET (tdata->tasks_table));
+ gtk_icon_size_lookup_for_settings (settings,
+ GTK_ICON_SIZE_MENU,
+ &ideal_width,
+ &ideal_height);
+
+ ideal_size = (ideal_width + ideal_height) / 2;
+
+ /* Try to find the closest match */
+ best_data = NULL;
+ best_width = best_height = best_size = 0;
+
+ datap = data;
+ while (nitems > 0) {
+ int cur_width, cur_height, cur_size;
+ gboolean replace;
+
+ if (nitems < 3)
+ break;
+
+ cur_width = datap[0];
+ cur_height = datap[1];
+ cur_size = (cur_width + cur_height) / 2;
+
+ if (nitems < (2 + cur_width * cur_height))
+ break;
+
+ if (!best_data) {
+ replace = TRUE;
+ } else {
+ /* Always prefer bigger to smaller */
+ if (best_size < ideal_size &&
+ cur_size > best_size)
+ replace = TRUE;
+ /* Prefer smaller bigger */
+ else if (best_size > ideal_size &&
+ cur_size >= ideal_size &&
+ cur_size < best_size)
+ replace = TRUE;
+ else
+ replace = FALSE;
+ }
+
+ if (replace) {
+ best_data = datap + 2;
+ best_width = cur_width;
+ best_height = cur_height;
+ best_size = cur_size;
+ }
+
+ datap += (2 + cur_width * cur_height);
+ nitems -= (2 + cur_width * cur_height);
+ }
+
+ if (!best_data) {
+ XFree (data);
+
+ return NULL;
+ }
+
+ /* Got it. Load it into a pixbuf. */
+ npixels = best_width * best_height;
+ pixdata = g_new (guchar, npixels * 4);
+
+ for (i = 0, ip = 0; i < npixels; i++) {
+ /* red */
+ pixdata[ip] = (best_data[i] >> 16) & 0xff;
+ ip++;
+
+ /* green */
+ pixdata[ip] = (best_data[i] >> 8) & 0xff;
+ ip++;
+
+ /* blue */
+ pixdata[ip] = best_data[i] & 0xff;
+ ip++;
+
+ /* alpha */
+ pixdata[ip] = best_data[i] >> 24;
+ ip++;
+ }
+
+ pixbuf = gdk_pixbuf_new_from_data (pixdata,
+ GDK_COLORSPACE_RGB,
+ TRUE,
+ 8,
+ best_width,
+ best_height,
+ best_width * 4,
+ (GdkPixbufDestroyNotify) g_free,
+ NULL);
+
+ /* Scale if necessary */
+ if (best_width != ideal_width &&
+ best_height != ideal_height) {
+ GdkPixbuf *scaled;
+
+ scaled = gdk_pixbuf_scale_simple (pixbuf,
+ ideal_width,
+ ideal_height,
+ GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+
+ pixbuf = scaled;
+ }
+
+ /* Cleanup */
+ XFree (data);
+
+ /* Return */
+ return pixbuf;
+}
+
+static void
+today_task_manager_free_tasks (TodayData *data)
+{
+ GList *c, *children;
+
+ /* Free window list */
+ children = gtk_container_get_children (
+ GTK_CONTAINER (data->tasks_table));
+
+ for (c = children; c; c = c->next) {
+ GtkWidget *child = GTK_WIDGET (c->data);
+ if (TAKU_IS_TILE (child))
+ gtk_container_remove (GTK_CONTAINER (
+ data->tasks_table), child);
+ }
+}
+
+static void
+today_task_manager_populate_tasks (TodayData *data)
+{
+ GdkDisplay *display;
+ Atom type;
+ int format, result, i;
+ gulong nitems, bytes_after;
+ Window *windows;
+
+ /* Empty menu */
+ today_task_manager_free_tasks (data);
+
+ /* Retrieve list of app windows from root window */
+ display = gtk_widget_get_display (GTK_WIDGET (data->tasks_table));
+
+ type = None;
+
+ gdk_error_trap_push ();
+ result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
+ GDK_WINDOW_XWINDOW (data->root_window),
+ atoms[_MB_APP_WINDOW_LIST_STACKING],
+ 0,
+ G_MAXLONG,
+ False,
+ XA_WINDOW,
+ &type,
+ &format,
+ &nitems,
+ &bytes_after,
+ (gpointer) &windows);
+ if (gdk_error_trap_pop () || result != Success)
+ return;
+
+ if (type != XA_WINDOW) {
+ XFree (windows);
+
+ return;
+ }
+
+ /* Load into menu */
+ for (i = 0; i < nitems; i++) {
+ char *name;
+ GtkWidget *task_tile;
+ GdkPixbuf *icon;
+
+ name = window_get_name (data, windows[i]);
+ task_tile = taku_icon_tile_new ();
+ taku_icon_tile_set_primary (TAKU_ICON_TILE (task_tile), name);
+ taku_icon_tile_set_secondary (TAKU_ICON_TILE (task_tile), "");
+ g_free (name);
+
+ icon = window_get_icon (data, windows[i]);
+ if (icon) {
+ taku_icon_tile_set_pixbuf (
+ TAKU_ICON_TILE (task_tile), icon);
+ g_object_unref (icon);
+ } else {
+ taku_icon_tile_set_icon_name (
+ TAKU_ICON_TILE (task_tile),
+ DEFAULT_WINDOW_ICON_NAME);
+ }
+
+ g_object_set_data (G_OBJECT (task_tile),
+ "window",
+ GUINT_TO_POINTER (windows[i]));
+
+ /*g_signal_connect (task_tile,
+ "activate",
+ G_CALLBACK (window_menu_item_activate_cb),
+ applet);*/
+
+ gtk_container_add (GTK_CONTAINER (data->tasks_table),
+ task_tile);
+ gtk_widget_show (task_tile);
+ }
+
+ /* If no windows were found, insert an insensitive "No tasks" item */
+ /*if (nitems == 0) {
+ GtkWidget *menu_item;
+
+ menu_item = gtk_menu_item_new_with_label (_("No tasks"));
+
+ gtk_widget_set_sensitive (menu_item, FALSE);
+
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (applet->menu),
+ menu_item);
+ gtk_widget_show (menu_item);
+ }*/
+
+ /* Cleanup */
+ XFree (windows);
+}
+
+static void
+today_task_manager_notify_visible_cb (GObject *gobject,
+ GParamSpec *arg1,
+ TodayData *data)
+{
+ if ((!hidden) && (!GTK_WIDGET_VISIBLE (gobject))) {
+ hidden = TRUE;
+ today_task_manager_free_tasks (data);
+ }
+}
+
+static gboolean
+today_task_manager_visibility_notify_event_cb (GtkWidget *widget,
+ GdkEventVisibility *event,
+ TodayData *data)
+{
+ if (((event->state == GDK_VISIBILITY_PARTIAL) ||
+ (event->state == GDK_VISIBILITY_UNOBSCURED)) && (hidden)) {
+ hidden = FALSE;
+ today_task_manager_populate_tasks (data);
+ } else if ((event->state == GDK_VISIBILITY_FULLY_OBSCURED) &&
+ (!hidden)) {
+ hidden = TRUE;
+ today_task_manager_free_tasks (data);
+ }
+
+ return FALSE;
+}
+
+static void
+today_task_manager_unmap_cb (GtkWidget *widget, TodayData *data)
+{
+ if (!hidden) {
+ hidden = TRUE;
+ today_task_manager_free_tasks (data);
+ }
+}
+
+static void
+screen_changed_cb (GtkWidget *button, GdkScreen *old_screen, TodayData *data)
+{
+ GdkScreen *screen;
+ GdkDisplay *display;
+ GdkEventMask events;
+
+ if (data->root_window) {
+ gdk_window_remove_filter (data->root_window,
+ (GdkFilterFunc) filter_func,
+ data);
+ }
+
+ screen = gtk_widget_get_screen (data->tasks_table);
+ display = gdk_screen_get_display (screen);
+
+ /* Get atoms */
+ atoms[_MB_APP_WINDOW_LIST_STACKING] =
+ gdk_x11_get_xatom_by_name_for_display
+ (display, "_MB_APP_WINDOW_LIST_STACKING");
+ atoms[_MB_CURRENT_APP_WINDOW] =
+ gdk_x11_get_xatom_by_name_for_display
+ (display, "_MB_CURRENT_APP_WINDOW");
+ atoms[UTF8_STRING] =
+ gdk_x11_get_xatom_by_name_for_display
+ (display, "UTF8_STRING");
+ atoms[_NET_WM_NAME] =
+ gdk_x11_get_xatom_by_name_for_display
+ (display, "_NET_WM_NAME");
+ atoms[_NET_WM_VISIBLE_NAME] =
+ gdk_x11_get_xatom_by_name_for_display
+ (display, "_NET_WM_VISIBLE_NAME");
+ atoms[_NET_WM_ICON] =
+ gdk_x11_get_xatom_by_name_for_display
+ (display, "_NET_WM_ICON");
+ atoms[_NET_ACTIVE_WINDOW] =
+ gdk_x11_get_xatom_by_name_for_display
+ (display, "_NET_ACTIVE_WINDOW");
+
+ /* Get root window */
+ data->root_window = gdk_screen_get_root_window (screen);
+
+ /* Watch _MB_APP_WINDOW_LIST_STACKING */
+ events = gdk_window_get_events (data->root_window);
+ if ((events & GDK_PROPERTY_CHANGE_MASK) == 0) {
+ gdk_window_set_events (data->root_window,
+ events & GDK_PROPERTY_CHANGE_MASK);
+ }
+
+ gdk_window_add_filter (data->root_window,
+ (GdkFilterFunc) filter_func,
+ data);
+
+ /* Rebuild list if around */
+ if (!hidden) today_task_manager_populate_tasks (data);
+}
+
+/* Something happened on the root window */
+static GdkFilterReturn
+filter_func (GdkXEvent *xevent, GdkEvent *event, TodayData *data)
+{
+ XEvent *xev;
+
+ xev = (XEvent *) xevent;
+
+ if (xev->type == PropertyNotify) {
+ if (xev->xproperty.atom ==
+ atoms[_MB_APP_WINDOW_LIST_STACKING]) {
+ /* _MB_APP_WINDOW_LIST_STACKING changed.
+ * Rebuild menu if around. */
+ if (!hidden)
+ today_task_manager_populate_tasks (data);
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
GtkWidget *
today_task_manager_page_create (TodayData *data)
{
- GtkWidget *vbox, *toolbar, *table, *viewport, *scroll;
+ GtkWidget *vbox, *toolbar, *viewport, *scroll;
GtkToolItem *button;
vbox = gtk_vbox_new (FALSE, 0);
@@ -33,12 +562,14 @@
button = today_toolbutton_new (GTK_STOCK_JUMP_TO);
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), button, 0);
+ /* Viewport / tasks table */
viewport = gtk_viewport_new (NULL, NULL);
gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport),
GTK_SHADOW_NONE);
- table = taku_table_new ();
- gtk_container_add (GTK_CONTAINER (viewport), table);
- gtk_widget_show (table);
+
+ data->tasks_table = taku_table_new ();
+ gtk_container_add (GTK_CONTAINER (viewport), data->tasks_table);
+ gtk_widget_show (data->tasks_table);
scroll = moko_finger_scroll_new ();
gtk_container_add (GTK_CONTAINER (scroll), viewport);
@@ -47,5 +578,19 @@
gtk_widget_show (scroll);
gtk_widget_show_all (toolbar);
+
+ data->root_window = NULL;
+
+ gtk_widget_add_events (viewport, GDK_VISIBILITY_NOTIFY_MASK);
+ g_signal_connect (G_OBJECT (viewport), "visibility-notify-event",
+ G_CALLBACK (today_task_manager_visibility_notify_event_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->tasks_table), "notify::visible",
+ G_CALLBACK (today_task_manager_notify_visible_cb), data);
+ g_signal_connect (G_OBJECT (data->tasks_table), "screen-changed",
+ G_CALLBACK (screen_changed_cb), data);
+ g_signal_connect (G_OBJECT (vbox), "unmap",
+ G_CALLBACK (today_task_manager_unmap_cb), data);
+
return vbox;
}
Modified: trunk/src/target/OM-2007.2/applications/openmoko-today2/src/today.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-today2/src/today.h 2007-07-30 15:50:16 UTC (rev 2572)
+++ trunk/src/target/OM-2007.2/applications/openmoko-today2/src/today.h 2007-07-30 16:07:50 UTC (rev 2573)
@@ -6,6 +6,9 @@
#include <libtaku/taku-launcher-tile.h>
typedef struct {
+ /* UI vars */
+
+ /* Main */
GtkWidget *window;
GtkWidget *notebook;
GtkWidget *home_toolbar;
@@ -17,8 +20,16 @@
GtkToolItem *contacts_button;
GtkToolItem *messages_button;
GtkToolItem *dates_button;
+
+ /* Misc. vars */
+
+ /* App launcher */
GList *categories;
GtkWidget *launcher_table;
+
+ /* App manager */
+ GdkWindow *root_window;
+ GtkWidget *tasks_table;
} TodayData;
#endif
More information about the commitlog
mailing list