iconmgr.c revision f66df612
1/*
2 *
3Copyright 1989,1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 * */
25
26/***********************************************************************
27 *
28 * Icon Manager routines
29 *
30 * 09-Mar-89 Tom LaStrange              File Created
31 *
32 ***********************************************************************/
33
34#include <stdio.h>
35#include "twm.h"
36#include "util.h"
37#include "parse.h"
38#include "screen.h"
39#include "resize.h"
40#include "add_window.h"
41#include "siconify.bm"
42#include <X11/Xos.h>
43#include <X11/Xmu/CharSet.h>
44
45static void InsertInIconManager(IconMgr *ip, WList *tmp, TwmWindow *tmp_win);
46
47int iconmgr_textx = siconify_width + 11;
48static WList *Active = NULL;
49WList *DownIconManager = NULL;
50int iconifybox_width = siconify_width;
51int iconifybox_height = siconify_height;
52
53/**
54 * create all the icon manager windows for this screen.
55 */
56void
57CreateIconManagers(void)
58{
59    IconMgr *p;
60    char str[100];
61    char str1[100];
62    Pixel background;
63    const char *icon_name;
64
65    if (Scr->NoIconManagers)
66        return;
67
68    if (Scr->siconifyPm == None) {
69        Scr->siconifyPm = XCreatePixmapFromBitmapData(dpy, Scr->Root,
70                                                      (char *) siconify_bits,
71                                                      siconify_width,
72                                                      siconify_height, 1, 0, 1);
73    }
74
75    for (p = &Scr->iconmgr; p != NULL; p = p->next) {
76        int mask = XParseGeometry(p->geometry, &JunkX, &JunkY,
77                                  (unsigned int *) &p->width,
78                                  (unsigned int *) &p->height);
79
80        if (mask & XNegative)
81            JunkX = Scr->MyDisplayWidth - p->width -
82                (2 * Scr->BorderWidth) + JunkX;
83
84        if (mask & YNegative)
85            JunkY = Scr->MyDisplayHeight - p->height -
86                (2 * Scr->BorderWidth) + JunkY;
87
88        background = Scr->IconManagerC.back;
89        GetColorFromList(Scr->IconManagerBL, p->name, (XClassHint *) NULL,
90                         &background);
91
92        p->w = XCreateSimpleWindow(dpy, Scr->Root,
93                                   JunkX, JunkY, (unsigned) p->width,
94                                   (unsigned) p->height, 1, Scr->Black,
95                                   background);
96
97        snprintf(str, sizeof(str), "%s Icon Manager", p->name);
98        snprintf(str1, sizeof(str1), "%s Icons", p->name);
99        if (p->icon_name)
100            icon_name = p->icon_name;
101        else
102            icon_name = str1;
103
104        XSetStandardProperties(dpy, p->w, str, icon_name, None, NULL, 0, NULL);
105
106        p->twm_win = AddWindow(p->w, TRUE, p);
107        SetMapStateProp(p->twm_win, WithdrawnState);
108    }
109    for (p = &Scr->iconmgr; p != NULL; p = p->next) {
110        GrabButtons(p->twm_win);
111        GrabKeys(p->twm_win);
112    }
113}
114
115/**
116 * allocate a new icon manager
117 *
118 *  \param name     the name of this icon manager
119 *  \param con_name the name of the associated icon
120 *  \param geom     a geometry string to eventually parse
121 *      \param columns  the number of columns this icon manager has
122 */
123IconMgr *
124AllocateIconManager(char *name, char *icon_name, char *geom, int columns)
125{
126    IconMgr *p;
127
128#ifdef DEBUG_ICONMGR
129    fprintf(stderr, "AllocateIconManager\n");
130    fprintf(stderr, "  name=\"%s\" icon_name=\"%s\", geom=\"%s\", col=%d\n",
131            name, icon_name ? icon_name : "<null>", geom, columns);
132#endif
133
134    if (Scr->NoIconManagers)
135        return NULL;
136
137    p = malloc(sizeof(IconMgr));
138    p->name = name;
139    p->icon_name = icon_name;
140    p->geometry = geom;
141    p->columns = columns;
142    p->first = NULL;
143    p->last = NULL;
144    p->active = NULL;
145    p->scr = Scr;
146    p->count = 0;
147    p->x = 0;
148    p->y = 0;
149    p->width = 150;
150    p->height = 10;
151
152    Scr->iconmgr.lasti->next = p;
153    p->prev = Scr->iconmgr.lasti;
154    Scr->iconmgr.lasti = p;
155    p->next = NULL;
156
157    return (p);
158}
159
160/**
161 * move the pointer around in an icon manager
162 *
163 *  \param dir one of the following:
164 *    - F_FORWICONMGR:  forward in the window list
165 *    - F_BACKICONMGR:  backward in the window list
166 *    - F_UPICONMGR:    up one row
167 *    - F_DOWNICONMG:   down one row
168 *    - F_LEFTICONMGR:  left one column
169 *    - F_RIGHTICONMGR: right one column
170 */
171void
172MoveIconManager(int dir)
173{
174    IconMgr *ip;
175    WList *tmp = NULL;
176    int cur_row, cur_col, new_row, new_col;
177    int row_inc, col_inc;
178    int got_it;
179
180    if (!Active)
181        return;
182
183    cur_row = Active->row;
184    cur_col = Active->col;
185    ip = Active->iconmgr;
186
187    row_inc = 0;
188    col_inc = 0;
189    got_it = FALSE;
190
191    switch (dir) {
192    case F_FORWICONMGR:
193        if ((tmp = Active->next) == NULL)
194            tmp = ip->first;
195        got_it = TRUE;
196        break;
197
198    case F_BACKICONMGR:
199        if ((tmp = Active->prev) == NULL)
200            tmp = ip->last;
201        got_it = TRUE;
202        break;
203
204    case F_UPICONMGR:
205        row_inc = -1;
206        break;
207
208    case F_DOWNICONMGR:
209        row_inc = 1;
210        break;
211
212    case F_LEFTICONMGR:
213        col_inc = -1;
214        break;
215
216    case F_RIGHTICONMGR:
217        col_inc = 1;
218        break;
219    }
220
221    /* If got_it is FALSE ast this point then we got a left, right,
222     * up, or down, command.  We will enter this loop until we find
223     * a window to warp to.
224     */
225    new_row = cur_row;
226    new_col = cur_col;
227
228    while (!got_it) {
229        new_row += row_inc;
230        new_col += col_inc;
231        if (new_row < 0)
232            new_row = ip->cur_rows - 1;
233        if (new_col < 0)
234            new_col = ip->cur_columns - 1;
235        if (new_row >= ip->cur_rows)
236            new_row = 0;
237        if (new_col >= ip->cur_columns)
238            new_col = 0;
239
240        /* Now let's go through the list to see if there is an entry with this
241         * new position
242         */
243        for (tmp = ip->first; tmp != NULL; tmp = tmp->next) {
244            if (tmp->row == new_row && tmp->col == new_col) {
245                got_it = TRUE;
246                break;
247            }
248        }
249    }
250
251    if (!got_it) {
252        twmWarning("unable to find window (%d, %d) in icon manager",
253                   new_row, new_col);
254        return;
255    }
256
257    if (tmp == NULL)
258        return;
259
260    /* raise the frame so the icon manager is visible */
261    if (ip->twm_win->mapped) {
262        XRaiseWindow(dpy, ip->twm_win->frame);
263        XWarpPointer(dpy, None, tmp->icon, 0, 0, 0, 0, 5, 5);
264    }
265    else {
266        if (tmp->twm->title_height) {
267            int tbx = Scr->TBInfo.titlex;
268            int x = tmp->twm->highlightx;
269
270            XWarpPointer(dpy, None, tmp->twm->title_w, 0, 0, 0, 0,
271                         tbx + (x - tbx) / 2, Scr->TitleHeight / 4);
272        }
273        else {
274            XWarpPointer(dpy, None, tmp->twm->w, 0, 0, 0, 0, 5, 5);
275        }
276    }
277}
278
279/**
280 * jump from one icon manager to another, possibly even on another screen
281 *  \param dir one of the following:
282 *    - F_NEXTICONMGR - go to the next icon manager
283 *    - F_PREVICONMGR - go to the previous one
284 */
285
286void
287JumpIconManager(int dir)
288{
289    IconMgr *ip, *tmp_ip = NULL;
290    int got_it = FALSE;
291
292    if (!Active)
293        return;
294
295#define ITER(i) (dir == F_NEXTICONMGR ? (i)->next : (i)->prev)
296#define IPOFSP(sp) (dir == F_NEXTICONMGR ? &(sp->iconmgr) : sp->iconmgr.lasti)
297#define TEST(ip) if ((ip)->count != 0 && (ip)->twm_win->mapped) \
298                 { got_it = TRUE; break; }
299
300    ip = Active->iconmgr;
301    for (tmp_ip = ITER(ip); tmp_ip; tmp_ip = ITER(tmp_ip)) {
302        TEST(tmp_ip);
303    }
304
305    if (!got_it) {
306        int origscreen = ip->scr->screen;
307        int inc = (dir == F_NEXTICONMGR ? 1 : -1);
308        int screen;
309
310        for (screen = origscreen + inc;; screen += inc) {
311            ScreenInfo *sp;
312
313            if (screen >= NumScreens)
314                screen = 0;
315            else if (screen < 0)
316                screen = NumScreens - 1;
317
318            sp = ScreenList[screen];
319            if (sp) {
320                for (tmp_ip = IPOFSP(sp); tmp_ip; tmp_ip = ITER(tmp_ip)) {
321                    TEST(tmp_ip);
322                }
323            }
324            if (got_it || screen == origscreen)
325                break;
326        }
327    }
328
329#undef ITER
330#undef IPOFSP
331#undef TEST
332
333    if (!got_it) {
334        Bell(XkbBI_MinorError, 0, None);
335        return;
336    }
337
338    /* raise the frame so it is visible */
339    XRaiseWindow(dpy, tmp_ip->twm_win->frame);
340    if (tmp_ip->active)
341        XWarpPointer(dpy, None, tmp_ip->active->icon, 0, 0, 0, 0, 5, 5);
342    else
343        XWarpPointer(dpy, None, tmp_ip->w, 0, 0, 0, 0, 5, 5);
344}
345
346/**
347 * add a window to an icon manager
348 *
349 *  \param tmp_win the TwmWindow structure
350 */
351WList *
352AddIconManager(TwmWindow *tmp_win)
353{
354    WList *tmp;
355    int h;
356    unsigned long valuemask;    /* mask for create windows */
357    XSetWindowAttributes attributes;    /* attributes for create windows */
358    IconMgr *ip;
359
360    tmp_win->list = NULL;
361
362    if (tmp_win->iconmgr || tmp_win->transient || Scr->NoIconManagers)
363        return NULL;
364
365    if (LookInList(Scr->IconMgrNoShow, tmp_win->full_name, &tmp_win->class))
366        return NULL;
367    if (Scr->IconManagerDontShow &&
368        !LookInList(Scr->IconMgrShow, tmp_win->full_name, &tmp_win->class))
369        return NULL;
370    if ((ip = (IconMgr *) LookInList(Scr->IconMgrs, tmp_win->full_name,
371                                     &tmp_win->class)) == NULL)
372        ip = &Scr->iconmgr;
373
374    tmp = malloc(sizeof(WList));
375    tmp->iconmgr = ip;
376    tmp->next = NULL;
377    tmp->active = FALSE;
378    tmp->down = FALSE;
379
380    InsertInIconManager(ip, tmp, tmp_win);
381
382    tmp->twm = tmp_win;
383
384    tmp->fore = Scr->IconManagerC.fore;
385    tmp->back = Scr->IconManagerC.back;
386    tmp->highlight = Scr->IconManagerHighlight;
387
388    GetColorFromList(Scr->IconManagerFL, tmp_win->full_name, &tmp_win->class,
389                     &tmp->fore);
390    GetColorFromList(Scr->IconManagerBL, tmp_win->full_name, &tmp_win->class,
391                     &tmp->back);
392    GetColorFromList(Scr->IconManagerHighlightL, tmp_win->full_name,
393                     &tmp_win->class, &tmp->highlight);
394
395    h = Scr->IconManagerFont.height + 10;
396    if (h < (siconify_height + 4))
397        h = siconify_height + 4;
398
399    ip->height = h * ip->count;
400    tmp->me = ip->count;
401    tmp->x = -1;
402    tmp->y = -1;
403
404    valuemask = (CWBackPixel | CWBorderPixel | CWEventMask | CWCursor);
405    attributes.background_pixel = tmp->back;
406    attributes.border_pixel = tmp->back;
407    attributes.event_mask = (KeyPressMask | ButtonPressMask |
408                             ButtonReleaseMask | ExposureMask |
409                             EnterWindowMask | LeaveWindowMask);
410    attributes.cursor = Scr->IconMgrCursor;
411    tmp->w = XCreateWindow(dpy, ip->w, 0, 0, (unsigned int) 1,
412                           (unsigned int) h, (unsigned int) 0,
413                           CopyFromParent, (unsigned int) CopyFromParent,
414                           (Visual *) CopyFromParent, valuemask, &attributes);
415
416    valuemask = (CWBackPixel | CWBorderPixel | CWEventMask | CWCursor);
417    attributes.background_pixel = tmp->back;
418    attributes.border_pixel = Scr->Black;
419    attributes.event_mask = (ButtonReleaseMask | ButtonPressMask |
420                             ExposureMask);
421    attributes.cursor = Scr->ButtonCursor;
422    tmp->icon = XCreateWindow(dpy, tmp->w, 5, (int) (h - siconify_height) / 2,
423                              (unsigned int) siconify_width,
424                              (unsigned int) siconify_height,
425                              (unsigned int) 0, CopyFromParent,
426                              (unsigned int) CopyFromParent,
427                              (Visual *) CopyFromParent,
428                              valuemask, &attributes);
429
430    ip->count += 1;
431    PackIconManager(ip);
432    XMapWindow(dpy, tmp->w);
433
434    XSaveContext(dpy, tmp->w, IconManagerContext, (XPointer) tmp);
435    XSaveContext(dpy, tmp->w, TwmContext, (XPointer) tmp_win);
436    XSaveContext(dpy, tmp->w, ScreenContext, (XPointer) Scr);
437    XSaveContext(dpy, tmp->icon, TwmContext, (XPointer) tmp_win);
438    XSaveContext(dpy, tmp->icon, ScreenContext, (XPointer) Scr);
439    tmp_win->list = tmp;
440
441    if (!ip->twm_win->icon) {
442        XMapWindow(dpy, ip->w);
443        XMapWindow(dpy, ip->twm_win->frame);
444    }
445
446    if (Active == NULL)
447        Active = tmp;
448
449    return (tmp);
450}
451
452/**
453 * put an allocated entry into an icon manager
454 *
455 *  \param ip  the icon manager pointer
456 *  \param tmp the entry to insert
457 */
458static void
459InsertInIconManager(IconMgr *ip, WList *tmp, TwmWindow *tmp_win)
460{
461    WList *tmp1;
462    int added;
463    int (*compar) (const char *, const char *)
464        = (Scr->CaseSensitive ? strcmp : XmuCompareISOLatin1);
465
466    added = FALSE;
467    if (ip->first == NULL) {
468        ip->first = tmp;
469        tmp->prev = NULL;
470        ip->last = tmp;
471        added = TRUE;
472    }
473    else if (Scr->SortIconMgr) {
474        for (tmp1 = ip->first; tmp1 != NULL; tmp1 = tmp1->next) {
475            if ((*compar) (tmp_win->icon_name, tmp1->twm->icon_name) < 0) {
476                tmp->next = tmp1;
477                tmp->prev = tmp1->prev;
478                tmp1->prev = tmp;
479                if (tmp->prev == NULL)
480                    ip->first = tmp;
481                else
482                    tmp->prev->next = tmp;
483                added = TRUE;
484                break;
485            }
486        }
487    }
488
489    if (!added) {
490        ip->last->next = tmp;
491        tmp->prev = ip->last;
492        ip->last = tmp;
493    }
494}
495
496static void
497RemoveFromIconManager(IconMgr *ip, WList *tmp)
498{
499    if (tmp->prev == NULL)
500        ip->first = tmp->next;
501    else
502        tmp->prev->next = tmp->next;
503
504    if (tmp->next == NULL)
505        ip->last = tmp->prev;
506    else
507        tmp->next->prev = tmp->prev;
508}
509
510/**
511 * remove a window from the icon manager
512 *  \param tmp_win the TwmWindow structure
513 */
514void
515RemoveIconManager(TwmWindow *tmp_win)
516{
517    IconMgr *ip;
518    WList *tmp;
519
520    if (tmp_win->list == NULL)
521        return;
522
523    tmp = tmp_win->list;
524    tmp_win->list = NULL;
525    ip = tmp->iconmgr;
526
527    RemoveFromIconManager(ip, tmp);
528
529    XDeleteContext(dpy, tmp->icon, TwmContext);
530    XDeleteContext(dpy, tmp->icon, ScreenContext);
531    XDestroyWindow(dpy, tmp->icon);
532    XDeleteContext(dpy, tmp->w, IconManagerContext);
533    XDeleteContext(dpy, tmp->w, TwmContext);
534    XDeleteContext(dpy, tmp->w, ScreenContext);
535    XDestroyWindow(dpy, tmp->w);
536    ip->count -= 1;
537    free(tmp);
538
539    PackIconManager(ip);
540
541    if (ip->count == 0) {
542        XUnmapWindow(dpy, ip->twm_win->frame);
543    }
544
545}
546
547void
548ActiveIconManager(WList *active)
549{
550    active->active = TRUE;
551    Active = active;
552    Active->iconmgr->active = active;
553    DrawIconManagerBorder(active);
554}
555
556void
557NotActiveIconManager(WList *active)
558{
559    active->active = FALSE;
560    DrawIconManagerBorder(active);
561}
562
563void
564DrawIconManagerBorder(WList *tmp)
565{
566    {
567        XSetForeground(dpy, Scr->NormalGC, tmp->fore);
568        XDrawRectangle(dpy, tmp->w, Scr->NormalGC, 2, 2,
569                       (unsigned) (tmp->width - 5),
570                       (unsigned) (tmp->height - 5));
571
572        if (tmp->active && Scr->Highlight)
573            XSetForeground(dpy, Scr->NormalGC, tmp->highlight);
574        else
575            XSetForeground(dpy, Scr->NormalGC, tmp->back);
576
577        XDrawRectangle(dpy, tmp->w, Scr->NormalGC, 0, 0,
578                       (unsigned) (tmp->width - 1),
579                       (unsigned) (tmp->height - 1));
580        XDrawRectangle(dpy, tmp->w, Scr->NormalGC, 1, 1,
581                       (unsigned) (tmp->width - 3),
582                       (unsigned) (tmp->height - 3));
583    }
584}
585
586/**
587 * sort The Dude
588 *
589 *  \param ip a pointer to the icon manager structure
590 */
591void
592SortIconManager(IconMgr *ip)
593{
594    WList *tmp1, *tmp2;
595    int done;
596    int (*compar) (const char *, const char *)
597        = (Scr->CaseSensitive ? strcmp : XmuCompareISOLatin1);
598
599    if (ip == NULL)
600        ip = Active->iconmgr;
601
602    done = FALSE;
603    do {
604        for (tmp1 = ip->first; tmp1 != NULL; tmp1 = tmp1->next) {
605            if ((tmp2 = tmp1->next) == NULL) {
606                done = TRUE;
607                break;
608            }
609            if ((*compar) (tmp1->twm->icon_name, tmp2->twm->icon_name) > 0) {
610                /* take it out and put it back in */
611                RemoveFromIconManager(ip, tmp2);
612                InsertInIconManager(ip, tmp2, tmp2->twm);
613                break;
614            }
615        }
616    }
617    while (!done);
618    PackIconManager(ip);
619}
620
621/**
622 * pack the icon manager windows following
623 *              an addition or deletion
624 *
625 *  \param ip a pointer to the icon manager structure
626 */
627void
628PackIconManager(IconMgr *ip)
629{
630    int newwidth, i, row, col, maxcol, colinc, rowinc, wheight, wwidth;
631    int savewidth;
632    WList *tmp;
633
634    wheight = Scr->IconManagerFont.height + 10;
635    if (wheight < (siconify_height + 4))
636        wheight = siconify_height + 4;
637
638    wwidth = ip->width / ip->columns;
639
640    rowinc = wheight;
641    colinc = wwidth;
642
643    row = 0;
644    col = ip->columns;
645    maxcol = 0;
646
647    for (i = 0, tmp = ip->first; tmp != NULL; i++, tmp = tmp->next) {
648        int new_x, new_y;
649
650        tmp->me = i;
651        if (++col >= ip->columns) {
652            col = 0;
653            row += 1;
654        }
655        if (col > maxcol)
656            maxcol = col;
657
658        new_x = col * colinc;
659        new_y = (row - 1) * rowinc;
660
661        /* if the position or size has not changed, don't touch it */
662        if (tmp->x != new_x || tmp->y != new_y ||
663            tmp->width != wwidth || tmp->height != wheight) {
664            XMoveResizeWindow(dpy, tmp->w,
665                              new_x, new_y,
666                              (unsigned) wwidth, (unsigned) wheight);
667
668            tmp->row = row - 1;
669            tmp->col = col;
670            tmp->x = new_x;
671            tmp->y = new_y;
672            tmp->width = wwidth;
673            tmp->height = wheight;
674        }
675    }
676    maxcol += 1;
677
678    ip->cur_rows = row;
679    ip->cur_columns = maxcol;
680    ip->height = row * rowinc;
681    if (ip->height == 0)
682        ip->height = rowinc;
683    newwidth = maxcol * colinc;
684    if (newwidth == 0)
685        newwidth = colinc;
686
687    XResizeWindow(dpy, ip->w, (unsigned) newwidth, (unsigned) ip->height);
688
689    savewidth = ip->width;
690    if (ip->twm_win)
691        SetupWindow(ip->twm_win,
692                    ip->twm_win->frame_x, ip->twm_win->frame_y,
693                    newwidth, ip->height + ip->twm_win->title_height, -1);
694    ip->width = savewidth;
695}
696