summaryrefslogtreecommitdiff
path: root/libwnck/class-group.c
diff options
context:
space:
mode:
authorFederico Mena Quintero <federico@ximian.com>2003-11-22 23:49:12 +0000
committerFederico Mena Quintero <federico@src.gnome.org>2003-11-22 23:49:12 +0000
commite6ec7fc9a3fca98ac1cfa12aa920f6e7b459af8b (patch)
tree9c0e804f6892ef1e39dbc297bef486a47c7ac7e6 /libwnck/class-group.c
parente0879f7607565847f694a4127807557c1b032d60 (diff)
downloadlibwnck-e6ec7fc9a3fca98ac1cfa12aa920f6e7b459af8b.tar.gz
Added a WNCK_TASK_CLASS_GROUP type. (struct _WnckTask): Added a
2003-11-22 Federico Mena Quintero <federico@ximian.com> * libwnck/tasklist.c (WnckTaskType): Added a WNCK_TASK_CLASS_GROUP type. (struct _WnckTask): Added a class_group field, analogous to the application and window fields. (struct _WnckTasklistPrivate): Added class_groups and class_group_hash fields. Replaced the active_app field with an active_class_group field. (wnck_task_new_from_window): Set the class_group of the task from the window. (wnck_tasklist_update_lists): Create a WnckClassGroup if necessary; do not use widgets for applications. (wnck_task_new_from_class_group): New function. (wnck_task_new_from_application): Set the class_group of the task to NULL. (wnck_task_new_from_startup_sequence): Likewise. (wnck_task_compare): Sort groups before everything else. (wnck_task_state_changed): Use the class_group rather than the application. (wnck_task_get_text): Handle class groups. (wnck_task_button_toggled): Likewise. (wnck_task_popup_menu): Handle class groups as well as applications. (wnck_tasklist_free_tasks): s/active_app/active_class_group. (wnck_tasklist_change_active_task): Likewise. (wnck_task_finalize): Unref the class_group. (wnck_tasklist_init): Create the class_group_hash. (wnck_tasklist_finalize): Free the class_group_hash. (wnck_tasklist_size_request): Use the class groups rather than applications. (wnck_tasklist_size_allocate): Likewise. (wnck_tasklist_forall): Likewise. (wnck_tasklist_remove): Likewise. (wnck_task_get_highest_scored): Likewise. (wnck_tasklist_score_groups): Likewise. (wnck_task_new_from_application): Do not create widgets. (wnck_task_button_toggled): We don't need the window state here, so don't fetch it. (wnck_task_get_text): Use a window's name rather than its icon name. They seem to be the same for most windows, and Emacs screws up the icon name, setting it to "emacs" only --- it's useless. (WnckTaskType): Removed WNCK_TASK_APPLICATION. (struct _WnckTask): Removed the application-related fields. (struct _WnckTasklistPrivate): Likewise. (wnck_task_finalize): Likewise. (wnck_tasklist_finalize): Likewise. (wnck_tasklist_free_tasks): Likewise. (wnck_tasklist_update_lists): Likewise. (wnck_task_popup_menu): Likewise. (wnck_task_button_toggled): Likewise. (wnck_task_get_text): Likewise. (wnck_task_get_icon): Likewise. (wnck_task_button_press_event): Likewise. (wnck_task_create_widgets): Likewise. (wnck_task_compare): Likewise. (wnck_task_new_from_window): Likewise. (wnck_task_new_from_startup_sequence): Likewise. (wnck_task_app_name_changed): Removed. (wnck_task_new_from_application): Removed. (wnck_task_class_group_expose): Renamed from wnck_task_app_expose(). * libwnck/screen.h (struct _WnckScreenClass): Added ::class_group_opened() and ::class_group_closed() signals. * libwnck/screen.c (update_client_list): Handle class groups by creating new ones as needed and getting rid of empty ones. (emit_class_group_opened): New function. (emit_application_closed): New function. (wnck_screen_class_init): Create the new signals. * libwnck/window.c (struct _WnckWindowPrivate): Added a class_group field. (wnck_window_get_class_group): New function. (_wnck_window_set_class_group): New function. * libwnck/class-group.[ch]: New files that implement a simple set of windows grouped by their resource class names. * libwnck/Makefile.am: Added class-group.[ch]. * libwnck/test-tasklist.c (main): Set a default size so I don't have to resize the test window every time. * configure.in: Increment LIBWNCK_CURRENT and LIBWNCK_AGE.
Diffstat (limited to 'libwnck/class-group.c')
-rw-r--r--libwnck/class-group.c612
1 files changed, 612 insertions, 0 deletions
diff --git a/libwnck/class-group.c b/libwnck/class-group.c
new file mode 100644
index 0000000..dcf5c02
--- /dev/null
+++ b/libwnck/class-group.c
@@ -0,0 +1,612 @@
+/* class group object */
+
+/*
+ * Copyright (C) 2003 Ximian, Inc.
+ * Authors: Federico Mena-Quintero <federico@ximian.com>
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include "class-group.h"
+#include "window.h"
+#include "private.h"
+
+
+/* Private part of the WnckClassGroup structure */
+struct _WnckClassGroupPrivate {
+ char *res_class;
+ char *name;
+ GList *windows;
+
+ GdkPixbuf *icon;
+ GdkPixbuf *mini_icon;
+};
+
+#define ICON_SIZE 32
+#define MINI_ICON_SIZE 16
+
+/* Hash table that maps res_class strings -> WnckClassGroup instances */
+static GHashTable *class_group_hash = NULL;
+
+
+
+static void wnck_class_group_class_init (WnckClassGroupClass *class);
+static void wnck_class_group_init (WnckClassGroup *class_group);
+static void wnck_class_group_finalize (GObject *object);
+
+enum {
+ NAME_CHANGED,
+ ICON_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static gpointer *parent_class;
+
+
+GType
+wnck_class_group_get_type (void)
+{
+ static GType object_type = 0;
+
+ g_type_init ();
+
+ if (!object_type)
+ {
+ static const GTypeInfo object_info =
+ {
+ sizeof (WnckClassGroupClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) wnck_class_group_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (WnckClassGroup),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) wnck_class_group_init,
+ };
+
+ object_type = g_type_register_static (G_TYPE_OBJECT,
+ "WnckClassGroup",
+ &object_info, 0);
+ }
+
+ return object_type;
+}
+
+static void
+wnck_class_group_class_init (WnckClassGroupClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->finalize = wnck_class_group_finalize;
+
+ signals[NAME_CHANGED] =
+ g_signal_new ("name_changed",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (WnckClassGroupClass, name_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[ICON_CHANGED] =
+ g_signal_new ("icon_changed",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (WnckClassGroupClass, icon_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+wnck_class_group_init (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+
+ priv = g_new (WnckClassGroupPrivate, 1);
+ class_group->priv = priv;
+
+ priv->res_class = NULL;
+ priv->name = NULL;
+ priv->windows = NULL;
+
+ priv->icon = NULL;
+ priv->mini_icon = NULL;
+}
+
+static void
+wnck_class_group_finalize (GObject *object)
+{
+ WnckClassGroup *class_group;
+ WnckClassGroupPrivate *priv;
+
+ class_group = WNCK_CLASS_GROUP (object);
+ priv = class_group->priv;
+
+ if (priv->res_class)
+ g_free (priv->res_class);
+
+ if (priv->name)
+ g_free (priv->name);
+
+ g_list_free (priv->windows);
+
+ if (priv->icon)
+ g_object_unref (priv->icon);
+
+ if (priv->mini_icon)
+ g_object_unref (priv->mini_icon);
+
+ g_free (priv);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * wnck_class_group_get:
+ * @res_class: Name of the sought resource class.
+ *
+ * Gets an existing class group based on its resource class name.
+ *
+ * Return value: An existing #WnckClassGroup, or NULL if there is no groups with
+ * the specified @res_class.
+ **/
+WnckClassGroup *
+wnck_class_group_get (const char *res_class)
+{
+ if (!class_group_hash)
+ return NULL;
+ else
+ return g_hash_table_lookup (class_group_hash, res_class ? res_class : "");
+}
+
+/**
+ * _wnck_class_group_create:
+ * @res_class: Name of the resource class for the group.
+ *
+ * Creates a new WnckClassGroup with the specified resource class name. If
+ * @res_class is #NULL, then windows without a resource class name will get
+ * grouped under this class group.
+ *
+ * Return value: A newly-created #WnckClassGroup, or an existing one that
+ * matches the @res_class.
+ **/
+WnckClassGroup *
+_wnck_class_group_create (const char *res_class)
+{
+ WnckClassGroup *class_group;
+ WnckClassGroupPrivate *priv;
+
+ if (class_group_hash == NULL)
+ class_group_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_return_val_if_fail (g_hash_table_lookup (class_group_hash, res_class ? res_class : "") == NULL,
+ NULL);
+
+ class_group = g_object_new (WNCK_TYPE_CLASS_GROUP, NULL);
+ priv = class_group->priv;
+
+ priv->res_class = g_strdup (res_class ? res_class : "");
+
+ g_hash_table_insert (class_group_hash, priv->res_class, class_group);
+ /* Hash now owns one ref, caller gets none */
+
+ return class_group;
+}
+
+/**
+ * _wnck_class_group_destroy:
+ * @class_group: A window class group.
+ *
+ * Destroys the specified @class_group.
+ **/
+void
+_wnck_class_group_destroy (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+
+ g_return_if_fail (WNCK_IS_CLASS_GROUP (class_group));
+
+ priv = class_group->priv;
+
+ g_hash_table_remove (class_group_hash, priv->res_class);
+
+ g_free (priv->res_class);
+ priv->res_class = NULL;
+
+ /* remove hash's ref on the class group */
+ g_object_unref (class_group);
+}
+
+static const char *
+get_name_from_applications (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+ const char *first_name;
+ GList *l;
+
+ priv = class_group->priv;
+
+ /* Try to get the name from the group leaders. If all have the same name, we
+ * can use that.
+ */
+
+ first_name = NULL;
+
+ for (l = priv->windows; l; l = l->next)
+ {
+ WnckWindow *w;
+ WnckApplication *app;
+
+ w = WNCK_WINDOW (l->data);
+ app = wnck_window_get_application (w);
+
+ if (!first_name)
+ {
+ if (app)
+ first_name = wnck_application_get_name (app);
+ }
+ else
+ {
+ if (!app || strcmp (first_name, wnck_application_get_name (app)) != 0)
+ break;
+ }
+ }
+
+ if (!l)
+ {
+ /* All names are the same, so use one of them */
+ return first_name;
+ }
+ else
+ return NULL;
+}
+
+static const char *
+get_name_from_windows (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+ const char *first_name;
+ GList *l;
+
+ priv = class_group->priv;
+
+ /* Try to get the name from windows, following the same rationale as
+ * get_name_from_applications()
+ */
+
+ first_name = NULL;
+
+ for (l = priv->windows; l; l = l->next)
+ {
+ WnckWindow *window;
+
+ window = WNCK_WINDOW (l->data);
+
+ if (!first_name)
+ first_name = wnck_window_get_name (window);
+ else
+ if (strcmp (first_name, wnck_window_get_name (window)) != 0)
+ break;
+ }
+
+ if (!l)
+ {
+ /* All names are the same, so use one of them */
+ return first_name;
+ }
+ else
+ return NULL;
+}
+
+
+/* Gets a sensible name for the class group from the application group leaders
+ * or from individual windows.
+ */
+static void
+set_name (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+ const char *new_name;
+
+ priv = class_group->priv;
+
+ if (priv->name)
+ {
+ g_free (priv->name);
+ priv->name = NULL;
+ }
+
+ new_name = get_name_from_applications (class_group);
+
+ if (!new_name)
+ {
+ new_name = get_name_from_windows (class_group);
+
+ if (!new_name)
+ new_name = priv->res_class;
+ }
+
+ g_assert (new_name != NULL);
+
+ if (!priv->name || strcmp (priv->name, new_name) != 0)
+ {
+ g_free (priv->name);
+ priv->name = g_strdup (new_name);
+
+ g_signal_emit (G_OBJECT (class_group), signals[NAME_CHANGED], 0);
+ }
+}
+
+/* Walks the list of applications, trying to get an icon from them */
+static void
+get_icons_from_applications (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf **mini_icon)
+{
+ WnckClassGroupPrivate *priv;
+ GList *l;
+
+ priv = class_group->priv;
+
+ *icon = NULL;
+ *mini_icon = NULL;
+
+ for (l = priv->windows; l; l = l->next)
+ {
+ WnckWindow *window;
+ WnckApplication *app;
+
+ window = WNCK_WINDOW (l->data);
+ app = wnck_window_get_application (window);
+ if (app)
+ {
+ *icon = wnck_application_get_icon (app);
+ *mini_icon = wnck_application_get_mini_icon (app);
+
+ if (*icon && *mini_icon)
+ return;
+ else
+ {
+ *icon = NULL;
+ *mini_icon = NULL;
+ }
+ }
+ }
+}
+
+/* Walks the list of windows, trying to get an icon from them */
+static void
+get_icons_from_windows (WnckClassGroup *class_group, GdkPixbuf **icon, GdkPixbuf **mini_icon)
+{
+ WnckClassGroupPrivate *priv;
+ GList *l;
+
+ priv = class_group->priv;
+
+ *icon = NULL;
+ *mini_icon = NULL;
+
+ for (l = priv->windows; l; l = l->next)
+ {
+ WnckWindow *window;
+
+ window = WNCK_WINDOW (l->data);
+
+ *icon = wnck_window_get_icon (window);
+ *mini_icon = wnck_window_get_mini_icon (window);
+
+ if (*icon && *mini_icon)
+ return;
+ else
+ {
+ *icon = NULL;
+ *mini_icon = NULL;
+ }
+ }
+}
+
+/* Gets a sensible icon and mini_icon for the class group from the application
+ * group leaders or from individual windows.
+ */
+static void
+set_icon (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+ GdkPixbuf *icon, *mini_icon;
+
+ priv = class_group->priv;
+
+ get_icons_from_applications (class_group, &icon, &mini_icon);
+
+ if (!icon || !mini_icon)
+ get_icons_from_windows (class_group, &icon, &mini_icon);
+
+ if (!icon || !mini_icon)
+ _wnck_get_fallback_icons (&icon, ICON_SIZE, ICON_SIZE,
+ &mini_icon, MINI_ICON_SIZE, MINI_ICON_SIZE);
+
+ g_assert (icon && mini_icon);
+
+ if (priv->icon)
+ g_object_unref (priv->icon);
+
+ if (priv->mini_icon)
+ g_object_unref (priv->mini_icon);
+
+ priv->icon = g_object_ref (icon);
+ priv->mini_icon = g_object_ref (mini_icon);
+
+ g_signal_emit (G_OBJECT (class_group), signals[ICON_CHANGED], 0);
+}
+
+/**
+ * _wnck_class_group_add_window:
+ * @class_group: A window class group.
+ * @window: A window.
+ *
+ * Adds a window to a class group. You should only do this if the resource
+ * class of the window matches the @class_group<!-- -->'s.
+ **/
+void
+_wnck_class_group_add_window (WnckClassGroup *class_group,
+ WnckWindow *window)
+{
+ WnckClassGroupPrivate *priv;
+
+ g_return_if_fail (WNCK_IS_CLASS_GROUP (class_group));
+ g_return_if_fail (WNCK_IS_WINDOW (window));
+ g_return_if_fail (wnck_window_get_class_group (window) == NULL);
+
+ priv = class_group->priv;
+
+ priv->windows = g_list_prepend (priv->windows, window);
+ _wnck_window_set_class_group (window, class_group);
+
+ set_name (class_group);
+ set_icon (class_group);
+
+ /* FIXME: should we monitor class group changes on the window? The ICCCM says
+ * that clients should never change WM_CLASS unless the window is withdrawn.
+ */
+}
+
+/**
+ * _wnck_class_group_remove_window:
+ * @class_group: A window class group.
+ * @window: A window.
+ *
+ * Removes a window from the list of windows that are grouped under the
+ * specified @class_group.
+ **/
+void
+_wnck_class_group_remove_window (WnckClassGroup *class_group,
+ WnckWindow *window)
+{
+ WnckClassGroupPrivate *priv;
+
+ g_return_if_fail (WNCK_IS_CLASS_GROUP (class_group));
+ g_return_if_fail (WNCK_IS_WINDOW (window));
+ g_return_if_fail (wnck_window_get_class_group (window) == class_group);
+
+ priv = class_group->priv;
+
+ priv->windows = g_list_remove (priv->windows, window);
+ _wnck_window_set_class_group (window, NULL);
+}
+
+/**
+ * wnck_class_group_get_windows:
+ * @class_group: A window class group.
+ *
+ * Gets the list of windows that are grouped in a @class_group.
+ *
+ * Return value: A list of windows, or NULL if the group contains no windows.
+ * The list should not be freed, as it belongs to the @class_group.
+ **/
+GList *
+wnck_class_group_get_windows (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+
+ g_return_val_if_fail (class_group != NULL, NULL);
+
+ priv = class_group->priv;
+ return priv->windows;
+}
+
+/**
+ * wnck_class_group_get_res_class:
+ * @class_group: A window class group.
+ *
+ * Queries the resource class name for a class group.
+ *
+ * Return value: The resource class name of the specified @class_group, or the
+ * empty string if the group has no name. The string should not be freed.
+ **/
+const char *
+wnck_class_group_get_res_class (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+
+ g_return_val_if_fail (class_group != NULL, NULL);
+
+ priv = class_group->priv;
+ return priv->res_class;
+}
+
+/**
+ * wnck_class_group_get_name:
+ * @class_group: A window class group.
+ *
+ * Queries the human-readable name for a class group.
+ *
+ * Return value: Name of the class group.
+ **/
+const char *
+wnck_class_group_get_name (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+
+ g_return_val_if_fail (class_group != NULL, NULL);
+
+ priv = class_group->priv;
+
+ return priv->name;
+}
+
+/**
+ * wnck_class_group_get_icon:
+ * @class_group: A window class group.
+ *
+ * Queries the icon to be used for a class group.
+ *
+ * Return value: The icon to use.
+ **/
+GdkPixbuf *
+wnck_class_group_get_icon (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+
+ g_return_val_if_fail (class_group != NULL, NULL);
+
+ priv = class_group->priv;
+
+ return priv->icon;
+}
+
+/**
+ * wnck_class_group_get_mini_icon:
+ * @class_group: A window class group.
+ *
+ * Queries the mini-icon to be used for a class group.
+ *
+ * Return value: The mini-icon to use.
+ **/
+GdkPixbuf *
+wnck_class_group_get_mini_icon (WnckClassGroup *class_group)
+{
+ WnckClassGroupPrivate *priv;
+
+ g_return_val_if_fail (class_group != NULL, NULL);
+
+ priv = class_group->priv;
+
+ return priv->mini_icon;
+}