Re: Fixing window grouping
- From: Federico Mena Quintero <federico ximian com>
- To: Havoc Pennington <hp redhat com>
- Cc: desktop-devel-list gnome org
- Subject: Re: Fixing window grouping
- Date: Fri, 03 Oct 2003 16:45:15 -0500
On Wed, 2003-10-01 at 18:00, Havoc Pennington wrote:
> - pick name of an arbitrary WnckApplication in the class group
> - if no name set on any WnckApplication, if all the windows in the
> group have the same title, you could use that
> - otherwise use the name of the class
Done. The code now does this:
- If all the group leaders have the same title, use that.
- Else, if all the windows have the same title, use that.
- Else, use the WM_CLASS.
WnckClassGroup has a name_changed signal, so bizarre apps that change
this should work fine.
Emacs and everything else I tested seem to work nicely with this scheme.
> Or other heuristic along these lines. Of course icon works the same way.
Done.
> - if adding/removing/changing ABI, the libtool versioning needs to be
> updated in configure.in
Done. See below; I put the signal handler pointers inside the existing
#ifdef.
> - I don't really understand what's going on in tasklist.c,
> specifically why WNCK_TASK_APPLICATION continues to exist
Fixed. I had left the application bits there because I was not sure if
we would use them for anything in the tasklist.
[We still need WnckApplication to keep track of group leaders, but it's
all behind-the-scenes now.]
> - there are various FIXME and #if 0
Fixed.
> - WnckClassGroup has a get_name() method returning the class,
> I would have: get_name() (human-readable name), get_class or
> get_res_class returning the actual res class string, and
> get_icon().
Done.
> - in screen.h you break the ABI, which means the libtool needs
> adjusting as above, but also it's time to add padding to all the
> vtables (see WnckScreenClass #if 0)
OK, I put the signals in an #if block, just like the
showing_desktop_changed signal. This would be for inclusion in 2.2.
For 2.4 and later, it would probably be a good idea to add the padding
fields.
> - _wnck_get_res_class_utf8() is a round trip; as such, I think it
> should be done only once. Which is just a matter of using
> wnck_window_get_resource_class instead in screen.c I think.
Oops, you are right. Done.
> - You are messing up my beautiful while loops! for loops are the
> spawn of evil!
Naah. A for loop is more clear; it reads as
iterate_over (list) {
blah blah;
}
where in a while loop you have to decode what the hell is being
iterated.
The GNOME Programming Guidelines says you should use for loops to
iterate over lists, anyway...
> - shouldn't your copyright notice say Novell? ;-)
Mu :)
> Thanks for the patch; does this mean you're the libwnck maintainer now?
Eek, I don't know. Ideally wnck should just be a Solved Problem(tm),
but I guess it does need a maintainer. I guess I can do tarball
releases every now and then if needed.
Federico
? confdefs.h
? libwnck/class-group.c
? libwnck/class-group.h
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/libwnck/ChangeLog,v
retrieving revision 1.174.2.19
diff -u -r1.174.2.19 ChangeLog
--- ChangeLog 31 Jul 2003 10:08:02 -0000 1.174.2.19
+++ ChangeLog 3 Oct 2003 21:36:57 -0000
@@ -1,3 +1,89 @@
+2003-10-03 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.
+
2003-07-29 Arvind Samptur <arvind samptur wipro com>
* libwnck/xutils.c (_wnck_get_utf8_list): Number of
Index: configure.in
===================================================================
RCS file: /cvs/gnome/libwnck/configure.in,v
retrieving revision 1.73.2.16
diff -u -r1.73.2.16 configure.in
--- configure.in 1 Jul 2003 17:24:42 -0000 1.73.2.16
+++ configure.in 3 Oct 2003 21:36:57 -0000
@@ -8,7 +8,7 @@
dnl libtool versioning for libwnck
dnl increment if the interface has additions, changes, removals.
-LIBWNCK_CURRENT=9
+LIBWNCK_CURRENT=10
dnl increment any time the source changes; set to
dnl 0 if you increment CURRENT
@@ -17,7 +17,7 @@
dnl increment if any interfaces have been added; set to 0
dnl if any interfaces have been removed. removal has
dnl precedence over adding, so set to 0 if both happened.
-LIBWNCK_AGE=5
+LIBWNCK_AGE=6
AC_SUBST(LIBWNCK_CURRENT)
AC_SUBST(LIBWNCK_REVISION)
Index: libwnck/Makefile.am
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/Makefile.am,v
retrieving revision 1.16
diff -u -r1.16 Makefile.am
--- libwnck/Makefile.am 31 Oct 2002 05:43:44 -0000 1.16
+++ libwnck/Makefile.am 3 Oct 2003 21:36:57 -0000
@@ -10,6 +10,7 @@
libwnck.h \
pager.h \
application.h \
+ class-group.h \
screen.h \
tasklist.h \
util.h \
@@ -39,6 +40,7 @@
libwnck_1_la_SOURCES= \
application.c \
+ class-group.c \
inlinepixbufs.h \
pager.c \
private.h \
Index: libwnck/private.h
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/private.h,v
retrieving revision 1.13.2.1
diff -u -r1.13.2.1 private.h
--- libwnck/private.h 23 Jan 2003 02:12:21 -0000 1.13.2.1
+++ libwnck/private.h 3 Oct 2003 21:36:57 -0000
@@ -62,6 +62,10 @@
void _wnck_window_set_application (WnckWindow *window,
WnckApplication *app);
+
+void _wnck_window_set_class_group (WnckWindow *window,
+ WnckClassGroup *class_group);
+
void _wnck_application_add_window (WnckApplication *app,
WnckWindow *window);
void _wnck_application_remove_window (WnckApplication *app,
Index: libwnck/screen.c
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/screen.c,v
retrieving revision 1.22
diff -u -r1.22 screen.c
--- libwnck/screen.c 3 Nov 2002 19:04:18 -0000 1.22
+++ libwnck/screen.c 3 Oct 2003 21:36:57 -0000
@@ -23,6 +23,7 @@
#include "window.h"
#include "workspace.h"
#include "application.h"
+#include "class-group.h"
#include "xutils.h"
#include "private.h"
#include <gdk/gdk.h>
@@ -81,6 +82,8 @@
WORKSPACE_DESTROYED,
APPLICATION_OPENED,
APPLICATION_CLOSED,
+ CLASS_GROUP_OPENED,
+ CLASS_GROUP_CLOSED,
BACKGROUND_CHANGED,
SHOWING_DESKTOP_CHANGED,
LAST_SIGNAL
@@ -116,6 +119,10 @@
WnckApplication *app);
static void emit_application_closed (WnckScreen *screen,
WnckApplication *app);
+static void emit_class_group_opened (WnckScreen *screen,
+ WnckClassGroup *class_group);
+static void emit_class_group_closed (WnckScreen *screen,
+ WnckClassGroup *class_group);
static void emit_background_changed (WnckScreen *screen);
static void emit_showing_desktop_changed (WnckScreen *screen);
@@ -251,6 +258,34 @@
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, WNCK_TYPE_APPLICATION);
+ signals[CLASS_GROUP_OPENED] =
+ g_signal_new ("class_group_opened",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+#if 0
+ /* FIXME when we can break ABI add this */
+ G_STRUCT_OFFSET (WnckScreenClass, class_group_opened),
+#else
+ 0,
+#endif
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, WNCK_TYPE_CLASS_GROUP);
+
+ signals[CLASS_GROUP_CLOSED] =
+ g_signal_new ("class_group_closed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+#if 0
+ /* FIXME when we can break ABI add this */
+ G_STRUCT_OFFSET (WnckScreenClass, class_group_closed),
+#else
+ 0,
+#endif
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, WNCK_TYPE_CLASS_GROUP);
+
signals[BACKGROUND_CHANGED] =
g_signal_new ("background_changed",
G_OBJECT_CLASS_TYPE (object_class),
@@ -757,8 +792,8 @@
GList *new_list;
GList *created;
GList *closed;
- GList *created_apps;
- GList *closed_apps;
+ GList *created_apps, *closed_apps;
+ GList *created_class_groups, *closed_class_groups;
GList *tmp;
int i;
GHashTable *new_hash;
@@ -800,6 +835,8 @@
closed = NULL;
created_apps = NULL;
closed_apps = NULL;
+ created_class_groups = NULL;
+ closed_class_groups = NULL;
new_hash = g_hash_table_new (NULL, NULL);
@@ -815,10 +852,15 @@
{
Window leader;
WnckApplication *app;
+ const char *res_class;
+ WnckClassGroup *class_group;
+ gulong xid;
window = _wnck_window_create (stack[i], screen);
created = g_list_prepend (created, window);
+ /* Application */
+
leader = wnck_window_get_group_leader (window);
app = wnck_application_get (leader);
@@ -829,6 +871,21 @@
}
_wnck_application_add_window (app, window);
+
+ /* Class group */
+
+ xid = wnck_window_get_xid (window);
+
+ res_class = _wnck_window_get_resource_class (window);
+
+ class_group = wnck_class_group_get (res_class);
+ if (class_group == NULL)
+ {
+ class_group = _wnck_class_group_create (res_class);
+ created_class_groups = g_list_prepend (created_class_groups, class_group);
+ }
+
+ _wnck_class_group_add_window (class_group, window);
}
new_stack_list = g_list_prepend (new_stack_list, window);
@@ -852,15 +909,25 @@
if (g_hash_table_lookup (new_hash, window) == NULL)
{
WnckApplication *app;
+ WnckClassGroup *class_group;
closed = g_list_prepend (closed, window);
- app = wnck_window_get_application (window);
+ /* Remove from the app */
+ app = wnck_window_get_application (window);
_wnck_application_remove_window (app, window);
if (wnck_application_get_windows (app) == NULL)
closed_apps = g_list_prepend (closed_apps, app);
+
+ /* Remove from the class group */
+
+ class_group = wnck_window_get_class_group (window);
+ _wnck_class_group_remove_window (class_group, window);
+
+ if (wnck_class_group_get_windows (class_group) == NULL)
+ closed_class_groups = g_list_prepend (closed_class_groups, class_group);
}
tmp = tmp->next;
@@ -904,6 +971,8 @@
g_assert (closed == NULL);
g_assert (created_apps == NULL);
g_assert (closed_apps == NULL);
+ g_assert (created_class_groups == NULL);
+ g_assert (closed_class_groups == NULL);
g_list_free (new_stack_list);
g_list_free (new_list);
--reentrancy_guard;
@@ -911,7 +980,7 @@
}
g_list_free (screen->priv->mapped_windows);
- g_list_free (screen->priv->stacked_windows);
+ g_list_free (screen->priv->stacked_windows);
screen->priv->mapped_windows = new_list;
screen->priv->stacked_windows = new_stack_list;
@@ -920,31 +989,22 @@
* don't handle it, but we do warn about it using reentrancy_guard
*/
- /* Sequence is: application_opened, window_opened, window_closed,
- * application_closed. We have to do all window list changes
- * BEFORE doing any other signals, so that any observers
- * have valid state for the window structure before they take
- * further action
+ /* Sequence is: class_group_opened, application_opened, window_opened,
+ * window_closed, application_closed, class_group_closed. We have to do all
+ * window list changes BEFORE doing any other signals, so that any observers
+ * have valid state for the window structure before they take further action
*/
- tmp = created_apps;
- while (tmp != NULL)
- {
- emit_application_opened (screen, WNCK_APPLICATION (tmp->data));
-
- tmp = tmp->next;
- }
-
- tmp = created;
- while (tmp != NULL)
- {
- emit_window_opened (screen, WNCK_WINDOW (tmp->data));
-
- tmp = tmp->next;
- }
+ for (tmp = created_class_groups; tmp; tmp = tmp->next)
+ emit_class_group_opened (screen, WNCK_CLASS_GROUP (tmp->data));
+
+ for (tmp = created_apps; tmp; tmp = tmp->next)
+ emit_application_opened (screen, WNCK_APPLICATION (tmp->data));
+
+ for (tmp = created; tmp; tmp = tmp->next)
+ emit_window_opened (screen, WNCK_WINDOW (tmp->data));
active_changed = FALSE;
- tmp = closed;
- while (tmp != NULL)
+ for (tmp = closed; tmp; tmp = tmp->next)
{
WnckWindow *window;
@@ -957,21 +1017,13 @@
}
emit_window_closed (screen, window);
-
- tmp = tmp->next;
}
- tmp = closed_apps;
- while (tmp != NULL)
- {
- WnckApplication *app;
-
- app = WNCK_APPLICATION (tmp->data);
+ for (tmp = closed_apps; tmp; tmp = tmp->next)
+ emit_application_closed (screen, WNCK_APPLICATION (tmp->data));
- emit_application_closed (screen, app);
-
- tmp = tmp->next;
- }
+ for (tmp = closed_class_groups; tmp; tmp = tmp->next)
+ emit_class_group_closed (screen, WNCK_CLASS_GROUP (tmp->data));
if (stack_changed)
emit_window_stacking_changed (screen);
@@ -980,29 +1032,17 @@
emit_active_window_changed (screen);
/* Now free the closed windows */
- tmp = closed;
- while (tmp != NULL)
- {
- WnckWindow *window = tmp->data;
-
- _wnck_window_destroy (window);
-
- tmp = tmp->next;
- }
+ for (tmp = closed; tmp; tmp = tmp->next)
+ _wnck_window_destroy (WNCK_WINDOW (tmp->data));
/* Free the closed apps */
- tmp = closed_apps;
- while (tmp != NULL)
- {
- WnckApplication *app;
+ for (tmp = closed_apps; tmp; tmp = tmp->next)
+ _wnck_application_destroy (WNCK_APPLICATION (tmp->data));
- app = WNCK_APPLICATION (tmp->data);
+ /* Free the closed class groups */
+ for (tmp = closed_class_groups; tmp; tmp = tmp->next)
+ _wnck_class_group_destroy (WNCK_CLASS_GROUP (tmp->data));
- _wnck_application_destroy (app);
-
- tmp = tmp->next;
- }
-
g_list_free (closed);
g_list_free (created);
g_list_free (closed_apps);
@@ -1400,6 +1440,24 @@
g_signal_emit (G_OBJECT (screen),
signals[APPLICATION_CLOSED],
0, app);
+}
+
+static void
+emit_class_group_opened (WnckScreen *screen,
+ WnckClassGroup *class_group)
+{
+ g_signal_emit (G_OBJECT (screen),
+ signals[CLASS_GROUP_OPENED],
+ 0, class_group);
+}
+
+static void
+emit_class_group_closed (WnckScreen *screen,
+ WnckClassGroup *class_group)
+{
+ g_signal_emit (G_OBJECT (screen),
+ signals[CLASS_GROUP_CLOSED],
+ 0, class_group);
}
static void
Index: libwnck/screen.h
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/screen.h,v
retrieving revision 1.12
diff -u -r1.12 screen.h
--- libwnck/screen.h 3 Nov 2002 19:04:18 -0000 1.12
+++ libwnck/screen.h 3 Oct 2003 21:36:57 -0000
@@ -28,6 +28,7 @@
/* forward decls */
typedef struct _WnckApplication WnckApplication;
+typedef struct _WnckClassGroup WnckClassGroup;
typedef struct _WnckWindow WnckWindow;
typedef struct _WnckWorkspace WnckWorkspace;
@@ -86,6 +87,12 @@
#if 0
/* FIXME uncomment all this next time we feel like breaking ABI */
+ /* new class group */
+ void (* class_group_opened) (WnckScreen *screen,
+ WnckClassGroup *class_group);
+ /* class group gone */
+ void (* class_group_closed) (WnckScreen *screen,
+ WnckClassGroup *class_group);
/* Toggle showing desktop */
void (* showing_desktop_changed) (WnckScreen *screen);
Index: libwnck/tasklist.c
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/tasklist.c,v
retrieving revision 1.45.2.2
diff -u -r1.45.2.2 tasklist.c
--- libwnck/tasklist.c 25 Jan 2003 21:16:58 -0000 1.45.2.2
+++ libwnck/tasklist.c 3 Oct 2003 21:36:58 -0000
@@ -24,9 +24,9 @@
#include <stdio.h>
#include "tasklist.h"
#include "window.h"
+#include "class-group.h"
#include "window-action-menu.h"
#include "workspace.h"
-#include "application.h"
#include "xutils.h"
#include "private.h"
@@ -38,7 +38,7 @@
* Maybe fine tune size_allocate() some more...
* Better vertical layout handling
* prefs
- * ellipsizing lables
+ * ellipsizing labels
* support for right-click menu merging w/ bonobo for the applet
*
*/
@@ -66,10 +66,9 @@
typedef enum
{
- WNCK_TASK_APPLICATION,
+ WNCK_TASK_CLASS_GROUP,
WNCK_TASK_WINDOW,
WNCK_TASK_STARTUP_SEQUENCE
-
} WnckTaskType;
struct _WnckTask
@@ -83,8 +82,8 @@
GtkWidget *label;
WnckTaskType type;
-
- WnckApplication *application;
+
+ WnckClassGroup *class_group;
WnckWindow *window;
#ifdef HAVE_STARTUP_NOTIFICATION
SnStartupSequence *startup_sequence;
@@ -93,11 +92,12 @@
gdouble grouping_score;
GList *windows; /* List of the WnckTask for the window,
- if this is an application */
+ if this is a class group */
gulong state_changed_tag;
gulong icon_changed_tag;
gulong name_changed_tag;
- gulong app_name_changed_tag;
+ gulong class_name_changed_tag;
+ gulong class_icon_changed_tag;
/* task menu */
GtkWidget *menu;
@@ -122,19 +122,19 @@
WnckScreen *screen;
WnckTask *active_task; /* NULL if active window not in tasklist */
- WnckTask *active_app; /* NULL if active window not in tasklist */
+ WnckTask *active_class_group; /* NULL if active window not in tasklist */
gboolean include_all_workspaces;
/* Calculated by update_lists */
+ GList *class_groups;
GList *windows;
- GList *applications;
/* Not handled by update_lists */
GList *startup_sequences;
+ GHashTable *class_group_hash;
GHashTable *win_hash;
- GHashTable *app_hash;
GtkTooltips *tooltips;
@@ -172,8 +172,8 @@
static WnckTask *wnck_task_new_from_window (WnckTasklist *tasklist,
WnckWindow *window);
-static WnckTask *wnck_task_new_from_application (WnckTasklist *tasklist,
- WnckApplication *application);
+static WnckTask *wnck_task_new_from_class_group (WnckTasklist *tasklist,
+ WnckClassGroup *class_group);
#ifdef HAVE_STARTUP_NOTIFICATION
static WnckTask *wnck_task_new_from_startup_sequence (WnckTasklist *tasklist,
SnStartupSequence *sequence);
@@ -340,13 +340,20 @@
task->name_changed_tag = 0;
}
- if (task->app_name_changed_tag)
+ if (task->class_name_changed_tag)
{
- g_signal_handler_disconnect (task->application,
- task->app_name_changed_tag);
- task->app_name_changed_tag = 0;
+ g_signal_handler_disconnect (task->class_group,
+ task->class_name_changed_tag);
+ task->class_name_changed_tag = 0;
}
-
+
+ if (task->class_icon_changed_tag)
+ {
+ g_signal_handler_disconnect (task->class_group,
+ task->class_icon_changed_tag);
+ task->class_icon_changed_tag = 0;
+ }
+
if (task->menu)
{
gtk_widget_destroy (task->menu);
@@ -365,10 +372,10 @@
task->window = NULL;
}
- if (task->application)
+ if (task->class_group)
{
- g_object_unref (task->application);
- task->application = NULL;
+ g_object_unref (task->class_group);
+ task->class_group = NULL;
}
#ifdef HAVE_STARTUP_NOTIFICATION
@@ -428,7 +435,7 @@
tasklist->priv->include_all_workspaces = FALSE;
tasklist->priv->win_hash = g_hash_table_new (NULL, NULL);
- tasklist->priv->app_hash = g_hash_table_new (NULL, NULL);
+ tasklist->priv->class_group_hash = g_hash_table_new (NULL, NULL);
tasklist->priv->grouping = WNCK_TASKLIST_AUTO_GROUP;
tasklist->priv->grouping_limit = DEFAULT_GROUPING_LIMIT;
@@ -473,15 +480,15 @@
* buttons in container destruction
*/
g_assert (tasklist->priv->windows == NULL);
- g_assert (tasklist->priv->applications == NULL);
+ g_assert (tasklist->priv->class_groups == NULL);
g_assert (tasklist->priv->startup_sequences == NULL);
/* wnck_tasklist_free_tasks (tasklist); */
g_hash_table_destroy (tasklist->priv->win_hash);
tasklist->priv->win_hash = NULL;
- g_hash_table_destroy (tasklist->priv->app_hash);
- tasklist->priv->app_hash = NULL;
+ g_hash_table_destroy (tasklist->priv->class_group_hash);
+ tasklist->priv->class_group_hash = NULL;
if (tasklist->priv->activate_timeout_id != 0)
gtk_timeout_remove (tasklist->priv->activate_timeout_id);
@@ -666,9 +673,9 @@
static void
wnck_tasklist_score_groups (WnckTasklist *tasklist,
- GList *ungrouped_apps)
+ GList *ungrouped_class_groups)
{
- WnckTask *app_task;
+ WnckTask *class_group_task;
WnckTask *win_task;
GList *l, *w;
const char *first_name = NULL;
@@ -676,15 +683,15 @@
int n_same_title;
double same_window_ratio;
- l = ungrouped_apps;
+ l = ungrouped_class_groups;
while (l != NULL)
{
- app_task = WNCK_TASK (l->data);
+ class_group_task = WNCK_TASK (l->data);
- n_windows = g_list_length (app_task->windows);
+ n_windows = g_list_length (class_group_task->windows);
n_same_title = 0;
- w = app_task->windows;
+ w = class_group_task->windows;
while (w != NULL)
{
win_task = WNCK_TASK (w->data);
@@ -709,38 +716,38 @@
* XP groups by least used, so we probably want to add
* total focused time to this expression.
*/
- app_task->grouping_score = -same_window_ratio * 5 + n_windows;
+ class_group_task->grouping_score = -same_window_ratio * 5 + n_windows;
l = l->next;
}
}
static GList *
-wnck_task_get_highest_scored (GList *ungrouped_apps,
- WnckTask **app_task_out)
+wnck_task_get_highest_scored (GList *ungrouped_class_groups,
+ WnckTask **class_group_task_out)
{
- WnckTask *app_task;
+ WnckTask *class_group_task;
WnckTask *best_task = NULL;
double max_score = -1000000000.0; /* Large negative score */
GList *l;
- l = ungrouped_apps;
+ l = ungrouped_class_groups;
while (l != NULL)
{
- app_task = WNCK_TASK (l->data);
+ class_group_task = WNCK_TASK (l->data);
- if (app_task->grouping_score >= max_score)
+ if (class_group_task->grouping_score >= max_score)
{
- max_score = app_task->grouping_score;
- best_task = app_task;
+ max_score = class_group_task->grouping_score;
+ best_task = class_group_task;
}
l = l->next;
}
- *app_task_out = best_task;
+ *class_group_task_out = best_task;
- return g_list_remove (ungrouped_apps, best_task);
+ return g_list_remove (ungrouped_class_groups, best_task);
}
static void
@@ -755,7 +762,7 @@
/* int u_width, u_height; */
GList *l;
GArray *array;
- GList *ungrouped_apps;
+ GList *ungrouped_class_groups;
int n_windows;
int n_startup_sequences;
int n_rows;
@@ -763,7 +770,7 @@
int n_grouped_buttons;
gboolean score_set;
int val;
- WnckTask *app_task;
+ WnckTask *class_group_task;
int lowest_range;
int grouping_limit;
@@ -785,7 +792,7 @@
l = l->next;
}
- l = tasklist->priv->applications;
+ l = tasklist->priv->class_groups;
while (l != NULL)
{
WnckTask *task = WNCK_TASK (l->data);
@@ -852,7 +859,7 @@
n_windows = g_list_length (tasklist->priv->windows);
n_startup_sequences = g_list_length (tasklist->priv->startup_sequences);
n_grouped_buttons = 0;
- ungrouped_apps = g_list_copy (tasklist->priv->applications);
+ ungrouped_class_groups = g_list_copy (tasklist->priv->class_groups);
score_set = FALSE;
grouping_limit = MIN (tasklist->priv->grouping_limit,
@@ -878,18 +885,18 @@
lowest_range = val;
}
- while (ungrouped_apps != NULL &&
+ while (ungrouped_class_groups != NULL &&
tasklist->priv->grouping != WNCK_TASKLIST_NEVER_GROUP)
{
if (!score_set)
{
- wnck_tasklist_score_groups (tasklist, ungrouped_apps);
+ wnck_tasklist_score_groups (tasklist, ungrouped_class_groups);
score_set = TRUE;
}
- ungrouped_apps = wnck_task_get_highest_scored (ungrouped_apps, &app_task);
+ ungrouped_class_groups = wnck_task_get_highest_scored (ungrouped_class_groups, &class_group_task);
- n_grouped_buttons += g_list_length (app_task->windows) - 1;
+ n_grouped_buttons += g_list_length (class_group_task->windows) - 1;
wnck_tasklist_layout (&fake_allocation,
tasklist->priv->max_button_width,
@@ -898,7 +905,7 @@
&n_cols, &n_rows);
if (n_cols != last_n_cols &&
(tasklist->priv->grouping == WNCK_TASKLIST_AUTO_GROUP ||
- ungrouped_apps == NULL))
+ ungrouped_class_groups == NULL))
{
val = n_cols * tasklist->priv->max_button_width;
if (val >= lowest_range)
@@ -953,7 +960,7 @@
{
GtkAllocation child_allocation;
WnckTasklist *tasklist;
- WnckTask *app_task;
+ WnckTask *class_group_task;
int n_windows;
int n_startup_sequences;
GList *l;
@@ -964,7 +971,7 @@
int n_grouped_buttons;
int i;
gboolean score_set;
- GList *ungrouped_apps;
+ GList *ungrouped_class_groups;
WnckTask *win_task;
GList *visible_tasks = NULL;
int grouping_limit;
@@ -974,7 +981,7 @@
n_windows = g_list_length (tasklist->priv->windows);
n_startup_sequences = g_list_length (tasklist->priv->startup_sequences);
n_grouped_buttons = 0;
- ungrouped_apps = g_list_copy (tasklist->priv->applications);
+ ungrouped_class_groups = g_list_copy (tasklist->priv->class_groups);
score_set = FALSE;
grouping_limit = MIN (tasklist->priv->grouping_limit,
@@ -986,27 +993,27 @@
tasklist->priv->max_button_height,
n_startup_sequences + n_windows,
&n_cols, &n_rows);
- while (ungrouped_apps != NULL &&
+ while (ungrouped_class_groups != NULL &&
((tasklist->priv->grouping == WNCK_TASKLIST_ALWAYS_GROUP) ||
((tasklist->priv->grouping == WNCK_TASKLIST_AUTO_GROUP) &&
(button_width < grouping_limit))))
{
if (!score_set)
{
- wnck_tasklist_score_groups (tasklist, ungrouped_apps);
+ wnck_tasklist_score_groups (tasklist, ungrouped_class_groups);
score_set = TRUE;
}
- ungrouped_apps = wnck_task_get_highest_scored (ungrouped_apps, &app_task);
+ ungrouped_class_groups = wnck_task_get_highest_scored (ungrouped_class_groups, &class_group_task);
- n_grouped_buttons += g_list_length (app_task->windows) - 1;
+ n_grouped_buttons += g_list_length (class_group_task->windows) - 1;
- if (g_list_length (app_task->windows) > 1)
+ if (g_list_length (class_group_task->windows) > 1)
{
- visible_tasks = g_list_prepend (visible_tasks, app_task);
+ visible_tasks = g_list_prepend (visible_tasks, class_group_task);
- /* Hide all this apps windows */
- l = app_task->windows;
+ /* Hide all this group's windows */
+ l = class_group_task->windows;
while (l != NULL)
{
win_task = WNCK_TASK (l->data);
@@ -1017,8 +1024,8 @@
}
else
{
- visible_tasks = g_list_prepend (visible_tasks, app_task->windows->data);
- gtk_widget_set_child_visible (GTK_WIDGET (app_task->button), FALSE);
+ visible_tasks = g_list_prepend (visible_tasks, class_group_task->windows->data);
+ gtk_widget_set_child_visible (GTK_WIDGET (class_group_task->button), FALSE);
}
button_width = wnck_tasklist_layout (allocation,
@@ -1028,14 +1035,14 @@
&n_cols, &n_rows);
}
- /* Add all ungrouped windows to visible_tasks, and hide their apps */
- l = ungrouped_apps;
+ /* Add all ungrouped windows to visible_tasks, and hide their class groups */
+ l = ungrouped_class_groups;
while (l != NULL)
{
- app_task = WNCK_TASK (l->data);
+ class_group_task = WNCK_TASK (l->data);
- visible_tasks = g_list_concat (visible_tasks, g_list_copy (app_task->windows));
- gtk_widget_set_child_visible (GTK_WIDGET (app_task->button), FALSE);
+ visible_tasks = g_list_concat (visible_tasks, g_list_copy (class_group_task->windows));
+ gtk_widget_set_child_visible (GTK_WIDGET (class_group_task->button), FALSE);
l = l->next;
}
@@ -1132,7 +1139,7 @@
(* callback) (task->button, callback_data);
}
- tmp = tasklist->priv->applications;
+ tmp = tasklist->priv->class_groups;
while (tmp != NULL)
{
WnckTask *task = WNCK_TASK (tmp->data);
@@ -1182,8 +1189,8 @@
break;
}
}
-
- tmp = tasklist->priv->applications;
+
+ tmp = tasklist->priv->class_groups;
while (tmp != NULL)
{
WnckTask *task = WNCK_TASK (tmp->data);
@@ -1191,10 +1198,10 @@
if (task->button == widget)
{
- g_hash_table_remove (tasklist->priv->app_hash,
- task->application);
- tasklist->priv->applications =
- g_list_remove (tasklist->priv->applications,
+ g_hash_table_remove (tasklist->priv->class_group_hash,
+ task->class_group);
+ tasklist->priv->class_groups =
+ g_list_remove (tasklist->priv->class_groups,
task);
gtk_widget_unparent (widget);
@@ -1323,7 +1330,7 @@
GList *l;
tasklist->priv->active_task = NULL;
- tasklist->priv->active_app = NULL;
+ tasklist->priv->active_class_group = NULL;
if (tasklist->priv->windows)
{
@@ -1341,9 +1348,9 @@
g_assert (tasklist->priv->windows == NULL);
g_assert (g_hash_table_size (tasklist->priv->win_hash) == 0);
- if (tasklist->priv->applications)
+ if (tasklist->priv->class_groups)
{
- l = tasklist->priv->applications;
+ l = tasklist->priv->class_groups;
while (l != NULL)
{
WnckTask *task = WNCK_TASK (l->data);
@@ -1354,8 +1361,8 @@
gtk_widget_destroy (task->button);
}
}
- g_assert (tasklist->priv->applications == NULL);
- g_assert (g_hash_table_size (tasklist->priv->app_hash) == 0);
+ g_assert (tasklist->priv->class_groups == NULL);
+ g_assert (g_hash_table_size (tasklist->priv->class_group_hash) == 0);
}
static void
@@ -1363,11 +1370,11 @@
{
GList *windows;
WnckWindow *win;
- WnckApplication *app;
+ WnckClassGroup *class_group;
GList *l;
WnckWorkspace *active_workspace;
- WnckTask *app_task;
WnckTask *win_task;
+ WnckTask *class_group_task;
wnck_tasklist_free_tasks (tasklist);
@@ -1392,39 +1399,41 @@
gtk_widget_set_parent (win_task->button, GTK_WIDGET (tasklist));
gtk_widget_show (win_task->button);
-
- app = wnck_window_get_application (win);
- app_task = g_hash_table_lookup (tasklist->priv->app_hash, app);
- if (app_task == NULL)
+ /* Class group */
+
+ class_group = wnck_window_get_class_group (win);
+ class_group_task = g_hash_table_lookup (tasklist->priv->class_group_hash, class_group);
+
+ if (class_group_task == NULL)
{
- app_task = wnck_task_new_from_application (tasklist, app);
- gtk_widget_set_parent (app_task->button, GTK_WIDGET (tasklist));
- gtk_widget_show (app_task->button);
+ class_group_task = wnck_task_new_from_class_group (tasklist, class_group);
+ gtk_widget_set_parent (class_group_task->button, GTK_WIDGET (tasklist));
+ gtk_widget_show (class_group_task->button);
- tasklist->priv->applications = g_list_prepend (tasklist->priv->applications,
- app_task);
- g_hash_table_insert (tasklist->priv->app_hash, app, app_task);
+ tasklist->priv->class_groups = g_list_prepend (tasklist->priv->class_groups,
+ class_group_task);
+ g_hash_table_insert (tasklist->priv->class_group_hash, class_group, class_group_task);
}
-
- app_task->windows = g_list_prepend (app_task->windows, win_task);
+
+ class_group_task->windows = g_list_prepend (class_group_task->windows, win_task);
}
l = l->next;
}
- /* Sort the application lists */
- l = tasklist->priv->applications;
+ /* Sort the class group list */
+ l = tasklist->priv->class_groups;
while (l)
{
- app_task = WNCK_TASK (l->data);
+ class_group_task = WNCK_TASK (l->data);
- app_task->windows = g_list_sort (app_task->windows, wnck_task_compare);
+ class_group_task->windows = g_list_sort (class_group_task->windows, wnck_task_compare);
/* so the number of windows in the task gets reset on the
* task label
*/
- wnck_task_update_visible_state (app_task);
+ wnck_task_update_visible_state (class_group_task);
l = l->next;
@@ -1467,29 +1476,29 @@
if (active_task)
{
- active_task = g_hash_table_lookup (tasklist->priv->app_hash,
- active_task->application);
+ active_task = g_hash_table_lookup (tasklist->priv->class_group_hash,
+ active_task->class_group);
if (active_task &&
- active_task == tasklist->priv->active_app)
+ active_task == tasklist->priv->active_class_group)
return;
- if (tasklist->priv->active_app)
+ if (tasklist->priv->active_class_group)
{
- tasklist->priv->active_app->really_toggling = TRUE;
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_app->button),
+ tasklist->priv->active_class_group->really_toggling = TRUE;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_class_group->button),
FALSE);
- tasklist->priv->active_app->really_toggling = FALSE;
+ tasklist->priv->active_class_group->really_toggling = FALSE;
}
- tasklist->priv->active_app = active_task;
+ tasklist->priv->active_class_group = active_task;
- if (tasklist->priv->active_app)
+ if (tasklist->priv->active_class_group)
{
- tasklist->priv->active_app->really_toggling = TRUE;
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_app->button),
+ tasklist->priv->active_class_group->really_toggling = TRUE;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tasklist->priv->active_class_group->button),
TRUE);
- tasklist->priv->active_app->really_toggling = FALSE;
+ tasklist->priv->active_class_group->really_toggling = FALSE;
}
}
}
@@ -1731,7 +1740,7 @@
GtkWidget *image;
GList *l, *list;
- if (task->application == NULL)
+ if (task->class_group == NULL)
return;
if (task->menu == NULL)
@@ -1799,8 +1808,6 @@
wnck_task_button_toggled (GtkButton *button,
WnckTask *task)
{
- WnckWindowState state;
-
/* Did we really want to change the state of the togglebutton? */
if (task->really_toggling)
return;
@@ -1813,7 +1820,7 @@
switch (task->type)
{
- case WNCK_TASK_APPLICATION:
+ case WNCK_TASK_CLASS_GROUP:
wnck_task_popup_menu (task, FALSE);
break;
case WNCK_TASK_WINDOW:
@@ -1821,9 +1828,6 @@
return;
wnck_tasklist_activate_task_window (task);
-
- /* FIXME why is this here? WTF? */
- state = wnck_window_get_state (task->window);
break;
case WNCK_TASK_STARTUP_SEQUENCE:
break;
@@ -1838,15 +1842,19 @@
switch (task->type)
{
- case WNCK_TASK_APPLICATION:
- return g_strdup_printf ("%s (%d)",
- wnck_application_get_icon_name (task->application),
- g_list_length (task->windows));
- break;
+ case WNCK_TASK_CLASS_GROUP:
+ name = wnck_class_group_get_name (task->class_group);
+ if (name[0] != 0)
+ return g_strdup_printf ("%s (%d)",
+ name,
+ g_list_length (task->windows));
+ else
+ return g_strdup_printf ("(%d)",
+ g_list_length (task->windows));
case WNCK_TASK_WINDOW:
state = wnck_window_get_state (task->window);
- name = wnck_window_get_icon_name (task->window);
+ name = wnck_window_get_name (task->window);
if (state & WNCK_WINDOW_STATE_MINIMIZED)
return g_strdup_printf ("[%s]", name);
@@ -1961,12 +1969,13 @@
pixbuf = NULL;
- switch (task->type)
+ switch (task->type)
{
- case WNCK_TASK_APPLICATION:
- pixbuf = wnck_task_scale_icon (wnck_application_get_mini_icon (task->application),
- FALSE);
+ case WNCK_TASK_CLASS_GROUP:
+ pixbuf = wnck_task_scale_icon (wnck_class_group_get_mini_icon (task->class_group),
+ FALSE);
break;
+
case WNCK_TASK_WINDOW:
state = wnck_window_get_state (task->window);
@@ -2050,19 +2059,21 @@
if (changed_mask & WNCK_WINDOW_STATE_MINIMIZED)
{
- WnckTask *win_task, *app_task;
+ WnckTask *win_task;
win_task = g_hash_table_lookup (tasklist->priv->win_hash,
window);
+
if (win_task)
{
+ WnckTask *class_group_task;
+
wnck_task_update_visible_state (win_task);
- app_task = g_hash_table_lookup (tasklist->priv->app_hash,
- win_task->application);
+ class_group_task = g_hash_table_lookup (tasklist->priv->class_group_hash, win_task->class_group);
- if (app_task)
- wnck_task_update_visible_state (app_task);
+ if (class_group_task)
+ wnck_task_update_visible_state (class_group_task);
}
}
@@ -2089,8 +2100,18 @@
}
static void
-wnck_task_app_name_changed (WnckApplication *app,
- gpointer data)
+wnck_task_class_name_changed (WnckClassGroup *class_group,
+ gpointer data)
+{
+ WnckTask *task = WNCK_TASK (data);
+
+ if (task)
+ wnck_task_update_visible_state (task);
+}
+
+static void
+wnck_task_class_icon_changed (WnckClassGroup *class_group,
+ gpointer data)
{
WnckTask *task = WNCK_TASK (data);
@@ -2150,11 +2171,10 @@
switch (task->type)
{
- case WNCK_TASK_APPLICATION:
+ case WNCK_TASK_CLASS_GROUP:
wnck_task_popup_menu (task,
- event->button == 3);
+ event->button == 3);
return TRUE;
- break;
case WNCK_TASK_WINDOW:
if (event->button == 1)
@@ -2288,11 +2308,13 @@
switch (task->type)
{
- case WNCK_TASK_APPLICATION:
- task->app_name_changed_tag = g_signal_connect (G_OBJECT (task->application), "name_changed",
- G_CALLBACK (wnck_task_app_name_changed), task);
+ case WNCK_TASK_CLASS_GROUP:
+ task->class_name_changed_tag = g_signal_connect (G_OBJECT (task->class_group), "name_changed",
+ G_CALLBACK (wnck_task_class_name_changed), task);
+ task->class_icon_changed_tag = g_signal_connect (G_OBJECT (task->class_group), "icon_changed",
+ G_CALLBACK (wnck_task_class_icon_changed), task);
break;
-
+
case WNCK_TASK_WINDOW:
task->state_changed_tag = g_signal_connect (G_OBJECT (task->window), "state_changed",
G_CALLBACK (wnck_task_state_changed), task->tasklist);
@@ -2304,6 +2326,9 @@
case WNCK_TASK_STARTUP_SEQUENCE:
break;
+
+ default:
+ g_assert_not_reached ();
}
}
@@ -2316,9 +2341,9 @@
gboolean
-wnck_task_app_expose (GtkWidget *widget,
- GdkEventExpose *event,
- gpointer data)
+wnck_task_class_group_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer data)
{
GtkStyle *style;
GdkGC *lgc, *dgc;
@@ -2342,6 +2367,17 @@
}
static gint
+compare_class_group_tasks (WnckTask *task1, WnckTask *task2)
+{
+ const char *name1, *name2;
+
+ name1 = wnck_class_group_get_name (task1->class_group);
+ name2 = wnck_class_group_get_name (task2->class_group);
+
+ return g_utf8_collate (name1, name2);
+}
+
+static gint
wnck_task_compare (gconstpointer a,
gconstpointer b)
{
@@ -2354,9 +2390,12 @@
switch (task1->type)
{
- case WNCK_TASK_APPLICATION:
- xid1_1 = xid1_2 = wnck_application_get_xid (task1->application);
- break;
+ case WNCK_TASK_CLASS_GROUP:
+ if (task2->type == WNCK_TASK_CLASS_GROUP)
+ return compare_class_group_tasks (task1, task2);
+ else
+ return -1; /* Sort groups before everything else */
+
case WNCK_TASK_WINDOW:
xid1_1 = wnck_window_get_group_leader (task1->window);
xid1_2 = wnck_window_get_xid (task1->window);
@@ -2368,9 +2407,12 @@
switch (task2->type)
{
- case WNCK_TASK_APPLICATION:
- xid2_1 = xid2_2 = wnck_application_get_xid (task2->application);
- break;
+ case WNCK_TASK_CLASS_GROUP:
+ if (task1->type == WNCK_TASK_CLASS_GROUP)
+ return compare_class_group_tasks (task1, task2);
+ else
+ return 1; /* Sort groups before everything else */
+
case WNCK_TASK_WINDOW:
xid2_1 = wnck_window_get_group_leader (task2->window);
xid2_2 = wnck_window_get_xid (task2->window);
@@ -2431,7 +2473,7 @@
task->type = WNCK_TASK_WINDOW;
task->window = g_object_ref (window);
- task->application = g_object_ref (wnck_window_get_application (window));
+ task->class_group = g_object_ref (wnck_window_get_class_group (window));
task->tasklist = tasklist;
wnck_task_create_widgets (task);
@@ -2442,25 +2484,25 @@
}
static WnckTask *
-wnck_task_new_from_application (WnckTasklist *tasklist,
- WnckApplication *application)
+wnck_task_new_from_class_group (WnckTasklist *tasklist,
+ WnckClassGroup *class_group)
{
WnckTask *task;
task = g_object_new (WNCK_TYPE_TASK, NULL);
- task->type = WNCK_TASK_APPLICATION;
- task->application = g_object_ref (application);
+ task->type = WNCK_TASK_CLASS_GROUP;
task->window = NULL;
+ task->class_group = g_object_ref (class_group);
task->tasklist = tasklist;
-
+
wnck_task_create_widgets (task);
-
+
g_signal_connect_object (task->button, "expose_event",
- G_CALLBACK (wnck_task_app_expose),
+ G_CALLBACK (wnck_task_class_group_expose),
G_OBJECT (task),
G_CONNECT_AFTER);
-
+
return task;
}
@@ -2474,8 +2516,8 @@
task = g_object_new (WNCK_TYPE_TASK, NULL);
task->type = WNCK_TASK_STARTUP_SEQUENCE;
- task->application = NULL;
task->window = NULL;
+ task->class_group = NULL;
task->startup_sequence = sequence;
sn_startup_sequence_ref (task->startup_sequence);
task->tasklist = tasklist;
Index: libwnck/test-tasklist.c
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/test-tasklist.c,v
retrieving revision 1.7
diff -u -r1.7 test-tasklist.c
--- libwnck/test-tasklist.c 14 May 2002 16:55:38 -0000 1.7
+++ libwnck/test-tasklist.c 3 Oct 2003 21:36:58 -0000
@@ -18,6 +18,7 @@
wnck_screen_force_update (screen);
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (win), 200, 100);
gtk_window_stick (GTK_WINDOW (win));
/* wnck_gtk_window_set_dock_type (GTK_WINDOW (win)); */
@@ -32,7 +33,7 @@
tasklist = wnck_tasklist_new (screen);
- wnck_tasklist_set_grouping (WNCK_TASKLIST (tasklist), WNCK_TASKLIST_AUTO_GROUP);
+ wnck_tasklist_set_grouping (WNCK_TASKLIST (tasklist), WNCK_TASKLIST_ALWAYS_GROUP);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (win), frame);
Index: libwnck/window.c
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/window.c,v
retrieving revision 1.33.2.1
diff -u -r1.33.2.1 window.c
--- libwnck/window.c 23 Jan 2003 02:12:21 -0000 1.33.2.1
+++ libwnck/window.c 3 Oct 2003 21:36:58 -0000
@@ -21,6 +21,7 @@
#include <string.h>
#include "window.h"
+#include "class-group.h"
#include "xutils.h"
#include "private.h"
#include "wnck-enum-types.h"
@@ -49,6 +50,7 @@
Window xwindow;
WnckScreen *screen;
WnckApplication *app;
+ WnckClassGroup *class_group;
Window group_leader;
Window transient_for;
char *name;
@@ -475,6 +477,14 @@
return window->priv->xwindow;
}
+WnckClassGroup *
+wnck_window_get_class_group (WnckWindow *window)
+{
+ g_return_val_if_fail (WNCK_IS_WINDOW (window), NULL);
+
+ return window->priv->class_group;
+}
+
/**
* wnck_window_get_session_id:
* @window: a #WnckWindow
@@ -1296,6 +1306,20 @@
if (window->priv->app)
g_object_unref (G_OBJECT (window->priv->app));
window->priv->app = app;
+}
+
+void
+_wnck_window_set_class_group (WnckWindow *window,
+ WnckClassGroup *class_group)
+{
+ g_return_if_fail (WNCK_IS_WINDOW (window));
+ g_return_if_fail (class_group == NULL || WNCK_IS_CLASS_GROUP (class_group));
+
+ if (class_group)
+ g_object_ref (G_OBJECT (class_group));
+ if (window->priv->class_group)
+ g_object_unref (G_OBJECT (window->priv->class_group));
+ window->priv->class_group = class_group;
}
void
Index: libwnck/window.h
===================================================================
RCS file: /cvs/gnome/libwnck/libwnck/window.h,v
retrieving revision 1.19
diff -u -r1.19 window.h
--- libwnck/window.h 31 Oct 2002 05:43:44 -0000 1.19
+++ libwnck/window.h 3 Oct 2003 21:36:58 -0000
@@ -136,6 +136,8 @@
gulong wnck_window_get_group_leader (WnckWindow *window);
gulong wnck_window_get_xid (WnckWindow *window);
+WnckClassGroup *wnck_window_get_class_group (WnckWindow *window);
+
const char* wnck_window_get_session_id (WnckWindow *window);
const char* wnck_window_get_session_id_utf8 (WnckWindow *window);
int wnck_window_get_pid (WnckWindow *window);
--- /dev/null 2003-01-30 04:24:37.000000000 -0600
+++ libwnck/class-group.h 2003-10-03 15:54:58.000000000 -0500
@@ -0,0 +1,76 @@
+/* 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.
+ */
+
+#ifndef WNCK_CLASS_GROUP_H
+#define WNCK_CLASS_GROUP_H
+
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <libwnck/screen.h>
+
+G_BEGIN_DECLS
+
+#define WNCK_TYPE_CLASS_GROUP (wnck_class_group_get_type ())
+#define WNCK_CLASS_GROUP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WNCK_TYPE_CLASS_GROUP, WnckClassGroup))
+#define WNCK_CLASS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WNCK_TYPE_CLASS_GROUP, WnckClassGroupClass))
+#define WNCK_IS_CLASS_GROUP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WNCK_TYPE_CLASS_GROUP))
+#define WNCK_IS_CLASS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WNCK_TYPE_CLASS_GROUP))
+#define WNCK_CLASS_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WNCK_TYPE_CLASS_GROUP, WnckClassGroupClass))
+
+typedef struct _WnckClassGroupClass WnckClassGroupClass;
+typedef struct _WnckClassGroupPrivate WnckClassGroupPrivate;
+
+struct _WnckClassGroup
+{
+ GObject parent_instance;
+
+ WnckClassGroupPrivate *priv;
+};
+
+struct _WnckClassGroupClass
+{
+ GObjectClass parent_class;
+
+ void (* name_changed) (WnckApplication *app);
+ void (* icon_changed) (WnckApplication *app);
+};
+
+GType wnck_class_group_get_type (void) G_GNUC_CONST;
+
+WnckClassGroup *wnck_class_group_get (const char *res_class);
+
+GList *wnck_class_group_get_windows (WnckClassGroup *class_group);
+const char * wnck_class_group_get_res_class (WnckClassGroup *class_group);
+
+const char * wnck_class_group_get_name (WnckClassGroup *class_group);
+
+GdkPixbuf *wnck_class_group_get_icon (WnckClassGroup *class_group);
+GdkPixbuf *wnck_class_group_get_mini_icon (WnckClassGroup *class_group);
+
+WnckClassGroup *_wnck_class_group_create (const char *res_class);
+void _wnck_class_group_destroy (WnckClassGroup *class_group);
+void _wnck_class_group_add_window (WnckClassGroup *class_group, WnckWindow *window);
+void _wnck_class_group_remove_window (WnckClassGroup *class_group, WnckWindow *window);
+
+G_END_DECLS
+
+#endif
--- /dev/null 2003-01-30 04:24:37.000000000 -0600
+++ libwnck/class-group.c 2003-10-03 16:21:40.000000000 -0500
@@ -0,0 +1,611 @@
+/* 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 "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 || g_utf8_collate (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 (g_utf8_collate (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 || g_utf8_collate (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;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]