Add option to merge the workspace-switching functionality into the dock
[wmaker-crm.git] / src / appicon.c
blobb719d05d15b6a9e920aecea1034fb0253107c077
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 <string.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <errno.h>
33 #include "WindowMaker.h"
34 #include "window.h"
35 #include "icon.h"
36 #include "application.h"
37 #include "appicon.h"
38 #include "actions.h"
39 #include "stacking.h"
40 #include "dock.h"
41 #include "main.h"
42 #include "defaults.h"
43 #include "workspace.h"
44 #include "superfluous.h"
45 #include "menu.h"
46 #include "framewin.h"
47 #include "dialog.h"
48 #include "xinerama.h"
49 #include "client.h"
50 #ifdef XDND
51 #include "xdnd.h"
52 #endif
55 * icon_file for the dock is got from the preferences file by
56 * using the classname/instancename
59 /**** Global variables ****/
60 extern Cursor wCursor[WCUR_LAST];
61 extern WPreferences wPreferences;
62 extern WDDomain *WDWindowAttributes;
63 extern XContext wWinContext;
65 #define MOD_MASK wPreferences.modifier_mask
66 #define ICON_SIZE wPreferences.icon_size
68 void appIconMouseDown(WObjDescriptor * desc, XEvent * event);
69 static void iconDblClick(WObjDescriptor * desc, XEvent * event);
70 static void iconExpose(WObjDescriptor * desc, XEvent * event);
71 static void wApplicationSaveIconPathFor(char *iconPath, char *wm_instance, char *wm_class);
72 static WAppIcon *wAppIconCreate(WWindow * leader_win);
73 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon);
74 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon);
75 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window);
77 /* This function is used if the application is a .app. It checks if it has an icon in it
78 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
80 void wApplicationExtractDirPackIcon(WScreen * scr, char *path, char *wm_instance, char *wm_class)
82 char *iconPath = NULL;
83 char *tmp = NULL;
85 if (strstr(path, ".app")) {
86 tmp = wmalloc(strlen(path) + 16);
88 if (scr->flags.supports_tiff) {
89 strcpy(tmp, path);
90 strcat(tmp, ".tiff");
91 if (access(tmp, R_OK) == 0)
92 iconPath = tmp;
95 if (!iconPath) {
96 strcpy(tmp, path);
97 strcat(tmp, ".xpm");
98 if (access(tmp, R_OK) == 0)
99 iconPath = tmp;
102 if (!iconPath)
103 wfree(tmp);
105 if (iconPath) {
106 wApplicationSaveIconPathFor(iconPath, wm_instance, wm_class);
107 wfree(iconPath);
112 WAppIcon *wAppIconCreateForDock(WScreen *scr, char *command, char *wm_instance, char *wm_class, int tile)
114 WAppIcon *aicon;
116 aicon = wmalloc(sizeof(WAppIcon));
117 wretain(aicon);
118 aicon->yindex = -1;
119 aicon->xindex = -1;
121 add_to_appicon_list(scr, aicon);
123 if (command)
124 aicon->command = wstrdup(command);
126 if (wm_class)
127 aicon->wm_class = wstrdup(wm_class);
129 if (wm_instance)
130 aicon->wm_instance = wstrdup(wm_instance);
132 if (strcmp(wm_class, "WMDock") == 0 && wPreferences.flags.clip_merged_in_dock)
133 tile = TILE_CLIP;
134 aicon->icon = icon_create_for_dock(scr, command, wm_instance, wm_class, tile);
136 #ifdef XDND
137 wXDNDMakeAwareness(aicon->icon->core->window);
138 #endif
140 /* will be overriden by dock */
141 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
142 aicon->icon->core->descriptor.handle_expose = iconExpose;
143 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
144 aicon->icon->core->descriptor.parent = aicon;
145 AddToStackList(aicon->icon->core);
147 return aicon;
150 void create_appicon_for_application(WApplication *wapp, WWindow *wwin)
152 /* Try to create an icon from the dock or clip */
153 create_appicon_from_dock(wwin, wapp, wapp->main_window);
155 /* If app_icon was not found, create it */
156 if (!wapp->app_icon) {
157 /* Create the icon */
158 wapp->app_icon = wAppIconCreate(wapp->main_window_desc);
159 wIconUpdate(wapp->app_icon->icon, NULL);
161 /* Now, paint the icon */
162 if (!WFLAGP(wapp->main_window_desc, no_appicon))
163 paint_app_icon(wapp);
166 /* Save the app_icon in a file */
167 save_appicon(wapp->app_icon, False);
170 void unpaint_app_icon(WApplication *wapp)
172 WAppIcon *aicon;
173 WScreen *scr;
174 WDock *clip;
176 if (!wapp || !wapp->app_icon)
177 return;
179 aicon = wapp->app_icon;
181 /* If the icon is docked, don't continue */
182 if (aicon->docked)
183 return;
185 scr = wapp->main_window_desc->screen_ptr;
186 clip = scr->workspaces[scr->current_workspace]->clip;
188 if (!clip || !aicon->attracted || !clip->collapsed)
189 XUnmapWindow(dpy, aicon->icon->core->window);
191 /* We want to avoid having it on the list because otherwise
192 * there will be a hole when the icons are arranged with
193 * wArrangeIcons() */
194 remove_from_appicon_list(scr, aicon);
196 if (wPreferences.auto_arrange_icons && !aicon->attracted)
197 wArrangeIcons(scr, True);
200 void paint_app_icon(WApplication *wapp)
202 WIcon *icon;
203 WScreen *scr = wapp->main_window_desc->screen_ptr;
204 WDock *attracting_dock;
205 int x = 0, y = 0;
206 Bool update_icon = False;
208 if (!wapp || !wapp->app_icon)
209 return;
211 icon = wapp->app_icon->icon;
212 wapp->app_icon->main_window = wapp->main_window;
214 /* If the icon is docked, don't continue */
215 if (wapp->app_icon->docked)
216 return;
218 attracting_dock = scr->attracting_drawer != NULL ?
219 scr->attracting_drawer :
220 scr->workspaces[scr->current_workspace]->clip;
221 if (attracting_dock && attracting_dock->attract_icons &&
222 wDockFindFreeSlot(attracting_dock, &x, &y)) {
223 wapp->app_icon->attracted = 1;
224 if (!icon->shadowed) {
225 icon->shadowed = 1;
226 update_icon = True;
228 wDockAttachIcon(attracting_dock, wapp->app_icon, x, y, update_icon);
229 } else {
230 /* We must know if the icon is painted in the screen,
231 * because if painted, then PlaceIcon will return the next
232 * space on the screen, and the icon will move */
233 if (wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL) {
234 PlaceIcon(scr, &x, &y, wGetHeadForWindow(wapp->main_window_desc));
235 wAppIconMove(wapp->app_icon, x, y);
236 wLowerFrame(icon->core);
240 /* If we want appicon (no_appicon is not set) and the icon is not
241 * in the appicon_list, we must add it. Else, we want to avoid
242 * having it on the list */
243 if (!WFLAGP(wapp->main_window_desc, no_appicon) &&
244 wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL)
245 add_to_appicon_list(scr, wapp->app_icon);
247 if (!attracting_dock || !wapp->app_icon->attracted || !attracting_dock->collapsed)
248 XMapWindow(dpy, icon->core->window);
250 if (wPreferences.auto_arrange_icons && !wapp->app_icon->attracted)
251 wArrangeIcons(scr, True);
254 void removeAppIconFor(WApplication *wapp)
256 if (!wapp->app_icon)
257 return;
259 if (wPreferences.highlight_active_app)
260 wIconSetHighlited(wapp->app_icon->icon, False);
261 if (wapp->app_icon->docked && !wapp->app_icon->attracted) {
262 wapp->app_icon->running = 0;
263 /* since we keep it, we don't care if it was attracted or not */
264 wapp->app_icon->attracted = 0;
265 wapp->app_icon->icon->shadowed = 0;
266 wapp->app_icon->main_window = None;
267 wapp->app_icon->pid = 0;
268 wapp->app_icon->icon->owner = NULL;
269 wapp->app_icon->icon->icon_win = None;
271 /* Set the icon image */
272 set_icon_image_from_database(wapp->app_icon->icon, wapp->app_icon->wm_instance,
273 wapp->app_icon->wm_class, wapp->app_icon->command);
275 /* Paint it */
276 wAppIconPaint(wapp->app_icon);
277 } else if (wapp->app_icon->docked) {
278 wapp->app_icon->running = 0;
279 if (wapp->app_icon->dock->type == WM_DRAWER) {
280 wDrawerFillTheGap(wapp->app_icon->dock, wapp->app_icon, True);
282 wDockDetach(wapp->app_icon->dock, wapp->app_icon);
283 } else {
284 wAppIconDestroy(wapp->app_icon);
287 wapp->app_icon = NULL;
289 if (wPreferences.auto_arrange_icons)
290 wArrangeIcons(wapp->main_window_desc->screen_ptr, True);
293 static WAppIcon *wAppIconCreate(WWindow *leader_win)
295 WAppIcon *aicon;
297 aicon = wmalloc(sizeof(WAppIcon));
298 wretain(aicon);
299 aicon->yindex = -1;
300 aicon->xindex = -1;
301 aicon->prev = NULL;
302 aicon->next = NULL;
304 if (leader_win->wm_class)
305 aicon->wm_class = wstrdup(leader_win->wm_class);
307 if (leader_win->wm_instance)
308 aicon->wm_instance = wstrdup(leader_win->wm_instance);
310 aicon->icon = icon_create_for_wwindow(leader_win);
311 #ifdef XDND
312 wXDNDMakeAwareness(aicon->icon->core->window);
313 #endif
315 /* will be overriden if docked */
316 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
317 aicon->icon->core->descriptor.handle_expose = iconExpose;
318 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
319 aicon->icon->core->descriptor.parent = aicon;
320 AddToStackList(aicon->icon->core);
321 aicon->icon->show_title = 0;
323 return aicon;
326 void wAppIconDestroy(WAppIcon * aicon)
328 WScreen *scr = aicon->icon->core->screen_ptr;
330 RemoveFromStackList(aicon->icon->core);
331 wIconDestroy(aicon->icon);
332 if (aicon->command)
333 wfree(aicon->command);
334 #ifdef XDND
335 if (aicon->dnd_command)
336 wfree(aicon->dnd_command);
337 #endif
338 if (aicon->wm_instance)
339 wfree(aicon->wm_instance);
341 if (aicon->wm_class)
342 wfree(aicon->wm_class);
344 remove_from_appicon_list(scr, aicon);
346 aicon->destroyed = 1;
347 wrelease(aicon);
350 static void drawCorner(WIcon * icon)
352 WScreen *scr = icon->core->screen_ptr;
353 XPoint points[3];
355 points[0].x = 1;
356 points[0].y = 1;
357 points[1].x = 12;
358 points[1].y = 1;
359 points[2].x = 1;
360 points[2].y = 12;
361 XFillPolygon(dpy, icon->core->window, scr->icon_title_texture->normal_gc,
362 points, 3, Convex, CoordModeOrigin);
363 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 0, 12);
364 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 12, 0);
367 void wAppIconMove(WAppIcon * aicon, int x, int y)
369 XMoveWindow(dpy, aicon->icon->core->window, x, y);
370 aicon->x_pos = x;
371 aicon->y_pos = y;
374 #ifdef WS_INDICATOR
375 static void updateDockNumbers(WScreen * scr)
377 int length;
378 char *ws_numbers;
379 WAppIcon *dicon = scr->dock->icon_array[0];
381 ws_numbers = wmalloc(20);
382 snprintf(ws_numbers, 20, "%i [ %i ]", scr->current_workspace + 1, ((scr->current_workspace / 10) + 1));
383 length = strlen(ws_numbers);
385 XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, WMFontHeight(scr->icon_title_font) + 1, False);
387 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->black,
388 scr->icon_title_font, 4, 3, ws_numbers, length);
390 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->white,
391 scr->icon_title_font, 3, 2, ws_numbers, length);
393 wfree(ws_numbers);
395 #endif /* WS_INDICATOR */
397 void wAppIconPaint(WAppIcon *aicon)
399 WApplication *wapp;
400 WScreen *scr = aicon->icon->core->screen_ptr;
402 if (aicon->icon->owner)
403 wapp = wApplicationOf(aicon->icon->owner->main_window);
404 else
405 wapp = NULL;
407 wIconPaint(aicon->icon);
409 # ifdef WS_INDICATOR
410 if (aicon->docked && scr->dock && scr->dock == aicon->dock && aicon->yindex == 0)
411 updateDockNumbers(scr);
412 # endif
413 if (scr->dock_dots && aicon->docked && !aicon->running && aicon->command != NULL) {
414 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
415 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
416 XCopyArea(dpy, scr->dock_dots->image, aicon->icon->core->window,
417 scr->copy_gc, 0, 0, scr->dock_dots->width, scr->dock_dots->height, 0, 0);
419 #ifdef HIDDENDOT
420 if (wapp && wapp->flags.hidden) {
421 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
422 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
423 XCopyArea(dpy, scr->dock_dots->image,
424 aicon->icon->core->window, scr->copy_gc, 0, 0, 7, scr->dock_dots->height, 0, 0);
426 #endif /* HIDDENDOT */
428 if (aicon->omnipresent)
429 drawCorner(aicon->icon);
431 XSetClipMask(dpy, scr->copy_gc, None);
432 if (aicon->launching)
433 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
434 0, 0, wPreferences.icon_size, wPreferences.icon_size);
437 /* Save the application icon, if it's a dockapp then use it with dock = True */
438 void save_appicon(WAppIcon *aicon, Bool dock)
440 char *path;
442 if (!aicon)
443 return;
445 if (dock && (!aicon->docked || aicon->attracted))
446 return;
448 path = wIconStore(aicon->icon);
449 if (!path)
450 return;
452 wApplicationSaveIconPathFor(path, aicon->wm_instance, aicon->wm_class);
453 wfree(path);
456 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
458 /* main_window may not have the full command line; try to find one which does */
459 static void relaunchApplication(WApplication *wapp)
461 WScreen *scr;
462 WWindow *wlist, *next;
464 scr = wapp->main_window_desc->screen_ptr;
465 wlist = scr->focused_window;
466 if (! wlist)
467 return;
469 while (wlist->prev)
470 wlist = wlist->prev;
472 while (wlist) {
473 next = wlist->next;
475 if (wlist->main_window == wapp->main_window) {
476 if (RelaunchWindow(wlist))
477 return;
480 wlist = next;
484 static void relaunchCallback(WMenu * menu, WMenuEntry * entry)
486 WApplication *wapp = (WApplication *) entry->clientdata;
488 relaunchApplication(wapp);
491 static void hideCallback(WMenu * menu, WMenuEntry * entry)
493 WApplication *wapp = (WApplication *) entry->clientdata;
495 if (wapp->flags.hidden) {
496 wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
497 wUnhideApplication(wapp, False, False);
498 } else {
499 wHideApplication(wapp);
503 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
505 WApplication *wapp = (WApplication *) entry->clientdata;
507 wUnhideApplication(wapp, False, True);
510 static void setIconCallback(WMenu *menu, WMenuEntry *entry)
512 WAppIcon *icon = ((WApplication *) entry->clientdata)->app_icon;
513 char *file = NULL;
514 WScreen *scr;
515 int result;
517 assert(icon != NULL);
519 if (icon->editing)
520 return;
522 icon->editing = 1;
523 scr = icon->icon->core->screen_ptr;
525 wretain(icon);
527 result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
529 if (result && !icon->destroyed) {
530 if (file && *file == 0) {
531 wfree(file);
532 file = NULL;
534 if (!wIconChangeImageFile(icon->icon, file)) {
535 wMessageDialog(scr, _("Error"),
536 _("Could not open specified icon file"), _("OK"), NULL, NULL);
537 } else {
538 wDefaultChangeIcon(scr, icon->wm_instance, icon->wm_class, file);
539 wAppIconPaint(icon);
541 if (file)
542 wfree(file);
544 icon->editing = 0;
545 wrelease(icon);
548 static void killCallback(WMenu * menu, WMenuEntry * entry)
550 WApplication *wapp = (WApplication *) entry->clientdata;
551 WFakeGroupLeader *fPtr;
552 char *buffer;
553 char *shortname;
554 char *basename(const char *shortname);
556 if (!WCHECK_STATE(WSTATE_NORMAL))
557 return;
559 WCHANGE_STATE(WSTATE_MODAL);
561 assert(entry->clientdata != NULL);
563 shortname = basename(wapp->app_icon->wm_instance);
565 buffer = wstrconcat(wapp->app_icon ? shortname : NULL,
566 _(" will be forcibly closed.\n"
567 "Any unsaved changes will be lost.\n" "Please confirm."));
569 fPtr = wapp->main_window_desc->fake_group;
571 wretain(wapp->main_window_desc);
572 if (wPreferences.dont_confirm_kill
573 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
574 buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
575 if (fPtr != NULL) {
576 WWindow *wwin, *twin;
578 wwin = wapp->main_window_desc->screen_ptr->focused_window;
579 while (wwin) {
580 twin = wwin->prev;
581 if (wwin->fake_group == fPtr)
582 wClientKill(wwin);
583 wwin = twin;
585 } else if (!wapp->main_window_desc->flags.destroyed) {
586 wClientKill(wapp->main_window_desc);
589 wrelease(wapp->main_window_desc);
590 wfree(buffer);
591 WCHANGE_STATE(WSTATE_NORMAL);
594 static WMenu *createApplicationMenu(WScreen *scr)
596 WMenu *menu;
598 menu = wMenuCreate(scr, NULL, False);
599 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
600 wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
601 wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
602 wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
603 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
605 return menu;
608 static void openApplicationMenu(WApplication * wapp, int x, int y)
610 WMenu *menu;
611 WScreen *scr = wapp->main_window_desc->screen_ptr;
612 int i;
614 if (!scr->icon_menu) {
615 scr->icon_menu = createApplicationMenu(scr);
616 wfree(scr->icon_menu->entries[1]->text);
619 menu = scr->icon_menu;
621 if (wapp->flags.hidden)
622 menu->entries[1]->text = _("Unhide");
623 else
624 menu->entries[1]->text = _("Hide");
626 menu->flags.realized = 0;
627 wMenuRealize(menu);
629 x -= menu->frame->core->width / 2;
630 if (x + menu->frame->core->width > scr->scr_width)
631 x = scr->scr_width - menu->frame->core->width;
633 if (x < 0)
634 x = 0;
636 /* set client data */
637 for (i = 0; i < menu->entry_no; i++)
638 menu->entries[i]->clientdata = wapp;
640 wMenuMapAt(menu, x, y, False);
643 /******************************************************************/
645 static void iconExpose(WObjDescriptor *desc, XEvent *event)
647 wAppIconPaint(desc->parent);
650 static void iconDblClick(WObjDescriptor *desc, XEvent *event)
652 WAppIcon *aicon = desc->parent;
653 WApplication *wapp;
654 WScreen *scr = aicon->icon->core->screen_ptr;
655 int unhideHere;
657 assert(aicon->icon->owner != NULL);
659 wapp = wApplicationOf(aicon->icon->owner->main_window);
661 if (event->xbutton.state & ControlMask) {
662 relaunchApplication(wapp);
663 return;
666 unhideHere = (event->xbutton.state & ShiftMask);
667 /* go to the last workspace that the user worked on the app */
668 if (!unhideHere && wapp->last_workspace != scr->current_workspace)
669 wWorkspaceChange(scr, wapp->last_workspace);
671 wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
673 if (event->xbutton.state & MOD_MASK)
674 wHideOtherApplications(aicon->icon->owner);
677 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
679 WAppIcon *aicon = desc->parent;
680 WScreen *scr = aicon->icon->core->screen_ptr;
681 Bool hasMoved;
683 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
684 return;
686 if (IsDoubleClick(scr, event)) {
687 /* Middle or right mouse actions were handled on first click */
688 if (event->xbutton.button == Button1)
689 iconDblClick(desc, event);
690 return;
693 if (event->xbutton.button == Button2) {
694 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
696 if (wapp)
697 relaunchApplication(wapp);
699 return;
702 if (event->xbutton.button == Button3) {
703 WObjDescriptor *desc;
704 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
706 if (!wapp)
707 return;
709 if (event->xbutton.send_event &&
710 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
711 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
712 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
713 wwarning("pointer grab failed for appicon menu");
714 return;
717 openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
719 /* allow drag select of menu */
720 desc = &scr->icon_menu->menu->descriptor;
721 event->xbutton.send_event = True;
722 (*desc->handle_mousedown) (desc, event);
723 return;
726 hasMoved = wHandleAppIconMove(aicon, event);
727 if (wPreferences.single_click && !hasMoved && aicon->dock != NULL)
729 iconDblClick(desc, event);
733 Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event)
735 WIcon *icon = aicon->icon;
736 WScreen *scr = icon->core->screen_ptr;
737 WDock *originalDock = aicon->dock; /* can be NULL */
738 WDock *lastDock = originalDock;
739 WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */
740 WDrawerChain *dc;
741 Bool done = False, dockable, ondock;
742 Bool grabbed = False;
743 Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
744 int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */
745 int omnipresent = aicon->omnipresent; /* this must be cached */
746 Bool showed_all_clips = False;
748 int clickButton = event->xbutton.button;
749 Pixmap ghost = None;
750 Window wins[2]; /* Managing shadow window */
751 XEvent ev;
753 int x = aicon->x_pos, y = aicon->y_pos;
754 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
755 int shad_x = x, shad_y = y;
756 int ix = aicon->xindex, iy = aicon->yindex;
757 int i;
758 int oldX = x;
759 int oldY = y;
760 Bool hasMoved = False;
762 if (wPreferences.flags.noupdates && originalDock != NULL)
763 return False;
765 if (!(event->xbutton.state & MOD_MASK))
766 wRaiseFrame(icon->core);
767 else {
768 /* If Mod is pressed for an docked appicon, assume it is to undock it,
769 * so don't lower it */
770 if (originalDock == NULL)
771 wLowerFrame(icon->core);
774 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
775 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
776 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
777 wwarning("Pointer grab failed in wHandleAppIconMove");
780 if (originalDock != NULL) {
781 dockable = True;
782 ondock = True;
784 else {
785 ondock = False;
786 if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer)
787 dockable = 0;
788 else
789 dockable = canBeDocked(icon->owner);
792 /* We try the various docks in that order:
793 * - First, the dock the appicon comes from, if any
794 * - Then, the drawers
795 * - Then, the "dock" (WM_DOCK)
796 * - Finally, the clip
798 i = 0;
799 if (originalDock != NULL)
800 allDocks[ i++ ] = originalDock;
801 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
802 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
803 if (dc->adrawer != originalDock)
804 allDocks[ i++ ] = dc->adrawer;
806 if (!wPreferences.flags.nodock && scr->dock != originalDock)
807 allDocks[ i++ ] = scr->dock;
808 if (!wPreferences.flags.noclip &&
809 originalDock != scr->workspaces[scr->current_workspace]->clip)
810 allDocks[ i++ ] = scr->workspaces[scr->current_workspace]->clip;
811 for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */
812 allDocks[ i ] = NULL;
814 wins[0] = icon->core->window;
815 wins[1] = scr->dock_shadow;
816 XRestackWindows(dpy, wins, 2);
817 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
818 if (superfluous) {
819 if (icon->pixmap != None)
820 ghost = MakeGhostIcon(scr, icon->pixmap);
821 else
822 ghost = MakeGhostIcon(scr, icon->core->window);
823 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
824 XClearWindow(dpy, scr->dock_shadow);
826 if (ondock)
827 XMapWindow(dpy, scr->dock_shadow);
829 while (!done) {
830 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
831 | ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
832 switch (ev.type) {
833 case Expose:
834 WMHandleEvent(&ev);
835 break;
837 case EnterNotify:
838 /* It means the cursor moved so fast that it entered
839 * something else (if moving slowly, it would have
840 * stayed in the appIcon that is being moved. Ignore
841 * such "spurious" EnterNotifiy's */
842 break;
844 case MotionNotify:
845 hasMoved = True;
846 if (!grabbed) {
847 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
848 || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
849 XChangeActivePointerGrab(dpy, ButtonMotionMask
850 | ButtonReleaseMask | ButtonPressMask,
851 wCursor[WCUR_MOVE], CurrentTime);
852 grabbed = 1;
853 } else {
854 break;
858 if (omnipresent && !showed_all_clips) {
859 int i;
860 for (i = 0; i < scr->workspace_count; i++) {
861 if (i == scr->current_workspace)
862 continue;
863 wDockShowIcons(scr->workspaces[i]->clip);
864 /* Note: if dock is collapsed (for instance, because it
865 auto-collapses), its icons still won't show up */
867 showed_all_clips = True; /* To prevent flickering */
870 x = ev.xmotion.x_root - ofs_x;
871 y = ev.xmotion.y_root - ofs_y;
872 wAppIconMove(aicon, x, y);
874 WDock *theNewDock = NULL;
875 if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock) {
876 for (i = 0; dockable && i < scr->drawer_count + 2; i++) {
877 WDock *theDock = allDocks[i];
878 if (theDock == NULL)
879 break;
880 if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) {
881 theNewDock = theDock;
882 break;
885 if (originalDock != NULL && theNewDock == NULL &&
886 (aicon->launching || aicon->lock || aicon->running)) {
887 /* In those cases, stay in lastDock if no dock really wants us */
888 theNewDock = lastDock;
891 if (lastDock != NULL && lastDock != theNewDock) {
892 /* Leave lastDock in the state we found it */
893 if (lastDock->type == WM_DRAWER) {
894 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
896 if (collapsed) {
897 lastDock->collapsed = 1;
898 wDockHideIcons(lastDock);
899 collapsed = False;
901 if (lastDock->auto_raise_lower) {
902 wDockLower(lastDock);
905 if (theNewDock != NULL) {
906 if (lastDock != theNewDock) {
907 collapsed = theNewDock->collapsed;
908 if (collapsed) {
909 theNewDock->collapsed = 0;
910 wDockShowIcons(theNewDock);
912 if (theNewDock->auto_raise_lower) {
913 wDockRaise(theNewDock);
914 /* And raise the moving tile above it */
915 wRaiseFrame(aicon->icon->core);
917 lastDock = theNewDock;
920 shad_x = lastDock->x_pos + ix*wPreferences.icon_size;
921 shad_y = lastDock->y_pos + iy*wPreferences.icon_size;
923 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
925 if (!ondock) {
926 XMapWindow(dpy, scr->dock_shadow);
928 ondock = 1;
929 } else {
930 lastDock = theNewDock; // i.e., NULL
931 if (ondock) {
932 XUnmapWindow(dpy, scr->dock_shadow);
934 * Leaving that weird comment for now.
935 * But if we see no gap, there is no need to fill one!
936 * We could test ondock first and the lastDock to NULL afterwards
937 if (lastDock_before_it_was_null->type == WM_DRAWER) {
938 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
939 } */
942 ondock = 0;
944 break;
946 case ButtonPress:
947 break;
949 case ButtonRelease:
950 if (ev.xbutton.button != clickButton)
951 break;
952 XUngrabPointer(dpy, CurrentTime);
954 Bool docked = False;
955 if (ondock) {
956 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
957 XUnmapWindow(dpy, scr->dock_shadow);
958 if (originalDock == NULL) { // docking an undocked appicon
959 docked = wDockAttachIcon(lastDock, aicon, ix, iy, False);
960 if (!docked) {
961 /* AppIcon got rejected (happens only when we can't get the
962 command for that appicon, and the user cancels the
963 wInputDialog asking for one). Make the rejection obvious by
964 sliding the icon to its old position */
965 if (lastDock->type == WM_DRAWER) {
966 // Also fill the gap left in the drawer
967 wDrawerFillTheGap(lastDock, aicon, False);
969 SlideWindow(icon->core->window, x, y, oldX, oldY);
972 else { // moving a docked appicon to a dock
973 if (originalDock == lastDock) {
974 docked = True;
975 wDockReattachIcon(originalDock, aicon, ix, iy);
977 else {
978 docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy);
979 if (!docked) {
980 /* Possible scenario: user moved an auto-attracted appicon
981 from the clip to the dock, and cancelled the wInputDialog
982 asking for a command */
983 if (lastDock->type == WM_DRAWER) {
984 wDrawerFillTheGap(lastDock, aicon, False);
986 /* If aicon comes from a drawer, make some room to reattach it */
987 if (originalDock->type == WM_DRAWER) {
988 WAppIcon *aiconsToShift[ originalDock->icon_count ];
989 int j = 0;
991 for (i = 0; i < originalDock->max_icons; i++) {
992 WAppIcon *ai = originalDock->icon_array[ i ];
993 if (ai && ai != aicon &&
994 abs(ai->xindex) >= abs(aicon->xindex))
995 aiconsToShift[j++] = ai;
997 if (j != originalDock->icon_count - abs(aicon->xindex) - 1)
998 // Trust this never happens?
999 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1000 j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex);
1001 wSlideAppicons(aiconsToShift, j, originalDock->on_right_side);
1002 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1005 SlideWindow(icon->core->window, x, y, oldX, oldY);
1006 wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex);
1008 else {
1009 if (originalDock->auto_collapse && !originalDock->collapsed) {
1010 originalDock->collapsed = 1;
1011 wDockHideIcons(originalDock);
1013 if (originalDock->auto_raise_lower)
1014 wDockLower(originalDock);
1018 // No matter what happened above, check to lower lastDock
1019 // Don't see why I commented out the following 2 lines
1020 /* if (lastDock->auto_raise_lower)
1021 wDockLower(lastDock); */
1022 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1023 * collapsed, so that wHandleAppIconMove doesn't collapse it
1024 * right away (the timer will take care of it) */
1025 if (lastDock->auto_collapse)
1026 collapsed = 0;
1028 else {
1029 if (originalDock != NULL) { /* Detaching a docked appicon */
1030 if (superfluous) {
1031 if (!aicon->running && !wPreferences.no_animations) {
1032 /* We need to deselect it, even if is deselected in
1033 * wDockDetach(), because else DoKaboom() will fail.
1035 if (aicon->icon->selected)
1036 wIconSelect(aicon->icon);
1037 DoKaboom(scr, aicon->icon->core->window, x, y);
1040 wDockDetach(originalDock, aicon);
1041 if (originalDock->auto_collapse && !originalDock->collapsed) {
1042 originalDock->collapsed = 1;
1043 wDockHideIcons(originalDock);
1045 if (originalDock->auto_raise_lower)
1046 wDockLower(originalDock);
1049 // Can't remember why the icon hiding is better done above than below (commented out)
1050 // Also, lastDock is quite different from originalDock
1052 if (collapsed) {
1053 lastDock->collapsed = 1;
1054 wDockHideIcons(lastDock);
1055 collapsed = 0;
1058 if (superfluous) {
1059 if (ghost != None)
1060 XFreePixmap(dpy, ghost);
1061 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
1063 if (showed_all_clips) {
1064 int i;
1065 for (i = 0; i < scr->workspace_count; i++) {
1066 if (i == scr->current_workspace)
1067 continue;
1068 wDockHideIcons(scr->workspaces[i]->clip);
1071 if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked))
1072 /* Need to rearrange unless moving from dock to dock */
1073 wArrangeIcons(scr, True);
1074 return hasMoved;
1077 return False; /* Never reached */
1080 /* This function save the application icon and store the path in the Dictionary */
1081 static void wApplicationSaveIconPathFor(char *iconPath, char *wm_instance, char *wm_class)
1083 WMPropList *dict = WDWindowAttributes->dictionary;
1084 WMPropList *adict, *key, *iconk;
1085 WMPropList *val;
1086 char *tmp;
1088 tmp = get_name_for_instance_class(wm_instance, wm_class);
1089 key = WMCreatePLString(tmp);
1090 wfree(tmp);
1092 adict = WMGetFromPLDictionary(dict, key);
1093 iconk = WMCreatePLString("Icon");
1095 if (adict) {
1096 val = WMGetFromPLDictionary(adict, iconk);
1097 } else {
1098 /* no dictionary for app, so create one */
1099 adict = WMCreatePLDictionary(NULL, NULL);
1100 WMPutInPLDictionary(dict, key, adict);
1101 WMReleasePropList(adict);
1102 val = NULL;
1105 if (!val) {
1106 val = WMCreatePLString(iconPath);
1107 WMPutInPLDictionary(adict, iconk, val);
1108 WMReleasePropList(val);
1109 } else {
1110 val = NULL;
1113 WMReleasePropList(key);
1114 WMReleasePropList(iconk);
1116 if (val && !wPreferences.flags.noupdates)
1117 UpdateDomainFile(WDWindowAttributes);
1120 static WAppIcon *findDockIconFor(WDock *dock, Window main_window)
1122 WAppIcon *aicon = NULL;
1124 aicon = wDockFindIconForWindow(dock, main_window);
1125 if (!aicon) {
1126 wDockTrackWindowLaunch(dock, main_window);
1127 aicon = wDockFindIconForWindow(dock, main_window);
1129 return aicon;
1132 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window)
1134 WScreen *scr = wwin->screen_ptr;
1135 wapp->app_icon = NULL;
1137 if (scr->last_dock)
1138 wapp->app_icon = findDockIconFor(scr->last_dock, main_window);
1140 /* check main dock if we did not find it in last dock */
1141 if (!wapp->app_icon && scr->dock)
1142 wapp->app_icon = findDockIconFor(scr->dock, main_window);
1144 /* check clips */
1145 if (!wapp->app_icon) {
1146 int i;
1147 for (i = 0; i < scr->workspace_count; i++) {
1148 WDock *dock = scr->workspaces[i]->clip;
1149 if (dock)
1150 wapp->app_icon = findDockIconFor(dock, main_window);
1151 if (wapp->app_icon)
1152 break;
1156 /* Finally check drawers */
1157 if (!wapp->app_icon) {
1158 WDrawerChain *dc;
1159 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
1160 wapp->app_icon = findDockIconFor(dc->adrawer, main_window);
1161 if (wapp->app_icon)
1162 break;
1166 /* If created, then set some flags */
1167 if (wapp->app_icon) {
1168 WWindow *mainw = wapp->main_window_desc;
1170 wapp->app_icon->running = 1;
1171 wapp->app_icon->icon->owner = mainw;
1172 if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
1173 wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
1175 /* Update the icon images */
1176 wIconUpdate(wapp->app_icon->icon, NULL);
1178 /* Paint it */
1179 wAppIconPaint(wapp->app_icon);
1180 save_appicon(wapp->app_icon, True);
1184 /* Add the appicon to the appiconlist */
1185 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon)
1187 appicon->prev = NULL;
1188 appicon->next = scr->app_icon_list;
1189 if (scr->app_icon_list)
1190 scr->app_icon_list->prev = appicon;
1192 scr->app_icon_list = appicon;
1195 /* Remove the appicon from the appiconlist */
1196 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon)
1198 if (appicon == scr->app_icon_list) {
1199 if (appicon->next)
1200 appicon->next->prev = NULL;
1201 scr->app_icon_list = appicon->next;
1202 } else {
1203 if (appicon->next)
1204 appicon->next->prev = appicon->prev;
1205 if (appicon->prev)
1206 appicon->prev->next = appicon->next;
1209 appicon->prev = NULL;
1210 appicon->next = NULL;
1213 /* Return the AppIcon associated with a given (Xlib) Window. */
1214 WAppIcon *wAppIconFor(Window window)
1216 WObjDescriptor *desc;
1218 if (window == None)
1219 return NULL;
1221 if (XFindContext(dpy, window, wWinContext, (XPointer *) & desc) == XCNOENT)
1222 return NULL;
1224 if (desc->parent_type == WCLASS_APPICON || desc->parent_type == WCLASS_DOCK_ICON)
1225 return desc->parent;
1227 return NULL;