source: trunk/third/gnome-core/applets/tasklist/tasklist_applet.c @ 16298

Revision 16298, 45.1 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16297, which included commits to RCS files with non-trunk default branches.
Line 
1#include <config.h>
2#include <gnome.h>
3#include <libgnomeui/gnome-window-icon.h>
4
5#include "gstc.h"
6#include "gwmh.h"
7
8#include <gdk/gdkx.h>
9#include <X11/Xlib.h>
10#include <X11/Xutil.h>
11
12#include <gdk-pixbuf/gdk-pixbuf.h>
13
14#include "tasklist_applet.h"
15#include "unknown.xpm"
16
17/* from gtkhandlebox.c */
18#define DRAG_HANDLE_SIZE 10
19
20/* define to x for debugging output */
21#define d(x)
22
23#define APPLET_COMPILE_AS_PROCESS
24/* sorting the list... */
25static gint
26tlsort (gconstpointer data1, gconstpointer data2)
27{
28        const TasklistTask *la = data1;
29        const TasklistTask *lb = data2;
30        const char *sa, *sb;
31
32        d(g_print ("la: %p\tlb: %p\n", la, lb));
33
34        sa = la->task_group ? la->group_name : (la->group ? la->group->group_name : la->gwmh_task->name);
35        sb = lb->task_group ? lb->group_name : (lb->group ? lb->group->group_name : lb->gwmh_task->name);
36
37        if (sa && sb) {
38                int cmp = g_strcasecmp (sa, sb);
39                /* if the same, then sort by the window id, this is arbitrary
40                 * but makes tasks of the same group stay in the same order */
41                if (cmp == 0) {
42                        if (la->gwmh_task->xwin > lb->gwmh_task->xwin)
43                                return 1;
44                        else
45                                return -1;
46                } else {
47                        return cmp;
48                }
49        } else if (sa) {
50                return 1;
51        } else if (sb) {
52                return -1;
53        } else {
54                return 0;
55        }
56}
57
58static void
59clamp_size (Tasklist *tasklist, int *size)
60{
61        int free_space;
62
63        free_space = applet_widget_get_free_space (APPLET_WIDGET (tasklist->applet));
64
65        if (free_space > 0 && free_space < *size)
66                *size = free_space;
67}
68
69void
70tasklist_clean_menu (TasklistTask *task)
71{
72        if(task->menu) {
73                d(g_print ("group had a menu: %p\t", task->menu));
74                gtk_widget_destroy (task->menu);
75                d(g_print ("%p\n", task->menu));
76        }
77}
78
79static char *
80get_task_class (GwmhTask *task)
81{
82        XClassHint hint;
83        char *retval;
84        Status status;
85
86        gdk_error_trap_push ();
87        status = XGetClassHint (GDK_DISPLAY (), task->xwin, &hint);
88        gdk_flush ();
89        if (gdk_error_trap_pop ())
90                return NULL;
91
92        if ( ! status)
93                return NULL;
94
95        d(g_print ("name: %s\tclass: %s\n", hint.res_name, hint.res_class));
96        retval = g_strdup (hint.res_class);
97
98        XFree (hint.res_name);
99        XFree (hint.res_class);
100
101        return retval;
102}
103
104/* get the horz_rows depending on the configuration settings */
105static gint
106get_horz_rows(Tasklist *tasklist)
107{
108        int result;
109
110        g_return_val_if_fail (tasklist != NULL, 1);
111                             
112        if (tasklist->config.follow_panel_size)
113                result = tasklist->panel_size/ROW_HEIGHT;
114        else
115                result = tasklist->config.horz_rows;
116
117        if (result < 1)
118                result = 1;
119
120        return result;
121}
122
123/* Shorten a label that is too long */
124gchar *
125tasklist_task_get_label (TasklistTask *task, int width, gboolean add_groupcount)
126{       
127        Tasklist *tasklist = task->tasklist;
128        gchar *das_string;
129        gchar *str, *tempstr, *groupcount = NULL;
130        gint len, label_len, overhead, allowed_width;
131
132        das_string = task->gwmh_task->name;
133
134        if (das_string == NULL)
135                das_string = _("???");
136
137        label_len = gdk_string_width (tasklist->area->style->font, das_string);
138
139        overhead = tasklist->config.show_mini_icons ? 30 : 6;
140
141        if (add_groupcount) {
142                GSList *vtasks = task->vtasks;
143                /* not sure why this was there but at least
144                 * now it has the correct logic, perhaps sometime
145                 * we'd want to draw a task with it's group count */
146                if (vtasks == NULL &&
147                    task->group != NULL)
148                        vtasks = task->group->vtasks;
149                groupcount = g_strdup_printf ("(%d) ", g_slist_length (vtasks));
150               
151                overhead += 10 + gdk_string_width (tasklist->area->style->font,
152                                                   groupcount);
153        }
154       
155        if (GWMH_TASK_ICONIFIED (task->gwmh_task))
156                overhead += gdk_string_width (tasklist->area->style->font, "[]");       
157
158        allowed_width = width - overhead;
159       
160        if ( (width > 0) && (label_len > allowed_width) ) {
161                GdkWChar *wstr;
162
163                g_assert (width > 0);
164
165                len = strlen (das_string);
166                wstr = g_new (GdkWChar, len + 3);
167                len = gdk_mbstowcs (wstr, das_string, len);
168                /* ok, the below thing is broken */
169                if ( len < 0 ) { /* if the conversion is failed */
170                        wstr[0] = wstr[1] = wstr[2] = '?';
171                        wstr[3] = '\0'; /* wcscpy(wstr,"???");*/
172                        len = 3;
173                        label_len = gdk_text_width_wc(tasklist->area->style->font,
174                                                      wstr, len);
175                        if (label_len <= allowed_width) {
176                                str = gdk_wcstombs(wstr);
177                                g_free(wstr);
178                                goto finish_label_up;
179                        }
180                }
181                wstr[len] = wstr[len+1] = '.';
182                wstr[len+2] = '\0'; /*wcscat(wstr,"..");*/
183                len--;
184               
185                for (; len > 0; len--) {
186                        wstr[len] = '.';
187                        wstr[len + 3] = '\0';
188                       
189                        label_len = gdk_text_width_wc (tasklist->area->style->font,
190                                                       wstr, len + 3);
191                       
192                        if (label_len <= allowed_width)
193                                break;
194                }
195                str = gdk_wcstombs (wstr);
196                g_free (wstr);
197        } else {
198                str = g_strdup (das_string);
199        }
200
201finish_label_up:
202
203        if (task->gwmh_task && GWMH_TASK_ICONIFIED (task->gwmh_task)) {
204                tempstr = g_strdup_printf ("[%s]", str);       
205                g_free(str);
206                str = tempstr;
207        }
208       
209        if (groupcount) {
210                tempstr = g_strconcat (groupcount, str, NULL);
211                g_free (str);
212                str = tempstr;
213        }
214
215        return str;
216}
217
218/* Check if a task is "visible",
219   if it should be drawn onto the tasklist */
220static gboolean
221is_task_visible (TasklistTask *task)
222{
223        Tasklist *tasklist;
224        GwmhDesk *desk_info;
225
226        if (!task || task->destroyed || task->task_group)
227                return FALSE;
228
229        tasklist = task->tasklist;
230
231        desk_info = gwmh_desk_get_config ();
232
233        if (GWMH_TASK_SKIP_TASKBAR (task->gwmh_task))
234                return FALSE;
235       
236        if (task->gwmh_task->desktop != desk_info->current_desktop ||
237            task->gwmh_task->harea != desk_info->current_harea ||
238            task->gwmh_task->varea != desk_info->current_varea) {
239                if (!GWMH_TASK_STICKY (task->gwmh_task)) {
240                        if (!tasklist->config.all_desks_minimized &&
241                            !tasklist->config.all_desks_normal)
242                                return FALSE;
243                               
244                        else if (tasklist->config.all_desks_minimized &&
245                                 !tasklist->config.all_desks_normal) {
246                                if (!GWMH_TASK_ICONIFIED (task->gwmh_task))
247                                        return FALSE;
248                        }
249                        else if (tasklist->config.all_desks_normal &&
250                                 !tasklist->config.all_desks_minimized) {
251                                if (GWMH_TASK_ICONIFIED (task->gwmh_task))
252                                        return FALSE;
253                        }
254                }
255        }                       
256
257        if (GWMH_TASK_ICONIFIED (task->gwmh_task)) {
258                if (!tasklist->config.show_minimized)
259                        return FALSE;
260        } else {
261                if (!tasklist->config.show_normal)
262                        return FALSE;
263        }
264               
265        return TRUE;
266}
267
268static gboolean
269is_task_really_visible (TasklistTask *task)
270{
271        g_return_val_if_fail (task != NULL, FALSE);
272
273        if (!task->tasklist->config.enable_grouping)
274                return is_task_visible (task);
275
276        /* we can probably unroll the length test */
277        if (task->group && g_slist_length (task->group->vtasks) > task->tasklist->config.grouping_min)
278                return FALSE;
279        else if (task->task_group)
280                return g_slist_length (task->vtasks) > task->tasklist->config.grouping_min;
281        return is_task_visible (task);
282}
283
284static void print_task (TasklistTask *task);
285
286static void
287print_task_iterator (gpointer data, gpointer user_data)
288{
289        TasklistTask *task = data;
290        print_task (task);
291}
292
293static void
294print_task (TasklistTask *task)
295{
296        if (!task)
297                g_print (" * * NULL TASK * *\n");
298        else if (task->group)
299                g_print ("task: %p (%p) [%d, %d]: %s\n",
300                         task, task->gwmh_task,
301                         is_task_visible (task),
302                         is_task_really_visible (task),
303                         task->gwmh_task->name);
304        else if (task->task_group) {
305                g_print ("group: %p [%d]: %s\n", task, is_task_really_visible (task), task->group_name);
306                g_slist_foreach (task->tasks, print_task_iterator, NULL);
307                g_print ("/\n");
308        } else {
309                g_print ("Unknown task: %p\n", task);
310                g_assert_not_reached ();
311        }
312}
313
314/* returns TRUE if the group entry should be redrawn */
315static gboolean
316fixup_group (TasklistTask *group)
317{
318        TasklistTask *focused_task;
319        gboolean iconified;
320        TasklistTask *task;
321        GSList *item;
322       
323        g_return_val_if_fail (group != NULL, FALSE);
324       
325        focused_task = NULL;
326        iconified = TRUE;
327       
328        g_slist_free (group->vtasks);
329        group->vtasks = NULL;
330       
331        for (item = group->tasks; item; item = item->next) {
332                task = (TasklistTask *)item->data;
333                if (is_task_visible (task)) {
334                        group->vtasks = g_slist_prepend (group->vtasks, task);
335                        if (!task->gwmh_task->iconified)
336                                iconified = FALSE;
337                        if (task->gwmh_task->focused)
338                                focused_task = task;
339                }
340        }
341
342        if (group->focused_task != focused_task ||
343            group->gwmh_task->iconified != iconified) {
344                group->focused_task = focused_task;
345                group->gwmh_task->iconified = iconified;
346
347                return TRUE;
348        } else {
349                return FALSE;
350        }
351}
352
353static gboolean fixup_vtask (TasklistTask *task, gboolean *redraw);
354
355static void
356fixup_vtask_iterator (gpointer data, gpointer user_data)
357{
358        TasklistTask *task = data;
359        fixup_vtask (task, NULL);
360}
361
362static gboolean
363fixup_vtask (TasklistTask *task, gboolean *redraw)
364{
365        gboolean visible;
366
367        /* why not layout if we are confused */
368        g_return_val_if_fail (task, TRUE);
369
370        if (task->task_group) {
371                gboolean do_redraw = fixup_group (task);
372                if (redraw != NULL)
373                        *redraw = do_redraw;
374        }
375
376        visible = is_task_really_visible (task);
377
378        if (visible == task->visible)
379                return FALSE;
380
381        task->visible = visible;
382        if (visible) {
383                task->tasklist->vtasks =
384                        g_slist_insert_sorted (task->tasklist->vtasks, task, tlsort);
385        } else {
386                task->tasklist->vtasks =
387                        g_slist_remove (task->tasklist->vtasks, task);
388        }
389
390        if (task->tasklist->config.enable_grouping) {
391                if (task->task_group) {
392                        g_slist_foreach (task->tasks, fixup_vtask_iterator, NULL);
393                } else if (task->group) {
394                        fixup_vtask (task->group, NULL);
395                }
396        }
397
398        d(g_print (">>>>>\tfixup_vtask "));
399        d(print_task (task));
400        d(g_slist_foreach (task->tasklist->vtasks, print_task_iterator, NULL));
401        d(g_print ("<<<<<\n"));
402
403        return TRUE;
404}
405
406static void
407redo_groups_iterator (gpointer key, gpointer data, gpointer user_data)
408{
409        TasklistTask *task = data;
410        Tasklist *tasklist = user_data;
411
412        d (print_task (task));
413
414        fixup_group (task);
415        task->visible = is_task_really_visible (task);
416        if (task->visible)
417                tasklist->vtasks = g_slist_insert_sorted (tasklist->vtasks, task, tlsort);
418}
419
420void
421tasklist_redo_vtasks (Tasklist *tasklist)
422{
423        TasklistTask *task;
424        GList *item;
425       
426        if (tasklist->vtasks) {
427                g_slist_free (tasklist->vtasks);
428                tasklist->vtasks = NULL;
429        }
430
431        d(g_print ("\n\n\n\n\n\n\n\n\n\nredo_vtasks\n\n\n\n\n\n\n\n\n\n"));
432
433        if (tasklist->config.enable_grouping)
434                g_hash_table_foreach (tasklist->groups, redo_groups_iterator, tasklist);
435
436        for (item = gwmh_task_list_get (); item; item = item->next) {
437                task = g_hash_table_lookup (tasklist->tasks, item->data);
438
439                /* this should never actually happen */
440                if (!task) continue;
441
442                task->visible = is_task_really_visible (task);
443               
444                if (task->visible)
445                        tasklist->vtasks = g_slist_insert_sorted (tasklist->vtasks, task, tlsort);
446        }
447
448#if 0
449        if (!tasklist->config.sort_tasklist)
450                tasklist->vtasks = g_slist_reverse (tasklist->vtasks);
451#endif
452        d(g_print ("\n\n\n\n\n\n\n\n\n\nvtasks: %d\n", g_slist_length (tasklist->vtasks)));
453
454        d(g_print (">>>>>\tredo_vtasks:\n"));
455        d(g_slist_foreach (tasklist->vtasks, print_task_iterator, NULL));
456        d(g_print ("<<<<<\n"));
457}
458
459/* Check what task (if any) is at position x,y on the tasklist */
460static TasklistTask *
461task_get_xy (Tasklist *tasklist, gint x, gint y)
462{
463        GSList *temp_tasks, *temp;
464        TasklistTask *task;
465
466        temp_tasks = tasklist->vtasks;
467
468        for (temp = temp_tasks; temp != NULL; temp = temp->next) {
469                task = (TasklistTask *)temp->data;
470                if (x > task->x &&
471                    x < task->x + task->width &&
472                    y > task->y &&
473                    y < task->y + task->height)
474                        return task;
475        }
476
477        return NULL;
478}
479
480static void
481draw_dot (GdkWindow *window, GdkGC *lgc, GdkGC *dgc, int x, int y)
482{
483        gdk_draw_point (window, dgc, x,   y);
484        gdk_draw_point (window, lgc, x+1, y+1);
485}
486
487/* Draw a single task */
488void
489tasklist_draw_task (TasklistTask *task, GdkRectangle *rect)
490{
491        TasklistTask *real_task;
492        gchar *tempstr;
493        gint text_height, text_width;
494        gboolean focused;
495
496        /* For mini icons */
497        TasklistIcon *icon;
498        GdkPixbuf *pixbuf;
499       
500        Tasklist *tasklist;
501
502        if (!is_task_really_visible (task))
503                return;
504
505        /* is_task_visible should return FALSE for task == NULL */
506        g_assert (task != NULL);
507
508        tasklist = task->tasklist;
509       
510        real_task = task;
511        if (is_task_visible (task->focused_task) && GWMH_TASK_FOCUSED (task->focused_task->gwmh_task))
512                task = task->focused_task;
513
514        focused = GWMH_TASK_FOCUSED (task->gwmh_task) || real_task->menu != NULL;
515
516        gtk_paint_box (tasklist->area->style, tasklist->area->window,
517                       focused ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL,                 
518                       focused ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
519                       rect, tasklist->area, "button",
520                       real_task->x, real_task->y,
521                       real_task->width, real_task->height);
522
523        tempstr = tasklist_task_get_label (task, real_task->width, real_task->task_group);
524        if (tempstr) {
525                text_height = gdk_string_height (tasklist->area->style->font, "1");
526                text_width = gdk_string_width (tasklist->area->style->font, tempstr);
527                gdk_draw_string (tasklist->area->window,
528                                 tasklist->area->style->font,
529                                 focused ?
530                                 tasklist->area->style->fg_gc[GTK_STATE_ACTIVE] :
531                                 tasklist->area->style->fg_gc[GTK_STATE_NORMAL],
532                                 real_task->x +
533                                 (tasklist->config.show_mini_icons ? 10 : 0) +
534                                 ((real_task->width - text_width) / 2),
535                                 real_task->y + ((real_task->height - text_height) / 2) + text_height,
536                                 tempstr);
537
538                g_free (tempstr);
539        }
540
541        if (tasklist->config.show_mini_icons) {
542                icon = task->icon;
543               
544                if ( GWMH_TASK_ICONIFIED (task->gwmh_task))
545                        pixbuf = icon->minimized;
546                else
547                        pixbuf = icon->normal;
548
549                gdk_pixbuf_render_to_drawable_alpha (
550                        pixbuf,
551                        tasklist->area->window,
552                        0, 0,
553                        real_task->x + 3 + (16 - gdk_pixbuf_get_width (pixbuf)) / 2,
554                        real_task->y + (real_task->height - gdk_pixbuf_get_height (pixbuf)) / 2,
555                        gdk_pixbuf_get_width (pixbuf),
556                        gdk_pixbuf_get_height (pixbuf),
557                        GDK_PIXBUF_ALPHA_BILEVEL,
558                        127,
559                        GDK_RGB_DITHER_NORMAL,
560                        gdk_pixbuf_get_width (pixbuf),
561                        gdk_pixbuf_get_height (pixbuf));
562
563        }
564
565        if (real_task->task_group) {
566                GtkStyle *style;
567                GdkWindow *window;
568                GdkGC *lgc, *dgc;
569                int x, y, i, j;
570
571                style = tasklist->area->style;
572
573                lgc = style->light_gc[focused ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL];
574                dgc = style->dark_gc[focused ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL];
575
576                window = tasklist->area->window;
577
578                x = real_task->x + real_task->width - style->klass->ythickness - 10;
579                y = real_task->y + style->klass->xthickness + 2;
580
581                for (i = 0; i < 3; i++) {
582                        for (j = i; j < 3; j++) {
583                                draw_dot (window, lgc, dgc, x + j*3, y + i*3);
584                        }
585                }
586
587               
588
589#if 0
590                gtk_draw_arrow (tasklist->area->style, tasklist->area->window,
591                                focused ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL,
592                                GTK_SHADOW_ETCHED_IN,
593                                GTK_ARROW_DOWN, TRUE,
594                                real_task->x + real_task->width - 12,
595                                real_task->y + (real_task->height - 8) / 2,
596                                9, 8);
597#endif
598        }
599
600}
601
602
603static int
604max_width (GSList *tasks)
605{
606        TasklistTask *task;
607        GSList *li;
608        int maxwidth = 0;
609        char *s;
610
611        for (li = tasks; li; li = li->next) {
612                task = li->data;
613
614                if (task->fullwidth < 0) {
615                        s = tasklist_task_get_label (task, -1, task->task_group);
616                        task->fullwidth = gdk_string_width (task->tasklist->area->style->font, s);
617                        g_free (s);
618                }
619
620                maxwidth = MAX (maxwidth,
621                                task->fullwidth +
622                                (task->tasklist->config.show_mini_icons
623                                 ? ROW_HEIGHT + 10 : 0));
624        }
625        return MIN (maxwidth, 512);
626}
627
628/* Layout the tasklist */
629static gboolean
630real_layout_tasklist (gpointer data)
631{
632        Tasklist *tasklist = data;
633        gint j = 0, k = 0, num = 0, p = 0;
634        TasklistTask *task;
635        GSList *temp = NULL;
636        gint num_rows = 0, num_cols = 0;
637        gint curx = 0, cury = 0, curwidth = 0, curheight = 0;
638
639        tasklist->layout_idle = 0;
640        d(g_message ("Layout!"));
641
642        if (!tasklist->vtasks) {
643                d(g_message ("no tasks :(\n"));
644                gtk_widget_draw (tasklist->area, NULL);
645                return FALSE;
646        }
647
648        num = g_slist_length (tasklist->vtasks);
649       
650        switch (applet_widget_get_panel_orient (APPLET_WIDGET (tasklist->applet))) {
651        case ORIENT_UP:
652        case ORIENT_DOWN:
653                if (num == 0) {
654                        if (tasklist->config.horz_fixed)
655                                tasklist->horz_width = tasklist->config.horz_width;
656                        else
657                                tasklist->horz_width = 4;
658
659                        tasklist_change_size (tasklist, FALSE, -1);
660                       
661                        gtk_widget_draw (tasklist->area, NULL);
662                        return FALSE;
663                }
664
665                while (p < num) {
666                        if (num < get_horz_rows(tasklist))
667                                num_rows = num;
668                       
669                        j++;
670                        if (num_cols < j)
671                                num_cols = j;
672                        if (num_rows < k + 1)
673                                num_rows = k + 1;
674                       
675                        if (get_horz_rows (tasklist) == 0 || j >= ((num + get_horz_rows(tasklist) - 1) / get_horz_rows(tasklist))) {
676                                j = 0;
677                                k++;
678                        }
679                        p++;
680                }
681               
682                if (tasklist->config.horz_fixed) {
683                        curheight = (ROW_HEIGHT * get_horz_rows(tasklist) - (tasklist->config.sunken?4:0)) / num_rows;
684                        curwidth = (tasklist->config.horz_width - (tasklist->config.sunken?4:0)) / num_cols;
685
686                } else {
687                        int width;
688
689                        width = tasklist->config.horz_taskwidth * num_cols + DRAG_HANDLE_SIZE;
690
691                        if (width > tasklist->config.horz_width)
692                                width = tasklist->config.horz_width;
693                       
694                        if (tasklist->config.horz_never_push)
695                                clamp_size (tasklist, &width);
696
697                        width -= DRAG_HANDLE_SIZE;
698
699                        curheight = (ROW_HEIGHT * get_horz_rows(tasklist) - (tasklist->config.sunken?4:0)) / num_rows;
700#if 0
701                        /* If the total width is higher than allowed,
702                           we use the "fixed" way instead */
703                        if ((curwidth * num_cols) > tasklist->config.horz_width)
704                                curwidth = (tasklist->config.horz_width - 0) / num_cols;
705#endif
706                        curwidth = width / num_cols;
707                }
708
709
710                curx = (tasklist->config.sunken?2:0);
711                cury = (tasklist->config.sunken?2:0);
712
713
714                for (temp = tasklist->vtasks; temp != NULL; temp = temp->next) {
715                        task = (TasklistTask *) temp->data;
716                       
717                        task->x = curx;
718                        task->y = cury;
719                        task->width = curwidth;
720                        task->height = curheight;
721                       
722                        if (tasklist->config.horz_fixed) {
723                                curx += curwidth;
724                       
725                                if (curx >= tasklist->config.horz_width ||
726                                    curx + curwidth > tasklist->config.horz_width) {
727                                        cury += curheight;
728                                        curx = (tasklist->config.sunken?2:0);
729                                }
730                        } else {
731
732                                curx += curwidth;
733
734                                if (curx >= num_cols * curwidth) {
735                                        cury += curheight;
736                                        curx = (tasklist->config.sunken?2:0);
737                                }
738                        }
739                }
740
741                if (tasklist->config.horz_fixed)
742                        tasklist->horz_width = tasklist->config.horz_width;
743                else
744                        tasklist->horz_width = num_cols * curwidth + 4;
745
746                tasklist_change_size (tasklist, FALSE, -1);
747
748                break;
749
750        case ORIENT_LEFT:
751        case ORIENT_RIGHT:
752
753                if (num == 0) {
754                        if (tasklist->config.vert_fixed)
755                                tasklist->vert_height = tasklist->config.vert_height;
756                        else
757                                tasklist->vert_height = 4;
758
759                        tasklist_change_size (tasklist, FALSE, -1);
760                       
761                        gtk_widget_draw (tasklist->area, NULL);
762                        return FALSE;
763                }
764
765                curheight = ROW_HEIGHT;
766                if (tasklist->config.follow_panel_size)
767                        curwidth = tasklist->panel_size - (tasklist->config.sunken?4:0);
768                else
769                        curwidth = tasklist->config.vert_width - (tasklist->config.sunken?4:0);
770               
771                if (tasklist->config.vert_width_full)
772                        curwidth = MAX (curwidth, max_width (tasklist->vtasks));
773
774                num_cols = 1;
775                num_rows = num;
776               
777                curx = (tasklist->config.sunken?2:0);
778                cury = (tasklist->config.sunken?2:0);
779
780                if (tasklist->config.vert_fixed) {
781                        tasklist->vert_height = tasklist->config.vert_height;
782                } else {
783                        tasklist->vert_height = curheight * num_rows + 4 + DRAG_HANDLE_SIZE;
784                        if (tasklist->config.vert_never_push)
785                                clamp_size (tasklist, &tasklist->vert_height);
786                        tasklist->vert_height -= DRAG_HANDLE_SIZE;
787                }
788
789                tasklist_change_size (tasklist, FALSE, curwidth);
790               
791                for (temp = tasklist->vtasks; temp != NULL; temp = temp->next) {
792                        task = (TasklistTask *) temp->data;
793                       
794                        task->x = curx;
795                        task->y = cury;
796                        task->width = curwidth;
797                        task->height = curheight;
798                       
799                        curx += curwidth;
800
801                        if (curx >= (tasklist->config.follow_panel_size?
802                                     tasklist->panel_size:
803                                     tasklist->config.vert_width) - (tasklist->config.sunken?4:0)) {
804                                cury += curheight;
805                                curx = (tasklist->config.sunken?2:0);
806                        }
807                }
808
809                break;
810        }
811
812        gtk_widget_draw (tasklist->area, NULL);
813
814        return FALSE;
815}
816
817/* this now actually just queues a relayout */
818void
819tasklist_layout_tasklist (Tasklist *tasklist)
820{
821        g_return_if_fail (tasklist);
822
823        /* don't queue another timeout */
824        if (tasklist->layout_idle) {
825                d(g_message ("Skipped layout!"));
826                return;
827        }
828       
829        d(g_message ("Adding layout callback..."));
830        tasklist->layout_idle = gtk_idle_add (real_layout_tasklist, tasklist);
831}
832
833#if 0
834/* Get a task from the list that has got the given gwmh_task */
835static TasklistTask *
836find_gwmh_task (Tasklist *tasklist, GwmhTask *gwmh_task)
837{
838        GList *temp_tasks;
839        TasklistTask *task;
840
841        temp_tasks = tasklist->tasks;
842
843        while (temp_tasks) {
844                task = (TasklistTask *)temp_tasks->data;
845                if (task->gwmh_task == gwmh_task)
846                        return task;
847                temp_tasks = temp_tasks->next;
848        }
849       
850        return NULL;
851}
852#endif
853
854/* This routine gets called when desktops are switched etc */
855static gboolean
856desk_notifier (gpointer func_data, GwmhDesk *desk,
857               GwmhDeskInfoMask change_mask)
858{
859        Tasklist *tasklist = (Tasklist *) func_data;
860       
861        if (tasklist->config.all_desks_minimized &&
862            tasklist->config.all_desks_normal)
863                return TRUE;
864
865        tasklist_redo_vtasks (tasklist);
866        tasklist_layout_tasklist (tasklist);
867
868        return TRUE;
869}
870
871static void
872tasklist_group_destroy (TasklistTask *group)
873{
874        d(g_print (" *** destroying group: %s\n", group->group_name));
875
876        tasklist_icon_destroy (group);
877
878        g_hash_table_remove (group->tasklist->groups, group->group_name);
879
880        g_free (group->gwmh_task);
881        group->gwmh_task = NULL;
882        g_free (group->group_name);
883        group->group_name = NULL;
884
885        tasklist_clean_menu (group);
886
887        g_free (group);
888}
889
890static void
891tasklist_task_destroy (GwmhTask *gtask, Tasklist *tasklist)
892{
893        TasklistTask *ttask;
894       
895        g_return_if_fail (gtask != NULL);
896        g_return_if_fail (tasklist != NULL);   
897       
898        ttask = g_hash_table_lookup (tasklist->tasks, gtask);
899       
900        if (!ttask) {
901                g_warning ("Task not found in tasklist: %p; not destroying", gtask);
902                return;
903        }
904
905        d(g_print (" *** removing: %p (%s)\n", ttask, gtask->name));
906       
907        ttask->destroyed = TRUE;
908
909        g_hash_table_remove (tasklist->tasks, gtask);
910       
911        if (ttask == tasklist->motion_task)
912                tasklist->motion_task = NULL;
913
914        if (ttask->menuitem) {
915                gtk_object_set_data (GTK_OBJECT (ttask->menuitem),
916                                     "task", NULL);
917                gtk_widget_hide (ttask->menuitem);
918                ttask->menuitem = NULL;
919        }
920
921        tasklist_icon_destroy (ttask);
922        tasklist_clean_menu (ttask);
923
924        if (ttask->group) {
925                if (ttask->group->focused_task == ttask)
926                        ttask->group->focused_task = NULL;
927
928                ttask->group->tasks = g_slist_remove (ttask->group->tasks, ttask);
929
930                if (!ttask->group->tasks)
931                        tasklist_group_destroy (ttask->group);
932                else
933                        fixup_vtask (ttask->group, NULL);
934        }
935
936        tasklist->vtasks = g_slist_remove (tasklist->vtasks, ttask);
937
938        g_free (ttask);
939
940        tasklist_layout_tasklist (tasklist);
941}
942
943static TasklistTask *
944tasklist_group_new (TasklistTask *first_task, const char *group_name)
945{
946        TasklistTask *group;
947
948        g_return_val_if_fail (first_task != NULL, NULL);
949        g_return_val_if_fail (group_name != NULL, NULL);
950
951        group = g_new0 (TasklistTask, 1);
952        group->tasklist = first_task->tasklist;
953        group->task_group = TRUE;
954        group->group_name = g_strdup (group_name);
955        group->fullwidth = -1;
956        g_hash_table_insert (group->tasklist->groups,
957                             group->group_name, group);
958
959        gdk_pixbuf_ref (first_task->icon->normal);
960        gdk_pixbuf_ref (first_task->icon->minimized);
961
962        group->icon = g_new (TasklistIcon, 1);
963        group->icon->normal    = first_task->icon->normal;
964        group->icon->minimized = first_task->icon->minimized;
965
966        group->tasks = g_slist_prepend (group->tasks, first_task);
967
968        group->gwmh_task = g_new0 (GwmhTask, 1);
969        group->gwmh_task->name = group->group_name;
970
971        return group;
972}
973
974/*
975 * this is void since we don't need to get the return value when a
976 * task is created and we can just create a lot of tasks from the gwmh
977 * task glist
978 */
979
980static void
981tasklist_task_new (GwmhTask *gtask, Tasklist *tasklist)
982{
983        TasklistTask *ttask;
984        char *class;
985
986        g_return_if_fail (gtask != NULL);
987        g_return_if_fail (tasklist != NULL);
988        g_return_if_fail (tasklist->tasks != NULL);
989
990        d(g_print ("Adding task: %s\n", gtask->name));
991
992        ttask = g_new0 (TasklistTask, 1);
993
994        ttask->tasklist = tasklist;
995        ttask->gwmh_task = gtask;
996
997        g_hash_table_insert (tasklist->tasks, gtask, ttask);
998
999        ttask->wmhints_icon = tasklist_icon_get_pixmap (ttask);
1000
1001        tasklist_icon_set (ttask);
1002        ttask->fullwidth = -1;
1003
1004        class = get_task_class (gtask);
1005        if (class == NULL)
1006                return;
1007
1008        ttask->group = g_hash_table_lookup (tasklist->groups, class);
1009
1010        if (ttask->group == NULL) {
1011                ttask->group = tasklist_group_new (ttask, class);
1012        } else {
1013                ttask->group->tasks = g_slist_prepend (ttask->group->tasks, ttask);
1014                fixup_vtask (ttask->group, NULL);
1015        }
1016
1017        g_free (class);
1018}
1019
1020static void
1021tasklist_task_new_iterator (gpointer data, gpointer user_data)
1022{
1023        GwmhTask *gtask = data;
1024        Tasklist *tasklist = user_data;
1025
1026        tasklist_task_new (gtask, tasklist);
1027}
1028
1029
1030/* This routine gets called when tasks are created/destroyed etc */
1031static gboolean
1032task_notifier (gpointer func_data, GwmhTask *gwmh_task,
1033               GwmhTaskNotifyType ntype,
1034               GwmhTaskInfoMask imask)
1035{
1036        Tasklist *tasklist = (Tasklist *) func_data;
1037        TasklistTask *task;
1038        gboolean redraw = FALSE;
1039        gboolean relayout = FALSE;
1040
1041        switch (ntype)
1042        {
1043        case GWMH_NOTIFY_INFO_CHANGED:
1044                /* if this is just an allocation change, then no we don't want
1045                 * to do anything */
1046                if ( ! (imask & ~GWMH_TASK_INFO_ALLOCATION))
1047                        break;
1048
1049                task = g_hash_table_lookup (tasklist->tasks, gwmh_task);
1050                if (!task) {
1051                        g_warning ("Getting info about task we don't know about: %p", gwmh_task);
1052                        break;
1053                }
1054
1055                if ((imask & GWMH_TASK_INFO_FOCUSED) &&
1056                    task->gwmh_task->focused && task->group)
1057                        task->group->focused_task = task;
1058
1059
1060                if (imask & GWMH_TASK_INFO_MISC) {
1061                        if (tasklist->config.vert_width_full &&
1062                            (tasklist->orient == ORIENT_LEFT ||
1063                             tasklist->orient == ORIENT_RIGHT))
1064                                relayout = TRUE;
1065                        task->fullwidth = -1;
1066                }
1067
1068                /* we only need to re-layout if the task has changed
1069                 * visibility status.  If it has, its group will also get
1070                 * fixed up
1071                 */
1072                if (fixup_vtask (task, &redraw)) {
1073                        relayout = TRUE;
1074                }
1075
1076
1077                if (imask & GWMH_TASK_INFO_WM_HINTS) {
1078                        if (tasklist_icon_get_pixmap (task) !=
1079                            task->wmhints_icon) {
1080                                tasklist_icon_destroy (task);
1081                                tasklist_icon_set (task);
1082                                redraw = TRUE;
1083                        }
1084                }
1085                if (imask & GWMH_TASK_INFO_GSTATE)
1086                        relayout = TRUE;
1087                if (imask & GWMH_TASK_INFO_ICONIFIED)
1088                        relayout = TRUE;
1089
1090                if (imask & GWMH_TASK_INFO_FOCUSED)
1091                        redraw = TRUE;
1092                if (imask & GWMH_TASK_INFO_MISC)
1093                        redraw = TRUE;
1094                if (imask & GWMH_TASK_INFO_DESKTOP)
1095                        relayout = TRUE;
1096
1097                /* BTW, change_size is always called from the layout_tasklist
1098                 * since we pass TRUE */
1099                if (relayout)
1100                        tasklist_layout_tasklist (tasklist);
1101                else if (redraw)
1102                        tasklist_draw_task (task->group && is_task_really_visible (task->group)
1103                                            ? task->group : task, NULL);
1104
1105                break;
1106        case GWMH_NOTIFY_NEW:
1107                tasklist_task_new (gwmh_task, tasklist);
1108                tasklist_layout_tasklist (tasklist);
1109                break;
1110        case GWMH_NOTIFY_DESTROY:
1111                tasklist_task_destroy (gwmh_task, tasklist);
1112                break;
1113        default:
1114                d(g_print ("Unknown ntype: %d\n", ntype));
1115        }
1116
1117        return TRUE;
1118}
1119
1120/* Show the task if need. Return TRUE if so */
1121static gboolean
1122show_task (Tasklist *tasklist, TasklistTask *task)
1123{
1124        if (!GWMH_TASK_ICONIFIED (task->gwmh_task) && GWMH_TASK_FOCUSED (task->gwmh_task))
1125                return FALSE;
1126
1127       
1128        if (!(tasklist->config.move_to_current && GWMH_TASK_ICONIFIED (task->gwmh_task))) {
1129                GwmhDesk *desk_info;
1130                desk_info = gwmh_desk_get_config ();
1131               
1132                if (task->gwmh_task->desktop != desk_info->current_desktop ||
1133                    task->gwmh_task->harea != desk_info->current_harea ||
1134                    task->gwmh_task->varea != desk_info->current_varea) {
1135                        gwmh_desk_set_current_area (task->gwmh_task->desktop,
1136                                                    task->gwmh_task->harea,
1137                                                    task->gwmh_task->varea);
1138                }
1139        }
1140       
1141        gwmh_task_show (task->gwmh_task);
1142#if 0
1143        /* Why is a focus needed here?
1144           gwmh_task_show is supposed to give focus */
1145       
1146        /*
1147         * i think this is a sawfish bug: when "give
1148         * uniconised windows focused" is unchecked it
1149         * probably doesn't reassign focus. -- jacob
1150         */
1151       
1152        gwmh_task_focus (task->gwmh_task);
1153        gwmh_task_focus (task->gwmh_task);
1154       
1155#endif
1156        return TRUE;
1157}
1158
1159/* This routine gets called when the mouse is pressed */
1160static gboolean
1161cb_button_press_event (GtkWidget *widget, GdkEventButton *event, Tasklist *tasklist)
1162{
1163        TasklistTask *task;
1164       
1165        task = task_get_xy (tasklist, (gint)event->x, (gint)event->y);
1166
1167        if (!task)
1168                return FALSE;
1169
1170        if (event->button == 1) {
1171                if (task->task_group)
1172                        tasklist_group_popup (task, event->button, event->time);
1173                else if (! show_task (tasklist, task))
1174                        gwmh_task_iconify (task->gwmh_task);
1175               
1176                return TRUE;
1177        }
1178       
1179        if (event->button == 3) {
1180                gtk_signal_emit_stop_by_name (GTK_OBJECT (widget),
1181                                              "button_press_event");
1182                tasklist_menu_popup (task, event->button, event->time);
1183                return TRUE;
1184        }
1185
1186        return FALSE;
1187}
1188
1189static void
1190cb_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time, Tasklist *tasklist)
1191{
1192        if (tasklist->motion_timeout) {
1193                gtk_timeout_remove (tasklist->motion_timeout);
1194                tasklist->motion_timeout = 0;
1195                tasklist->motion_task = NULL;
1196        }
1197}
1198
1199static gboolean
1200cb_motion_timeout (gpointer user_data)
1201{
1202        Tasklist *tasklist = user_data;
1203       
1204        if (tasklist->motion_task && !(tasklist->motion_task->task_group)) {
1205                show_task (tasklist, tasklist->motion_task);
1206        }
1207
1208        tasklist->motion_timeout = 0;
1209        tasklist->motion_task = NULL;
1210       
1211        return FALSE;   
1212}
1213
1214/* This routine gets called when user drag something over the tasklist */
1215static gboolean
1216cb_drag_motion (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, Tasklist *tasklist)
1217{
1218        TasklistTask *task;
1219       
1220        gdk_drag_status (context, 0, time);
1221       
1222        task = task_get_xy (tasklist, x, y);
1223       
1224        if (task != tasklist->motion_task) {
1225       
1226                if (tasklist->motion_timeout) {
1227                        gtk_timeout_remove (tasklist->motion_timeout);
1228                }
1229       
1230                tasklist->motion_task = task;
1231
1232                if (task) {     
1233                        tasklist->motion_timeout = gtk_timeout_add (MOTION_TIMEOUT, cb_motion_timeout, tasklist);               
1234                } else {
1235                        tasklist->motion_timeout = 0;
1236                }
1237        }
1238       
1239        return TRUE;
1240}
1241
1242/* FIXME: This routine is one of the ugliest ones in existence,
1243 * gtk purists should not look at it.  This should get rewritten such
1244 * that each label has a widget with an inputonly window OR draw the
1245 * tooltips ourselves.  However for the time being this is better then
1246 * nothing I suppose.  Feel free to flame me for this.  Tooltips have
1247 * been one of the more requested features for a LONG time.
1248 *
1249 * -George
1250 */
1251static gboolean
1252cb_area_event (GtkWidget *widget, GdkEvent *event, Tasklist *tasklist)
1253{
1254        GSList *temp_tasks, *temp;
1255        TasklistTask *task;
1256
1257        if ((event->type == GDK_LEAVE_NOTIFY ||
1258             event->type == GDK_ENTER_NOTIFY) &&
1259            event->crossing.detail == GDK_NOTIFY_INFERIOR)
1260                return FALSE;
1261
1262        if (event->type == GDK_LEAVE_NOTIFY) {
1263                gtk_tooltips_set_tip (tasklist->tooltips,
1264                                      tasklist->fake_tooltip_widget,
1265                                      NULL, NULL);
1266                tasklist->tooltip_task = NULL;
1267                return FALSE;
1268        }
1269
1270        if (event->type == GDK_MOTION_NOTIFY ||
1271            event->type == GDK_ENTER_NOTIFY) {
1272                int x, y;
1273
1274                if (event->type == GDK_MOTION_NOTIFY) {
1275                        x = event->motion.x;
1276                        y = event->motion.y;
1277                } else {
1278                        x = event->crossing.x;
1279                        y = event->crossing.y;
1280                }
1281
1282                temp_tasks = tasklist->vtasks;
1283                for (temp = temp_tasks; temp != NULL; temp = temp->next) {
1284                        task = (TasklistTask *)temp->data;
1285
1286                        if (!is_task_really_visible (task))
1287                                continue;
1288
1289                        if (x >= task->x &&
1290                            x <= task->x + task->width &&
1291                            y >= task->y &&
1292                            y <= task->y + task->height) {
1293                                /* This is it, and this is
1294                                 * also the utterly evil part */
1295                                if (tasklist->tooltip_task != task) {
1296                                        char *label;
1297                                        gboolean ignore;
1298                                        gpointer old_data;
1299                                        GdkEvent new_event = { 0 };
1300                                        GtkWidget *fake = tasklist->fake_tooltip_widget;
1301
1302                                        tasklist->tooltip_task = task;
1303
1304                                        fake->allocation.x = task->x;
1305                                        fake->allocation.y = task->y;
1306                                        fake->allocation.width = task->width;
1307                                        fake->allocation.height = task->height;
1308                                        fake->window = tasklist->area->window;
1309
1310                                        label = tasklist_task_get_label
1311                                                (task, 0, task->task_group);
1312
1313                                        gtk_tooltips_set_tip
1314                                                (tasklist->tooltips,
1315                                                 fake, label, NULL);
1316
1317                                        g_free (label);
1318
1319                                        if (event->type == GDK_ENTER_NOTIFY) {
1320                                                new_event = *event;
1321                                        } else {
1322                                                new_event.type = GDK_ENTER_NOTIFY;
1323                                                new_event.any.window = fake->window;
1324                                        }
1325
1326                                        /* EEEEEEEEEEEEEVIL, this is the only
1327                                         * way to make gtk draw the tooltips
1328                                         * short of drawing them ourselves.
1329                                         * The tooltips unfortunately do a whole
1330                                         * bunch of internal checking so we abuse
1331                                         * the area window.  This way it will
1332                                         * appear almost as if the window belongs
1333                                         * to the fake rather then to the area,
1334                                         * at least for the duration of this call. */
1335                                        old_data = fake->window->user_data;
1336                                        fake->window->user_data = fake;
1337                                        gtk_signal_emit_by_name (GTK_OBJECT (fake),
1338                                                                 "event",
1339                                                                 &new_event,
1340                                                                 &ignore);
1341                                        fake->window->user_data = old_data;
1342                                }
1343                                break;
1344                        }
1345                }
1346                /* No task found, so we want to turn off the tooltip */
1347                if (temp == NULL) {
1348                        gtk_tooltips_set_tip (tasklist->tooltips,
1349                                              tasklist->fake_tooltip_widget,
1350                                              NULL, NULL);
1351                        tasklist->tooltip_task = NULL;
1352                        return FALSE;
1353                }
1354        }
1355
1356        return FALSE;
1357}
1358
1359/* This routine gets called when the tasklist is exposed */
1360static gboolean
1361cb_expose_event (GtkWidget *widget, GdkEventExpose *event, Tasklist *tasklist)
1362{
1363        GSList *temp_tasks, *temp;
1364        TasklistTask *task;
1365
1366        temp_tasks = tasklist->vtasks;
1367
1368        gtk_paint_box (tasklist->area->style, tasklist->area->window,
1369                       tasklist->area->state,
1370                       tasklist->config.sunken?GTK_SHADOW_IN:GTK_SHADOW_NONE,
1371                       &event->area, tasklist->area, "button",
1372                       0, 0, -1, -1);
1373       
1374        for (temp = temp_tasks; temp != NULL; temp = temp->next) {
1375                GdkRectangle rect, dest;
1376                task = (TasklistTask *)temp->data;
1377
1378                rect.x = task->x;
1379                rect.y = task->y;
1380                rect.width = task->width;
1381                rect.height = task->height;
1382
1383                if(gdk_rectangle_intersect(&event->area, &rect, &dest))
1384                        tasklist_draw_task (task, &dest);
1385        }
1386
1387        return FALSE;
1388}
1389
1390/* This routine gets called when the user selects "properties" */
1391static void
1392cb_properties (AppletWidget *applet, Tasklist *tasklist)
1393{
1394        tasklist_display_properties (tasklist);
1395}
1396
1397/* This routine gets called when the user selects "about" */
1398static void
1399cb_about (AppletWidget *applet, Tasklist *tasklist)
1400{
1401
1402        const char *authors[] = {
1403                "Anders Carlsson (andersca@gnu.org)",
1404                "Miguel de Icaza (miguel@ximian.com)",
1405                "Jacob Berkman (jacob@ximian.com)",
1406                "George Lebl (jirka@5z.com)",
1407                NULL
1408        };
1409
1410        /* Stop the about box from being shown twice at once */
1411        if (tasklist->about_dialog != NULL)
1412        {
1413                gtk_widget_show_now (tasklist->about_dialog);
1414                gdk_window_raise (tasklist->about_dialog->window);
1415                return;
1416        }
1417       
1418        tasklist->about_dialog = gnome_about_new (
1419                "Gnome Tasklist",
1420                VERSION,
1421                _("Copyright (C) 1999 Anders Carlsson"),
1422                authors,
1423                _("A tasklist for the GNOME desktop environment.\nIcons by Tuomas Kuosmanen (tigert@gimp.org)."),
1424                NULL);
1425        gtk_signal_connect (GTK_OBJECT(tasklist->about_dialog), "destroy",
1426                            GTK_SIGNAL_FUNC(gtk_widget_destroyed), &tasklist->about_dialog);
1427
1428        gtk_widget_show_now (tasklist->about_dialog);
1429
1430        if (tasklist->about_dialog->window != NULL)
1431                gdk_window_raise (tasklist->about_dialog->window);
1432}
1433
1434/* Ignore mouse button 1 clicks */
1435static gboolean
1436ignore_1st_click (GtkWidget *widget, GdkEvent *event, Tasklist *tasklist)
1437{
1438        GdkEventButton *buttonevent = (GdkEventButton *)event;
1439
1440        if (event->type == GDK_BUTTON_PRESS &&
1441            buttonevent->button == 1) {
1442                if (buttonevent->window != tasklist->area->window)
1443                        buttonevent->button = 2;
1444        }
1445        if (event->type == GDK_BUTTON_RELEASE &&
1446            buttonevent->button == 1) {
1447                if (buttonevent->window != tasklist->area->window)
1448                        buttonevent->button = 2;
1449        }
1450         
1451        return FALSE;
1452}
1453
1454/* Changes size of the applet */
1455void
1456tasklist_change_size (Tasklist *tasklist, gboolean layout, int fullwidth)
1457{
1458        int handle_width = 0;
1459        int handle_height = 0;
1460        int width = 0;
1461        int height = 0;
1462
1463
1464        switch (applet_widget_get_panel_orient (APPLET_WIDGET (tasklist->applet))) {
1465        case ORIENT_UP:
1466        case ORIENT_DOWN:
1467                width = tasklist->horz_width;
1468                handle_width = tasklist->horz_width + DRAG_HANDLE_SIZE;
1469                height = handle_height = get_horz_rows (tasklist) * ROW_HEIGHT;
1470
1471                GTK_HANDLE_BOX (tasklist->handle)->handle_position = GTK_POS_LEFT;
1472                break;
1473        case ORIENT_LEFT:
1474        case ORIENT_RIGHT:
1475                width = tasklist->config.follow_panel_size
1476                        ? tasklist->panel_size
1477                        : tasklist->config.vert_width;
1478
1479                if (tasklist->config.vert_width_full) {
1480                        if (fullwidth < 0)
1481                                fullwidth = max_width (tasklist->vtasks);
1482
1483                        width = MAX (width, fullwidth);
1484                }
1485
1486                handle_width = width;
1487                handle_height = tasklist->vert_height + DRAG_HANDLE_SIZE;
1488                height = tasklist->vert_height;
1489                       
1490                GTK_HANDLE_BOX (tasklist->handle)->handle_position = GTK_POS_TOP;
1491        }
1492
1493        gtk_widget_set_usize (tasklist->handle, handle_width, handle_height);
1494        gtk_drawing_area_size (GTK_DRAWING_AREA (tasklist->area), width, height);
1495       
1496        if (layout)
1497                tasklist_layout_tasklist (tasklist);
1498}
1499
1500/* Called when the panel's orient changes */
1501static void
1502cb_change_orient (GtkWidget *widget, GNOME_Panel_OrientType orient, Tasklist *tasklist)
1503{
1504        tasklist->orient = orient;
1505
1506        /* Change size accordingly */
1507        tasklist_change_size (tasklist, TRUE, -1);
1508}
1509
1510static void
1511cb_change_pixel_size (GtkWidget *widget, int size, Tasklist *tasklist)
1512{
1513        tasklist->panel_size = size;
1514       
1515        /* Change size accordingly */
1516        if(tasklist->config.follow_panel_size)
1517                tasklist_change_size (tasklist, TRUE, -1);
1518}
1519
1520static void
1521cb_help (AppletWidget *w, Tasklist *tasklist)
1522{
1523        GnomeHelpMenuEntry help_entry = { "tasklist_applet",
1524                                          "index.html" };
1525        gnome_help_display(NULL, &help_entry);
1526}
1527
1528static void
1529tasklist_destroy (GtkObject *applet_widget, Tasklist *tasklist)
1530{
1531        gwmh_task_notifier_remove (tasklist->task_notifier_id);
1532        gwmh_desk_notifier_remove (tasklist->desk_notifier_id);
1533
1534        tasklist->task_notifier_id = -1;
1535        tasklist->desk_notifier_id = -1;
1536
1537        if (tasklist->layout_idle != 0) {
1538                gtk_idle_remove (tasklist->layout_idle);
1539                tasklist->layout_idle = 0;
1540        }
1541
1542        if (tasklist->motion_timeout) {
1543                gtk_timeout_remove (tasklist->motion_timeout);
1544                tasklist->motion_timeout = 0;
1545                tasklist->motion_task = NULL;
1546        }
1547
1548        if (tasklist->prop)
1549                gtk_widget_destroy (tasklist->prop);
1550        tasklist->prop = NULL;
1551
1552        if (tasklist->about_dialog)
1553                gtk_widget_destroy (tasklist->about_dialog);
1554        tasklist->about_dialog = NULL;
1555
1556        if (tasklist->unknown_icon != NULL) {
1557                if (tasklist->unknown_icon->normal != NULL) {
1558                        gdk_pixbuf_unref (tasklist->unknown_icon->normal);
1559                        tasklist->unknown_icon->normal = NULL;
1560                }
1561                if (tasklist->unknown_icon->minimized != NULL) {
1562                        gdk_pixbuf_unref (tasklist->unknown_icon->minimized);
1563                        tasklist->unknown_icon->minimized = NULL;
1564                }
1565                g_free (tasklist->unknown_icon);
1566                tasklist->unknown_icon = NULL;
1567        }
1568
1569        g_slist_free (tasklist->vtasks);
1570        tasklist->vtasks = NULL;
1571
1572#ifndef APPLET_COMPILE_AS_PROCESS
1573        #warning Here we save peoples memory by making this a shlib applet and leaking whatever we possibly can!
1574#endif
1575
1576        if (tasklist->fake_tooltip_widget != NULL) {
1577                tasklist->fake_tooltip_widget->window = NULL;
1578                gtk_widget_unref (tasklist->fake_tooltip_widget);
1579                tasklist->fake_tooltip_widget = NULL;
1580        }
1581
1582        if (tasklist->tooltips != NULL) {
1583                gtk_object_destroy (GTK_OBJECT (tasklist->tooltips));
1584                tasklist->tooltips = NULL;
1585        }
1586
1587        if (tasklist->groups != NULL) {
1588                /* FIXME: we leak EVERYTHING here */
1589                g_hash_table_destroy (tasklist->groups);
1590                tasklist->groups = NULL;
1591        }
1592        if (tasklist->tasks != NULL) {
1593                /* FIXME: we leak EVERYTHING here */
1594                g_hash_table_destroy (tasklist->tasks);
1595                tasklist->tasks = NULL;
1596        }
1597
1598        g_free (tasklist);
1599}
1600
1601/* Create the applet */
1602static Tasklist *
1603tasklist_new (void)
1604{
1605        Tasklist *tasklist;
1606        GtkWidget *hbox;
1607
1608        tasklist = g_new0 (Tasklist, 1);
1609        tasklist->panel_size = 48;
1610        tasklist->horz_width = 0;
1611        tasklist->vert_height = 0;
1612
1613        tasklist->applet = applet_widget_new ("tasklist_applet");
1614        if (!tasklist->applet) {
1615                g_warning (_("Tasklist: Unable to create applet widget"));
1616                g_free (tasklist);
1617                return NULL;
1618        }
1619
1620        /* Some evil tooltip stuff, this is some pretty evil stuff */
1621        tasklist->tooltips = gtk_tooltips_new ();
1622        tasklist->fake_tooltip_widget = gtk_type_new (gtk_widget_get_type ());
1623        GTK_WIDGET_SET_FLAGS (tasklist->fake_tooltip_widget, GTK_NO_WINDOW);
1624        GTK_WIDGET_SET_FLAGS (tasklist->fake_tooltip_widget, GTK_VISIBLE);
1625        GTK_WIDGET_SET_FLAGS (tasklist->fake_tooltip_widget, GTK_MAPPED);
1626        gtk_widget_ref (tasklist->fake_tooltip_widget);
1627        gtk_object_sink (GTK_OBJECT (tasklist->fake_tooltip_widget));
1628        tasklist->tooltip_task = NULL;
1629
1630        hbox = gtk_hbox_new (FALSE, 0);
1631        gtk_widget_show (hbox);
1632       
1633        tasklist->handle = gtk_handle_box_new ();
1634        gtk_signal_connect (GTK_OBJECT (tasklist->handle), "event",
1635                            GTK_SIGNAL_FUNC (ignore_1st_click), tasklist);
1636
1637        tasklist->area = gtk_drawing_area_new ();
1638
1639        gtk_widget_ensure_style (tasklist->area);
1640        tasklist->unknown_icon = g_new (TasklistIcon, 1);
1641        tasklist->unknown_icon->normal = gdk_pixbuf_new_from_xpm_data (unknown_xpm);
1642        tasklist->unknown_icon->minimized = tasklist_icon_create_minimized_icon (tasklist, tasklist->unknown_icon->normal);
1643
1644#if 0
1645        gtk_container_add (GTK_CONTAINER (tasklist->handle), tasklist->area);
1646#endif
1647
1648        /* we must bind signals BEFORE applet_widget_add to avoid
1649         * a race */
1650        gtk_signal_connect (GTK_OBJECT (tasklist->applet), "change-orient",
1651                            GTK_SIGNAL_FUNC (cb_change_orient), tasklist);
1652        gtk_signal_connect (GTK_OBJECT (tasklist->applet), "save-session",
1653                            GTK_SIGNAL_FUNC (tasklist_write_config), tasklist);
1654        gtk_signal_connect (GTK_OBJECT (tasklist->applet), "change-pixel-size",
1655                            GTK_SIGNAL_FUNC (cb_change_pixel_size), tasklist);
1656
1657        gtk_widget_set_events (tasklist->area, GDK_EXPOSURE_MASK |
1658                               GDK_BUTTON_PRESS_MASK |
1659                               GDK_BUTTON_RELEASE_MASK |
1660                               GDK_ENTER_NOTIFY_MASK |
1661                               GDK_LEAVE_NOTIFY_MASK |
1662                               GDK_POINTER_MOTION_MASK);
1663        gtk_signal_connect (GTK_OBJECT (tasklist->area), "event",
1664                            GTK_SIGNAL_FUNC (cb_area_event), tasklist);
1665        gtk_signal_connect (GTK_OBJECT (tasklist->area), "expose_event",
1666                            GTK_SIGNAL_FUNC (cb_expose_event), tasklist);
1667        gtk_signal_connect (GTK_OBJECT (tasklist->area), "button_press_event",
1668                            GTK_SIGNAL_FUNC (cb_button_press_event), tasklist);
1669        gtk_signal_connect (GTK_OBJECT (tasklist->area), "drag_motion",
1670                            GTK_SIGNAL_FUNC (cb_drag_motion), tasklist);
1671        gtk_signal_connect (GTK_OBJECT (tasklist->area), "drag_leave",
1672                            GTK_SIGNAL_FUNC (cb_drag_leave), tasklist);                     
1673                                                   
1674        gtk_drag_dest_set (GTK_WIDGET (tasklist->area), 0,
1675                           NULL, 0, GDK_ACTION_COPY);
1676
1677        /*
1678         * we add the area *after* the widget so that we get events on it
1679         */
1680        gtk_container_add (GTK_CONTAINER (hbox), tasklist->handle);
1681        applet_widget_add (APPLET_WIDGET (tasklist->applet), hbox);
1682        gtk_container_add (GTK_CONTAINER (tasklist->handle), tasklist->area);
1683       
1684        tasklist_read_config (tasklist);
1685
1686        if ( ! tasklist->config.enable_tooltips)
1687                gtk_tooltips_disable (tasklist->tooltips);
1688       
1689        applet_widget_register_stock_callback (
1690                APPLET_WIDGET (tasklist->applet),
1691                "properties",
1692                GNOME_STOCK_MENU_PROP,
1693                _("Properties..."),
1694                (AppletCallbackFunc) cb_properties,
1695                tasklist);
1696
1697        applet_widget_register_stock_callback (
1698                APPLET_WIDGET (tasklist->applet),
1699                "help",
1700                GNOME_STOCK_PIXMAP_HELP,
1701                _("Help"),
1702                (AppletCallbackFunc) cb_help,
1703                tasklist);
1704
1705        applet_widget_register_stock_callback (
1706                APPLET_WIDGET (tasklist->applet),
1707                "about",
1708                GNOME_STOCK_MENU_ABOUT,
1709                _("About..."),
1710                (AppletCallbackFunc) cb_about,
1711                tasklist);
1712
1713        tasklist->panel_size = applet_widget_get_panel_pixel_size(APPLET_WIDGET(tasklist->applet));
1714        tasklist->orient = applet_widget_get_panel_orient(APPLET_WIDGET(tasklist->applet));
1715
1716        tasklist->task_notifier_id = gwmh_task_notifier_add (task_notifier, tasklist);
1717        tasklist->desk_notifier_id = gwmh_desk_notifier_add (desk_notifier, tasklist);
1718
1719        gtk_signal_connect (GTK_OBJECT (tasklist->applet), "destroy",
1720                            GTK_SIGNAL_FUNC (tasklist_destroy), tasklist);
1721       
1722        gtk_widget_show_all (tasklist->area);
1723        gtk_widget_show_all (tasklist->handle);
1724
1725        tasklist_change_size (tasklist, TRUE, -1);
1726
1727        tasklist->tasks = g_hash_table_new (g_direct_hash, g_direct_equal);
1728        tasklist->groups = g_hash_table_new (g_str_hash, g_str_equal);
1729
1730        g_list_foreach (gwmh_task_list_get (),
1731                        tasklist_task_new_iterator,
1732                        tasklist);
1733
1734        return tasklist;
1735}
1736
1737static void
1738tasklist_init (void)
1739{
1740        gwmh_init ();
1741}
1742
1743#ifdef APPLET_COMPILE_AS_PROCESS
1744gint
1745main (gint argc, gchar *argv[])
1746{
1747        Tasklist *tasklist;
1748       
1749        /* Initialize i18n */
1750        bindtextdomain (PACKAGE, GNOMELOCALEDIR);
1751        textdomain (PACKAGE);
1752
1753        applet_widget_init ("tasklist_applet",
1754                            VERSION,
1755                            argc, argv,
1756                            NULL, 0, NULL);
1757
1758        gdk_rgb_init ();
1759
1760        gnome_window_icon_set_default_from_file (GNOME_ICONDIR"/gnome-tasklist.png");
1761
1762        gtk_widget_set_default_colormap (gdk_rgb_get_cmap ());
1763        gtk_widget_set_default_visual (gdk_rgb_get_visual ());
1764
1765        tasklist_init ();
1766       
1767        tasklist = tasklist_new ();
1768
1769        gtk_widget_show_all (tasklist->applet);
1770
1771        applet_widget_gtk_main ();
1772
1773        return 0;
1774}
1775#else
1776static GtkWidget *
1777make_new_applet (const char *goad_id)
1778{
1779        static int inited = 0;
1780        Tasklist *tasklist;
1781       
1782        if (!inited){
1783                tasklist_init ();
1784                inited = 1;
1785        }
1786        tasklist = tasklist_new ();
1787
1788        if (!tasklist)
1789                return NULL;
1790
1791        gtk_widget_show_all (tasklist->applet);
1792
1793        return tasklist->applet;
1794}
1795
1796static CORBA_Object
1797activator (PortableServer_POA poa,
1798           const char *goad_id,
1799           const char **params,
1800           gpointer *impl_ptr,
1801           CORBA_Environment *ev)
1802{
1803        GtkWidget *widget;
1804
1805        widget = make_new_applet (goad_id);
1806        if (widget == NULL) {
1807                g_warning (_("Don't know how to activate `%s'\n"), goad_id);
1808                return CORBA_OBJECT_NIL;
1809        }
1810
1811        return applet_widget_corba_activate (widget, poa, goad_id,
1812                                             params, impl_ptr, ev);
1813}
1814static void
1815deactivator (PortableServer_POA poa,
1816             const char *goad_id,
1817             gpointer impl_ptr,
1818             CORBA_Environment *ev)
1819{
1820        applet_widget_corba_deactivate (poa, goad_id, impl_ptr, ev);
1821}
1822
1823static const char *repo_id[]={ "IDL:GNOME/Applet:1.0", NULL };
1824static GnomePluginObject applets_list[] = {
1825        { repo_id, "tasklist_applet", NULL, "Task list applet",
1826          &activator, &deactivator },
1827        { NULL }
1828};
1829
1830GnomePlugin GNOME_Plugin_info = {
1831        applets_list,
1832        NULL
1833};
1834
1835#endif
Note: See TracBrowser for help on using the repository browser.