diff options
author | Federico Mena Quintero <federico@ximian.com> | 2003-11-22 23:49:12 +0000 |
---|---|---|
committer | Federico Mena Quintero <federico@src.gnome.org> | 2003-11-22 23:49:12 +0000 |
commit | e6ec7fc9a3fca98ac1cfa12aa920f6e7b459af8b (patch) | |
tree | 9c0e804f6892ef1e39dbc297bef486a47c7ac7e6 /libwnck/class-group.c | |
parent | e0879f7607565847f694a4127807557c1b032d60 (diff) | |
download | libwnck-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.c | 612 |
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; +} |