/* selector */ /* vim: set sw=2 et: */ /* * Copyright (C) 2003 Sun Microsystems, Inc. * Copyright (C) 2001 Free Software Foundation, Inc. * Copyright (C) 2000 Helix Code, Inc. * Copyright (C) 2005-2007 Vincent Untz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, see . * * Authors: * Mark McLoughlin * George Lebl * Jacob Berkman */ #include #include #include #include "selector.h" #include "libwnck.h" #include "screen.h" #include "wnck-image-menu-item-private.h" #include "private.h" /** * SECTION:selector * @short_description: a window selector widget, showing the list of windows as * a menu. * @see_also: #WnckTasklist * @stability: Unstable * * The #WnckSelector represents client windows on a screen as a menu, where * menu items are labelled with the window titles and icons. Activating a menu * item activates the represented window. * * The #WnckSelector will automatically detect the screen it is on, and will * represent windows of this screen only. */ struct _WnckSelectorPrivate { WnckHandle *handle; GtkWidget *image; WnckWindow *icon_window; /* those have the same lifecycle as the menu */ GtkWidget *menu; GtkWidget *no_windows_item; GHashTable *window_hash; }; enum { PROP_0, PROP_HANDLE, LAST_PROP }; static GParamSpec *selector_properties[LAST_PROP] = { NULL }; G_DEFINE_TYPE_WITH_PRIVATE (WnckSelector, wnck_selector, GTK_TYPE_MENU_BAR); static GObject *wnck_selector_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); static void wnck_selector_dispose (GObject *object); static void wnck_selector_finalize (GObject *object); static void wnck_selector_realize (GtkWidget *widget); static void wnck_selector_unrealize (GtkWidget *widget); static gboolean wnck_selector_scroll_event (GtkWidget *widget, GdkEventScroll *event); static void wnck_selector_connect_to_window (WnckSelector *selector, WnckWindow *window); static void wnck_selector_insert_window (WnckSelector *selector, WnckWindow *window); static void wnck_selector_append_window (WnckSelector *selector, WnckWindow *window); static gint wnck_selector_windows_compare (gconstpointer a, gconstpointer b) { int posa; int posb; posa = wnck_window_get_sort_order (WNCK_WINDOW (a)); posb = wnck_window_get_sort_order (WNCK_WINDOW (b)); return (posa - posb); } static void wncklet_connect_while_alive (gpointer object, const char *signal, GCallback func, gpointer func_data, gpointer alive_object) { GClosure *closure; closure = g_cclosure_new (func, func_data, NULL); g_object_watch_closure (G_OBJECT (alive_object), closure); g_signal_connect_closure_by_id (object, g_signal_lookup (signal, G_OBJECT_TYPE (object)), 0, closure, FALSE); } static WnckScreen * wnck_selector_get_screen (WnckSelector *selector) { GdkScreen *screen; g_assert (gtk_widget_has_screen (GTK_WIDGET (selector))); screen = gtk_widget_get_screen (GTK_WIDGET (selector)); return wnck_handle_get_screen (selector->priv->handle, gdk_x11_screen_get_screen_number (screen)); } static GdkPixbuf * wnck_selector_get_default_window_icon (void) { static GdkPixbuf *retval = NULL; if (retval) return retval; retval = gdk_pixbuf_new_from_resource ("/org/gnome/libwnck/default_icon.png", NULL); g_assert (retval); return retval; } static GdkPixbuf * wnck_selector_dimm_icon (GdkPixbuf *pixbuf) { int x, y, pixel_stride, row_stride; guchar *row, *pixels; int w, h; GdkPixbuf *dimmed; w = gdk_pixbuf_get_width (pixbuf); h = gdk_pixbuf_get_height (pixbuf); if (gdk_pixbuf_get_has_alpha (pixbuf)) dimmed = gdk_pixbuf_copy (pixbuf); else dimmed = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0); pixel_stride = 4; row = gdk_pixbuf_get_pixels (dimmed); row_stride = gdk_pixbuf_get_rowstride (dimmed); for (y = 0; y < h; y++) { pixels = row; for (x = 0; x < w; x++) { pixels[3] /= 2; pixels += pixel_stride; } row += row_stride; } return dimmed; } void _wnck_selector_set_window_icon (GtkWidget *image, WnckWindow *window) { GdkPixbuf *pixbuf, *freeme, *freeme2; int width, height; int icon_size = -1; pixbuf = NULL; freeme = NULL; freeme2 = NULL; if (window) pixbuf = wnck_window_get_mini_icon (window); if (!pixbuf) pixbuf = wnck_selector_get_default_window_icon (); if (icon_size == -1) gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, NULL, &icon_size); width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); if (icon_size != -1 && (width > icon_size || height > icon_size)) { double scale; scale = ((double) icon_size) / MAX (width, height); pixbuf = gdk_pixbuf_scale_simple (pixbuf, width * scale, height * scale, GDK_INTERP_BILINEAR); freeme = pixbuf; } if (window && wnck_window_is_minimized (window)) { pixbuf = wnck_selector_dimm_icon (pixbuf); freeme2 = pixbuf; } gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); if (freeme) g_object_unref (freeme); if (freeme2) g_object_unref (freeme2); } static void wnck_selector_set_active_window (WnckSelector *selector, WnckWindow *window) { _wnck_selector_set_window_icon (selector->priv->image, window); selector->priv->icon_window = window; } static void wnck_selector_make_menu_consistent (WnckSelector *selector) { GList *l, *children; int workspace_n; GtkWidget *workspace_item; GtkWidget *separator; gboolean separator_is_first; gboolean separator_is_last; gboolean visible_window; workspace_n = -1; workspace_item = NULL; separator = NULL; separator_is_first = FALSE; separator_is_last = FALSE; visible_window = FALSE; children = gtk_container_get_children (GTK_CONTAINER (selector->priv->menu)); for (l = children; l; l = l->next) { int i; i = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (l->data), "wnck-selector-workspace-n")); if (i > 0) { workspace_n = i - 1; /* we have two consecutive workspace items => hide the first */ if (workspace_item) gtk_widget_hide (workspace_item); workspace_item = GTK_WIDGET (l->data); } else if (GTK_IS_SEPARATOR_MENU_ITEM (l->data)) { if (!visible_window) separator_is_first = TRUE; separator_is_last = TRUE; separator = GTK_WIDGET (l->data); } else if (gtk_widget_get_visible (l->data) && l->data != selector->priv->no_windows_item) { separator_is_last = FALSE; visible_window = TRUE; /* if we know of a workspace item that was not shown */ if (workspace_item) { WnckWindow *window; WnckWorkspace *workspace; window = g_object_get_data (G_OBJECT (l->data), "wnck-selector-window"); if (window) { workspace = wnck_window_get_workspace (window); if (workspace && workspace_n == wnck_workspace_get_number (workspace)) { gtk_widget_show (workspace_item); workspace_n = -1; workspace_item = NULL; } } } } /* end if (normal item) */ } g_list_free (children); /* do we have a trailing workspace item to be hidden? */ if (workspace_item) gtk_widget_hide (workspace_item); if (separator) { if (separator_is_first || separator_is_last) gtk_widget_hide (separator); else gtk_widget_show (separator); } if (visible_window) gtk_widget_hide (selector->priv->no_windows_item); else gtk_widget_show (selector->priv->no_windows_item); } static void wnck_selector_window_icon_changed (WnckWindow *window, WnckSelector *selector) { GtkWidget *item; if (selector->priv->icon_window == window) wnck_selector_set_active_window (selector, window); if (!selector->priv->window_hash) return; item = g_hash_table_lookup (selector->priv->window_hash, window); if (item != NULL) { wnck_image_menu_item_set_image_from_window (WNCK_IMAGE_MENU_ITEM (item), window); } } static void wnck_selector_window_name_changed (WnckWindow *window, WnckSelector *selector) { GtkWidget *item; char *window_name; if (!selector->priv->window_hash) return; item = g_hash_table_lookup (selector->priv->window_hash, window); if (item != NULL) { window_name = _wnck_window_get_name_for_display (window, FALSE, TRUE); gtk_menu_item_set_label (GTK_MENU_ITEM (item), window_name); g_free (window_name); } } static void wnck_selector_window_state_changed (WnckWindow *window, WnckWindowState changed_mask, WnckWindowState new_state, WnckSelector *selector) { GtkWidget *item; char *window_name; if (! (changed_mask & (WNCK_WINDOW_STATE_MINIMIZED | WNCK_WINDOW_STATE_SHADED | WNCK_WINDOW_STATE_SKIP_TASKLIST | WNCK_WINDOW_STATE_DEMANDS_ATTENTION | WNCK_WINDOW_STATE_URGENT))) return; if (!selector->priv->window_hash) return; item = g_hash_table_lookup (selector->priv->window_hash, window); if (item == NULL) return; if (changed_mask & WNCK_WINDOW_STATE_SKIP_TASKLIST) { if (wnck_window_is_skip_tasklist (window)) gtk_widget_hide (item); else gtk_widget_show (item); wnck_selector_make_menu_consistent (selector); gtk_menu_reposition (GTK_MENU (selector->priv->menu)); } if (changed_mask & (WNCK_WINDOW_STATE_DEMANDS_ATTENTION | WNCK_WINDOW_STATE_URGENT)) { if (wnck_window_or_transient_needs_attention (window)) wnck_image_menu_item_make_label_bold (WNCK_IMAGE_MENU_ITEM (item)); else wnck_image_menu_item_make_label_normal (WNCK_IMAGE_MENU_ITEM (item)); } if (changed_mask & (WNCK_WINDOW_STATE_MINIMIZED | WNCK_WINDOW_STATE_SHADED)) { window_name = _wnck_window_get_name_for_display (window, FALSE, TRUE); gtk_menu_item_set_label (GTK_MENU_ITEM (item), window_name); g_free (window_name); } } static void wnck_selector_window_workspace_changed (WnckWindow *window, WnckSelector *selector) { GtkWidget *item; if (!selector->priv->menu || !gtk_widget_get_visible (selector->priv->menu)) return; if (!selector->priv->window_hash) return; item = g_hash_table_lookup (selector->priv->window_hash, window); if (!item) return; /* destroy the item and recreate one so it's at the right position */ gtk_widget_destroy (item); g_hash_table_remove (selector->priv->window_hash, window); wnck_selector_insert_window (selector, window); wnck_selector_make_menu_consistent (selector); gtk_menu_reposition (GTK_MENU (selector->priv->menu)); } static void wnck_selector_active_window_changed (WnckScreen *screen, WnckWindow *previous_window, WnckSelector *selector) { WnckWindow *window; window = wnck_screen_get_active_window (screen); if (selector->priv->icon_window != window) wnck_selector_set_active_window (selector, window); } static void wnck_selector_activate_window (WnckWindow *window) { WnckWorkspace *workspace; guint32 timestamp; /* We're in an activate callback, so gtk_get_current_time() works... */ timestamp = gtk_get_current_event_time (); /* FIXME: THIS IS SICK AND WRONG AND BUGGY. See the end of * http://mail.gnome.org/archives/wm-spec-list/2005-July/msg00032.html * There should only be *one* activate call. */ workspace = wnck_window_get_workspace (window); if (workspace) wnck_workspace_activate (workspace, timestamp); wnck_window_activate (window, timestamp); } static void wnck_selector_drag_begin (GtkWidget *widget, GdkDragContext *context, WnckWindow *window) { while (widget) { if (WNCK_IS_SELECTOR (widget)) break; if (GTK_IS_MENU (widget)) widget = gtk_menu_get_attach_widget (GTK_MENU (widget)); else widget = gtk_widget_get_parent (widget); } if (widget) _wnck_window_set_as_drag_icon (window, context, widget); } static void wnck_selector_drag_data_get (GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time, WnckWindow *window) { gulong xid; xid = wnck_window_get_xid (window); gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, (guchar *)&xid, sizeof (gulong)); } static GtkWidget * wnck_selector_item_new (WnckSelector *selector, const gchar *label, WnckWindow *window) { GtkWidget *item; static const GtkTargetEntry targets[] = { { (gchar *) "application/x-wnck-window-id", 0, 0 } }; item = wnck_image_menu_item_new_with_label (label); if (window != NULL) { /* if window demands attention, bold the label */ if (wnck_window_or_transient_needs_attention (window)) wnck_image_menu_item_make_label_bold (WNCK_IMAGE_MENU_ITEM (item)); g_hash_table_insert (selector->priv->window_hash, window, item); } if (window != NULL) { gtk_drag_source_set (item, GDK_BUTTON1_MASK, targets, 1, GDK_ACTION_MOVE); g_signal_connect_object (item, "drag_data_get", G_CALLBACK (wnck_selector_drag_data_get), G_OBJECT (window), 0); g_signal_connect_object (item, "drag_begin", G_CALLBACK (wnck_selector_drag_begin), G_OBJECT (window), 0); } return item; } static void wnck_selector_workspace_name_changed (WnckWorkspace *workspace, GtkLabel *label) { GtkStyleContext *context; GdkRGBA color; char *name; char *markup; context = gtk_widget_get_style_context (GTK_WIDGET (label)); gtk_style_context_save (context); gtk_style_context_set_state (context, GTK_STATE_FLAG_INSENSITIVE); gtk_style_context_get_color (context, GTK_STATE_FLAG_INSENSITIVE, &color); gtk_style_context_restore (context); name = g_markup_escape_text (wnck_workspace_get_name (workspace), -1); markup = g_strdup_printf ("%s", (int)(color.red * 65535 + 0.5), (int)(color.green * 65535 + 0.5), (int)(color.blue * 65535 + 0.5), name); g_free (name); gtk_label_set_markup (label, markup); g_free (markup); } static void wnck_selector_workspace_label_style_updated (GtkLabel *label, WnckWorkspace *workspace) { wnck_selector_workspace_name_changed (workspace, label); } static void wnck_selector_add_workspace (WnckSelector *selector, WnckScreen *screen, int workspace_n) { WnckWorkspace *workspace; GtkWidget *item; GtkWidget *label; workspace = wnck_screen_get_workspace (screen, workspace_n); /* We use a separator in which we add a label. This makes the menu item not * selectable without any hack. */ item = gtk_separator_menu_item_new (); label = gtk_label_new (""); gtk_label_set_xalign (GTK_LABEL (label), 1.0); gtk_widget_show (label); /* the handler will also take care of setting the name for the first time, * and we'll be able to adapt to theme changes */ g_signal_connect (G_OBJECT (label), "style-updated", G_CALLBACK (wnck_selector_workspace_label_style_updated), workspace); wncklet_connect_while_alive (workspace, "name_changed", G_CALLBACK (wnck_selector_workspace_name_changed), label, label); gtk_container_add (GTK_CONTAINER (item), label); gtk_menu_shell_append (GTK_MENU_SHELL (selector->priv->menu), item); g_object_set_data (G_OBJECT (item), "wnck-selector-workspace-n", GINT_TO_POINTER (workspace_n + 1)); } static GtkWidget * wnck_selector_create_window (WnckSelector *selector, WnckWindow *window) { GtkWidget *item; char *name; name = _wnck_window_get_name_for_display (window, FALSE, TRUE); item = wnck_selector_item_new (selector, name, window); g_free (name); wnck_image_menu_item_set_image_from_window (WNCK_IMAGE_MENU_ITEM (item), window); g_signal_connect_swapped (item, "activate", G_CALLBACK (wnck_selector_activate_window), window); if (!wnck_window_is_skip_tasklist (window)) gtk_widget_show (item); g_object_set_data (G_OBJECT (item), "wnck-selector-window", window); return item; } static void wnck_selector_insert_window (WnckSelector *selector, WnckWindow *window) { GtkWidget *item; WnckScreen *screen; WnckWorkspace *workspace; int workspace_n; int i; screen = wnck_selector_get_screen (selector); workspace = wnck_window_get_workspace (window); if (!workspace && !wnck_window_is_pinned (window)) return; item = wnck_selector_create_window (selector, window); if (!workspace || workspace == wnck_screen_get_active_workspace (screen)) { /* window is pinned or in the current workspace * => insert before the separator */ GList *l, *children; i = 0; children = gtk_container_get_children (GTK_CONTAINER (selector->priv->menu)); for (l = children; l; l = l->next) { if (GTK_IS_SEPARATOR_MENU_ITEM (l->data)) break; i++; } g_list_free (children); gtk_menu_shell_insert (GTK_MENU_SHELL (selector->priv->menu), item, i); } else { workspace_n = wnck_workspace_get_number (workspace); if (workspace_n == wnck_screen_get_workspace_count (screen) - 1) /* window is in last workspace => just append */ gtk_menu_shell_append (GTK_MENU_SHELL (selector->priv->menu), item); else { /* insert just before the next workspace item */ GList *l, *children; i = 0; children = gtk_container_get_children (GTK_CONTAINER (selector->priv->menu)); for (l = children; l; l = l->next) { int j; j = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (l->data), "wnck-selector-workspace-n")); if (j - 1 == workspace_n + 1) break; i++; } g_list_free (children); gtk_menu_shell_insert (GTK_MENU_SHELL (selector->priv->menu), item, i); } } } static void wnck_selector_append_window (WnckSelector *selector, WnckWindow *window) { GtkWidget *item; item = wnck_selector_create_window (selector, window); gtk_menu_shell_append (GTK_MENU_SHELL (selector->priv->menu), item); } static void wnck_selector_window_opened (WnckScreen *screen, WnckWindow *window, WnckSelector *selector) { wnck_selector_connect_to_window (selector, window); if (!selector->priv->menu || !gtk_widget_get_visible (selector->priv->menu)) return; if (!selector->priv->window_hash) return; wnck_selector_insert_window (selector, window); wnck_selector_make_menu_consistent (selector); gtk_menu_reposition (GTK_MENU (selector->priv->menu)); } static void wnck_selector_window_closed (WnckScreen *screen, WnckWindow *window, WnckSelector *selector) { GtkWidget *item; if (window == selector->priv->icon_window) wnck_selector_set_active_window (selector, NULL); if (!selector->priv->menu || !gtk_widget_get_visible (selector->priv->menu)) return; if (!selector->priv->window_hash) return; item = g_hash_table_lookup (selector->priv->window_hash, window); if (!item) return; g_object_set_data (G_OBJECT (item), "wnck-selector-window", NULL); gtk_widget_hide (item); wnck_selector_make_menu_consistent (selector); gtk_menu_reposition (GTK_MENU (selector->priv->menu)); } static void wnck_selector_workspace_created (WnckScreen *screen, WnckWorkspace *workspace, WnckSelector *selector) { if (!selector->priv->menu || !gtk_widget_get_visible (selector->priv->menu)) return; /* this is assuming that the new workspace will have a higher number * than all the old workspaces, which is okay since the old workspaces * didn't disappear in the meantime */ wnck_selector_add_workspace (selector, screen, wnck_workspace_get_number (workspace)); wnck_selector_make_menu_consistent (selector); gtk_menu_reposition (GTK_MENU (selector->priv->menu)); } static void wnck_selector_workspace_destroyed (WnckScreen *screen, WnckWorkspace *workspace, WnckSelector *selector) { GList *l, *children; GtkWidget *destroy; int i; if (!selector->priv->menu || !gtk_widget_get_visible (selector->priv->menu)) return; destroy = NULL; i = wnck_workspace_get_number (workspace); /* search for the item of this workspace so that we destroy it */ children = gtk_container_get_children (GTK_CONTAINER (selector->priv->menu)); for (l = children; l; l = l->next) { int j; j = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (l->data), "wnck-selector-workspace-n")); if (j - 1 == i) destroy = GTK_WIDGET (l->data); else if (j - 1 > i) /* shift the following workspaces */ g_object_set_data (G_OBJECT (l->data), "wnck-selector-workspace-n", GINT_TO_POINTER (j - 1)); } g_list_free (children); if (destroy) gtk_widget_destroy (destroy); wnck_selector_make_menu_consistent (selector); gtk_menu_reposition (GTK_MENU (selector->priv->menu)); } static void wnck_selector_connect_to_window (WnckSelector *selector, WnckWindow *window) { wncklet_connect_while_alive (window, "icon_changed", G_CALLBACK (wnck_selector_window_icon_changed), selector, selector); wncklet_connect_while_alive (window, "name_changed", G_CALLBACK (wnck_selector_window_name_changed), selector, selector); wncklet_connect_while_alive (window, "state_changed", G_CALLBACK (wnck_selector_window_state_changed), selector, selector); wncklet_connect_while_alive (window, "workspace_changed", G_CALLBACK (wnck_selector_window_workspace_changed), selector, selector); } static void wnck_selector_disconnect_from_window (WnckSelector *selector, WnckWindow *window) { g_signal_handlers_disconnect_by_func (window, wnck_selector_window_icon_changed, selector); g_signal_handlers_disconnect_by_func (window, wnck_selector_window_name_changed, selector); g_signal_handlers_disconnect_by_func (window, wnck_selector_window_state_changed, selector); g_signal_handlers_disconnect_by_func (window, wnck_selector_window_workspace_changed, selector); } static void wnck_selector_connect_to_screen (WnckSelector *selector, WnckScreen *screen) { wncklet_connect_while_alive (screen, "active_window_changed", G_CALLBACK (wnck_selector_active_window_changed), selector, selector); wncklet_connect_while_alive (screen, "window_opened", G_CALLBACK (wnck_selector_window_opened), selector, selector); wncklet_connect_while_alive (screen, "window_closed", G_CALLBACK (wnck_selector_window_closed), selector, selector); wncklet_connect_while_alive (screen, "workspace_created", G_CALLBACK (wnck_selector_workspace_created), selector, selector); wncklet_connect_while_alive (screen, "workspace_destroyed", G_CALLBACK (wnck_selector_workspace_destroyed), selector, selector); } static void wnck_selector_disconnect_from_screen (WnckSelector *selector, WnckScreen *screen) { g_signal_handlers_disconnect_by_func (screen, wnck_selector_active_window_changed, selector); g_signal_handlers_disconnect_by_func (screen, wnck_selector_window_opened, selector); g_signal_handlers_disconnect_by_func (screen, wnck_selector_window_closed, selector); g_signal_handlers_disconnect_by_func (screen, wnck_selector_workspace_created, selector); g_signal_handlers_disconnect_by_func (screen, wnck_selector_workspace_destroyed, selector); } static void wnck_selector_destroy_menu (GtkWidget *widget, WnckSelector *selector) { selector->priv->menu = NULL; if (selector->priv->window_hash) g_hash_table_destroy (selector->priv->window_hash); selector->priv->window_hash = NULL; selector->priv->no_windows_item = NULL; } static gboolean wnck_selector_scroll_event (GtkWidget *widget, GdkEventScroll *event) { WnckSelector *selector; WnckScreen *screen; WnckWorkspace *workspace; GList *windows_list; GList *l; WnckWindow *window; WnckWindow *previous_window; gboolean should_activate_next_window; selector = WNCK_SELECTOR (widget); screen = wnck_selector_get_screen (selector); workspace = wnck_screen_get_active_workspace (screen); windows_list = wnck_screen_get_windows (screen); windows_list = g_list_sort (windows_list, wnck_selector_windows_compare); /* Walk through the list of windows until we find the active one * (considering only those windows on the same workspace). * Then, depending on whether we're scrolling up or down, activate the next * window in the list (if it exists), or the previous one. */ previous_window = NULL; should_activate_next_window = FALSE; for (l = windows_list; l; l = l->next) { window = WNCK_WINDOW (l->data); if (wnck_window_is_skip_tasklist (window)) continue; if (workspace && !wnck_window_is_pinned (window) && wnck_window_get_workspace (window) != workspace) continue; if (should_activate_next_window) { wnck_window_activate_transient (window, event->time); return TRUE; } if (wnck_window_is_active (window)) { switch (event->direction) { case GDK_SCROLL_UP: if (previous_window != NULL) { wnck_window_activate_transient (previous_window, event->time); return TRUE; } break; case GDK_SCROLL_DOWN: should_activate_next_window = TRUE; break; case GDK_SCROLL_LEFT: case GDK_SCROLL_RIGHT: /* We ignore LEFT and RIGHT scroll events. */ break; case GDK_SCROLL_SMOOTH: break; default: g_assert_not_reached (); } } previous_window = window; } return TRUE; } static void wnck_selector_menu_hidden (GtkWidget *menu, WnckSelector *selector) { gtk_widget_set_state_flags (GTK_WIDGET (selector), GTK_STATE_FLAG_NORMAL, TRUE); } static void wnck_selector_on_show (GtkWidget *widget, WnckSelector *selector) { GtkWidget *separator; WnckScreen *screen; WnckWorkspace *workspace; int nb_workspace; int i; GList **windows_per_workspace; GList *windows; GList *l, *children; /* Remove existing items */ children = gtk_container_get_children (GTK_CONTAINER (selector->priv->menu)); for (l = children; l; l = l->next) gtk_container_remove (GTK_CONTAINER (selector->priv->menu), l->data); g_list_free (children); if (selector->priv->window_hash) g_hash_table_destroy (selector->priv->window_hash); selector->priv->window_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); screen = wnck_selector_get_screen (selector); nb_workspace = wnck_screen_get_workspace_count (screen); windows_per_workspace = g_malloc0 (nb_workspace * sizeof (GList *)); /* Get windows ordered by workspaces */ windows = wnck_screen_get_windows (screen); windows = g_list_sort (windows, wnck_selector_windows_compare); for (l = windows; l; l = l->next) { workspace = wnck_window_get_workspace (l->data); if (!workspace && wnck_window_is_pinned (l->data)) workspace = wnck_screen_get_active_workspace (screen); if (!workspace) continue; i = wnck_workspace_get_number (workspace); windows_per_workspace[i] = g_list_prepend (windows_per_workspace[i], l->data); } /* Add windows from the current workspace */ workspace = wnck_screen_get_active_workspace (screen); if (workspace) { i = wnck_workspace_get_number (workspace); windows_per_workspace[i] = g_list_reverse (windows_per_workspace[i]); for (l = windows_per_workspace[i]; l; l = l->next) wnck_selector_append_window (selector, l->data); g_list_free (windows_per_workspace[i]); windows_per_workspace[i] = NULL; } /* Add separator */ separator = gtk_separator_menu_item_new (); gtk_menu_shell_append (GTK_MENU_SHELL (selector->priv->menu), separator); /* Add windows from other workspaces */ for (i = 0; i < nb_workspace; i++) { wnck_selector_add_workspace (selector, screen, i); windows_per_workspace[i] = g_list_reverse (windows_per_workspace[i]); for (l = windows_per_workspace[i]; l; l = l->next) wnck_selector_append_window (selector, l->data); g_list_free (windows_per_workspace[i]); windows_per_workspace[i] = NULL; } g_free (windows_per_workspace); selector->priv->no_windows_item = wnck_selector_item_new (selector, _("No Windows Open"), NULL); gtk_widget_set_sensitive (selector->priv->no_windows_item, FALSE); gtk_menu_shell_append (GTK_MENU_SHELL (selector->priv->menu), selector->priv->no_windows_item); wnck_selector_make_menu_consistent (selector); } static void wnck_selector_fill (WnckSelector *selector) { GtkWidget *menu_item; GtkCssProvider *provider; menu_item = gtk_menu_item_new (); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (selector), menu_item); selector->priv->image = gtk_image_new (); gtk_widget_show (selector->priv->image); gtk_container_add (GTK_CONTAINER (menu_item), selector->priv->image); selector->priv->menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), selector->priv->menu); g_signal_connect (selector->priv->menu, "hide", G_CALLBACK (wnck_selector_menu_hidden), selector); g_signal_connect (selector->priv->menu, "destroy", G_CALLBACK (wnck_selector_destroy_menu), selector); g_signal_connect (selector->priv->menu, "show", G_CALLBACK (wnck_selector_on_show), selector); gtk_widget_set_name (GTK_WIDGET (selector), "gnome-panel-window-menu-menu-bar"); provider = gtk_css_provider_new (); gtk_css_provider_load_from_data (provider, "#gnome-panel-window-menu-menu-bar {\n" " border-width: 0px;\n" "}", -1, NULL); gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (selector)), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_unref (provider); gtk_widget_show (GTK_WIDGET (selector)); } static void wnck_selector_init (WnckSelector *selector) { AtkObject *atk_obj; atk_obj = gtk_widget_get_accessible (GTK_WIDGET (selector)); atk_object_set_name (atk_obj, _("Window Selector")); atk_object_set_description (atk_obj, _("Tool to switch between windows")); selector->priv = wnck_selector_get_instance_private (selector); gtk_widget_add_events (GTK_WIDGET (selector), GDK_SCROLL_MASK); } static void wnck_selector_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { WnckSelector *self; self = WNCK_SELECTOR (object); switch (property_id) { case PROP_HANDLE: g_value_set_object (value, self->priv->handle); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void wnck_selector_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { WnckSelector *self; self = WNCK_SELECTOR (object); switch (property_id) { case PROP_HANDLE: g_assert (self->priv->handle == NULL); self->priv->handle = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void install_properties (GObjectClass *object_class) { selector_properties[PROP_HANDLE] = g_param_spec_object ("handle", "handle", "handle", WNCK_TYPE_HANDLE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, LAST_PROP, selector_properties); } static void wnck_selector_class_init (WnckSelectorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->constructor = wnck_selector_constructor; object_class->dispose = wnck_selector_dispose; object_class->finalize = wnck_selector_finalize; object_class->get_property = wnck_selector_get_property; object_class->set_property = wnck_selector_set_property; widget_class->realize = wnck_selector_realize; widget_class->unrealize = wnck_selector_unrealize; widget_class->scroll_event = wnck_selector_scroll_event; install_properties (object_class); gtk_widget_class_set_css_name (widget_class, "wnck-selector"); } static GObject * wnck_selector_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *obj; obj = G_OBJECT_CLASS (wnck_selector_parent_class)->constructor ( type, n_construct_properties, construct_properties); wnck_selector_fill (WNCK_SELECTOR (obj)); return obj; } static void wnck_selector_finalize (GObject *object) { WnckSelector *selector; selector = WNCK_SELECTOR (object); if (selector->priv->window_hash) g_hash_table_destroy (selector->priv->window_hash); selector->priv->window_hash = NULL; g_clear_object (&selector->priv->handle); G_OBJECT_CLASS (wnck_selector_parent_class)->finalize (object); } static void wnck_selector_dispose (GObject *object) { WnckSelector *selector; selector = WNCK_SELECTOR (object); if (selector->priv->menu) gtk_widget_destroy (selector->priv->menu); selector->priv->menu = NULL; selector->priv->image = NULL; selector->priv->icon_window = NULL; G_OBJECT_CLASS (wnck_selector_parent_class)->dispose (object); } static void wnck_selector_realize (GtkWidget *widget) { WnckSelector *selector; WnckScreen *screen; WnckWindow *window; GList *l; GTK_WIDGET_CLASS (wnck_selector_parent_class)->realize (widget); selector = WNCK_SELECTOR (widget); screen = wnck_selector_get_screen (selector); window = wnck_screen_get_active_window (screen); wnck_selector_set_active_window (selector, window); for (l = wnck_screen_get_windows (screen); l; l = l->next) wnck_selector_connect_to_window (selector, l->data); wnck_selector_connect_to_screen (selector, screen); } static void wnck_selector_unrealize (GtkWidget *widget) { WnckSelector *selector; WnckScreen *screen; GList *l; selector = WNCK_SELECTOR (widget); screen = wnck_selector_get_screen (selector); wnck_selector_disconnect_from_screen (selector, screen); for (l = wnck_screen_get_windows (screen); l; l = l->next) wnck_selector_disconnect_from_window (selector, l->data); GTK_WIDGET_CLASS (wnck_selector_parent_class)->unrealize (widget); } /** * wnck_selector_new: * * Creates a new #WnckSelector. The #WnckSelector will list #WnckWindow of the * #WnckScreen it is on. * * Return value: a newly created #WnckSelector. * * Since: 2.10 */ GtkWidget * wnck_selector_new (void) { WnckSelector *selector; selector = g_object_new (WNCK_TYPE_SELECTOR, "handle", _wnck_get_handle (), NULL); return GTK_WIDGET (selector); } /** * wnck_selector_new_with_handle: * @handle: a #WnckHandle * * Creates a new #WnckSelector. The #WnckSelector will list #WnckWindow of the * #WnckScreen it is on. * * Returns: a newly created #WnckSelector. */ GtkWidget * wnck_selector_new_with_handle (WnckHandle *handle) { WnckSelector *self; self = g_object_new (WNCK_TYPE_SELECTOR, "handle", handle, NULL); return GTK_WIDGET (self); }