New applications do not create Cached Icon
[wmaker-crm.git] / src / appicon.c
blob2a9db5cacac1df2f0711bedab745d643b9ad644b
1 /* appicon.c- icon for applications (not mini-window)
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
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.
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.
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.
23 #include "wconfig.h"
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>
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
59 * icon_file for the dock is got from the preferences file by
60 * using the classname/instancename
63 #define MOD_MASK wPreferences.modifier_mask
64 #define ICON_SIZE wPreferences.icon_size
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);
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
77 void wApplicationExtractDirPackIcon(const char *path, const char *wm_instance, const char *wm_class)
79 char *iconPath = NULL;
80 char *tmp = NULL;
82 if (strstr(path, ".app")) {
83 tmp = wmalloc(strlen(path) + 16);
85 if (wPreferences.supports_tiff) {
86 strcpy(tmp, path);
87 strcat(tmp, ".tiff");
88 if (access(tmp, R_OK) == 0)
89 iconPath = tmp;
92 if (!iconPath) {
93 strcpy(tmp, path);
94 strcat(tmp, ".xpm");
95 if (access(tmp, R_OK) == 0)
96 iconPath = tmp;
99 if (!iconPath)
100 wfree(tmp);
102 if (iconPath) {
103 wApplicationSaveIconPathFor(iconPath, wm_instance, wm_class);
104 wfree(iconPath);
109 WAppIcon *wAppIconCreateForDock(WScreen *scr, const char *command, const char *wm_instance, const char *wm_class, int tile)
111 WAppIcon *aicon;
113 aicon = wmalloc(sizeof(WAppIcon));
114 wretain(aicon);
115 aicon->yindex = -1;
116 aicon->xindex = -1;
118 add_to_appicon_list(scr, aicon);
120 if (command)
121 aicon->command = wstrdup(command);
123 if (wm_class)
124 aicon->wm_class = wstrdup(wm_class);
126 if (wm_instance)
127 aicon->wm_instance = wstrdup(wm_instance);
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);
133 #ifdef USE_DOCK_XDND
134 wXDNDMakeAwareness(aicon->icon->core->window);
135 #endif
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);
144 return aicon;
147 void create_appicon_for_application(WApplication *wapp, WWindow *wwin)
149 /* Try to create an icon from the dock or clip */
150 create_appicon_from_dock(wwin, wapp, wapp->main_window);
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);
158 /* Now, paint the icon */
159 if (!WFLAGP(wapp->main_window_desc, no_appicon))
160 paint_app_icon(wapp);
164 void unpaint_app_icon(WApplication *wapp)
166 WAppIcon *aicon;
167 WScreen *scr;
168 WDock *clip;
170 if (!wapp || !wapp->app_icon)
171 return;
173 aicon = wapp->app_icon;
175 /* If the icon is docked, don't continue */
176 if (aicon->docked)
177 return;
179 scr = wapp->main_window_desc->screen_ptr;
180 clip = scr->workspaces[scr->current_workspace]->clip;
182 if (!clip || !aicon->attracted || !clip->collapsed)
183 XUnmapWindow(dpy, aicon->icon->core->window);
185 /* We want to avoid having it on the list because otherwise
186 * there will be a hole when the icons are arranged with
187 * wArrangeIcons() */
188 remove_from_appicon_list(scr, aicon);
190 if (wPreferences.auto_arrange_icons && !aicon->attracted)
191 wArrangeIcons(scr, True);
194 void paint_app_icon(WApplication *wapp)
196 WIcon *icon;
197 WScreen *scr;
198 WDock *attracting_dock;
199 int x = 0, y = 0;
200 Bool update_icon = False;
202 if (!wapp || !wapp->app_icon || !wapp->main_window_desc)
203 return;
205 icon = wapp->app_icon->icon;
206 scr = wapp->main_window_desc->screen_ptr;
207 wapp->app_icon->main_window = wapp->main_window;
209 /* If the icon is docked, don't continue */
210 if (wapp->app_icon->docked)
211 return;
213 attracting_dock = scr->attracting_drawer != NULL ?
214 scr->attracting_drawer :
215 scr->workspaces[scr->current_workspace]->clip;
216 if (attracting_dock && attracting_dock->attract_icons &&
217 wDockFindFreeSlot(attracting_dock, &x, &y)) {
218 wapp->app_icon->attracted = 1;
219 if (!icon->shadowed) {
220 icon->shadowed = 1;
221 update_icon = True;
223 wDockAttachIcon(attracting_dock, wapp->app_icon, x, y, update_icon);
224 } else {
225 /* We must know if the icon is painted in the screen,
226 * because if painted, then PlaceIcon will return the next
227 * space on the screen, and the icon will move */
228 if (wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL) {
229 PlaceIcon(scr, &x, &y, wGetHeadForWindow(wapp->main_window_desc));
230 wAppIconMove(wapp->app_icon, x, y);
231 wLowerFrame(icon->core);
235 /* If we want appicon (no_appicon is not set) and the icon is not
236 * in the appicon_list, we must add it. Else, we want to avoid
237 * having it on the list */
238 if (!WFLAGP(wapp->main_window_desc, no_appicon) &&
239 wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL)
240 add_to_appicon_list(scr, wapp->app_icon);
242 if (!attracting_dock || !wapp->app_icon->attracted || !attracting_dock->collapsed)
243 XMapWindow(dpy, icon->core->window);
245 if (wPreferences.auto_arrange_icons && !wapp->app_icon->attracted)
246 wArrangeIcons(scr, True);
249 void removeAppIconFor(WApplication *wapp)
251 if (!wapp->app_icon)
252 return;
254 if (wPreferences.highlight_active_app)
255 wIconSetHighlited(wapp->app_icon->icon, False);
256 if (wapp->app_icon->docked && !wapp->app_icon->attracted) {
257 wapp->app_icon->running = 0;
258 /* since we keep it, we don't care if it was attracted or not */
259 wapp->app_icon->attracted = 0;
260 wapp->app_icon->icon->shadowed = 0;
261 wapp->app_icon->main_window = None;
262 wapp->app_icon->pid = 0;
263 wapp->app_icon->icon->owner = NULL;
264 wapp->app_icon->icon->icon_win = None;
266 /* Set the icon image */
267 set_icon_image_from_database(wapp->app_icon->icon, wapp->app_icon->wm_instance,
268 wapp->app_icon->wm_class, wapp->app_icon->command);
270 /* Update the icon, because wapp->app_icon->icon could be NULL */
271 wIconUpdate(wapp->app_icon->icon);
273 /* Paint it */
274 wAppIconPaint(wapp->app_icon);
275 } else if (wapp->app_icon->docked) {
276 wapp->app_icon->running = 0;
277 if (wapp->app_icon->dock->type == WM_DRAWER) {
278 wDrawerFillTheGap(wapp->app_icon->dock, wapp->app_icon, True);
280 wDockDetach(wapp->app_icon->dock, wapp->app_icon);
281 } else {
282 wAppIconDestroy(wapp->app_icon);
285 wapp->app_icon = NULL;
287 if (wPreferences.auto_arrange_icons)
288 wArrangeIcons(wapp->main_window_desc->screen_ptr, True);
291 static WAppIcon *wAppIconCreate(WWindow *leader_win)
293 WAppIcon *aicon;
295 aicon = wmalloc(sizeof(WAppIcon));
296 wretain(aicon);
297 aicon->yindex = -1;
298 aicon->xindex = -1;
299 aicon->prev = NULL;
300 aicon->next = NULL;
302 if (leader_win->wm_class)
303 aicon->wm_class = wstrdup(leader_win->wm_class);
305 if (leader_win->wm_instance)
306 aicon->wm_instance = wstrdup(leader_win->wm_instance);
308 aicon->icon = icon_create_for_wwindow(leader_win);
309 #ifdef USE_DOCK_XDND
310 wXDNDMakeAwareness(aicon->icon->core->window);
311 #endif
313 /* will be overriden if docked */
314 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
315 aicon->icon->core->descriptor.handle_expose = iconExpose;
316 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
317 aicon->icon->core->descriptor.parent = aicon;
318 AddToStackList(aicon->icon->core);
319 aicon->icon->show_title = 0;
321 return aicon;
324 void wAppIconDestroy(WAppIcon * aicon)
326 WScreen *scr = aicon->icon->core->screen_ptr;
328 RemoveFromStackList(aicon->icon->core);
329 wIconDestroy(aicon->icon);
330 if (aicon->command)
331 wfree(aicon->command);
332 #ifdef USE_DOCK_XDND
333 if (aicon->dnd_command)
334 wfree(aicon->dnd_command);
335 #endif
336 if (aicon->wm_instance)
337 wfree(aicon->wm_instance);
339 if (aicon->wm_class)
340 wfree(aicon->wm_class);
342 remove_from_appicon_list(scr, aicon);
344 aicon->destroyed = 1;
345 wrelease(aicon);
348 static void drawCorner(WIcon * icon)
350 WScreen *scr = icon->core->screen_ptr;
351 XPoint points[3];
353 points[0].x = 1;
354 points[0].y = 1;
355 points[1].x = 12;
356 points[1].y = 1;
357 points[2].x = 1;
358 points[2].y = 12;
359 XFillPolygon(dpy, icon->core->window, scr->icon_title_texture->normal_gc,
360 points, 3, Convex, CoordModeOrigin);
361 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 0, 12);
362 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 12, 0);
365 void wAppIconMove(WAppIcon * aicon, int x, int y)
367 XMoveWindow(dpy, aicon->icon->core->window, x, y);
368 aicon->x_pos = x;
369 aicon->y_pos = y;
372 #ifdef WS_INDICATOR
373 static void updateDockNumbers(WScreen *scr)
375 int length;
376 char *ws_numbers;
377 WAppIcon *dicon = scr->dock->icon_array[0];
379 ws_numbers = wmalloc(20);
380 snprintf(ws_numbers, 20, "%i [ %i ]", scr->current_workspace + 1, ((scr->current_workspace / 10) + 1));
381 length = strlen(ws_numbers);
383 XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, WMFontHeight(scr->icon_title_font) + 1, False);
385 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->black,
386 scr->icon_title_font, 4, 3, ws_numbers, length);
388 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->white,
389 scr->icon_title_font, 3, 2, ws_numbers, length);
391 wfree(ws_numbers);
393 #endif /* WS_INDICATOR */
395 void wAppIconPaint(WAppIcon *aicon)
397 WApplication *wapp;
398 WScreen *scr = aicon->icon->core->screen_ptr;
400 if (aicon->icon->owner)
401 wapp = wApplicationOf(aicon->icon->owner->main_window);
402 else
403 wapp = NULL;
405 wIconPaint(aicon->icon);
407 # ifdef WS_INDICATOR
408 if (aicon->docked && scr->dock && scr->dock == aicon->dock && aicon->yindex == 0)
409 updateDockNumbers(scr);
410 # endif
411 if (aicon->docked && !aicon->running && aicon->command != NULL) {
412 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
413 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
414 XCopyArea(dpy, scr->dock_dots->image, aicon->icon->core->window,
415 scr->copy_gc, 0, 0, scr->dock_dots->width, scr->dock_dots->height, 0, 0);
417 #ifdef HIDDENDOT
418 if (wapp && wapp->flags.hidden) {
419 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
420 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
421 XCopyArea(dpy, scr->dock_dots->image,
422 aicon->icon->core->window, scr->copy_gc, 0, 0, 7, scr->dock_dots->height, 0, 0);
424 #endif /* HIDDENDOT */
426 if (aicon->omnipresent)
427 drawCorner(aicon->icon);
429 XSetClipMask(dpy, scr->copy_gc, None);
430 if (aicon->launching)
431 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
432 0, 0, wPreferences.icon_size, wPreferences.icon_size);
435 /* Save the application icon, if it's a dockapp then use it with dock = True */
436 void save_appicon(WAppIcon *aicon, Bool dock)
438 char *path;
440 if (!aicon)
441 return;
443 if (dock && (!aicon->docked || aicon->attracted))
444 return;
446 path = wIconStore(aicon->icon);
447 if (!path)
448 return;
450 wApplicationSaveIconPathFor(path, aicon->wm_instance, aicon->wm_class);
451 wfree(path);
454 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
456 /* main_window may not have the full command line; try to find one which does */
457 static void relaunchApplication(WApplication *wapp)
459 WScreen *scr;
460 WWindow *wlist, *next;
462 scr = wapp->main_window_desc->screen_ptr;
463 wlist = scr->focused_window;
464 if (! wlist)
465 return;
467 while (wlist->prev)
468 wlist = wlist->prev;
470 while (wlist) {
471 next = wlist->next;
473 if (wlist->main_window == wapp->main_window) {
474 if (RelaunchWindow(wlist))
475 return;
478 wlist = next;
482 static void relaunchCallback(WMenu * menu, WMenuEntry * entry)
484 WApplication *wapp = (WApplication *) entry->clientdata;
486 /* Parameter not used, but tell the compiler that it is ok */
487 (void) menu;
489 relaunchApplication(wapp);
492 static void hideCallback(WMenu * menu, WMenuEntry * entry)
494 WApplication *wapp = (WApplication *) entry->clientdata;
496 if (wapp->flags.hidden) {
497 wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
498 wUnhideApplication(wapp, False, False);
499 } else {
500 wHideApplication(wapp);
504 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
506 WApplication *wapp = (WApplication *) entry->clientdata;
508 /* Parameter not used, but tell the compiler that it is ok */
509 (void) menu;
511 wUnhideApplication(wapp, False, True);
514 static void setIconCallback(WMenu *menu, WMenuEntry *entry)
516 WAppIcon *icon = ((WApplication *) entry->clientdata)->app_icon;
517 char *file = NULL;
518 WScreen *scr;
519 int result;
521 /* Parameter not used, but tell the compiler that it is ok */
522 (void) menu;
524 assert(icon != NULL);
526 if (icon->editing)
527 return;
529 icon->editing = 1;
530 scr = icon->icon->core->screen_ptr;
532 wretain(icon);
534 result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
536 if (result) {
537 if (!icon->destroyed) {
538 if (!wIconChangeImageFile(icon->icon, file)) {
539 wMessageDialog(scr, _("Error"),
540 _("Could not open specified icon file"),
541 _("OK"), NULL, NULL);
542 } else {
543 wDefaultChangeIcon(icon->wm_instance, icon->wm_class, file);
544 wAppIconPaint(icon);
547 if (file)
548 wfree(file);
550 icon->editing = 0;
551 wrelease(icon);
554 static void killCallback(WMenu * menu, WMenuEntry * entry)
556 WApplication *wapp = (WApplication *) entry->clientdata;
557 WFakeGroupLeader *fPtr;
558 char *buffer;
559 char *shortname;
561 if (!WCHECK_STATE(WSTATE_NORMAL))
562 return;
564 WCHANGE_STATE(WSTATE_MODAL);
566 assert(entry->clientdata != NULL);
568 shortname = basename(wapp->app_icon->wm_instance);
570 buffer = wstrconcat(wapp->app_icon ? shortname : NULL,
571 _(" will be forcibly closed.\n"
572 "Any unsaved changes will be lost.\n" "Please confirm."));
574 fPtr = wapp->main_window_desc->fake_group;
576 wretain(wapp->main_window_desc);
577 if (wPreferences.dont_confirm_kill
578 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
579 buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
580 if (fPtr != NULL) {
581 WWindow *wwin, *twin;
583 wwin = wapp->main_window_desc->screen_ptr->focused_window;
584 while (wwin) {
585 twin = wwin->prev;
586 if (wwin->fake_group == fPtr)
587 wClientKill(wwin);
588 wwin = twin;
590 } else if (!wapp->main_window_desc->flags.destroyed) {
591 wClientKill(wapp->main_window_desc);
594 wrelease(wapp->main_window_desc);
595 wfree(buffer);
596 WCHANGE_STATE(WSTATE_NORMAL);
599 static WMenu *createApplicationMenu(WScreen *scr)
601 WMenu *menu;
603 menu = wMenuCreate(scr, NULL, False);
604 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
605 wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
606 wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
607 wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
608 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
610 return menu;
613 static void openApplicationMenu(WApplication * wapp, int x, int y)
615 WMenu *menu;
616 WScreen *scr = wapp->main_window_desc->screen_ptr;
617 int i;
619 if (!scr->icon_menu) {
620 scr->icon_menu = createApplicationMenu(scr);
621 wfree(scr->icon_menu->entries[1]->text);
624 menu = scr->icon_menu;
626 if (wapp->flags.hidden)
627 menu->entries[1]->text = _("Unhide");
628 else
629 menu->entries[1]->text = _("Hide");
631 menu->flags.realized = 0;
632 wMenuRealize(menu);
634 x -= menu->frame->core->width / 2;
635 if (x + menu->frame->core->width > scr->scr_width)
636 x = scr->scr_width - menu->frame->core->width;
638 if (x < 0)
639 x = 0;
641 /* set client data */
642 for (i = 0; i < menu->entry_no; i++)
643 menu->entries[i]->clientdata = wapp;
645 wMenuMapAt(menu, x, y, False);
648 /******************************************************************/
650 static void iconExpose(WObjDescriptor *desc, XEvent *event)
652 /* Parameter not used, but tell the compiler that it is ok */
653 (void) event;
655 wAppIconPaint(desc->parent);
658 static void iconDblClick(WObjDescriptor *desc, XEvent *event)
660 WAppIcon *aicon = desc->parent;
661 WApplication *wapp;
662 WScreen *scr = aicon->icon->core->screen_ptr;
663 int unhideHere;
665 assert(aicon->icon->owner != NULL);
667 wapp = wApplicationOf(aicon->icon->owner->main_window);
669 if (event->xbutton.state & ControlMask) {
670 relaunchApplication(wapp);
671 return;
674 unhideHere = (event->xbutton.state & ShiftMask);
675 /* go to the last workspace that the user worked on the app */
676 if (!unhideHere && wapp->last_workspace != scr->current_workspace)
677 wWorkspaceChange(scr, wapp->last_workspace);
679 wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
681 if (event->xbutton.state & MOD_MASK)
682 wHideOtherApplications(aicon->icon->owner);
685 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
687 WAppIcon *aicon = desc->parent;
688 WScreen *scr = aicon->icon->core->screen_ptr;
689 Bool hasMoved;
691 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
692 return;
694 if (IsDoubleClick(scr, event)) {
695 /* Middle or right mouse actions were handled on first click */
696 if (event->xbutton.button == Button1)
697 iconDblClick(desc, event);
698 return;
701 if (event->xbutton.button == Button2) {
702 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
704 if (wapp)
705 relaunchApplication(wapp);
707 return;
710 if (event->xbutton.button == Button3) {
711 WObjDescriptor *desc;
712 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
714 if (!wapp)
715 return;
717 if (event->xbutton.send_event &&
718 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
719 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
720 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
721 wwarning("pointer grab failed for appicon menu");
722 return;
725 openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
727 /* allow drag select of menu */
728 desc = &scr->icon_menu->menu->descriptor;
729 event->xbutton.send_event = True;
730 (*desc->handle_mousedown) (desc, event);
731 return;
734 hasMoved = wHandleAppIconMove(aicon, event);
735 if (wPreferences.single_click && !hasMoved && aicon->dock != NULL)
737 iconDblClick(desc, event);
741 Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event)
743 WIcon *icon = aicon->icon;
744 WScreen *scr = icon->core->screen_ptr;
745 WDock *originalDock = aicon->dock; /* can be NULL */
746 WDock *lastDock = originalDock;
747 WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */
748 WDrawerChain *dc;
749 Bool dockable, ondock;
750 Bool grabbed = False;
751 Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
752 int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */
753 int omnipresent = aicon->omnipresent; /* this must be cached */
754 Bool showed_all_clips = False;
756 int clickButton = event->xbutton.button;
757 Pixmap ghost = None;
758 Window wins[2]; /* Managing shadow window */
759 XEvent ev;
761 int x = aicon->x_pos, y = aicon->y_pos;
762 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
763 int shad_x = x, shad_y = y;
764 int ix = aicon->xindex, iy = aicon->yindex;
765 int i;
766 int oldX = x;
767 int oldY = y;
768 Bool hasMoved = False;
770 if (wPreferences.flags.noupdates && originalDock != NULL)
771 return False;
773 if (!(event->xbutton.state & MOD_MASK))
774 wRaiseFrame(icon->core);
775 else {
776 /* If Mod is pressed for an docked appicon, assume it is to undock it,
777 * so don't lower it */
778 if (originalDock == NULL)
779 wLowerFrame(icon->core);
782 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
783 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
784 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
785 wwarning("Pointer grab failed in wHandleAppIconMove");
788 if (originalDock != NULL) {
789 dockable = True;
790 ondock = True;
792 else {
793 ondock = False;
794 if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer)
795 dockable = 0;
796 else
797 dockable = canBeDocked(icon->owner);
800 /* We try the various docks in that order:
801 * - First, the dock the appicon comes from, if any
802 * - Then, the drawers
803 * - Then, the "dock" (WM_DOCK)
804 * - Finally, the clip
806 i = 0;
807 if (originalDock != NULL)
808 allDocks[ i++ ] = originalDock;
809 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
810 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
811 if (dc->adrawer != originalDock)
812 allDocks[ i++ ] = dc->adrawer;
814 if (!wPreferences.flags.nodock && scr->dock != originalDock)
815 allDocks[ i++ ] = scr->dock;
817 if (!wPreferences.flags.noclip &&
818 originalDock != scr->workspaces[scr->current_workspace]->clip)
819 allDocks[ i++ ] = scr->workspaces[scr->current_workspace]->clip;
821 for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */
822 allDocks[ i ] = NULL;
824 wins[0] = icon->core->window;
825 wins[1] = scr->dock_shadow;
826 XRestackWindows(dpy, wins, 2);
827 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
828 if (superfluous) {
829 if (icon->pixmap != None)
830 ghost = MakeGhostIcon(scr, icon->pixmap);
831 else
832 ghost = MakeGhostIcon(scr, icon->core->window);
833 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
834 XClearWindow(dpy, scr->dock_shadow);
836 if (ondock)
837 XMapWindow(dpy, scr->dock_shadow);
839 while (1) {
840 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
841 | ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
842 switch (ev.type) {
843 case Expose:
844 WMHandleEvent(&ev);
845 break;
847 case EnterNotify:
848 /* It means the cursor moved so fast that it entered
849 * something else (if moving slowly, it would have
850 * stayed in the appIcon that is being moved. Ignore
851 * such "spurious" EnterNotifiy's */
852 break;
854 case MotionNotify:
855 hasMoved = True;
856 if (!grabbed) {
857 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
858 || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
859 XChangeActivePointerGrab(dpy, ButtonMotionMask
860 | ButtonReleaseMask | ButtonPressMask,
861 wPreferences.cursor[WCUR_MOVE], CurrentTime);
862 grabbed = 1;
863 } else {
864 break;
868 if (omnipresent && !showed_all_clips) {
869 int i;
870 for (i = 0; i < scr->workspace_count; i++) {
871 if (i == scr->current_workspace)
872 continue;
874 wDockShowIcons(scr->workspaces[i]->clip);
875 /* Note: if dock is collapsed (for instance, because it
876 auto-collapses), its icons still won't show up */
878 showed_all_clips = True; /* To prevent flickering */
881 x = ev.xmotion.x_root - ofs_x;
882 y = ev.xmotion.y_root - ofs_y;
883 wAppIconMove(aicon, x, y);
885 WDock *theNewDock = NULL;
886 if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock || originalDock == NULL) {
887 for (i = 0; dockable && i < scr->drawer_count + 2; i++) {
888 WDock *theDock = allDocks[i];
889 if (theDock == NULL)
890 break;
891 if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) {
892 theNewDock = theDock;
893 break;
896 if (originalDock != NULL && theNewDock == NULL &&
897 (aicon->launching || aicon->lock || aicon->running)) {
898 /* In those cases, stay in lastDock if no dock really wants us */
899 theNewDock = lastDock;
902 if (lastDock != NULL && lastDock != theNewDock) {
903 /* Leave lastDock in the state we found it */
904 if (lastDock->type == WM_DRAWER) {
905 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
907 if (collapsed) {
908 lastDock->collapsed = 1;
909 wDockHideIcons(lastDock);
910 collapsed = False;
912 if (lastDock->auto_raise_lower) {
913 wDockLower(lastDock);
916 if (theNewDock != NULL) {
917 if (lastDock != theNewDock) {
918 collapsed = theNewDock->collapsed;
919 if (collapsed) {
920 theNewDock->collapsed = 0;
921 wDockShowIcons(theNewDock);
923 if (theNewDock->auto_raise_lower) {
924 wDockRaise(theNewDock);
925 /* And raise the moving tile above it */
926 wRaiseFrame(aicon->icon->core);
928 lastDock = theNewDock;
931 shad_x = lastDock->x_pos + ix*wPreferences.icon_size;
932 shad_y = lastDock->y_pos + iy*wPreferences.icon_size;
934 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
936 if (!ondock) {
937 XMapWindow(dpy, scr->dock_shadow);
939 ondock = 1;
940 } else {
941 lastDock = theNewDock; // i.e., NULL
942 if (ondock) {
943 XUnmapWindow(dpy, scr->dock_shadow);
945 * Leaving that weird comment for now.
946 * But if we see no gap, there is no need to fill one!
947 * We could test ondock first and the lastDock to NULL afterwards
948 if (lastDock_before_it_was_null->type == WM_DRAWER) {
949 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
950 } */
953 ondock = 0;
955 break;
957 case ButtonPress:
958 break;
960 case ButtonRelease:
961 if (ev.xbutton.button != clickButton)
962 break;
963 XUngrabPointer(dpy, CurrentTime);
965 Bool docked = False;
966 if (ondock) {
967 slide_window(icon->core->window, x, y, shad_x, shad_y);
968 XUnmapWindow(dpy, scr->dock_shadow);
969 if (originalDock == NULL) { // docking an undocked appicon
970 docked = wDockAttachIcon(lastDock, aicon, ix, iy, False);
971 if (!docked) {
972 /* AppIcon got rejected (happens only when we can't get the
973 command for that appicon, and the user cancels the
974 wInputDialog asking for one). Make the rejection obvious by
975 sliding the icon to its old position */
976 if (lastDock->type == WM_DRAWER) {
977 // Also fill the gap left in the drawer
978 wDrawerFillTheGap(lastDock, aicon, False);
980 slide_window(icon->core->window, x, y, oldX, oldY);
983 else { // moving a docked appicon to a dock
984 if (originalDock == lastDock) {
985 docked = True;
986 wDockReattachIcon(originalDock, aicon, ix, iy);
988 else {
989 docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy);
990 if (!docked) {
991 /* Possible scenario: user moved an auto-attracted appicon
992 from the clip to the dock, and cancelled the wInputDialog
993 asking for a command */
994 if (lastDock->type == WM_DRAWER) {
995 wDrawerFillTheGap(lastDock, aicon, False);
997 /* If aicon comes from a drawer, make some room to reattach it */
998 if (originalDock->type == WM_DRAWER) {
999 WAppIcon *aiconsToShift[ originalDock->icon_count ];
1000 int j = 0;
1002 for (i = 0; i < originalDock->max_icons; i++) {
1003 WAppIcon *ai = originalDock->icon_array[ i ];
1004 if (ai && ai != aicon &&
1005 abs(ai->xindex) >= abs(aicon->xindex))
1006 aiconsToShift[j++] = ai;
1008 if (j != originalDock->icon_count - abs(aicon->xindex) - 1)
1009 // Trust this never happens?
1010 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1011 j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex);
1012 wSlideAppicons(aiconsToShift, j, originalDock->on_right_side);
1013 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1016 slide_window(icon->core->window, x, y, oldX, oldY);
1017 wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex);
1019 else {
1020 if (originalDock->auto_collapse && !originalDock->collapsed) {
1021 originalDock->collapsed = 1;
1022 wDockHideIcons(originalDock);
1024 if (originalDock->auto_raise_lower)
1025 wDockLower(originalDock);
1029 // No matter what happened above, check to lower lastDock
1030 // Don't see why I commented out the following 2 lines
1031 /* if (lastDock->auto_raise_lower)
1032 wDockLower(lastDock); */
1033 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1034 * collapsed, so that wHandleAppIconMove doesn't collapse it
1035 * right away (the timer will take care of it) */
1036 if (lastDock->auto_collapse)
1037 collapsed = 0;
1039 else {
1040 if (originalDock != NULL) { /* Detaching a docked appicon */
1041 if (superfluous) {
1042 if (!aicon->running && !wPreferences.no_animations) {
1043 /* We need to deselect it, even if is deselected in
1044 * wDockDetach(), because else DoKaboom() will fail.
1046 if (aicon->icon->selected)
1047 wIconSelect(aicon->icon);
1048 DoKaboom(scr, aicon->icon->core->window, x, y);
1051 wDockDetach(originalDock, aicon);
1052 if (originalDock->auto_collapse && !originalDock->collapsed) {
1053 originalDock->collapsed = 1;
1054 wDockHideIcons(originalDock);
1056 if (originalDock->auto_raise_lower)
1057 wDockLower(originalDock);
1060 // Can't remember why the icon hiding is better done above than below (commented out)
1061 // Also, lastDock is quite different from originalDock
1063 if (collapsed) {
1064 lastDock->collapsed = 1;
1065 wDockHideIcons(lastDock);
1066 collapsed = 0;
1069 if (superfluous) {
1070 if (ghost != None)
1071 XFreePixmap(dpy, ghost);
1072 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
1074 if (showed_all_clips) {
1075 int i;
1076 for (i = 0; i < scr->workspace_count; i++) {
1077 if (i == scr->current_workspace)
1078 continue;
1080 wDockHideIcons(scr->workspaces[i]->clip);
1083 if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked))
1084 /* Need to rearrange unless moving from dock to dock */
1085 wArrangeIcons(scr, True);
1086 return hasMoved;
1091 /* This function save the application icon and store the path in the Dictionary */
1092 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const char *wm_class)
1094 WMPropList *dict = w_global.domain.window_attr->dictionary;
1095 WMPropList *adict, *key, *iconk;
1096 WMPropList *val;
1097 char *tmp;
1099 tmp = get_name_for_instance_class(wm_instance, wm_class);
1100 key = WMCreatePLString(tmp);
1101 wfree(tmp);
1103 adict = WMGetFromPLDictionary(dict, key);
1104 iconk = WMCreatePLString("Icon");
1106 if (adict) {
1107 val = WMGetFromPLDictionary(adict, iconk);
1108 } else {
1109 /* no dictionary for app, so create one */
1110 adict = WMCreatePLDictionary(NULL, NULL);
1111 WMPutInPLDictionary(dict, key, adict);
1112 WMReleasePropList(adict);
1113 val = NULL;
1116 if (!val) {
1117 val = WMCreatePLString(iconPath);
1118 WMPutInPLDictionary(adict, iconk, val);
1119 WMReleasePropList(val);
1120 } else {
1121 val = NULL;
1124 WMReleasePropList(key);
1125 WMReleasePropList(iconk);
1127 if (val && !wPreferences.flags.noupdates)
1128 UpdateDomainFile(w_global.domain.window_attr);
1131 static WAppIcon *findDockIconFor(WDock *dock, Window main_window)
1133 WAppIcon *aicon = NULL;
1135 aicon = wDockFindIconForWindow(dock, main_window);
1136 if (!aicon) {
1137 wDockTrackWindowLaunch(dock, main_window);
1138 aicon = wDockFindIconForWindow(dock, main_window);
1140 return aicon;
1143 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window)
1145 WScreen *scr = wwin->screen_ptr;
1146 wapp->app_icon = NULL;
1148 if (scr->last_dock)
1149 wapp->app_icon = findDockIconFor(scr->last_dock, main_window);
1151 /* check main dock if we did not find it in last dock */
1152 if (!wapp->app_icon && scr->dock)
1153 wapp->app_icon = findDockIconFor(scr->dock, main_window);
1155 /* check clips */
1156 if (!wapp->app_icon) {
1157 int i;
1158 for (i = 0; i < scr->workspace_count; i++) {
1159 WDock *dock = scr->workspaces[i]->clip;
1161 if (dock)
1162 wapp->app_icon = findDockIconFor(dock, main_window);
1164 if (wapp->app_icon)
1165 break;
1169 /* Finally check drawers */
1170 if (!wapp->app_icon) {
1171 WDrawerChain *dc;
1172 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
1173 wapp->app_icon = findDockIconFor(dc->adrawer, main_window);
1174 if (wapp->app_icon)
1175 break;
1179 /* If created, then set some flags */
1180 if (wapp->app_icon && !WFLAGP(wapp->main_window_desc, no_appicon)) {
1181 WWindow *mainw = wapp->main_window_desc;
1183 wapp->app_icon->running = 1;
1184 wapp->app_icon->icon->owner = mainw;
1185 if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
1186 wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
1188 /* Update the icon images */
1189 wIconUpdate(wapp->app_icon->icon);
1191 /* Paint it */
1192 wAppIconPaint(wapp->app_icon);
1193 save_appicon(wapp->app_icon, True);
1197 /* Add the appicon to the appiconlist */
1198 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon)
1200 appicon->prev = NULL;
1201 appicon->next = scr->app_icon_list;
1202 if (scr->app_icon_list)
1203 scr->app_icon_list->prev = appicon;
1205 scr->app_icon_list = appicon;
1208 /* Remove the appicon from the appiconlist */
1209 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon)
1211 if (appicon == scr->app_icon_list) {
1212 if (appicon->next)
1213 appicon->next->prev = NULL;
1214 scr->app_icon_list = appicon->next;
1215 } else {
1216 if (appicon->next)
1217 appicon->next->prev = appicon->prev;
1218 if (appicon->prev)
1219 appicon->prev->next = appicon->next;
1222 appicon->prev = NULL;
1223 appicon->next = NULL;
1226 /* Return the AppIcon associated with a given (Xlib) Window. */
1227 WAppIcon *wAppIconFor(Window window)
1229 WObjDescriptor *desc;
1231 if (window == None)
1232 return NULL;
1234 if (XFindContext(dpy, window, w_global.context.client_win, (XPointer *) & desc) == XCNOENT)
1235 return NULL;
1237 if (desc->parent_type == WCLASS_APPICON || desc->parent_type == WCLASS_DOCK_ICON)
1238 return desc->parent;
1240 return NULL;