icons.c revision c2535118
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 releated routines
29 *
30 * 10-Apr-89 Tom LaStrange        Initial Version.
31 *
32 **********************************************************************/
33
34#include <stdio.h>
35#include "twm.h"
36#include "screen.h"
37#include "icons.h"
38#include "gram.h"
39#include "parse.h"
40#include "util.h"
41
42#define iconWidth(w)	(Scr->IconBorderWidth * 2 + w->icon_w_width)
43#define iconHeight(w)	(Scr->IconBorderWidth * 2 + w->icon_w_height)
44
45static void splitEntry ( IconEntry *ie, int grav1, int grav2, int w, int h );
46static IconEntry * FindIconEntry ( TwmWindow *tmp_win, IconRegion **irp );
47static IconEntry * prevIconEntry ( IconEntry *ie, IconRegion *ir );
48static void mergeEntries ( IconEntry *old, IconEntry *ie );
49
50static void
51splitEntry (IconEntry *ie, int grav1, int grav2, int w, int h)
52{
53    IconEntry	*new;
54
55    switch (grav1) {
56    case D_NORTH:
57    case D_SOUTH:
58	if (w != ie->w)
59	    splitEntry (ie, grav2, grav1, w, ie->h);
60	if (h != ie->h) {
61	    new = malloc (sizeof (IconEntry));
62	    new->twm_win = 0;
63	    new->used = 0;
64	    new->next = ie->next;
65	    ie->next = new;
66	    new->x = ie->x;
67	    new->h = (ie->h - h);
68	    new->w = ie->w;
69	    ie->h = h;
70	    if (grav1 == D_SOUTH) {
71		new->y = ie->y;
72		ie->y = new->y + new->h;
73	    } else
74		new->y = ie->y + ie->h;
75	}
76	break;
77    case D_EAST:
78    case D_WEST:
79	if (h != ie->h)
80	    splitEntry (ie, grav2, grav1, ie->w, h);
81	if (w != ie->w) {
82	    new = malloc (sizeof (IconEntry));
83	    new->twm_win = 0;
84	    new->used = 0;
85	    new->next = ie->next;
86	    ie->next = new;
87	    new->y = ie->y;
88	    new->w = (ie->w - w);
89	    new->h = ie->h;
90	    ie->w = w;
91	    if (grav1 == D_EAST) {
92		new->x = ie->x;
93		ie->x = new->x + new->w;
94	    } else
95		new->x = ie->x + ie->w;
96	}
97	break;
98    }
99}
100
101static inline int
102roundUp (int v, int multiple)
103{
104    return ((v + multiple - 1) / multiple) * multiple;
105}
106
107static void
108PlaceIcon(TwmWindow *tmp_win, int def_x, int def_y, int *final_x, int *final_y)
109{
110    IconRegion	*ir;
111    IconEntry	*ie;
112    int		w = 0, h = 0;
113
114    ie = 0;
115    for (ir = Scr->FirstRegion; ir; ir = ir->next) {
116	w = roundUp (iconWidth (tmp_win), ir->stepx);
117	h = roundUp (iconHeight (tmp_win), ir->stepy);
118	for (ie = ir->entries; ie; ie=ie->next) {
119	    if (ie->used)
120		continue;
121	    if (ie->w >= w && ie->h >= h)
122		break;
123	}
124	if (ie)
125	    break;
126    }
127    if (ie) {
128	splitEntry (ie, ir->grav1, ir->grav2, w, h);
129	ie->used = 1;
130	ie->twm_win = tmp_win;
131	*final_x = ie->x + (ie->w - iconWidth (tmp_win)) / 2;
132	*final_y = ie->y + (ie->h - iconHeight (tmp_win)) / 2;
133    } else {
134	*final_x = def_x;
135	*final_y = def_y;
136    }
137    return;
138}
139
140static IconEntry *
141FindIconEntry (TwmWindow *tmp_win, IconRegion **irp)
142{
143    IconRegion	*ir;
144    IconEntry	*ie;
145
146    for (ir = Scr->FirstRegion; ir; ir = ir->next) {
147	for (ie = ir->entries; ie; ie=ie->next)
148	    if (ie->twm_win == tmp_win) {
149		if (irp)
150		    *irp = ir;
151		return ie;
152	    }
153    }
154    return 0;
155}
156
157void
158IconUp (TwmWindow *tmp_win)
159{
160    int		x, y;
161    int		defx, defy;
162    struct IconRegion *ir;
163
164    /*
165     * If the client specified a particular location, let's use it (this might
166     * want to be an option at some point).  Otherwise, try to fit within the
167     * icon region.
168     */
169    if (tmp_win->wmhints && (tmp_win->wmhints->flags & IconPositionHint))
170      return;
171
172    if (tmp_win->icon_moved) {
173	if (!XGetGeometry (dpy, tmp_win->icon_w, &JunkRoot, &defx, &defy,
174			   &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth))
175	  return;
176
177	x = defx + ((int) JunkWidth) / 2;
178	y = defy + ((int) JunkHeight) / 2;
179
180	for (ir = Scr->FirstRegion; ir; ir = ir->next) {
181	    if (x >= ir->x && x < (ir->x + ir->w) &&
182		y >= ir->y && y < (ir->y + ir->h))
183	      break;
184	}
185	if (!ir) return;		/* outside icon regions, leave alone */
186    }
187
188    defx = -100;
189    defy = -100;
190    PlaceIcon(tmp_win, defx, defy, &x, &y);
191    if (x != defx || y != defy) {
192	XMoveWindow (dpy, tmp_win->icon_w, x, y);
193	tmp_win->icon_moved = FALSE;	/* since we've restored it */
194    }
195}
196
197static IconEntry *
198prevIconEntry (IconEntry *ie, IconRegion *ir)
199{
200    IconEntry	*ip;
201
202    if (ie == ir->entries)
203	return 0;
204    for (ip = ir->entries; ip->next != ie; ip=ip->next)
205	;
206    return ip;
207}
208
209/**
210 * old is being freed; and is adjacent to ie.  Merge
211 * regions together
212 */
213static void
214mergeEntries (IconEntry *old, IconEntry *ie)
215{
216    if (old->y == ie->y) {
217	ie->w = old->w + ie->w;
218	if (old->x < ie->x)
219	    ie->x = old->x;
220    } else {
221	ie->h = old->h + ie->h;
222	if (old->y < ie->y)
223	    ie->y = old->y;
224    }
225}
226
227void
228IconDown (TwmWindow *tmp_win)
229{
230    IconEntry	*ie, *ip, *in;
231    IconRegion	*ir;
232
233    ie = FindIconEntry (tmp_win, &ir);
234    if (ie) {
235	ie->twm_win = 0;
236	ie->used = 0;
237	ip = prevIconEntry (ie, ir);
238	in = ie->next;
239	for (;;) {
240	    if (ip && ip->used == 0 &&
241	       ((ip->x == ie->x && ip->w == ie->w) ||
242	        (ip->y == ie->y && ip->h == ie->h)))
243	    {
244	    	ip->next = ie->next;
245	    	mergeEntries (ie, ip);
246		free (ie);
247		ie = ip;
248	    	ip = prevIconEntry (ip, ir);
249	    } else if (in && in->used == 0 &&
250	       ((in->x == ie->x && in->w == ie->w) ||
251	        (in->y == ie->y && in->h == ie->h)))
252	    {
253	    	ie->next = in->next;
254	    	mergeEntries (in, ie);
255		free (in);
256	    	in = ie->next;
257	    } else
258		break;
259	}
260    }
261}
262
263void
264AddIconRegion(char *geom, int grav1, int grav2, int stepx, int stepy)
265{
266    IconRegion *ir;
267    int mask;
268
269    ir = malloc(sizeof(IconRegion));
270    ir->next = NULL;
271    if (Scr->LastRegion)
272	Scr->LastRegion->next = ir;
273    Scr->LastRegion = ir;
274    if (!Scr->FirstRegion)
275	Scr->FirstRegion = ir;
276
277    ir->entries = NULL;
278    ir->grav1 = grav1;
279    ir->grav2 = grav2;
280    if (stepx <= 0)
281	stepx = 1;
282    if (stepy <= 0)
283	stepy = 1;
284    ir->stepx = stepx;
285    ir->stepy = stepy;
286    ir->x = ir->y = ir->w = ir->h = 0;
287
288    mask = XParseGeometry(geom, &ir->x, &ir->y, (unsigned int *)&ir->w, (unsigned int *)&ir->h);
289
290    if (mask & XNegative)
291	ir->x += Scr->MyDisplayWidth - ir->w;
292
293    if (mask & YNegative)
294	ir->y += Scr->MyDisplayHeight - ir->h;
295    ir->entries = malloc(sizeof(IconEntry));
296    ir->entries->next = 0;
297    ir->entries->x = ir->x;
298    ir->entries->y = ir->y;
299    ir->entries->w = ir->w;
300    ir->entries->h = ir->h;
301    ir->entries->twm_win = 0;
302    ir->entries->used = 0;
303}
304
305#ifdef comment
306void
307FreeIconEntries (IconRegion *ir)
308{
309    IconEntry	*ie, *tmp;
310
311    for (ie = ir->entries; ie; ie=tmp)
312    {
313	tmp = ie->next;
314	free (ie);
315    }
316}
317
318void
319FreeIconRegions(void)
320{
321    IconRegion *ir, *tmp;
322
323    for (ir = Scr->FirstRegion; ir != NULL;)
324    {
325	tmp = ir;
326	FreeIconEntries (ir);
327	ir = ir->next;
328	free(tmp);
329    }
330    Scr->FirstRegion = NULL;
331    Scr->LastRegion = NULL;
332}
333#endif
334
335void
336CreateIconWindow(TwmWindow *tmp_win, int def_x, int def_y)
337{
338    unsigned long event_mask;
339    unsigned long valuemask;		/* mask for create windows */
340    XSetWindowAttributes attributes;	/* attributes for create windows */
341    Pixmap pm = None;			/* tmp pixmap variable */
342    int final_x, final_y;
343    int x;
344
345
346    FB(tmp_win->iconc.fore, tmp_win->iconc.back);
347
348    tmp_win->forced = FALSE;
349    tmp_win->icon_not_ours = FALSE;
350
351    /* now go through the steps to get an icon window,  if ForceIcon is
352     * set, then no matter what else is defined, the bitmap from the
353     * .twmrc file is used
354     */
355    if (Scr->ForceIcon)
356    {
357	char *icon_name;
358	Pixmap bm;
359
360	icon_name = LookInNameList(Scr->IconNames, tmp_win->full_name);
361        if (icon_name == NULL)
362	    icon_name = LookInList(Scr->IconNames, tmp_win->full_name,
363				   &tmp_win->class);
364
365	bm = None;
366	if (icon_name != NULL)
367	{
368	    if ((bm = (Pixmap)LookInNameList(Scr->Icons, icon_name)) == None)
369	    {
370		if ((bm = GetBitmap (icon_name)) != None)
371		    AddToList(&Scr->Icons, icon_name, (char *)bm);
372	    }
373	}
374
375	if (bm != None)
376	{
377	    XGetGeometry(dpy, bm, &JunkRoot, &JunkX, &JunkY,
378		(unsigned int *) &tmp_win->icon_width, (unsigned int *)&tmp_win->icon_height,
379		&JunkBW, &JunkDepth);
380
381	    pm = XCreatePixmap(dpy, Scr->Root, tmp_win->icon_width,
382		tmp_win->icon_height, Scr->d_depth);
383
384	    /* the copy plane works on color ! */
385	    XCopyPlane(dpy, bm, pm, Scr->NormalGC,
386		0,0, tmp_win->icon_width, tmp_win->icon_height, 0, 0, 1 );
387
388	    tmp_win->forced = TRUE;
389	}
390    }
391
392    /* if the pixmap is still NULL, we didn't get one from the above code,
393     * that could mean that ForceIcon was not set, or that the window
394     * was not in the Icons list, now check the WM hints for an icon
395     */
396    if (pm == None && tmp_win->wmhints &&
397	tmp_win->wmhints->flags & IconPixmapHint)
398    {
399
400	XGetGeometry(dpy,   tmp_win->wmhints->icon_pixmap,
401             &JunkRoot, &JunkX, &JunkY,
402	     (unsigned int *)&tmp_win->icon_width, (unsigned int *)&tmp_win->icon_height, &JunkBW, &JunkDepth);
403
404	pm = XCreatePixmap(dpy, Scr->Root,
405			   tmp_win->icon_width, tmp_win->icon_height,
406			   Scr->d_depth);
407
408	XCopyPlane(dpy, tmp_win->wmhints->icon_pixmap, pm, Scr->NormalGC,
409	    0,0, tmp_win->icon_width, tmp_win->icon_height, 0, 0, 1 );
410    }
411
412    /* if we still haven't got an icon, let's look in the Icon list
413     * if ForceIcon is not set
414     */
415    if (pm == None && !Scr->ForceIcon)
416    {
417	char *icon_name;
418	Pixmap bm;
419
420	icon_name = LookInNameList(Scr->IconNames, tmp_win->full_name);
421        if (icon_name == NULL)
422	    icon_name = LookInList(Scr->IconNames, tmp_win->full_name,
423				   &tmp_win->class);
424
425	bm = None;
426	if (icon_name != NULL)
427	{
428	    if ((bm = (Pixmap)LookInNameList(Scr->Icons, icon_name)) == None)
429	    {
430		if ((bm = GetBitmap (icon_name)) != None)
431		    AddToList(&Scr->Icons, icon_name, (char *)bm);
432	    }
433	}
434
435	if (bm != None)
436	{
437	    XGetGeometry(dpy, bm, &JunkRoot, &JunkX, &JunkY,
438		(unsigned int *)&tmp_win->icon_width, (unsigned int *)&tmp_win->icon_height,
439		&JunkBW, &JunkDepth);
440
441	    pm = XCreatePixmap(dpy, Scr->Root, tmp_win->icon_width,
442		tmp_win->icon_height, Scr->d_depth);
443
444	    /* the copy plane works on color ! */
445	    XCopyPlane(dpy, bm, pm, Scr->NormalGC,
446		0,0, tmp_win->icon_width, tmp_win->icon_height, 0, 0, 1 );
447	}
448    }
449
450    /* if we still don't have an icon, assign the UnknownIcon */
451
452    if (pm == None && Scr->UnknownPm != None)
453    {
454	tmp_win->icon_width = Scr->UnknownWidth;
455	tmp_win->icon_height = Scr->UnknownHeight;
456
457	pm = XCreatePixmap(dpy, Scr->Root, tmp_win->icon_width,
458	    tmp_win->icon_height, Scr->d_depth);
459
460	/* the copy plane works on color ! */
461	XCopyPlane(dpy, Scr->UnknownPm, pm, Scr->NormalGC,
462	    0,0, tmp_win->icon_width, tmp_win->icon_height, 0, 0, 1 );
463    }
464
465    if (pm == None)
466    {
467	tmp_win->icon_height = 0;
468	tmp_win->icon_width = 0;
469	valuemask = 0;
470    }
471    else
472    {
473	valuemask = CWBackPixmap;
474	attributes.background_pixmap = pm;
475    }
476
477    tmp_win->icon_w_width = MyFont_TextWidth(&Scr->IconFont,
478	tmp_win->icon_name, strlen(tmp_win->icon_name));
479
480    tmp_win->icon_w_width += 6;
481    if (tmp_win->icon_w_width < tmp_win->icon_width)
482    {
483	tmp_win->icon_x = (tmp_win->icon_width - tmp_win->icon_w_width)/2;
484	tmp_win->icon_x += 3;
485	tmp_win->icon_w_width = tmp_win->icon_width;
486    }
487    else
488    {
489	tmp_win->icon_x = 3;
490    }
491    tmp_win->icon_y = tmp_win->icon_height + Scr->IconFont.height;
492    tmp_win->icon_w_height = tmp_win->icon_height + Scr->IconFont.height + 4;
493
494    event_mask = 0;
495    if (tmp_win->wmhints && tmp_win->wmhints->flags & IconWindowHint)
496    {
497	tmp_win->icon_w = tmp_win->wmhints->icon_window;
498	if (tmp_win->forced ||
499	    XGetGeometry(dpy, tmp_win->icon_w, &JunkRoot, &JunkX, &JunkY,
500		     (unsigned int *)&tmp_win->icon_w_width, (unsigned int *)&tmp_win->icon_w_height,
501		     &JunkBW, &JunkDepth) == 0)
502	{
503	    tmp_win->icon_w = None;
504	    tmp_win->wmhints->flags &= ~IconWindowHint;
505	}
506	else
507	{
508	    tmp_win->icon_not_ours = TRUE;
509	    event_mask = EnterWindowMask | LeaveWindowMask;
510	}
511    }
512    else
513    {
514	tmp_win->icon_w = None;
515    }
516
517    if (tmp_win->icon_w == None)
518    {
519	tmp_win->icon_w = XCreateSimpleWindow(dpy, Scr->Root,
520	    0,0,
521	    tmp_win->icon_w_width, tmp_win->icon_w_height,
522	    Scr->IconBorderWidth, tmp_win->icon_border, tmp_win->iconc.back);
523	event_mask = ExposureMask;
524    }
525
526    XSelectInput (dpy, tmp_win->icon_w,
527		  KeyPressMask | ButtonPressMask | ButtonReleaseMask |
528		  event_mask);
529
530    tmp_win->icon_bm_w = None;
531    if (pm != None &&
532	(! (tmp_win->wmhints && tmp_win->wmhints->flags & IconWindowHint)))
533    {
534	int y;
535
536	y = 0;
537	if (tmp_win->icon_w_width == tmp_win->icon_width)
538	    x = 0;
539	else
540	    x = (tmp_win->icon_w_width - tmp_win->icon_width)/2;
541
542	tmp_win->icon_bm_w = XCreateWindow (dpy, tmp_win->icon_w, x, y,
543					    (unsigned int)tmp_win->icon_width,
544					    (unsigned int)tmp_win->icon_height,
545					    (unsigned int) 0, Scr->d_depth,
546					    (unsigned int) CopyFromParent,
547					    Scr->d_visual, valuemask,
548					    &attributes);
549    }
550
551    /* I need to figure out where to put the icon window now, because
552     * getting here means that I am going to make the icon visible
553     */
554    if (tmp_win->wmhints &&
555	tmp_win->wmhints->flags & IconPositionHint)
556    {
557	final_x = tmp_win->wmhints->icon_x;
558	final_y = tmp_win->wmhints->icon_y;
559    }
560    else
561    {
562	PlaceIcon(tmp_win, def_x, def_y, &final_x, &final_y);
563    }
564
565    if (final_x > Scr->MyDisplayWidth)
566	final_x = Scr->MyDisplayWidth - tmp_win->icon_w_width -
567	    (2 * Scr->IconBorderWidth);
568
569    if (final_y > Scr->MyDisplayHeight)
570	final_y = Scr->MyDisplayHeight - tmp_win->icon_height -
571	    Scr->IconFont.height - 4 - (2 * Scr->IconBorderWidth);
572
573    XMoveWindow(dpy, tmp_win->icon_w, final_x, final_y);
574    tmp_win->iconified = TRUE;
575
576    XMapSubwindows(dpy, tmp_win->icon_w);
577    XSaveContext(dpy, tmp_win->icon_w, TwmContext, (caddr_t)tmp_win);
578    XSaveContext(dpy, tmp_win->icon_w, ScreenContext, (caddr_t)Scr);
579    XDefineCursor(dpy, tmp_win->icon_w, Scr->IconCursor);
580    if (pm) XFreePixmap (dpy, pm);
581    return;
582}
583