1 /* appicon.c- icon for applications (not mini-window)
2  *
3  *  Window Maker window manager
4  *
5  *  Copyright (c) 1997-2003 Alfredo K. Kojima
6  *  Copyright (c) 1998-2003 Dan Pascu
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License along
19  *  with this program; if not, write to the Free Software Foundation, Inc.,
20  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #include "wconfig.h"
24 
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #include <stdlib.h>
28 #include <libgen.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
32 #include <errno.h>
33 
34 #include "WindowMaker.h"
35 #include "window.h"
36 #include "icon.h"
37 #include "application.h"
38 #include "appicon.h"
39 #include "actions.h"
40 #include "stacking.h"
41 #include "dock.h"
42 #include "main.h"
43 #include "defaults.h"
44 #include "workspace.h"
45 #include "superfluous.h"
46 #include "menu.h"
47 #include "framewin.h"
48 #include "dialog.h"
49 #include "xinerama.h"
50 #include "client.h"
51 #include "placement.h"
52 #include "misc.h"
53 #include "event.h"
54 #ifdef USE_DOCK_XDND
55 #include "xdnd.h"
56 #endif
57 
58 /*
59  * icon_file for the dock is got from the preferences file by
60  * using the classname/instancename
61  */
62 
63 #define MOD_MASK       wPreferences.modifier_mask
64 #define ICON_SIZE      wPreferences.icon_size
65 
66 static void iconDblClick(WObjDescriptor * desc, XEvent * event);
67 static void iconExpose(WObjDescriptor * desc, XEvent * event);
68 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const char *wm_class);
69 static WAppIcon *wAppIconCreate(WWindow * leader_win);
70 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon);
71 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon);
72 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window);
73 
74 /* This function is used if the application is a .app. It checks if it has an icon in it
75  * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
76  */
wApplicationExtractDirPackIcon(const char * path,const char * wm_instance,const char * wm_class)77 void wApplicationExtractDirPackIcon(const char *path, const char *wm_instance, const char *wm_class)
78 {
79 	char *iconPath = NULL;
80 	char *tmp = NULL;
81 
82 	if (strstr(path, ".app")) {
83 		tmp = wmalloc(strlen(path) + 16);
84 
85 		if (wPreferences.supports_tiff) {
86 			strcpy(tmp, path);
87 			strcat(tmp, ".tiff");
88 			if (access(tmp, R_OK) == 0)
89 				iconPath = tmp;
90 		}
91 
92 		if (!iconPath) {
93 			strcpy(tmp, path);
94 			strcat(tmp, ".xpm");
95 			if (access(tmp, R_OK) == 0)
96 				iconPath = tmp;
97 		}
98 
99 		if (!iconPath)
100 			wfree(tmp);
101 
102 		if (iconPath) {
103 			wApplicationSaveIconPathFor(iconPath, wm_instance, wm_class);
104 			wfree(iconPath);
105 		}
106 	}
107 }
108 
wAppIconCreateForDock(WScreen * scr,const char * command,const char * wm_instance,const char * wm_class,int tile)109 WAppIcon *wAppIconCreateForDock(WScreen *scr, const char *command, const char *wm_instance, const char *wm_class, int tile)
110 {
111 	WAppIcon *aicon;
112 
113 	aicon = wmalloc(sizeof(WAppIcon));
114 	wretain(aicon);
115 	aicon->yindex = -1;
116 	aicon->xindex = -1;
117 
118 	add_to_appicon_list(scr, aicon);
119 
120 	if (command)
121 		aicon->command = wstrdup(command);
122 
123 	if (wm_class)
124 		aicon->wm_class = wstrdup(wm_class);
125 
126 	if (wm_instance)
127 		aicon->wm_instance = wstrdup(wm_instance);
128 
129 	if (wPreferences.flags.clip_merged_in_dock && wm_class != NULL && strcmp(wm_class, "WMDock") == 0)
130 		tile = TILE_CLIP;
131 	aicon->icon = icon_create_for_dock(scr, command, wm_instance, wm_class, tile);
132 
133 #ifdef USE_DOCK_XDND
134 	wXDNDMakeAwareness(aicon->icon->core->window);
135 #endif
136 
137 	/* will be overriden by dock */
138 	aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
139 	aicon->icon->core->descriptor.handle_expose = iconExpose;
140 	aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
141 	aicon->icon->core->descriptor.parent = aicon;
142 	AddToStackList(aicon->icon->core);
143 
144 	return aicon;
145 }
146 
create_appicon_for_application(WApplication * wapp,WWindow * wwin)147 void create_appicon_for_application(WApplication *wapp, WWindow *wwin)
148 {
149 	/* Try to create an icon from the dock or clip */
150 	create_appicon_from_dock(wwin, wapp, wapp->main_window);
151 
152 	/* If app_icon was not found, create it */
153 	if (!wapp->app_icon) {
154 		/* Create the icon */
155 		wapp->app_icon = wAppIconCreate(wapp->main_window_desc);
156 		wIconUpdate(wapp->app_icon->icon);
157 
158 		/* Now, paint the icon */
159 		if (!WFLAGP(wapp->main_window_desc, no_appicon))
160 			paint_app_icon(wapp);
161 	}
162 
163 	/* At this point the application is fully set up and all icon and
164 	 * window data are known - so try to save the icon file for docked
165 	 * applications to ensure that the file exists when window maker
166 	 * starts up next time. */
167 	if (wapp->app_icon->docked && !WFLAGP(wapp->main_window_desc, no_appicon))
168 		save_appicon(wapp->app_icon);
169 }
170 
unpaint_app_icon(WApplication * wapp)171 void unpaint_app_icon(WApplication *wapp)
172 {
173 	WAppIcon *aicon;
174 	WScreen *scr;
175 	WDock *clip;
176 
177 	if (!wapp || !wapp->app_icon)
178 		return;
179 
180 	aicon = wapp->app_icon;
181 
182 	/* If the icon is docked, don't continue */
183 	if (aicon->docked)
184 		return;
185 
186 	scr = wapp->main_window_desc->screen_ptr;
187 	clip = scr->workspaces[scr->current_workspace]->clip;
188 
189 	if (!clip || !aicon->attracted || !clip->collapsed)
190 		XUnmapWindow(dpy, aicon->icon->core->window);
191 
192 	/* We want to avoid having it on the list  because otherwise
193 	 * there will be a hole when the icons are arranged with
194 	 * wArrangeIcons() */
195 	remove_from_appicon_list(scr, aicon);
196 
197 	if (wPreferences.auto_arrange_icons && !aicon->attracted)
198 		wArrangeIcons(scr, True);
199 }
200 
paint_app_icon(WApplication * wapp)201 void paint_app_icon(WApplication *wapp)
202 {
203 	WIcon *icon;
204 	WScreen *scr;
205 	WDock *attracting_dock;
206 	int x = 0, y = 0;
207 	Bool update_icon = False;
208 
209 	if (!wapp || !wapp->app_icon || !wapp->main_window_desc)
210 		return;
211 
212 	icon = wapp->app_icon->icon;
213 	scr = wapp->main_window_desc->screen_ptr;
214 	wapp->app_icon->main_window = wapp->main_window;
215 
216 	/* If the icon is docked, don't continue */
217 	if (wapp->app_icon->docked)
218 		return;
219 
220 	attracting_dock = scr->attracting_drawer != NULL ?
221 		scr->attracting_drawer :
222 		scr->workspaces[scr->current_workspace]->clip;
223 	if (attracting_dock && attracting_dock->attract_icons &&
224 		wDockFindFreeSlot(attracting_dock, &x, &y)) {
225 		wapp->app_icon->attracted = 1;
226 		if (!icon->shadowed) {
227 			icon->shadowed = 1;
228 			update_icon = True;
229 		}
230 		wDockAttachIcon(attracting_dock, wapp->app_icon, x, y, update_icon);
231 	} else {
232 		/* We must know if the icon is painted in the screen,
233 		 * because if painted, then PlaceIcon will return the next
234 		 * space on the screen, and the icon will move */
235 		if (wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL) {
236 			PlaceIcon(scr, &x, &y, wGetHeadForWindow(wapp->main_window_desc));
237 			wAppIconMove(wapp->app_icon, x, y);
238 			wLowerFrame(icon->core);
239 		}
240 	}
241 
242 	/* If we want appicon (no_appicon is not set) and the icon is not
243 	 * in the appicon_list, we must add it. Else, we want to avoid
244 	 * having it on the list */
245 	if (!WFLAGP(wapp->main_window_desc, no_appicon) &&
246 	    wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL)
247 		add_to_appicon_list(scr, wapp->app_icon);
248 
249 	if (!attracting_dock || !wapp->app_icon->attracted || !attracting_dock->collapsed)
250 		XMapWindow(dpy, icon->core->window);
251 
252 	if (wPreferences.auto_arrange_icons && !wapp->app_icon->attracted)
253 		wArrangeIcons(scr, True);
254 }
255 
removeAppIconFor(WApplication * wapp)256 void removeAppIconFor(WApplication *wapp)
257 {
258 	if (!wapp->app_icon)
259 		return;
260 
261 	if (wPreferences.highlight_active_app)
262 		wIconSetHighlited(wapp->app_icon->icon, False);
263 	if (wapp->app_icon->docked && !wapp->app_icon->attracted) {
264 		wapp->app_icon->running = 0;
265 		/* since we keep it, we don't care if it was attracted or not */
266 		wapp->app_icon->attracted = 0;
267 		wapp->app_icon->icon->shadowed = 0;
268 		wapp->app_icon->main_window = None;
269 		wapp->app_icon->pid = 0;
270 		wapp->app_icon->icon->owner = NULL;
271 		wapp->app_icon->icon->icon_win = None;
272 
273 		/* Set the icon image */
274 		set_icon_image_from_database(wapp->app_icon->icon, wapp->app_icon->wm_instance,
275 					     wapp->app_icon->wm_class, wapp->app_icon->command);
276 
277 		/* Update the icon, because wapp->app_icon->icon could be NULL */
278 		wIconUpdate(wapp->app_icon->icon);
279 
280 		/* Paint it */
281 		wAppIconPaint(wapp->app_icon);
282 	} else if (wapp->app_icon->docked) {
283 		wapp->app_icon->running = 0;
284 		if (wapp->app_icon->dock->type == WM_DRAWER) {
285 			wDrawerFillTheGap(wapp->app_icon->dock, wapp->app_icon, True);
286 		}
287 		wDockDetach(wapp->app_icon->dock, wapp->app_icon);
288 	} else {
289 		wAppIconDestroy(wapp->app_icon);
290 	}
291 
292 	wapp->app_icon = NULL;
293 
294 	if (wPreferences.auto_arrange_icons)
295 		wArrangeIcons(wapp->main_window_desc->screen_ptr, True);
296 }
297 
wAppIconCreate(WWindow * leader_win)298 static WAppIcon *wAppIconCreate(WWindow *leader_win)
299 {
300 	WAppIcon *aicon;
301 
302 	aicon = wmalloc(sizeof(WAppIcon));
303 	wretain(aicon);
304 	aicon->yindex = -1;
305 	aicon->xindex = -1;
306 	aicon->prev = NULL;
307 	aicon->next = NULL;
308 
309 	if (leader_win->wm_class)
310 		aicon->wm_class = wstrdup(leader_win->wm_class);
311 
312 	if (leader_win->wm_instance)
313 		aicon->wm_instance = wstrdup(leader_win->wm_instance);
314 
315 	aicon->icon = icon_create_for_wwindow(leader_win);
316 #ifdef USE_DOCK_XDND
317 	wXDNDMakeAwareness(aicon->icon->core->window);
318 #endif
319 
320 	/* will be overriden if docked */
321 	aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
322 	aicon->icon->core->descriptor.handle_expose = iconExpose;
323 	aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
324 	aicon->icon->core->descriptor.parent = aicon;
325 	AddToStackList(aicon->icon->core);
326 	aicon->icon->show_title = 0;
327 
328 	return aicon;
329 }
330 
wAppIconDestroy(WAppIcon * aicon)331 void wAppIconDestroy(WAppIcon * aicon)
332 {
333 	WScreen *scr = aicon->icon->core->screen_ptr;
334 
335 	RemoveFromStackList(aicon->icon->core);
336 	wIconDestroy(aicon->icon);
337 	if (aicon->command)
338 		wfree(aicon->command);
339 #ifdef USE_DOCK_XDND
340 	if (aicon->dnd_command)
341 		wfree(aicon->dnd_command);
342 #endif
343 	if (aicon->wm_instance)
344 		wfree(aicon->wm_instance);
345 
346 	if (aicon->wm_class)
347 		wfree(aicon->wm_class);
348 
349 	remove_from_appicon_list(scr, aicon);
350 
351 	aicon->destroyed = 1;
352 	wrelease(aicon);
353 }
354 
drawCorner(WIcon * icon)355 static void drawCorner(WIcon * icon)
356 {
357 	WScreen *scr = icon->core->screen_ptr;
358 	XPoint points[3];
359 
360 	points[0].x = 1;
361 	points[0].y = 1;
362 	points[1].x = 12;
363 	points[1].y = 1;
364 	points[2].x = 1;
365 	points[2].y = 12;
366 	XFillPolygon(dpy, icon->core->window, scr->icon_title_texture->normal_gc,
367 		     points, 3, Convex, CoordModeOrigin);
368 	XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 0, 12);
369 	XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 12, 0);
370 }
371 
wAppIconMove(WAppIcon * aicon,int x,int y)372 void wAppIconMove(WAppIcon * aicon, int x, int y)
373 {
374 	XMoveWindow(dpy, aicon->icon->core->window, x, y);
375 	aicon->x_pos = x;
376 	aicon->y_pos = y;
377 }
378 
379 #ifdef WS_INDICATOR
updateDockNumbers(WScreen * scr)380 static void updateDockNumbers(WScreen *scr)
381 {
382 	int length;
383 	char ws_numbers[20];
384 	WAppIcon *dicon = scr->dock->icon_array[0];
385 
386 	snprintf(ws_numbers, sizeof(ws_numbers), "%i [ %i ]", scr->current_workspace + 1, ((scr->current_workspace / 10) + 1));
387 	length = strlen(ws_numbers);
388 
389 	XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, WMFontHeight(scr->icon_title_font) + 1, False);
390 
391 	WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->black,
392 		     scr->icon_title_font, 4, 3, ws_numbers, length);
393 
394 	WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->white,
395 		     scr->icon_title_font, 3, 2, ws_numbers, length);
396 }
397 #endif				/* WS_INDICATOR */
398 
wAppIconPaint(WAppIcon * aicon)399 void wAppIconPaint(WAppIcon *aicon)
400 {
401 	WApplication *wapp;
402 	WScreen *scr = aicon->icon->core->screen_ptr;
403 
404 	if (aicon->icon->owner)
405 		wapp = wApplicationOf(aicon->icon->owner->main_window);
406 	else
407 		wapp = NULL;
408 
409 	wIconPaint(aicon->icon);
410 
411 # ifdef WS_INDICATOR
412 	if (aicon->docked && scr->dock && scr->dock == aicon->dock && aicon->yindex == 0)
413 		updateDockNumbers(scr);
414 # endif
415 	if (aicon->docked && !aicon->running && aicon->command != NULL) {
416 		XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
417 		XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
418 		XCopyArea(dpy, scr->dock_dots->image, aicon->icon->core->window,
419 			  scr->copy_gc, 0, 0, scr->dock_dots->width, scr->dock_dots->height, 0, 0);
420 	}
421 #ifdef HIDDENDOT
422 	if (wapp && wapp->flags.hidden) {
423 		XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
424 		XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
425 		XCopyArea(dpy, scr->dock_dots->image,
426 			  aicon->icon->core->window, scr->copy_gc, 0, 0, 7, scr->dock_dots->height, 0, 0);
427 	}
428 #endif				/* HIDDENDOT */
429 
430 	if (aicon->omnipresent)
431 		drawCorner(aicon->icon);
432 
433 	XSetClipMask(dpy, scr->copy_gc, None);
434 	if (aicon->launching)
435 		XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
436 			       0, 0, wPreferences.icon_size, wPreferences.icon_size);
437 }
438 
439 /* Save the application icon, if it's a dockapp then use it with dock = True */
save_appicon(WAppIcon * aicon)440 void save_appicon(WAppIcon *aicon)
441 {
442 	char *path;
443 
444 	if (!aicon)
445 		return;
446 
447 	if (!aicon->docked || aicon->attracted)
448 		return;
449 
450 	path = wIconStore(aicon->icon);
451 	if (!path)
452 		return;
453 
454 	wApplicationSaveIconPathFor(path, aicon->wm_instance, aicon->wm_class);
455 	wfree(path);
456 }
457 
458 #define canBeDocked(wwin)  ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
459 
460 /* main_window may not have the full command line; try to find one which does */
relaunchApplication(WApplication * wapp)461 static void relaunchApplication(WApplication *wapp)
462 {
463 	WScreen *scr;
464 	WWindow *wlist, *next;
465 
466 	scr = wapp->main_window_desc->screen_ptr;
467 	wlist = scr->focused_window;
468 	if (! wlist)
469 		return;
470 
471 	while (wlist->prev)
472 		wlist = wlist->prev;
473 
474 	while (wlist) {
475 		next = wlist->next;
476 
477 		if (wlist->main_window == wapp->main_window) {
478 			if (RelaunchWindow(wlist))
479 				return;
480 		}
481 
482 		wlist = next;
483 	}
484 }
485 
relaunchCallback(WMenu * menu,WMenuEntry * entry)486 static void relaunchCallback(WMenu * menu, WMenuEntry * entry)
487 {
488 	WApplication *wapp = (WApplication *) entry->clientdata;
489 
490 	/* Parameter not used, but tell the compiler that it is ok */
491 	(void) menu;
492 
493 	relaunchApplication(wapp);
494 }
495 
hideCallback(WMenu * menu,WMenuEntry * entry)496 static void hideCallback(WMenu * menu, WMenuEntry * entry)
497 {
498 	WApplication *wapp = (WApplication *) entry->clientdata;
499 
500 	if (wapp->flags.hidden) {
501 		wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
502 		wUnhideApplication(wapp, False, False);
503 	} else {
504 		wHideApplication(wapp);
505 	}
506 }
507 
unhideHereCallback(WMenu * menu,WMenuEntry * entry)508 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
509 {
510 	WApplication *wapp = (WApplication *) entry->clientdata;
511 
512 	/* Parameter not used, but tell the compiler that it is ok */
513 	(void) menu;
514 
515 	wUnhideApplication(wapp, False, True);
516 }
517 
setIconCallback(WMenu * menu,WMenuEntry * entry)518 static void setIconCallback(WMenu *menu, WMenuEntry *entry)
519 {
520 	WAppIcon *icon = ((WApplication *) entry->clientdata)->app_icon;
521 	char *file = NULL;
522 	WScreen *scr;
523 	int result;
524 
525 	/* Parameter not used, but tell the compiler that it is ok */
526 	(void) menu;
527 
528 	assert(icon != NULL);
529 
530 	if (icon->editing)
531 		return;
532 
533 	icon->editing = 1;
534 	scr = icon->icon->core->screen_ptr;
535 
536 	wretain(icon);
537 
538 	result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
539 
540 	if (result) {
541 		if (!icon->destroyed) {
542 			if (!wIconChangeImageFile(icon->icon, file)) {
543 				wMessageDialog(scr, _("Error"),
544 				               _("Could not open specified icon file"),
545 				               _("OK"), NULL, NULL);
546 			} else {
547 				wDefaultChangeIcon(icon->wm_instance, icon->wm_class, file);
548 				wAppIconPaint(icon);
549 			}
550 		}
551 		if (file)
552 			wfree(file);
553 	}
554 	icon->editing = 0;
555 	wrelease(icon);
556 }
557 
killCallback(WMenu * menu,WMenuEntry * entry)558 static void killCallback(WMenu * menu, WMenuEntry * entry)
559 {
560 	WApplication *wapp = (WApplication *) entry->clientdata;
561 	WFakeGroupLeader *fPtr;
562 	char *buffer;
563 	char *shortname;
564 
565 	if (!WCHECK_STATE(WSTATE_NORMAL))
566 		return;
567 
568 	WCHANGE_STATE(WSTATE_MODAL);
569 
570 	assert(entry->clientdata != NULL);
571 
572 	shortname = basename(wapp->app_icon->wm_instance);
573 
574 	buffer = wstrconcat(wapp->app_icon ? shortname : NULL,
575 			    _(" will be forcibly closed.\n"
576 			      "Any unsaved changes will be lost.\n" "Please confirm."));
577 
578 	fPtr = wapp->main_window_desc->fake_group;
579 
580 	wretain(wapp->main_window_desc);
581 	if (wPreferences.dont_confirm_kill
582 	    || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
583 			      buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
584 		if (fPtr != NULL) {
585 			WWindow *wwin, *twin;
586 
587 			wwin = wapp->main_window_desc->screen_ptr->focused_window;
588 			while (wwin) {
589 				twin = wwin->prev;
590 				if (wwin->fake_group == fPtr)
591 					wClientKill(wwin);
592 				wwin = twin;
593 			}
594 		} else if (!wapp->main_window_desc->flags.destroyed) {
595 			wClientKill(wapp->main_window_desc);
596 		}
597 	}
598 	wrelease(wapp->main_window_desc);
599 	wfree(buffer);
600 	WCHANGE_STATE(WSTATE_NORMAL);
601 }
602 
createApplicationMenu(WScreen * scr)603 static WMenu *createApplicationMenu(WScreen *scr)
604 {
605 	WMenu *menu;
606 
607 	menu = wMenuCreate(scr, NULL, False);
608 	wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
609 	wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
610 	wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
611 	wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
612 	wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
613 
614 	return menu;
615 }
616 
openApplicationMenu(WApplication * wapp,int x,int y)617 static void openApplicationMenu(WApplication * wapp, int x, int y)
618 {
619 	WMenu *menu;
620 	WScreen *scr = wapp->main_window_desc->screen_ptr;
621 	int i;
622 
623 	if (!scr->icon_menu) {
624 		scr->icon_menu = createApplicationMenu(scr);
625 		wfree(scr->icon_menu->entries[1]->text);
626 	}
627 
628 	menu = scr->icon_menu;
629 
630 	if (wapp->flags.hidden)
631 		menu->entries[1]->text = _("Unhide");
632 	else
633 		menu->entries[1]->text = _("Hide");
634 
635 	menu->flags.realized = 0;
636 	wMenuRealize(menu);
637 
638 	x -= menu->frame->core->width / 2;
639 	if (x + menu->frame->core->width > scr->scr_width)
640 		x = scr->scr_width - menu->frame->core->width;
641 
642 	if (x < 0)
643 		x = 0;
644 
645 	/* set client data */
646 	for (i = 0; i < menu->entry_no; i++)
647 		menu->entries[i]->clientdata = wapp;
648 
649 	wMenuMapAt(menu, x, y, False);
650 }
651 
652 /******************************************************************/
653 
iconExpose(WObjDescriptor * desc,XEvent * event)654 static void iconExpose(WObjDescriptor *desc, XEvent *event)
655 {
656 	/* Parameter not used, but tell the compiler that it is ok */
657 	(void) event;
658 
659 	wAppIconPaint(desc->parent);
660 }
661 
iconDblClick(WObjDescriptor * desc,XEvent * event)662 static void iconDblClick(WObjDescriptor *desc, XEvent *event)
663 {
664 	WAppIcon *aicon = desc->parent;
665 	WApplication *wapp;
666 	WScreen *scr = aicon->icon->core->screen_ptr;
667 	int unhideHere;
668 
669 	assert(aicon->icon->owner != NULL);
670 
671 	wapp = wApplicationOf(aicon->icon->owner->main_window);
672 
673 	if (event->xbutton.state & ControlMask) {
674 		relaunchApplication(wapp);
675 		return;
676 	}
677 
678 	unhideHere = (event->xbutton.state & ShiftMask);
679 	/* go to the last workspace that the user worked on the app */
680 	if (!unhideHere && wapp->last_workspace != scr->current_workspace)
681 		wWorkspaceChange(scr, wapp->last_workspace);
682 
683 	wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
684 
685 	if (event->xbutton.state & MOD_MASK)
686 		wHideOtherApplications(aicon->icon->owner);
687 }
688 
appIconMouseDown(WObjDescriptor * desc,XEvent * event)689 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
690 {
691 	WAppIcon *aicon = desc->parent;
692 	WScreen *scr = aicon->icon->core->screen_ptr;
693 	Bool hasMoved;
694 
695 	if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
696 		return;
697 
698 	if (IsDoubleClick(scr, event)) {
699 		/* Middle or right mouse actions were handled on first click */
700 		if (event->xbutton.button == Button1)
701 			iconDblClick(desc, event);
702 		return;
703 	}
704 
705 	if (event->xbutton.button == Button2) {
706 		WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
707 
708 		if (wapp)
709 			relaunchApplication(wapp);
710 
711 		return;
712 	}
713 
714 	if (event->xbutton.button == Button3) {
715 		WObjDescriptor *desc;
716 		WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
717 
718 		if (!wapp)
719 			return;
720 
721 		if (event->xbutton.send_event &&
722 		    XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
723 				 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
724 				 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
725 			wwarning("pointer grab failed for appicon menu");
726 			return;
727 		}
728 
729 		openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
730 
731 		/* allow drag select of menu */
732 		desc = &scr->icon_menu->menu->descriptor;
733 		event->xbutton.send_event = True;
734 		(*desc->handle_mousedown) (desc, event);
735 		return;
736 	}
737 
738 	hasMoved = wHandleAppIconMove(aicon, event);
739 	if (wPreferences.single_click && !hasMoved && aicon->dock != NULL)
740 	{
741 		iconDblClick(desc, event);
742 	}
743 }
744 
wHandleAppIconMove(WAppIcon * aicon,XEvent * event)745 Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event)
746 {
747 	WIcon *icon = aicon->icon;
748 	WScreen *scr = icon->core->screen_ptr;
749 	WDock *originalDock = aicon->dock; /* can be NULL */
750 	WDock *lastDock = originalDock;
751 	WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */
752 	WDrawerChain *dc;
753 	Bool dockable, ondock;
754 	Bool grabbed = False;
755 	Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
756 	int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */
757 	int omnipresent = aicon->omnipresent; /* this must be cached */
758 	Bool showed_all_clips = False;
759 
760 	int clickButton = event->xbutton.button;
761 	Pixmap ghost = None;
762 	Window wins[2]; /* Managing shadow window */
763 	XEvent ev;
764 
765 	int x = aicon->x_pos, y = aicon->y_pos;
766 	int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
767 	int shad_x = x, shad_y = y;
768 	int ix = aicon->xindex, iy = aicon->yindex;
769 	int i;
770 	int oldX = x;
771 	int oldY = y;
772 	Bool hasMoved = False;
773 
774 	if (wPreferences.flags.noupdates && originalDock != NULL)
775 		return False;
776 
777 	if (!(event->xbutton.state & MOD_MASK))
778 		wRaiseFrame(icon->core);
779 	else {
780 		/* If Mod is pressed for an docked appicon, assume it is to undock it,
781 		 * so don't lower it */
782 		if (originalDock == NULL)
783 			wLowerFrame(icon->core);
784 	}
785 
786 	if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
787 			 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
788 			 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
789 		wwarning("Pointer grab failed in wHandleAppIconMove");
790 	}
791 
792 	if (originalDock != NULL) {
793 	    dockable = True;
794 	    ondock = True;
795 	}
796 	else {
797 		ondock = False;
798 		if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer)
799 			dockable = 0;
800 		else
801 			dockable = canBeDocked(icon->owner);
802 	}
803 
804 	/* We try the various docks in that order:
805 	 * - First, the dock the appicon comes from, if any
806 	 * - Then, the drawers
807 	 * - Then, the "dock" (WM_DOCK)
808 	 * - Finally, the clip
809 	 */
810 	i = 0;
811 	if (originalDock != NULL)
812 		allDocks[ i++ ] = originalDock;
813 	/* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
814 	for (dc = scr->drawers; dc != NULL; dc = dc->next) {
815 		if (dc->adrawer != originalDock)
816 			allDocks[ i++ ] = dc->adrawer;
817 	}
818 	if (!wPreferences.flags.nodock && scr->dock != originalDock)
819 		allDocks[ i++ ] = scr->dock;
820 
821 	if (!wPreferences.flags.noclip &&
822 	    originalDock != scr->workspaces[scr->current_workspace]->clip)
823 		allDocks[ i++ ] = scr->workspaces[scr->current_workspace]->clip;
824 
825 	for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */
826 		allDocks[ i ] = NULL;
827 
828 	wins[0] = icon->core->window;
829 	wins[1] = scr->dock_shadow;
830 	XRestackWindows(dpy, wins, 2);
831 	XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
832 	if (superfluous) {
833 		if (icon->pixmap != None)
834 			ghost = MakeGhostIcon(scr, icon->pixmap);
835 		else
836 			ghost = MakeGhostIcon(scr, icon->core->window);
837 		XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
838 		XClearWindow(dpy, scr->dock_shadow);
839 	}
840 	if (ondock)
841 		XMapWindow(dpy, scr->dock_shadow);
842 
843 	while (1) {
844 		WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
845 			    | ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
846 		switch (ev.type) {
847 		case Expose:
848 			WMHandleEvent(&ev);
849 			break;
850 
851 		case EnterNotify:
852 			/* It means the cursor moved so fast that it entered
853 			 * something else (if moving slowly, it would have
854 			 * stayed in the appIcon that is being moved. Ignore
855 			 * such "spurious" EnterNotifiy's */
856 			break;
857 
858 		case MotionNotify:
859 			hasMoved = True;
860 			if (!grabbed) {
861 				if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
862 				    || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
863 					XChangeActivePointerGrab(dpy, ButtonMotionMask
864 								 | ButtonReleaseMask | ButtonPressMask,
865 								 wPreferences.cursor[WCUR_MOVE], CurrentTime);
866 					grabbed = 1;
867 				} else {
868 					break;
869 				}
870 			}
871 
872 			if (omnipresent && !showed_all_clips) {
873 				int i;
874 				for (i = 0; i < scr->workspace_count; i++) {
875 					if (i == scr->current_workspace)
876 						continue;
877 
878 					wDockShowIcons(scr->workspaces[i]->clip);
879 					/* Note: if dock is collapsed (for instance, because it
880 					   auto-collapses), its icons still won't show up */
881 				}
882 				showed_all_clips = True; /* To prevent flickering */
883 			}
884 
885 			x = ev.xmotion.x_root - ofs_x;
886 			y = ev.xmotion.y_root - ofs_y;
887 			wAppIconMove(aicon, x, y);
888 
889 			WDock *theNewDock = NULL;
890 			if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock || originalDock == NULL) {
891 				for (i = 0; dockable && i < scr->drawer_count + 2; i++) {
892 					WDock *theDock = allDocks[i];
893 					if (theDock == NULL)
894 						break;
895 					if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) {
896 						theNewDock = theDock;
897 						break;
898 					}
899 				}
900 				if (originalDock != NULL && theNewDock == NULL &&
901 					(aicon->launching || aicon->lock || aicon->running)) {
902 					/* In those cases, stay in lastDock if no dock really wants us */
903 					theNewDock = lastDock;
904 				}
905 			}
906 			if (lastDock != NULL && lastDock != theNewDock) {
907 				/* Leave lastDock in the state we found it */
908 				if (lastDock->type == WM_DRAWER) {
909 					wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
910 				}
911 				if (collapsed) {
912 					lastDock->collapsed = 1;
913 					wDockHideIcons(lastDock);
914 					collapsed = False;
915 				}
916 				if (lastDock->auto_raise_lower) {
917 					wDockLower(lastDock);
918 				}
919 			}
920 			if (theNewDock != NULL) {
921 				if (lastDock != theNewDock) {
922 					collapsed = theNewDock->collapsed;
923 					if (collapsed) {
924 						theNewDock->collapsed = 0;
925 						wDockShowIcons(theNewDock);
926 					}
927 					if (theNewDock->auto_raise_lower) {
928 						wDockRaise(theNewDock);
929 						/* And raise the moving tile above it */
930 						wRaiseFrame(aicon->icon->core);
931 					}
932 					lastDock = theNewDock;
933 				}
934 
935 				shad_x = lastDock->x_pos + ix*wPreferences.icon_size;
936 				shad_y = lastDock->y_pos + iy*wPreferences.icon_size;
937 
938 				XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
939 
940 				if (!ondock) {
941 					XMapWindow(dpy, scr->dock_shadow);
942 				}
943 				ondock = 1;
944 			} else {
945 				lastDock = theNewDock; // i.e., NULL
946 				if (ondock) {
947 					XUnmapWindow(dpy, scr->dock_shadow);
948 					/*
949 					 * Leaving that weird comment for now.
950 					 * But if we see no gap, there is no need to fill one!
951 					 * We could test ondock first and the lastDock to NULL afterwards
952 					   if (lastDock_before_it_was_null->type == WM_DRAWER) {
953 					   wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
954 					   } */
955 
956 				}
957 				ondock = 0;
958 			}
959 			break;
960 
961 		case ButtonPress:
962 			break;
963 
964 		case ButtonRelease:
965 			if (ev.xbutton.button != clickButton)
966 				break;
967 			XUngrabPointer(dpy, CurrentTime);
968 
969 			Bool docked = False;
970 			if (ondock) {
971 				slide_window(icon->core->window, x, y, shad_x, shad_y);
972 				XUnmapWindow(dpy, scr->dock_shadow);
973 				if (originalDock == NULL) { // docking an undocked appicon
974 					docked = wDockAttachIcon(lastDock, aicon, ix, iy, False);
975 					if (!docked) {
976 						/* AppIcon got rejected (happens only when we can't get the
977 						   command for that appicon, and the user cancels the
978 						   wInputDialog asking for one). Make the rejection obvious by
979 						   sliding the icon to its old position */
980 						if (lastDock->type == WM_DRAWER) {
981 							// Also fill the gap left in the drawer
982 							wDrawerFillTheGap(lastDock, aicon, False);
983 						}
984 						slide_window(icon->core->window, x, y, oldX, oldY);
985 					}
986 				}
987 				else { // moving a docked appicon to a dock
988 					if (originalDock == lastDock) {
989 						docked = True;
990 						wDockReattachIcon(originalDock, aicon, ix, iy);
991 					}
992 					else {
993 						docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy);
994 						if (!docked) {
995 							/* Possible scenario: user moved an auto-attracted appicon
996 							   from the clip to the dock, and cancelled the wInputDialog
997 							   asking for a command */
998 							if (lastDock->type == WM_DRAWER) {
999 								wDrawerFillTheGap(lastDock, aicon, False);
1000 							}
1001 							/* If aicon comes from a drawer, make some room to reattach it */
1002 							if (originalDock->type == WM_DRAWER) {
1003 								WAppIcon *aiconsToShift[ originalDock->icon_count ];
1004 								int j = 0;
1005 
1006 								for (i = 0; i < originalDock->max_icons; i++) {
1007 									WAppIcon *ai = originalDock->icon_array[ i ];
1008 									if (ai && ai != aicon &&
1009 										abs(ai->xindex) >= abs(aicon->xindex))
1010 										aiconsToShift[j++] = ai;
1011 								}
1012 								if (j != originalDock->icon_count - abs(aicon->xindex) - 1)
1013 									// Trust this never happens?
1014 									wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1015 										j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex);
1016 								wSlideAppicons(aiconsToShift, j, originalDock->on_right_side);
1017 								// Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1018 							}
1019 
1020 							slide_window(icon->core->window, x, y, oldX, oldY);
1021 							wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex);
1022 						}
1023 						else {
1024 							if (originalDock->auto_collapse && !originalDock->collapsed) {
1025 								originalDock->collapsed = 1;
1026 								wDockHideIcons(originalDock);
1027 							}
1028 							if (originalDock->auto_raise_lower)
1029 								wDockLower(originalDock);
1030 						}
1031 					}
1032 				}
1033 				// No matter what happened above, check to lower lastDock
1034 				// Don't see why I commented out the following 2 lines
1035 				/* if (lastDock->auto_raise_lower)
1036 				   wDockLower(lastDock); */
1037 				/* If docked (or tried to dock) to a auto_collapsing dock, unset
1038 				 * collapsed, so that wHandleAppIconMove doesn't collapse it
1039 				 * right away (the timer will take care of it) */
1040 				if (lastDock->auto_collapse)
1041 					collapsed = 0;
1042 			}
1043 			else {
1044 				if (originalDock != NULL) { /* Detaching a docked appicon */
1045 					if (superfluous) {
1046 						if (!aicon->running && !wPreferences.no_animations) {
1047 							/* We need to deselect it, even if is deselected in
1048 							 * wDockDetach(), because else DoKaboom() will fail.
1049 							 */
1050 							if (aicon->icon->selected)
1051 								wIconSelect(aicon->icon);
1052 							DoKaboom(scr, aicon->icon->core->window, x, y);
1053 						}
1054 					}
1055 					wDockDetach(originalDock, aicon);
1056 					if (originalDock->auto_collapse && !originalDock->collapsed) {
1057 						originalDock->collapsed = 1;
1058 						wDockHideIcons(originalDock);
1059 					}
1060 					if (originalDock->auto_raise_lower)
1061 						wDockLower(originalDock);
1062 				}
1063 			}
1064 			// Can't remember why the icon hiding is better done above than below (commented out)
1065 			// Also, lastDock is quite different from originalDock
1066 			/*
1067 			  if (collapsed) {
1068 				lastDock->collapsed = 1;
1069 				wDockHideIcons(lastDock);
1070 				collapsed = 0;
1071 			}
1072 			*/
1073 			if (superfluous) {
1074 				if (ghost != None)
1075 					XFreePixmap(dpy, ghost);
1076 				XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
1077 			}
1078 			if (showed_all_clips) {
1079 				int i;
1080 				for (i = 0; i < scr->workspace_count; i++) {
1081 					if (i == scr->current_workspace)
1082 						continue;
1083 
1084 					wDockHideIcons(scr->workspaces[i]->clip);
1085 				}
1086 			}
1087 			if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked))
1088 				/* Need to rearrange unless moving from dock to dock */
1089 				wArrangeIcons(scr, True);
1090 			return hasMoved;
1091 		}
1092 	}
1093 }
1094 
1095 /* This function save the application icon and store the path in the Dictionary */
wApplicationSaveIconPathFor(const char * iconPath,const char * wm_instance,const char * wm_class)1096 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const char *wm_class)
1097 {
1098 	WMPropList *dict = w_global.domain.window_attr->dictionary;
1099 	WMPropList *adict, *key, *iconk;
1100 	WMPropList *val;
1101 	char *tmp;
1102 
1103 	tmp = get_name_for_instance_class(wm_instance, wm_class);
1104 	key = WMCreatePLString(tmp);
1105 	wfree(tmp);
1106 
1107 	adict = WMGetFromPLDictionary(dict, key);
1108 	iconk = WMCreatePLString("Icon");
1109 
1110 	if (adict) {
1111 		val = WMGetFromPLDictionary(adict, iconk);
1112 	} else {
1113 		/* no dictionary for app, so create one */
1114 		adict = WMCreatePLDictionary(NULL, NULL);
1115 		WMPutInPLDictionary(dict, key, adict);
1116 		WMReleasePropList(adict);
1117 		val = NULL;
1118 	}
1119 
1120 	if (!val) {
1121 		val = WMCreatePLString(iconPath);
1122 		WMPutInPLDictionary(adict, iconk, val);
1123 		WMReleasePropList(val);
1124 	} else {
1125 		val = NULL;
1126 	}
1127 
1128 	WMReleasePropList(key);
1129 	WMReleasePropList(iconk);
1130 
1131 	if (val && !wPreferences.flags.noupdates)
1132 		UpdateDomainFile(w_global.domain.window_attr);
1133 }
1134 
findDockIconFor(WDock * dock,Window main_window)1135 static WAppIcon *findDockIconFor(WDock *dock, Window main_window)
1136 {
1137 	WAppIcon *aicon = NULL;
1138 
1139 	aicon = wDockFindIconForWindow(dock, main_window);
1140 	if (!aicon) {
1141 		wDockTrackWindowLaunch(dock, main_window);
1142 		aicon = wDockFindIconForWindow(dock, main_window);
1143 	}
1144 	return aicon;
1145 }
1146 
create_appicon_from_dock(WWindow * wwin,WApplication * wapp,Window main_window)1147 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window)
1148 {
1149 	WScreen *scr = wwin->screen_ptr;
1150 	wapp->app_icon = NULL;
1151 
1152 	if (scr->last_dock)
1153 		wapp->app_icon = findDockIconFor(scr->last_dock, main_window);
1154 
1155 	/* check main dock if we did not find it in last dock */
1156 	if (!wapp->app_icon && scr->dock)
1157 		wapp->app_icon = findDockIconFor(scr->dock, main_window);
1158 
1159 	/* check clips */
1160 	if (!wapp->app_icon) {
1161 		int i;
1162 		for (i = 0; i < scr->workspace_count; i++) {
1163 			WDock *dock = scr->workspaces[i]->clip;
1164 
1165 			if (dock)
1166 				wapp->app_icon = findDockIconFor(dock, main_window);
1167 
1168 			if (wapp->app_icon)
1169 				break;
1170 		}
1171 	}
1172 
1173 	/* Finally check drawers */
1174 	if (!wapp->app_icon) {
1175 		WDrawerChain *dc;
1176 		for (dc = scr->drawers; dc != NULL; dc = dc->next) {
1177 			wapp->app_icon = findDockIconFor(dc->adrawer, main_window);
1178 			if (wapp->app_icon)
1179 				break;
1180 		}
1181 	}
1182 
1183 	/* If created, then set some flags */
1184 	if (wapp->app_icon && !WFLAGP(wapp->main_window_desc, no_appicon)) {
1185 		WWindow *mainw = wapp->main_window_desc;
1186 
1187 		wapp->app_icon->running = 1;
1188 		wapp->app_icon->icon->owner = mainw;
1189 		if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
1190 			wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
1191 
1192 		/* Update the icon images */
1193 		wIconUpdate(wapp->app_icon->icon);
1194 
1195 		/* Paint it */
1196 		wAppIconPaint(wapp->app_icon);
1197 	}
1198 }
1199 
1200 /* Add the appicon to the appiconlist */
add_to_appicon_list(WScreen * scr,WAppIcon * appicon)1201 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon)
1202 {
1203 	appicon->prev = NULL;
1204 	appicon->next = scr->app_icon_list;
1205 	if (scr->app_icon_list)
1206 		scr->app_icon_list->prev = appicon;
1207 
1208 	scr->app_icon_list = appicon;
1209 }
1210 
1211 /* Remove the appicon from the appiconlist */
remove_from_appicon_list(WScreen * scr,WAppIcon * appicon)1212 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon)
1213 {
1214 	if (appicon == scr->app_icon_list) {
1215 		if (appicon->next)
1216 			appicon->next->prev = NULL;
1217 		scr->app_icon_list = appicon->next;
1218 	} else {
1219 		if (appicon->next)
1220 			appicon->next->prev = appicon->prev;
1221 		if (appicon->prev)
1222 			appicon->prev->next = appicon->next;
1223 	}
1224 
1225 	appicon->prev = NULL;
1226 	appicon->next = NULL;
1227 }
1228 
1229 /* Return the AppIcon associated with a given (Xlib) Window. */
wAppIconFor(Window window)1230 WAppIcon *wAppIconFor(Window window)
1231 {
1232 	WObjDescriptor *desc;
1233 
1234 	if (window == None)
1235 		return NULL;
1236 
1237 	if (XFindContext(dpy, window, w_global.context.client_win, (XPointer *) & desc) == XCNOENT)
1238 		return NULL;
1239 
1240 	if (desc->parent_type == WCLASS_APPICON || desc->parent_type == WCLASS_DOCK_ICON)
1241 		return desc->parent;
1242 
1243 	return NULL;
1244 }
1245