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