summaryrefslogtreecommitdiff
path: root/libwnck/selector.c
diff options
context:
space:
mode:
authorVincent Untz <vuntz@gnome.org>2007-05-19 00:11:34 +0000
committerVincent Untz <vuntz@src.gnome.org>2007-05-19 00:11:34 +0000
commitf86cf4888150c74e0bf594fca5bad067cd3e27ef (patch)
tree0021220fc460bab6bc078f55e7b67876441d2f4d /libwnck/selector.c
parent64c0261f8e19e0e8951ac434f9d1f357d83b47cf (diff)
downloadlibwnck-f86cf4888150c74e0bf594fca5bad067cd3e27ef.tar.gz
Show workspace names in the window selector. Fix bug #150703.
2007-05-19 Vincent Untz <vuntz@gnome.org> Show workspace names in the window selector. Fix bug #150703. * libwnck/selector.c: (wncklet_connect_while_alive): moved in the file (wnck_selector_make_menu_consistent): updated to handle with menu items that are representing a workspace name (wnck_selector_window_workspace_changed): new (wnck_selector_item_new): don't setup dnd if there's no window ("no windows open" case) (wnck_selector_workspace_label_exposed): new (wnck_selector_workspace_name_changed): new (wnck_selector_add_workspace): new, append a workspace menu item to the menu (wnck_selector_create_window): moved code from wnck_selector_add_window() here, but do not insert the menu item in the menu (wnck_selector_insert_window): new, to insert a window menu item at the correct place wrt workspaces (more expensive than just appending the menu item) (wnck_selector_append_window): new (wnck_selector_window_opened): updated, to insert the item at the correct place (wnck_selector_window_closed): updated, to disable the menu item wrt workspace consistency in the menu (wnck_selector_workspace_created): new; to add the workspace menu item (wnck_selector_workspace_destroyed): new, to remove the workspace menu item (wnck_selector_connect_to_window): connect to "workspace_changed" (wnck_selector_disconnect_from_window): disconnect (wnck_selector_connect_to_screen): connect to "workspace_created" and "workspace_destroyed" (wnck_selector_disconnect_from_screen): disconnect (wnck_selector_scroll_cb): update the algorithm since the way we append window menu items in the menu has changed (wnck_selector_on_show): updated to add windows, ordered by workspaces svn path=/trunk/; revision=1245
Diffstat (limited to 'libwnck/selector.c')
-rw-r--r--libwnck/selector.c471
1 files changed, 399 insertions, 72 deletions
diff --git a/libwnck/selector.c b/libwnck/selector.c
index e3a96bb..ecef3d4 100644
--- a/libwnck/selector.c
+++ b/libwnck/selector.c
@@ -64,6 +64,27 @@ static void wnck_selector_destroy (GtkObject *object);
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 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)
{
@@ -191,38 +212,87 @@ static void
wnck_selector_make_menu_consistent (WnckSelector *selector)
{
GList *l;
+ 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;
for (l = GTK_MENU_SHELL (selector->priv->menu)->children; l; l = l->next)
{
- if (GTK_IS_SEPARATOR_MENU_ITEM (l->data))
+ 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);
- continue;
}
-
- if (GTK_WIDGET_VISIBLE (l->data) &&
- l->data != selector->priv->no_windows_item)
+ else if (GTK_WIDGET_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) */
}
- if (separator_is_first || separator_is_last)
- gtk_widget_hide (separator);
- else
- gtk_widget_show (separator);
+ /* 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);
@@ -367,6 +437,32 @@ wnck_selector_window_state_changed (WnckWindow *window,
}
}
+
+static void
+wnck_selector_window_workspace_changed (WnckWindow *window,
+ WnckSelector *selector)
+{
+ window_hash_item *item;
+
+ item = NULL;
+
+ 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->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,
WnckSelector *selector)
@@ -496,26 +592,89 @@ wnck_selector_item_new (WnckSelector *selector,
wnck_selector_get_width (GTK_WIDGET (selector),
label), -1);
- gtk_drag_source_set (item,
- GDK_BUTTON1_MASK,
- targets, 1,
- GDK_ACTION_MOVE);
+ 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 gboolean
+wnck_selector_workspace_label_exposed (GtkWidget *widget)
+{
+ /* Bad hack to make the label draw normally, instead of insensitive. */
+ widget->state = GTK_STATE_NORMAL;
- g_signal_connect_object (item, "drag_data_get",
- G_CALLBACK (wnck_selector_drag_data_get),
- G_OBJECT (window),
- 0);
+ return FALSE;
+}
- g_signal_connect_object (item, "drag_begin",
- G_CALLBACK (wnck_selector_drag_begin),
- G_OBJECT (window),
- 0);
+static void
+wnck_selector_workspace_name_changed (WnckWorkspace *workspace,
+ GtkLabel *label)
+{
+ GdkColor *color;
+ char *name;
+ char *markup;
- return item;
+ color = &GTK_WIDGET (label)->style->fg[GTK_STATE_INSENSITIVE];
+
+ name = g_markup_escape_text (wnck_workspace_get_name (workspace), -1);
+ markup = g_strdup_printf ("<span size=\"x-small\" style=\"italic\" foreground=\"#%.2x%.2x%.2x\">%s</span>",
+ color->red, color->green, color->blue, name);
+ g_free (name);
+
+ gtk_label_set_markup (label, markup);
+ g_free (markup);
}
static void
-wnck_selector_add_window (WnckSelector *selector, WnckWindow *window)
+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);
+
+ item = gtk_menu_item_new ();
+ gtk_widget_set_sensitive (item, FALSE);
+
+ label = gtk_label_new ("");
+ gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+ gtk_widget_show (label);
+ g_signal_connect (G_OBJECT (label), "expose-event",
+ G_CALLBACK (wnck_selector_workspace_label_exposed), NULL);
+ wncklet_connect_while_alive (workspace, "name_changed",
+ G_CALLBACK (wnck_selector_workspace_name_changed),
+ label, label);
+ wnck_selector_workspace_name_changed (workspace, GTK_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)
{
WnckWorkspace *workspace;
GtkWidget *item;
@@ -540,33 +699,107 @@ wnck_selector_add_window (WnckSelector *selector, WnckWindow *window)
workspace =
wnck_screen_get_active_workspace (wnck_selector_get_screen (selector));
- /* FIXME: tasklist_include_window_impl() is similar to this, but more
- * complete */
- if (wnck_window_is_pinned (window) ||
- wnck_window_get_workspace (window) == workspace)
- gtk_menu_shell_prepend (GTK_MENU_SHELL (selector->priv->menu), item);
- else
- gtk_menu_shell_append (GTK_MENU_SHELL (selector->priv->menu), item);
-
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_window_opened (WnckScreen *screen,
- WnckWindow *window, WnckSelector *selector)
+wnck_selector_insert_window (WnckSelector *selector, WnckWindow *window)
{
- if (selector->priv->menu && GTK_WIDGET_VISIBLE (selector->priv->menu))
+ 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))
{
- wnck_selector_add_window (selector, window);
- wnck_selector_make_menu_consistent (selector);
+ /* window is pinned or in the current workspace
+ * => insert before the separator */
+ GList *l;
- gtk_menu_reposition (GTK_MENU (selector->priv->menu));
+ i = 0;
+
+ for (l = GTK_MENU_SHELL (selector->priv->menu)->children; l; l = l->next)
+ {
+ if (GTK_IS_SEPARATOR_MENU_ITEM (l->data))
+ break;
+ i++;
+ }
+
+ 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;
+
+ i = 0;
+
+ for (l = GTK_MENU_SHELL (selector->priv->menu)->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++;
+ }
+
+ 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)
+{
+ if (!selector->priv->menu || !GTK_WIDGET_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));
wnck_selector_connect_to_window (selector, window);
}
@@ -584,12 +817,14 @@ wnck_selector_window_closed (WnckScreen *screen,
return;
if (!selector->priv->window_hash)
- return;
+ return;
item = g_hash_table_lookup (selector->priv->window_hash, window);
if (!item)
return;
+ g_object_set_data (G_OBJECT (item->item), "wnck-selector-window", NULL);
+
gtk_widget_hide (item->item);
wnck_selector_make_menu_consistent (selector);
@@ -597,19 +832,57 @@ wnck_selector_window_closed (WnckScreen *screen,
}
static void
-wncklet_connect_while_alive (gpointer object,
- const char *signal,
- GCallback func,
- gpointer func_data, gpointer alive_object)
+wnck_selector_workspace_created (WnckScreen *screen,
+ WnckWorkspace *workspace,
+ WnckSelector *selector)
{
- GClosure *closure;
+ /* 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));
- 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);
+ 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;
+ GtkWidget *destroy;
+ int i;
+
+ destroy = NULL;
+
+ i = wnck_workspace_get_number (workspace);
+
+ /* search for the item of this workspace so that we destroy it */
+ for (l = GTK_MENU_SHELL (selector->priv->menu)->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));
+ }
+
+ if (destroy)
+ gtk_widget_destroy (destroy);
+
+ wnck_selector_make_menu_consistent (selector);
+
+ gtk_menu_reposition (GTK_MENU (selector->priv->menu));
}
static void
@@ -622,9 +895,11 @@ wnck_selector_connect_to_window (WnckSelector *selector, WnckWindow *window)
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);
+ 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
@@ -640,6 +915,9 @@ wnck_selector_disconnect_from_window (WnckSelector *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
@@ -657,6 +935,14 @@ wnck_selector_connect_to_screen (WnckSelector *selector, WnckScreen *screen)
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
@@ -672,6 +958,12 @@ wnck_selector_disconnect_from_screen (WnckSelector *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
@@ -707,11 +999,6 @@ wnck_selector_scroll_cb (WnckSelector *selector,
* (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.
- * Note that earlier windows in the GList* windows_list are shown lower in the
- * menu, and later windows higher, since the windows are added with
- * gtk_menu_shell_prepend in the function wnck_selector_add_window.
- * Thus, a SCROLL_DOWN should activate the previous window in the list, and
- * a SCROLL_UP should activate the next window in the list.
*/
previous_window = NULL;
should_activate_next_window = FALSE;
@@ -737,10 +1024,6 @@ wnck_selector_scroll_cb (WnckSelector *selector,
switch (event->direction)
{
case GDK_SCROLL_UP:
- should_activate_next_window = TRUE;
- break;
-
- case GDK_SCROLL_DOWN:
if (previous_window != NULL)
{
wnck_window_activate_transient (previous_window,
@@ -749,6 +1032,10 @@ wnck_selector_scroll_cb (WnckSelector *selector,
}
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. */
@@ -776,6 +1063,10 @@ 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;
@@ -785,23 +1076,59 @@ wnck_selector_on_show (GtkWidget *widget, WnckSelector *selector)
gtk_container_remove (GTK_CONTAINER (selector->priv->menu), l->data);
g_list_free (children);
- /* Add separator */
- separator = gtk_separator_menu_item_new ();
- gtk_menu_shell_append (GTK_MENU_SHELL (selector->priv->menu), separator);
-
-
- /* Add windows */
- screen = wnck_selector_get_screen (selector);
- windows = wnck_screen_get_windows (screen);
-
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, g_free);
+ 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);
for (l = windows; l; l = l->next)
- wnck_selector_add_window (selector, l->data);
+ {
+ 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"),