1/*
2 * Copyright 1989 Massachusetts Institute of Technology
3 * Copyright 1992 Claude Lecommandeur.
4 */
5
6/***********************************************************************
7 *
8 * $XConsortium: iconmgr.c,v 1.48 91/09/10 15:27:07 dave Exp $
9 *
10 * Icon Manager routines
11 *
12 * 09-Mar-89 Tom LaStrange              File Created
13 *
14 * Do the necessary modification to be integrated in ctwm.
15 * Can no longer be used for the standard twm.
16 *
17 * 22-April-92 Claude Lecommandeur.
18 *
19 *
20 ***********************************************************************/
21
22#include "ctwm.h"
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <strings.h>
28
29#include <X11/Xatom.h>
30
31#include "util.h"
32#include "iconmgr.h"
33#include "icons_builtin.h"
34#include "screen.h"
35#include "drawing.h"
36#include "functions_defs.h"
37#include "list.h"
38#include "occupation.h"
39#include "otp.h"
40#include "add_window.h"
41#include "gram.tab.h"
42#include "vscreen.h"
43#include "win_decorations.h"
44#include "win_resize.h"
45#include "win_utils.h"
46#include "xparsegeometry.h"
47
48
49/* Where we start drawing the name in the icon manager */
50static int iconmgr_textx;
51
52static WList *Active = NULL;
53static WList *Current = NULL;
54WList *DownIconManager = NULL;
55
56/***********************************************************************
57 *
58 *  Procedure:
59 *      CreateIconManagers - creat all the icon manager windows
60 *              for this screen.
61 *
62 *  Returned Value:
63 *      none
64 *
65 *  Inputs:
66 *      none
67 *
68 ***********************************************************************
69 */
70
71void CreateIconManagers(void)
72{
73	WorkSpace    *ws;
74
75	if(Scr->NoIconManagers) {
76		return;
77	}
78
79	/*
80	 * Move past the iconified icon to start the text.
81	 * XXX Semi-arbitrary magic add'l padding, to deal with various inner
82	 * positioning of the icon subwindow.  Be smarter (or at least
83	 * clearer) about this...
84	 */
85	iconmgr_textx = im_iconified_icon_width + 11;
86	if(Scr->use3Diconmanagers) {
87		iconmgr_textx += Scr->IconManagerShadowDepth;
88	}
89
90	if(Scr->siconifyPm == None) {
91		Scr->siconifyPm = Create2DIconManagerIcon();
92	}
93
94	// This loop is confusing.  The inner for() loops p over the ->next
95	// elements in the list, which is all the iconmgr's in the workspace.
96	// The outer for() loops q over the ->nextv (<-- extra 'v' on the
97	// end), which is a link to the head of the iconmgr list for the
98	// _next_ workspace.
99	ws = Scr->workSpaceMgr.workSpaceList;
100	for(IconMgr *q = Scr->iconmgr; q != NULL; q = q->nextv) {
101		for(IconMgr *p = q; p != NULL; p = p->next) {
102			int gx, gy;
103			char imname[100];
104			int mask;
105			int gravity;
106			int bw;
107			Pixel background;
108
109			snprintf(imname, sizeof(imname), "%s Icon Manager", p->name);
110
111			if(!p->geometry || !strlen(p->geometry)) {
112				p->geometry = "+0+0";
113			}
114			mask = RLayoutXParseGeometry(Scr->Layout, p->geometry,
115			                             &gx, &gy,
116			                             (unsigned int *) &p->width, (unsigned int *)&p->height);
117
118			bw = LookInList(Scr->NoBorder, imname, NULL) ? 0 :
119			     (Scr->ThreeDBorderWidth ? Scr->ThreeDBorderWidth : Scr->BorderWidth);
120
121			if(mask & XNegative) {
122				gx += Scr->rootw - p->width - 2 * bw;
123				gravity = (mask & YNegative) ? SouthEastGravity : NorthEastGravity;
124			}
125			else {
126				gravity = (mask & YNegative) ? SouthWestGravity : NorthWestGravity;
127			}
128			if(mask & YNegative) {
129				gy += Scr->rooth - p->height - 2 * bw;
130			}
131
132			background = Scr->IconManagerC.back;
133			GetColorFromList(Scr->IconManagerBL, p->name, NULL,
134			                 &background);
135
136			if(p->width  < 1) {
137				p->width  = 1;
138			}
139			if(p->height < 1) {
140				p->height = 1;
141			}
142			p->w = XCreateSimpleWindow(dpy, Scr->Root,
143			                           gx, gy, p->width, p->height, 1,
144			                           Scr->Black, background);
145
146
147			/* Scr->workSpaceMgr.activeWSPC = ws; */
148
149			/* Setup various WM properties on the iconmgr's window */
150			{
151				char *icon_name;
152				XWMHints wmhints;
153				XClassHint clhints;
154
155				if(p->icon_name) {
156					icon_name = strdup(p->icon_name);
157				}
158				else {
159					asprintf(&icon_name, "%s Icons", p->name);
160				}
161
162				wmhints.initial_state = NormalState;
163				wmhints.input         = True;
164				wmhints.flags         = InputHint | StateHint;
165
166				clhints.res_name  = icon_name;
167				clhints.res_class = "TwmIconManager";
168
169				XmbSetWMProperties(dpy, p->w, imname, icon_name, NULL, 0, NULL,
170				                   &wmhints, &clhints);
171				free(icon_name);
172			}
173
174
175			p->twm_win = AddWindow(p->w, AWT_ICON_MANAGER, p, Scr->currentvs);
176
177			// SetupOccupation() called from AddWindow() doesn't setup
178			// occupation for icon managers, nor clear vs if occupation
179			// lacks.  So make it occupy the one we're setting up, or the
180			// 1st if we ran out somehow...
181			if(ws) {
182				p->twm_win->occupation = 1 << ws->number;
183
184				// ConfigureWorkSpaceManager() ran before us, so we can
185				// tell whether we're in the ws to reveal this IM.
186				if(ws->number != Scr->currentvs->wsw->currentwspc->number) {
187					p->twm_win->vs = NULL;
188				}
189			}
190			else {
191				p->twm_win->occupation = 1;
192			}
193
194#ifdef DEBUG_ICONMGR
195			fprintf(stderr,
196			        "CreateIconManagers: IconMgr %p: twm_win=%p win=0x%lx "
197			        "name='%s' x=%d y=%d w=%d h=%d occupation=%x\n",
198			        p, p->twm_win, p->twm_win->w, p->name,
199			        gx, gy,  p->width, p->height, p->twm_win->occupation);
200#endif
201
202			{
203				XSizeHints sizehints;
204
205				sizehints.flags       = PWinGravity;
206				sizehints.win_gravity = gravity;
207				XSetWMSizeHints(dpy, p->w, &sizehints, XA_WM_NORMAL_HINTS);
208			}
209
210			p->twm_win->mapped = false;
211			SetMapStateProp(p->twm_win, WithdrawnState);
212			if(p->twm_win && (p->twm_win->wmhints->initial_state == IconicState)) {
213				p->twm_win->isicon = true;
214			}
215			else if(!Scr->NoIconManagers && Scr->ShowIconManager) {
216				p->twm_win->isicon = false;
217			}
218			else {
219				p->twm_win->isicon = true;
220			}
221		}
222		if(ws != NULL) {
223			ws = ws->next;
224		}
225	}
226
227	if(Scr->workSpaceManagerActive) {
228		Scr->workSpaceMgr.workSpaceList->iconmgr = Scr->iconmgr;
229	}
230
231
232	/*
233	 * Grab buttons/keystrokes for icon managers appropriately.
234	 * Normally, this is done in AddWindow(), but it explicitly skips it
235	 * for icon managers.  It's not at all clear why GrabButtons() would
236	 * do so; I don't think it needs to.  GrabKeys() does do some looping
237	 * over the Scr->iconmgr list at the end though, so it's possible we
238	 * need to delay calling it until now when the list is all filled up.
239	 * This needs further investigation; it may be that the special case
240	 * and this code can be removed.  X-ref comments in add_window.c
241	 * about it.
242	 */
243	for(IconMgr *q = Scr->iconmgr; q != NULL; q = q->nextv) {
244		for(IconMgr *p = q; p != NULL; p = p->next) {
245			GrabButtons(p->twm_win);
246			GrabKeys(p->twm_win);
247		}
248	}
249
250}
251
252/***********************************************************************
253 *
254 *  Procedure:
255 *      AllocateIconManager - allocate a new icon manager
256 *
257 *  Inputs:
258 *      name    - the name of this icon manager
259 *      icon_name - the name of the associated icon
260 *      geom    - a geometry string to eventually parse
261 *      columns - the number of columns this icon manager has
262 *
263 ***********************************************************************
264 */
265
266IconMgr *AllocateIconManager(char *name, char *icon_name, char *geom,
267                             int columns)
268{
269	IconMgr *p;
270
271#ifdef DEBUG_ICONMGR
272	fprintf(stderr, "AllocateIconManager\n");
273	fprintf(stderr, "  name=\"%s\" icon_name=\"%s\", geom=\"%s\", col=%d\n",
274	        name, icon_name, geom, columns);
275#endif
276
277	if(Scr->NoIconManagers) {
278		return NULL;
279	}
280
281	if(columns < 1) {
282		columns = 1;
283	}
284	p = calloc(1, sizeof(IconMgr));
285	p->name      = name;
286	p->icon_name = icon_name;
287	p->geometry  = geom;
288	p->columns   = columns;
289	p->scr       = Scr;
290	p->width     = 150;
291	p->height    = 10;
292
293	if(Scr->iconmgr == NULL) {
294		Scr->iconmgr = p;
295		Scr->iconmgr->lasti = p;
296	}
297	else {
298		Scr->iconmgr->lasti->next = p;
299		p->prev = Scr->iconmgr->lasti;
300		Scr->iconmgr->lasti = p;
301	}
302	return(p);
303}
304
305
306/*
307 * Each workspace has its own [set of] icon manager[s].  The initial main
308 * one was setup via AllocateIconManager() early in startup.  The others
309 * were setup during parsing the config file.  Then this gets called late
310 * in startup, after all the workspaces are setup, to copy them all into
311 * each one.
312 *
313 * Note this is distinct from CreateIconManagers(); that creates and
314 * draws the windows, this creates and connects up the data structures.
315 */
316void AllocateOtherIconManagers(void)
317{
318	IconMgr   *imfirst; // First IM on each workspace
319	WorkSpace *ws;
320
321	/* No defined workspaces?  Nothing to do. */
322	if(! Scr->workSpaceManagerActive) {
323		return;
324	}
325
326	/* The first workspace just gets the ones we already have */
327	ws = Scr->workSpaceMgr.workSpaceList;
328	ws->iconmgr = Scr->iconmgr;
329
330	/* From the second on, we start copying */
331	imfirst = ws->iconmgr;
332	for(ws = ws->next; ws != NULL; ws = ws->next) {
333		IconMgr *ip, *previ, *p = NULL;
334
335		/* Copy in the first iconmgr */
336		ws->iconmgr  = malloc(sizeof(IconMgr));
337		*ws->iconmgr = *imfirst;
338
339		/*
340		 * This first is now the nextv to the first in the previous WS,
341		 * and we don't [yet] have a nextv of our own.
342		 * */
343		imfirst->nextv = ws->iconmgr;
344		ws->iconmgr->nextv = NULL;
345
346		/*
347		 * Start from the second, and copy them each from the prior
348		 * workspace we just went through.
349		 * */
350		previ = ws->iconmgr;
351		for(ip = imfirst->next; ip != NULL; ip = ip->next) {
352			/* Copy the base bits */
353			p  = malloc(sizeof(IconMgr));
354			*p = *ip;
355
356			/* Link up the double-links, and there's no nextv [yet] */
357			previ->next = p;
358			p->prev     = previ;
359			p->next     = NULL;
360			p->nextv    = NULL;
361
362			/* We're now the nextv to that one in the old workspace */
363			ip->nextv  = p;
364
365			/* And back around to the next one to copy into this WS */
366			previ = p;
367		}
368
369		/* Each one has a pointer to the last IM in this WS, so save those */
370		for(ip = ws->iconmgr; ip != NULL; ip = ip->next) {
371			ip->lasti = p;
372		}
373
374		/*
375		 * And back around to the next workspace, which works from those
376		 * we made for this WS.  We go from imfirst rather than
377		 * Scr->iconmgr so the ip->nextv rewrites are correct above; we
378		 * have to fill them in on the next loop.
379		 */
380		imfirst = ws->iconmgr;
381	}
382}
383
384
385/***********************************************************************
386 *
387 *  Procedure:
388 *      MoveIconManager - move the pointer around in an icon manager
389 *
390 *  Inputs:
391 *      dir     - one of the following:
392 *                      F_FORWICONMGR   - forward in the window list
393 *                      F_BACKICONMGR   - backward in the window list
394 *                      F_UPICONMGR     - up one row
395 *                      F_DOWNICONMGR   - down one row
396 *                      F_LEFTICONMGR   - left one column
397 *                      F_RIGHTICONMGR  - right one column
398 *
399 *  Special Considerations:
400 *      none
401 *
402 ***********************************************************************
403 */
404
405void MoveIconManager(int dir)
406{
407	IconMgr *ip;
408	WList *tmp = NULL;
409	int cur_row, cur_col, new_row, new_col;
410	int row_inc, col_inc;
411	bool got_it;
412
413	if(!Current) {
414		return;
415	}
416
417	cur_row = Current->row;
418	cur_col = Current->col;
419	ip = Current->iconmgr;
420
421	row_inc = 0;
422	col_inc = 0;
423	got_it = false;
424
425	switch(dir) {
426		case F_FORWICONMGR:
427			if((tmp = Current->next) == NULL) {
428				tmp = ip->first;
429			}
430			got_it = true;
431			break;
432
433		case F_BACKICONMGR:
434			if((tmp = Current->prev) == NULL) {
435				tmp = ip->last;
436			}
437			got_it = true;
438			break;
439
440		case F_UPICONMGR:
441			row_inc = -1;
442			break;
443
444		case F_DOWNICONMGR:
445			row_inc = 1;
446			break;
447
448		case F_LEFTICONMGR:
449			col_inc = -1;
450			break;
451
452		case F_RIGHTICONMGR:
453			col_inc = 1;
454			break;
455	}
456
457	/* If got_it is false ast this point then we got a left, right,
458	 * up, or down, command.  We will enter this loop until we find
459	 * a window to warp to.
460	 */
461	new_row = cur_row;
462	new_col = cur_col;
463
464	while(!got_it) {
465		new_row += row_inc;
466		new_col += col_inc;
467		if(new_row < 0) {
468			new_row = ip->cur_rows - 1;
469		}
470		if(new_col < 0) {
471			new_col = ip->cur_columns - 1;
472		}
473		if(new_row >= ip->cur_rows) {
474			new_row = 0;
475		}
476		if(new_col >= ip->cur_columns) {
477			new_col = 0;
478		}
479
480		/* Now let's go through the list to see if there is an entry with this
481		 * new position
482		 */
483		for(tmp = ip->first; tmp != NULL; tmp = tmp->next) {
484			if(tmp->row == new_row && tmp->col == new_col) {
485				got_it = true;
486				break;
487			}
488		}
489	}
490
491	if(!got_it) {
492		fprintf(stderr,
493		        "%s:  unable to find window (%d, %d) in icon manager\n",
494		        ProgramName, new_row, new_col);
495		return;
496	}
497
498	if(tmp == NULL) {
499		return;
500	}
501
502	/* raise the frame so the icon manager is visible */
503	if(ip->twm_win->mapped) {
504		OtpRaise(ip->twm_win, WinWin);
505		XWarpPointer(dpy, None, tmp->icon, 0, 0, 0, 0, 5, 5);
506	}
507	else {
508		if(tmp->twm->title_height) {
509			int tbx = Scr->TBInfo.titlex;
510			int x = tmp->twm->highlightxr;
511			XWarpPointer(dpy, None, tmp->twm->title_w, 0, 0, 0, 0,
512			             tbx + (x - tbx) / 2,
513			             Scr->TitleHeight / 4);
514		}
515		else {
516			XWarpPointer(dpy, None, tmp->twm->w, 0, 0, 0, 0, 5, 5);
517		}
518	}
519}
520
521/***********************************************************************
522 *
523 *  Procedure:
524 *      MoveMappedIconManager - move the pointer around in an icon manager
525 *
526 *  Inputs:
527 *      dir     - one of the following:
528 *                      F_FORWMAPICONMGR        - forward in the window list
529 *                      F_BACKMAPICONMGR        - backward in the window list
530 *
531 *  Special Considerations:
532 *      none
533 *
534 ***********************************************************************
535 */
536
537void MoveMappedIconManager(int dir)
538{
539	IconMgr *ip;
540	WList *tmp = NULL;
541	WList *orig = NULL;
542	bool got_it;
543
544	if(!Current) {
545		Current = Active;
546	}
547	if(!Current) {
548		return;
549	}
550
551	ip = Current->iconmgr;
552
553	got_it = false;
554	tmp = Current;
555	orig = Current;
556
557	while(!got_it) {
558		switch(dir) {
559			case F_FORWMAPICONMGR:
560				if((tmp = tmp->next) == NULL) {
561					tmp = ip->first;
562				}
563				break;
564
565			case F_BACKMAPICONMGR:
566				if((tmp = tmp->prev) == NULL) {
567					tmp = ip->last;
568				}
569				break;
570		}
571		if(tmp->twm->mapped) {
572			got_it = true;
573			break;
574		}
575		if(tmp == orig) {
576			break;
577		}
578	}
579
580	if(!got_it) {
581		fprintf(stderr, "%s:  unable to find open window in icon manager\n",
582		        ProgramName);
583		return;
584	}
585
586	if(tmp == NULL) {
587		return;
588	}
589
590	/* raise the frame so the icon manager is visible */
591	if(ip->twm_win->mapped) {
592		OtpRaise(ip->twm_win, WinWin);
593		XWarpPointer(dpy, None, tmp->icon, 0, 0, 0, 0, 5, 5);
594	}
595	else {
596		if(tmp->twm->title_height) {
597			XWarpPointer(dpy, None, tmp->twm->title_w, 0, 0, 0, 0,
598			             tmp->twm->title_width / 2,
599			             Scr->TitleHeight / 4);
600		}
601		else {
602			XWarpPointer(dpy, None, tmp->twm->w, 0, 0, 0, 0, 5, 5);
603		}
604	}
605}
606
607/***********************************************************************
608 *
609 *  Procedure:
610 *      JumpIconManager - jump from one icon manager to another,
611 *              possibly even on another screen
612 *
613 *  Inputs:
614 *      dir     - one of the following:
615 *                      F_NEXTICONMGR   - go to the next icon manager
616 *                      F_PREVICONMGR   - go to the previous one
617 *
618 ***********************************************************************
619 */
620
621void JumpIconManager(int dir)
622{
623	IconMgr *ip, *tmp_ip = NULL;
624	bool got_it = false;
625	ScreenInfo *sp;
626	int screen;
627
628	if(!Current) {
629		return;
630	}
631
632
633#define ITER(i) (dir == F_NEXTICONMGR ? (i)->next : (i)->prev)
634#define IPOFSP(sp) (dir == F_NEXTICONMGR ? sp->iconmgr : sp->iconmgr->lasti)
635#define TEST(ip) if ((ip)->count != 0 && (ip)->twm_win->mapped) \
636                 { got_it = true; break; }
637
638	ip = Current->iconmgr;
639	for(tmp_ip = ITER(ip); tmp_ip; tmp_ip = ITER(tmp_ip)) {
640		TEST(tmp_ip);
641	}
642
643	if(!got_it) {
644		int origscreen = ip->scr->screen;
645		int inc = (dir == F_NEXTICONMGR ? 1 : -1);
646
647		for(screen = origscreen + inc; ; screen += inc) {
648			if(screen >= NumScreens) {
649				screen = 0;
650			}
651			else if(screen < 0) {
652				screen = NumScreens - 1;
653			}
654
655			sp = ScreenList[screen];
656			if(sp) {
657				for(tmp_ip = IPOFSP(sp); tmp_ip; tmp_ip = ITER(tmp_ip)) {
658					TEST(tmp_ip);
659				}
660			}
661			if(got_it || screen == origscreen) {
662				break;
663			}
664		}
665	}
666
667#undef ITER
668#undef IPOFSP
669#undef TEST
670
671	if(!got_it) {
672		XBell(dpy, 0);
673		return;
674	}
675
676	/* raise the frame so it is visible */
677	OtpRaise(tmp_ip->twm_win, WinWin);
678	if(tmp_ip->active) {
679		XWarpPointer(dpy, None, tmp_ip->active->icon, 0, 0, 0, 0, 5, 5);
680	}
681	else {
682		XWarpPointer(dpy, None, tmp_ip->w, 0, 0, 0, 0, 5, 5);
683	}
684}
685
686/***********************************************************************
687 *
688 *  Procedure:
689 *      AddIconManager - add a window to an icon manager
690 *
691 *  Inputs:
692 *      tmp_win - the TwmWindow structure
693 *
694 ***********************************************************************
695 */
696
697WList *AddIconManager(TwmWindow *tmp_win)
698{
699	WList *tmp, *old;
700	IconMgr *ip;
701
702	/* Some window types don't wind up in icon managers ever */
703	if(tmp_win->isiconmgr || tmp_win->istransient || tmp_win->iswspmgr
704	                || tmp_win->w == Scr->workSpaceMgr.occupyWindow->w) {
705		return NULL;
706	}
707
708	/* Icon managers can be shut off wholesale in the config */
709	if(Scr->NoIconManagers) {
710		return NULL;
711	}
712
713	/* Config could declare not to IMify this type of window in two ways */
714	if(LookInList(Scr->IconMgrNoShow, tmp_win->name, &tmp_win->class)) {
715		return NULL;
716	}
717	if(Scr->IconManagerDontShow
718	                && !LookInList(Scr->IconMgrShow, tmp_win->name, &tmp_win->class)) {
719		return NULL;
720	}
721
722	/* Dredge up the appropriate IM */
723	if((ip = (IconMgr *)LookInList(Scr->IconMgrs, tmp_win->name,
724	                               &tmp_win->class)) == NULL) {
725		if(Scr->workSpaceManagerActive) {
726			ip = Scr->workSpaceMgr.workSpaceList->iconmgr;
727		}
728		else {
729			ip = Scr->iconmgr;
730		}
731	}
732
733	/* IM's exist in all workspaces, so loop through WSen */
734	tmp = NULL;
735	old = tmp_win->iconmanagerlist;
736	while(ip != NULL) {
737		int h;
738		unsigned long valuemask;         /* mask for create windows */
739		XSetWindowAttributes attributes; /* attributes for create windows */
740
741		/* Is the window in this workspace? */
742		if((tmp_win->occupation & ip->twm_win->occupation) == 0) {
743			/* Nope, skip onward */
744			ip = ip->nextv;
745			continue;
746		}
747
748		/* Yep, create entry and stick it in */
749		tmp = calloc(1, sizeof(WList));
750		tmp->iconmgr = ip;
751		tmp->twm = tmp_win;
752
753		InsertInIconManager(ip, tmp, tmp_win);
754
755		/* IM color settings, shared worldwide */
756		tmp->cp.fore   = Scr->IconManagerC.fore;
757		tmp->cp.back   = Scr->IconManagerC.back;
758		tmp->highlight = Scr->IconManagerHighlight;
759
760		GetColorFromList(Scr->IconManagerFL, tmp_win->name,
761		                 &tmp_win->class, &tmp->cp.fore);
762		GetColorFromList(Scr->IconManagerBL, tmp_win->name,
763		                 &tmp_win->class, &tmp->cp.back);
764		GetColorFromList(Scr->IconManagerHighlightL, tmp_win->name,
765		                 &tmp_win->class, &tmp->highlight);
766
767		/*
768		 * If we're using 3d icon managers, each line item has its own
769		 * icon; see comment on creation function for details.  With 2d
770		 * icon managers, it's the same for all of them, so it's stored
771		 * screen-wide.
772		 */
773		if(Scr->use3Diconmanagers) {
774			if(!Scr->BeNiceToColormap) {
775				GetShadeColors(&tmp->cp);
776			}
777			tmp->iconifypm = Create3DIconManagerIcon(tmp->cp);
778		}
779
780		/* Refigure the height of the whole IM */
781		h = Scr->IconManagerFont.avg_height
782		    + 2 * (ICON_MGR_OBORDER + ICON_MGR_OBORDER);
783		if(h < (im_iconified_icon_height + 4)) {
784			h = im_iconified_icon_height + 4;
785		}
786
787		ip->height = h * ip->count;
788		tmp->me = ip->count;
789		tmp->x = -1;
790		tmp->y = -1;
791		tmp->height = -1;
792		tmp->width = -1;
793
794
795		/* Make a window for this row in the IM */
796		valuemask = (CWBackPixel | CWBorderPixel | CWEventMask | CWCursor);
797		attributes.background_pixel = tmp->cp.back;
798		attributes.border_pixel = tmp->cp.back;
799		attributes.event_mask = (KeyPressMask | ButtonPressMask |
800		                         ButtonReleaseMask | ExposureMask);
801		if(Scr->IconManagerFocus) {
802			attributes.event_mask |= (EnterWindowMask | LeaveWindowMask);
803		}
804		attributes.cursor = Scr->IconMgrCursor;
805		tmp->w = XCreateWindow(dpy, ip->w, 0, 0, 1,
806		                       h, 0,
807		                       CopyFromParent, CopyFromParent,
808		                       CopyFromParent,
809		                       valuemask, &attributes);
810
811
812		/* Setup the icon for it too */
813		valuemask = (CWBackPixel | CWBorderPixel | CWEventMask | CWCursor);
814		attributes.background_pixel = tmp->cp.back;
815		attributes.border_pixel = Scr->Black;
816		attributes.event_mask = (ButtonReleaseMask | ButtonPressMask
817		                         | ExposureMask);
818		attributes.cursor = Scr->ButtonCursor;
819		/* The precise location will be set it in PackIconManager.  */
820		tmp->icon = XCreateWindow(dpy, tmp->w, 0, 0,
821		                          im_iconified_icon_width,
822		                          im_iconified_icon_height,
823		                          0, CopyFromParent,
824		                          CopyFromParent,
825		                          CopyFromParent,
826		                          valuemask, &attributes);
827
828
829		/* Bump housekeeping for the IM */
830		ip->count += 1;
831		PackIconManager(ip);
832		if(Scr->WindowMask) {
833			XRaiseWindow(dpy, Scr->WindowMask);
834		}
835		XMapWindow(dpy, tmp->w);
836
837		XSaveContext(dpy, tmp->w, TwmContext, (XPointer) tmp_win);
838		XSaveContext(dpy, tmp->w, ScreenContext, (XPointer) Scr);
839		XSaveContext(dpy, tmp->icon, TwmContext, (XPointer) tmp_win);
840		XSaveContext(dpy, tmp->icon, ScreenContext, (XPointer) Scr);
841
842		if(!ip->twm_win->isicon) {
843			if(visible(ip->twm_win)) {
844				SetMapStateProp(ip->twm_win, NormalState);
845				XMapWindow(dpy, ip->w);
846				XMapWindow(dpy, ip->twm_win->frame);
847			}
848			ip->twm_win->mapped = true;
849		}
850
851
852		/*
853		 * Stick this entry on the head of our list of "IM entries we
854		 * created", and loop around to the next WS for this IM.
855		 */
856		tmp->nextv = old;
857		old = tmp;
858		ip = ip->nextv;
859	}
860
861	/* If we didn't create at least one thing, we're done here */
862	if(tmp == NULL) {
863		return NULL;
864	}
865
866	/* Stash where the window is IM-listed */
867	tmp_win->iconmanagerlist = tmp;
868
869	/* ??? */
870	if(! visible(tmp->iconmgr->twm_win)) {
871		old = tmp;
872		tmp = tmp->nextv;
873		while(tmp != NULL) {
874			if(visible(tmp->iconmgr->twm_win)) {
875				break;
876			}
877			old = tmp;
878			tmp = tmp->nextv;
879		}
880		if(tmp != NULL) {
881			old->nextv = tmp->nextv;
882			tmp->nextv = tmp_win->iconmanagerlist;
883			tmp_win->iconmanagerlist = tmp;
884		}
885	}
886
887	/* Hand back the list places we added */
888	return tmp_win->iconmanagerlist;
889}
890
891/***********************************************************************
892 *
893 *  Procedure:
894 *      InsertInIconManager - put an allocated entry into an icon
895 *              manager
896 *
897 *  Inputs:
898 *      ip      - the icon manager pointer
899 *      tmp     - the entry to insert
900 *
901 ***********************************************************************
902 */
903
904void InsertInIconManager(IconMgr *ip, WList *tmp, TwmWindow *tmp_win)
905{
906	WList *tmp1;
907	bool added;
908
909	added = false;
910	if(ip->first == NULL) {
911		ip->first = tmp;
912		tmp->prev = NULL;
913		ip->last = tmp;
914		added = true;
915	}
916	else if(Scr->SortIconMgr) {
917		for(tmp1 = ip->first; tmp1 != NULL; tmp1 = tmp1->next) {
918			int compresult;
919			if(Scr->CaseSensitive) {
920				compresult = strcmp(tmp_win->icon_name, tmp1->twm->icon_name);
921			}
922			else {
923				compresult = strcasecmp(tmp_win->icon_name, tmp1->twm->icon_name);
924			}
925			if(compresult < 0) {
926				tmp->next = tmp1;
927				tmp->prev = tmp1->prev;
928				tmp1->prev = tmp;
929				if(tmp->prev == NULL) {
930					ip->first = tmp;
931				}
932				else {
933					tmp->prev->next = tmp;
934				}
935				added = true;
936				break;
937			}
938		}
939	}
940
941	if(!added) {
942		ip->last->next = tmp;
943		tmp->prev = ip->last;
944		ip->last = tmp;
945	}
946}
947
948void RemoveFromIconManager(IconMgr *ip, WList *tmp)
949{
950	if(tmp->prev == NULL) {
951		ip->first = tmp->next;
952	}
953	else {
954		tmp->prev->next = tmp->next;
955	}
956
957	if(tmp->next == NULL) {
958		ip->last = tmp->prev;
959	}
960	else {
961		tmp->next->prev = tmp->prev;
962	}
963
964	/* pebl: If the list was the current and tmp was the last in the list
965	   reset current list */
966	if(Current == tmp) {
967		Current = ip->first;
968	}
969}
970
971/***********************************************************************
972 *
973 *  Procedure:
974 *      RemoveIconManager - remove a window from the icon manager
975 *
976 *  Inputs:
977 *      tmp_win - the TwmWindow structure
978 *
979 ***********************************************************************
980 */
981
982void RemoveIconManager(TwmWindow *tmp_win)
983{
984	IconMgr *ip;
985	WList *tmp, *tmp1, *save;
986
987	if(tmp_win->iconmanagerlist == NULL) {
988		return;
989	}
990
991	tmp  = tmp_win->iconmanagerlist;
992	tmp1 = NULL;
993
994	while(tmp != NULL) {
995		ip = tmp->iconmgr;
996		if((tmp_win->occupation & ip->twm_win->occupation) != 0) {
997			tmp1 = tmp;
998			tmp  = tmp->nextv;
999			continue;
1000		}
1001		RemoveFromIconManager(ip, tmp);
1002
1003		XDeleteContext(dpy, tmp->icon, TwmContext);
1004		XDeleteContext(dpy, tmp->icon, ScreenContext);
1005		XDestroyWindow(dpy, tmp->icon);
1006		XDeleteContext(dpy, tmp->w, TwmContext);
1007		XDeleteContext(dpy, tmp->w, ScreenContext);
1008		XDestroyWindow(dpy, tmp->w);
1009		ip->count -= 1;
1010
1011		PackIconManager(ip);
1012
1013		if(ip->count == 0) {
1014			XUnmapWindow(dpy, ip->twm_win->frame);
1015			ip->twm_win->mapped = false;
1016		}
1017		if(tmp1 == NULL) {
1018			tmp_win->iconmanagerlist = tmp_win->iconmanagerlist->nextv;
1019		}
1020		else {
1021			tmp1->nextv = tmp->nextv;
1022		}
1023
1024		save = tmp;
1025		tmp = tmp->nextv;
1026		free(save);
1027	}
1028}
1029
1030void CurrentIconManagerEntry(WList *current)
1031{
1032	Current = current;
1033}
1034
1035void ActiveIconManager(WList *active)
1036{
1037	active->active = true;
1038	Active = active;
1039	Active->iconmgr->active = active;
1040	Current = Active;
1041	DrawIconManagerBorder(active, false);
1042}
1043
1044void NotActiveIconManager(WList *active)
1045{
1046	active->active = false;
1047	DrawIconManagerBorder(active, false);
1048}
1049
1050void DrawIconManagerBorder(WList *tmp, bool fill)
1051{
1052	if(Scr->use3Diconmanagers) {
1053		Draw3DBorder(tmp->w, 0, 0, tmp->width, tmp->height,
1054		             Scr->IconManagerShadowDepth, tmp->cp,
1055		             (tmp->active && Scr->Highlight ? on : off),
1056		             fill, false);
1057	}
1058	else {
1059		XSetForeground(dpy, Scr->NormalGC, tmp->cp.fore);
1060		XDrawRectangle(dpy, tmp->w, Scr->NormalGC, 2, 2, tmp->width - 5,
1061		               tmp->height - 5);
1062
1063		XSetForeground(dpy, Scr->NormalGC,
1064		               (tmp->active && Scr->Highlight
1065		                ? tmp->highlight : tmp->cp.back));
1066
1067		XDrawRectangle(dpy, tmp->w, Scr->NormalGC, 0, 0, tmp->width - 1,
1068		               tmp->height - 1);
1069		XDrawRectangle(dpy, tmp->w, Scr->NormalGC, 1, 1, tmp->width - 3,
1070		               tmp->height - 3);
1071	}
1072}
1073
1074/***********************************************************************
1075 *
1076 *  Procedure:
1077 *      SortIconManager - sort the dude
1078 *
1079 *  Inputs:
1080 *      ip      - a pointer to the icon manager struture
1081 *
1082 ***********************************************************************
1083 */
1084
1085void SortIconManager(IconMgr *ip)
1086{
1087	WList *tmp1, *tmp2;
1088	int done;
1089
1090	if(ip == NULL) {
1091		ip = Active->iconmgr;
1092	}
1093
1094	done = false;
1095	do {
1096		for(tmp1 = ip->first; tmp1 != NULL; tmp1 = tmp1->next) {
1097			int compresult;
1098			if((tmp2 = tmp1->next) == NULL) {
1099				done = true;
1100				break;
1101			}
1102			if(Scr->CaseSensitive) {
1103				compresult = strcmp(tmp1->twm->icon_name, tmp2->twm->icon_name);
1104			}
1105			else {
1106				compresult = strcasecmp(tmp1->twm->icon_name, tmp2->twm->icon_name);
1107			}
1108			if(compresult > 0) {
1109				/* take it out and put it back in */
1110				RemoveFromIconManager(ip, tmp2);
1111				InsertInIconManager(ip, tmp2, tmp2->twm);
1112				break;
1113			}
1114		}
1115	}
1116	while(!done);
1117	PackIconManager(ip);
1118}
1119
1120/***********************************************************************
1121 *
1122 *  Procedure:
1123 *      PackIconManager - pack the icon manager windows following
1124 *              an addition or deletion
1125 *
1126 *  Inputs:
1127 *      ip      - a pointer to the icon manager struture
1128 *
1129 ***********************************************************************
1130 */
1131
1132void PackIconManagers(void)
1133{
1134	TwmWindow *twm_win;
1135
1136	for(twm_win = Scr->FirstWindow; twm_win != NULL; twm_win = twm_win->next) {
1137		if(twm_win->iconmgrp) {
1138			PackIconManager(twm_win->iconmgrp);
1139		}
1140	}
1141}
1142
1143void PackIconManager(IconMgr *ip)
1144{
1145	int newwidth, i, row, col, maxcol,  colinc, rowinc, wheight, wwidth;
1146	int new_x, new_y;
1147	int savewidth;
1148	WList *tmp;
1149	int mask;
1150
1151	wheight = Scr->IconManagerFont.avg_height
1152	          + 2 * (ICON_MGR_OBORDER + ICON_MGR_IBORDER);
1153	if(wheight < (im_iconified_icon_height + 4)) {
1154		wheight = im_iconified_icon_height + 4;
1155	}
1156
1157	wwidth = ip->width / ip->columns;
1158
1159	rowinc = wheight;
1160	colinc = wwidth;
1161
1162	row = 0;
1163	col = ip->columns;
1164	maxcol = 0;
1165	for(i = 0, tmp = ip->first; tmp != NULL; i++, tmp = tmp->next) {
1166		tmp->me = i;
1167		if(++col >= ip->columns) {
1168			col = 0;
1169			row += 1;
1170		}
1171		if(col > maxcol) {
1172			maxcol = col;
1173		}
1174
1175		new_x = col * colinc;
1176		new_y = (row - 1) * rowinc;
1177
1178		/* if the position or size has not changed, don't touch it */
1179		if(tmp->x != new_x || tmp->y != new_y ||
1180		                tmp->width != wwidth || tmp->height != wheight) {
1181			XMoveResizeWindow(dpy, tmp->w, new_x, new_y, wwidth, wheight);
1182			if(tmp->height != wheight)
1183				XMoveWindow(dpy, tmp->icon, ICON_MGR_OBORDER + ICON_MGR_IBORDER,
1184				            (wheight - im_iconified_icon_height) / 2);
1185
1186			tmp->row = row - 1;
1187			tmp->col = col;
1188			tmp->x = new_x;
1189			tmp->y = new_y;
1190			tmp->width = wwidth;
1191			tmp->height = wheight;
1192		}
1193	}
1194	maxcol += 1;
1195
1196	ip->cur_rows = row;
1197	ip->cur_columns = maxcol;
1198	ip->height = row * rowinc;
1199	if(ip->height == 0) {
1200		ip->height = rowinc;
1201	}
1202	newwidth = maxcol * colinc;
1203	if(newwidth == 0) {
1204		newwidth = colinc;
1205	}
1206
1207	XResizeWindow(dpy, ip->w, newwidth, ip->height);
1208
1209	mask = RLayoutXParseGeometry(Scr->Layout, ip->geometry, &JunkX, &JunkY,
1210	                             &JunkWidth, &JunkHeight);
1211	if(mask & XNegative) {
1212		ip->twm_win->frame_x += ip->twm_win->frame_width - newwidth -
1213		                        2 * ip->twm_win->frame_bw3D;
1214	}
1215	if(mask & YNegative) {
1216		ip->twm_win->frame_y += ip->twm_win->frame_height - ip->height -
1217		                        2 * ip->twm_win->frame_bw3D - ip->twm_win->title_height;
1218	}
1219	savewidth = ip->width;
1220	if(ip->twm_win)
1221		SetupWindow(ip->twm_win,
1222		            ip->twm_win->frame_x, ip->twm_win->frame_y,
1223		            newwidth + 2 * ip->twm_win->frame_bw3D,
1224		            ip->height + ip->twm_win->title_height + 2 * ip->twm_win->frame_bw3D, -1);
1225	ip->width = savewidth;
1226}
1227
1228void dump_iconmanager(IconMgr *mgr, char *label)
1229{
1230	fprintf(stderr, "IconMgr %s %p name='%s' geom='%s'\n",
1231	        label,
1232	        mgr,
1233	        mgr->name,
1234	        mgr->geometry);
1235	fprintf(stderr, "next = %p, prev = %p, lasti = %p, nextv = %p\n",
1236	        mgr->next,
1237	        mgr->prev,
1238	        mgr->lasti,
1239	        mgr->nextv);
1240}
1241
1242
1243/*
1244 * Draw the window name into the icon manager line
1245 */
1246void
1247DrawIconManagerIconName(TwmWindow *tmp_win)
1248{
1249	WList *iconmanagerlist = tmp_win->iconmanagerlist;
1250	XRectangle ink_rect, logical_rect;
1251
1252	XmbTextExtents(Scr->IconManagerFont.font_set,
1253	               tmp_win->icon_name, strlen(tmp_win->icon_name),
1254	               &ink_rect, &logical_rect);
1255
1256	if(UpdateFont(&Scr->IconManagerFont, logical_rect.height)) {
1257		PackIconManagers();
1258	}
1259
1260	// Write in the title
1261	FB(iconmanagerlist->cp.fore, iconmanagerlist->cp.back);
1262
1263	/* XXX This is a completely absurd way of writing this */
1264	((Scr->use3Diconmanagers && (Scr->Monochrome != COLOR)) ?
1265	 XmbDrawImageString : XmbDrawString)
1266	(dpy,
1267	 iconmanagerlist->w,
1268	 Scr->IconManagerFont.font_set,
1269	 Scr->NormalGC,
1270	 iconmgr_textx,
1271	 (Scr->IconManagerFont.avg_height - logical_rect.height) / 2
1272	 + (- logical_rect.y)
1273	 + ICON_MGR_OBORDER
1274	 + ICON_MGR_IBORDER,
1275	 tmp_win->icon_name,
1276	 strlen(tmp_win->icon_name));
1277
1278	// Draw the border around it.  Our "border" isn't an X border, it's
1279	// just our own drawing inside the X window.  Since XmbDrawString()
1280	// believes it has all the space in the window to fill, it might
1281	// scribble into the space where we're drawing the border, so draw
1282	// the border after the text to cover it up.
1283	DrawIconManagerBorder(iconmanagerlist, false);
1284}
1285
1286
1287/*
1288 * Copy the icon into the icon manager for a window that's iconified.
1289 * This is slightly different for the 3d vs 2d case, since the 3d is just
1290 * copying a pixmap in, while the 2d is drawing a bitmap in with the
1291 * fg/bg colors appropriate to the line.
1292 */
1293void
1294ShowIconifiedIcon(TwmWindow *tmp_win)
1295{
1296	WList *iconmanagerlist = tmp_win->iconmanagerlist;
1297
1298	if(Scr->use3Diconmanagers && iconmanagerlist->iconifypm) {
1299		XCopyArea(dpy, iconmanagerlist->iconifypm,
1300		          iconmanagerlist->icon,
1301		          Scr->NormalGC, 0, 0,
1302		          im_iconified_icon_width, im_iconified_icon_height, 0, 0);
1303	}
1304	else {
1305		FB(iconmanagerlist->cp.fore, iconmanagerlist->cp.back);
1306		XCopyPlane(dpy, Scr->siconifyPm, iconmanagerlist->icon,
1307		           Scr->NormalGC, 0, 0,
1308		           im_iconified_icon_width, im_iconified_icon_height, 0, 0, 1);
1309	}
1310}
1311