wmaker: Removed variable for X Context that is not used
[wmaker-crm.git] / src / appicon.c
blobe78ea739e336b7e9c6961cea18155420d50970c4
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 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 /**** Global variables ****/
64 extern WDDomain *WDWindowAttributes;
66 #define MOD_MASK wPreferences.modifier_mask
67 #define ICON_SIZE wPreferences.icon_size
69 static void iconDblClick(WObjDescriptor * desc, XEvent * event);
70 static void iconExpose(WObjDescriptor * desc, XEvent * event);
71 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const 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(const char *path, const char *wm_instance, const char *wm_class)
82 char *iconPath = NULL;
83 char *tmp = NULL;
85 if (strstr(path, ".app")) {
86 tmp = wmalloc(strlen(path) + 16);
88 if (wPreferences.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, const char *command, const char *wm_instance, const 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);
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 /* Update the icon, because wapp->app_icon->icon could be NULL */
276 wIconUpdate(wapp->app_icon->icon);
278 /* Paint it */
279 wAppIconPaint(wapp->app_icon);
280 } else if (wapp->app_icon->docked) {
281 wapp->app_icon->running = 0;
282 if (wapp->app_icon->dock->type == WM_DRAWER) {
283 wDrawerFillTheGap(wapp->app_icon->dock, wapp->app_icon, True);
285 wDockDetach(wapp->app_icon->dock, wapp->app_icon);
286 } else {
287 wAppIconDestroy(wapp->app_icon);
290 wapp->app_icon = NULL;
292 if (wPreferences.auto_arrange_icons)
293 wArrangeIcons(wapp->main_window_desc->screen_ptr, True);
296 static WAppIcon *wAppIconCreate(WWindow *leader_win)
298 WAppIcon *aicon;
300 aicon = wmalloc(sizeof(WAppIcon));
301 wretain(aicon);
302 aicon->yindex = -1;
303 aicon->xindex = -1;
304 aicon->prev = NULL;
305 aicon->next = NULL;
307 if (leader_win->wm_class)
308 aicon->wm_class = wstrdup(leader_win->wm_class);
310 if (leader_win->wm_instance)
311 aicon->wm_instance = wstrdup(leader_win->wm_instance);
313 aicon->icon = icon_create_for_wwindow(leader_win);
314 #ifdef XDND
315 wXDNDMakeAwareness(aicon->icon->core->window);
316 #endif
318 /* will be overriden if docked */
319 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
320 aicon->icon->core->descriptor.handle_expose = iconExpose;
321 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
322 aicon->icon->core->descriptor.parent = aicon;
323 AddToStackList(aicon->icon->core);
324 aicon->icon->show_title = 0;
326 return aicon;
329 void wAppIconDestroy(WAppIcon * aicon)
331 WScreen *scr = aicon->icon->core->screen_ptr;
333 RemoveFromStackList(aicon->icon->core);
334 wIconDestroy(aicon->icon);
335 if (aicon->command)
336 wfree(aicon->command);
337 #ifdef XDND
338 if (aicon->dnd_command)
339 wfree(aicon->dnd_command);
340 #endif
341 if (aicon->wm_instance)
342 wfree(aicon->wm_instance);
344 if (aicon->wm_class)
345 wfree(aicon->wm_class);
347 remove_from_appicon_list(scr, aicon);
349 aicon->destroyed = 1;
350 wrelease(aicon);
353 static void drawCorner(WIcon * icon)
355 WScreen *scr = icon->core->screen_ptr;
356 XPoint points[3];
358 points[0].x = 1;
359 points[0].y = 1;
360 points[1].x = 12;
361 points[1].y = 1;
362 points[2].x = 1;
363 points[2].y = 12;
364 XFillPolygon(dpy, icon->core->window, scr->icon_title_texture->normal_gc,
365 points, 3, Convex, CoordModeOrigin);
366 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 0, 12);
367 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 12, 0);
370 void wAppIconMove(WAppIcon * aicon, int x, int y)
372 XMoveWindow(dpy, aicon->icon->core->window, x, y);
373 aicon->x_pos = x;
374 aicon->y_pos = y;
377 #ifdef WS_INDICATOR
378 static void updateDockNumbers(WScreen * scr)
380 int length;
381 char *ws_numbers;
382 WAppIcon *dicon = scr->dock->icon_array[0];
384 ws_numbers = wmalloc(20);
385 snprintf(ws_numbers, 20, "%i [ %i ]", scr->current_workspace + 1, ((scr->current_workspace / 10) + 1));
386 length = strlen(ws_numbers);
388 XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, WMFontHeight(scr->icon_title_font) + 1, False);
390 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->black,
391 scr->icon_title_font, 4, 3, ws_numbers, length);
393 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->white,
394 scr->icon_title_font, 3, 2, ws_numbers, length);
396 wfree(ws_numbers);
398 #endif /* WS_INDICATOR */
400 void wAppIconPaint(WAppIcon *aicon)
402 WApplication *wapp;
403 WScreen *scr = aicon->icon->core->screen_ptr;
405 if (aicon->icon->owner)
406 wapp = wApplicationOf(aicon->icon->owner->main_window);
407 else
408 wapp = NULL;
410 wIconPaint(aicon->icon);
412 # ifdef WS_INDICATOR
413 if (aicon->docked && scr->dock && scr->dock == aicon->dock && aicon->yindex == 0)
414 updateDockNumbers(scr);
415 # endif
416 if (scr->dock_dots && aicon->docked && !aicon->running && aicon->command != NULL) {
417 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
418 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
419 XCopyArea(dpy, scr->dock_dots->image, aicon->icon->core->window,
420 scr->copy_gc, 0, 0, scr->dock_dots->width, scr->dock_dots->height, 0, 0);
422 #ifdef HIDDENDOT
423 if (wapp && wapp->flags.hidden) {
424 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
425 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
426 XCopyArea(dpy, scr->dock_dots->image,
427 aicon->icon->core->window, scr->copy_gc, 0, 0, 7, scr->dock_dots->height, 0, 0);
429 #endif /* HIDDENDOT */
431 if (aicon->omnipresent)
432 drawCorner(aicon->icon);
434 XSetClipMask(dpy, scr->copy_gc, None);
435 if (aicon->launching)
436 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
437 0, 0, wPreferences.icon_size, wPreferences.icon_size);
440 /* Save the application icon, if it's a dockapp then use it with dock = True */
441 void save_appicon(WAppIcon *aicon, Bool dock)
443 char *path;
445 if (!aicon)
446 return;
448 if (dock && (!aicon->docked || aicon->attracted))
449 return;
451 path = wIconStore(aicon->icon);
452 if (!path)
453 return;
455 wApplicationSaveIconPathFor(path, aicon->wm_instance, aicon->wm_class);
456 wfree(path);
459 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
461 /* main_window may not have the full command line; try to find one which does */
462 static void relaunchApplication(WApplication *wapp)
464 WScreen *scr;
465 WWindow *wlist, *next;
467 scr = wapp->main_window_desc->screen_ptr;
468 wlist = scr->focused_window;
469 if (! wlist)
470 return;
472 while (wlist->prev)
473 wlist = wlist->prev;
475 while (wlist) {
476 next = wlist->next;
478 if (wlist->main_window == wapp->main_window) {
479 if (RelaunchWindow(wlist))
480 return;
483 wlist = next;
487 static void relaunchCallback(WMenu * menu, WMenuEntry * entry)
489 WApplication *wapp = (WApplication *) entry->clientdata;
491 relaunchApplication(wapp);
494 static void hideCallback(WMenu * menu, WMenuEntry * entry)
496 WApplication *wapp = (WApplication *) entry->clientdata;
498 if (wapp->flags.hidden) {
499 wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
500 wUnhideApplication(wapp, False, False);
501 } else {
502 wHideApplication(wapp);
506 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
508 WApplication *wapp = (WApplication *) entry->clientdata;
510 wUnhideApplication(wapp, False, True);
513 static void setIconCallback(WMenu *menu, WMenuEntry *entry)
515 WAppIcon *icon = ((WApplication *) entry->clientdata)->app_icon;
516 char *file = NULL;
517 WScreen *scr;
518 int result;
520 assert(icon != NULL);
522 if (icon->editing)
523 return;
525 icon->editing = 1;
526 scr = icon->icon->core->screen_ptr;
528 wretain(icon);
530 result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
532 if (result && !icon->destroyed) {
533 if (file && *file == 0) {
534 wfree(file);
535 file = NULL;
537 if (!wIconChangeImageFile(icon->icon, file)) {
538 wMessageDialog(scr, _("Error"),
539 _("Could not open specified icon file"), _("OK"), NULL, NULL);
540 } else {
541 wDefaultChangeIcon(scr, icon->wm_instance, icon->wm_class, file);
542 wAppIconPaint(icon);
544 if (file)
545 wfree(file);
547 icon->editing = 0;
548 wrelease(icon);
551 static void killCallback(WMenu * menu, WMenuEntry * entry)
553 WApplication *wapp = (WApplication *) entry->clientdata;
554 WFakeGroupLeader *fPtr;
555 char *buffer;
556 char *shortname;
558 if (!WCHECK_STATE(WSTATE_NORMAL))
559 return;
561 WCHANGE_STATE(WSTATE_MODAL);
563 assert(entry->clientdata != NULL);
565 shortname = basename(wapp->app_icon->wm_instance);
567 buffer = wstrconcat(wapp->app_icon ? shortname : NULL,
568 _(" will be forcibly closed.\n"
569 "Any unsaved changes will be lost.\n" "Please confirm."));
571 fPtr = wapp->main_window_desc->fake_group;
573 wretain(wapp->main_window_desc);
574 if (wPreferences.dont_confirm_kill
575 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
576 buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
577 if (fPtr != NULL) {
578 WWindow *wwin, *twin;
580 wwin = wapp->main_window_desc->screen_ptr->focused_window;
581 while (wwin) {
582 twin = wwin->prev;
583 if (wwin->fake_group == fPtr)
584 wClientKill(wwin);
585 wwin = twin;
587 } else if (!wapp->main_window_desc->flags.destroyed) {
588 wClientKill(wapp->main_window_desc);
591 wrelease(wapp->main_window_desc);
592 wfree(buffer);
593 WCHANGE_STATE(WSTATE_NORMAL);
596 static WMenu *createApplicationMenu(WScreen *scr)
598 WMenu *menu;
600 menu = wMenuCreate(scr, NULL, False);
601 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
602 wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
603 wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
604 wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
605 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
607 return menu;
610 static void openApplicationMenu(WApplication * wapp, int x, int y)
612 WMenu *menu;
613 WScreen *scr = wapp->main_window_desc->screen_ptr;
614 int i;
616 if (!scr->icon_menu) {
617 scr->icon_menu = createApplicationMenu(scr);
618 wfree(scr->icon_menu->entries[1]->text);
621 menu = scr->icon_menu;
623 if (wapp->flags.hidden)
624 menu->entries[1]->text = _("Unhide");
625 else
626 menu->entries[1]->text = _("Hide");
628 menu->flags.realized = 0;
629 wMenuRealize(menu);
631 x -= menu->frame->core->width / 2;
632 if (x + menu->frame->core->width > scr->scr_width)
633 x = scr->scr_width - menu->frame->core->width;
635 if (x < 0)
636 x = 0;
638 /* set client data */
639 for (i = 0; i < menu->entry_no; i++)
640 menu->entries[i]->clientdata = wapp;
642 wMenuMapAt(menu, x, y, False);
645 /******************************************************************/
647 static void iconExpose(WObjDescriptor *desc, XEvent *event)
649 wAppIconPaint(desc->parent);
652 static void iconDblClick(WObjDescriptor *desc, XEvent *event)
654 WAppIcon *aicon = desc->parent;
655 WApplication *wapp;
656 WScreen *scr = aicon->icon->core->screen_ptr;
657 int unhideHere;
659 assert(aicon->icon->owner != NULL);
661 wapp = wApplicationOf(aicon->icon->owner->main_window);
663 if (event->xbutton.state & ControlMask) {
664 relaunchApplication(wapp);
665 return;
668 unhideHere = (event->xbutton.state & ShiftMask);
669 /* go to the last workspace that the user worked on the app */
670 if (!unhideHere && wapp->last_workspace != scr->current_workspace)
671 wWorkspaceChange(scr, wapp->last_workspace);
673 wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
675 if (event->xbutton.state & MOD_MASK)
676 wHideOtherApplications(aicon->icon->owner);
679 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
681 WAppIcon *aicon = desc->parent;
682 WScreen *scr = aicon->icon->core->screen_ptr;
683 Bool hasMoved;
685 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
686 return;
688 if (IsDoubleClick(scr, event)) {
689 /* Middle or right mouse actions were handled on first click */
690 if (event->xbutton.button == Button1)
691 iconDblClick(desc, event);
692 return;
695 if (event->xbutton.button == Button2) {
696 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
698 if (wapp)
699 relaunchApplication(wapp);
701 return;
704 if (event->xbutton.button == Button3) {
705 WObjDescriptor *desc;
706 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
708 if (!wapp)
709 return;
711 if (event->xbutton.send_event &&
712 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
713 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
714 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
715 wwarning("pointer grab failed for appicon menu");
716 return;
719 openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
721 /* allow drag select of menu */
722 desc = &scr->icon_menu->menu->descriptor;
723 event->xbutton.send_event = True;
724 (*desc->handle_mousedown) (desc, event);
725 return;
728 hasMoved = wHandleAppIconMove(aicon, event);
729 if (wPreferences.single_click && !hasMoved && aicon->dock != NULL)
731 iconDblClick(desc, event);
735 Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event)
737 WIcon *icon = aicon->icon;
738 WScreen *scr = icon->core->screen_ptr;
739 WDock *originalDock = aicon->dock; /* can be NULL */
740 WDock *lastDock = originalDock;
741 WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */
742 WDrawerChain *dc;
743 Bool done = False, dockable, ondock;
744 Bool grabbed = False;
745 Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
746 int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */
747 int omnipresent = aicon->omnipresent; /* this must be cached */
748 Bool showed_all_clips = False;
750 int clickButton = event->xbutton.button;
751 Pixmap ghost = None;
752 Window wins[2]; /* Managing shadow window */
753 XEvent ev;
755 int x = aicon->x_pos, y = aicon->y_pos;
756 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
757 int shad_x = x, shad_y = y;
758 int ix = aicon->xindex, iy = aicon->yindex;
759 int i;
760 int oldX = x;
761 int oldY = y;
762 Bool hasMoved = False;
764 if (wPreferences.flags.noupdates && originalDock != NULL)
765 return False;
767 if (!(event->xbutton.state & MOD_MASK))
768 wRaiseFrame(icon->core);
769 else {
770 /* If Mod is pressed for an docked appicon, assume it is to undock it,
771 * so don't lower it */
772 if (originalDock == NULL)
773 wLowerFrame(icon->core);
776 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
777 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
778 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
779 wwarning("Pointer grab failed in wHandleAppIconMove");
782 if (originalDock != NULL) {
783 dockable = True;
784 ondock = True;
786 else {
787 ondock = False;
788 if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer)
789 dockable = 0;
790 else
791 dockable = canBeDocked(icon->owner);
794 /* We try the various docks in that order:
795 * - First, the dock the appicon comes from, if any
796 * - Then, the drawers
797 * - Then, the "dock" (WM_DOCK)
798 * - Finally, the clip
800 i = 0;
801 if (originalDock != NULL)
802 allDocks[ i++ ] = originalDock;
803 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
804 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
805 if (dc->adrawer != originalDock)
806 allDocks[ i++ ] = dc->adrawer;
808 if (!wPreferences.flags.nodock && scr->dock != originalDock)
809 allDocks[ i++ ] = scr->dock;
810 if (!wPreferences.flags.noclip &&
811 originalDock != scr->workspaces[scr->current_workspace]->clip)
812 allDocks[ i++ ] = scr->workspaces[scr->current_workspace]->clip;
813 for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */
814 allDocks[ i ] = NULL;
816 wins[0] = icon->core->window;
817 wins[1] = scr->dock_shadow;
818 XRestackWindows(dpy, wins, 2);
819 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
820 if (superfluous) {
821 if (icon->pixmap != None)
822 ghost = MakeGhostIcon(scr, icon->pixmap);
823 else
824 ghost = MakeGhostIcon(scr, icon->core->window);
825 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
826 XClearWindow(dpy, scr->dock_shadow);
828 if (ondock)
829 XMapWindow(dpy, scr->dock_shadow);
831 while (!done) {
832 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
833 | ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
834 switch (ev.type) {
835 case Expose:
836 WMHandleEvent(&ev);
837 break;
839 case EnterNotify:
840 /* It means the cursor moved so fast that it entered
841 * something else (if moving slowly, it would have
842 * stayed in the appIcon that is being moved. Ignore
843 * such "spurious" EnterNotifiy's */
844 break;
846 case MotionNotify:
847 hasMoved = True;
848 if (!grabbed) {
849 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
850 || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
851 XChangeActivePointerGrab(dpy, ButtonMotionMask
852 | ButtonReleaseMask | ButtonPressMask,
853 wPreferences.cursor[WCUR_MOVE], CurrentTime);
854 grabbed = 1;
855 } else {
856 break;
860 if (omnipresent && !showed_all_clips) {
861 int i;
862 for (i = 0; i < scr->workspace_count; i++) {
863 if (i == scr->current_workspace)
864 continue;
865 wDockShowIcons(scr->workspaces[i]->clip);
866 /* Note: if dock is collapsed (for instance, because it
867 auto-collapses), its icons still won't show up */
869 showed_all_clips = True; /* To prevent flickering */
872 x = ev.xmotion.x_root - ofs_x;
873 y = ev.xmotion.y_root - ofs_y;
874 wAppIconMove(aicon, x, y);
876 WDock *theNewDock = NULL;
877 if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock) {
878 for (i = 0; dockable && i < scr->drawer_count + 2; i++) {
879 WDock *theDock = allDocks[i];
880 if (theDock == NULL)
881 break;
882 if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) {
883 theNewDock = theDock;
884 break;
887 if (originalDock != NULL && theNewDock == NULL &&
888 (aicon->launching || aicon->lock || aicon->running)) {
889 /* In those cases, stay in lastDock if no dock really wants us */
890 theNewDock = lastDock;
893 if (lastDock != NULL && lastDock != theNewDock) {
894 /* Leave lastDock in the state we found it */
895 if (lastDock->type == WM_DRAWER) {
896 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
898 if (collapsed) {
899 lastDock->collapsed = 1;
900 wDockHideIcons(lastDock);
901 collapsed = False;
903 if (lastDock->auto_raise_lower) {
904 wDockLower(lastDock);
907 if (theNewDock != NULL) {
908 if (lastDock != theNewDock) {
909 collapsed = theNewDock->collapsed;
910 if (collapsed) {
911 theNewDock->collapsed = 0;
912 wDockShowIcons(theNewDock);
914 if (theNewDock->auto_raise_lower) {
915 wDockRaise(theNewDock);
916 /* And raise the moving tile above it */
917 wRaiseFrame(aicon->icon->core);
919 lastDock = theNewDock;
922 shad_x = lastDock->x_pos + ix*wPreferences.icon_size;
923 shad_y = lastDock->y_pos + iy*wPreferences.icon_size;
925 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
927 if (!ondock) {
928 XMapWindow(dpy, scr->dock_shadow);
930 ondock = 1;
931 } else {
932 lastDock = theNewDock; // i.e., NULL
933 if (ondock) {
934 XUnmapWindow(dpy, scr->dock_shadow);
936 * Leaving that weird comment for now.
937 * But if we see no gap, there is no need to fill one!
938 * We could test ondock first and the lastDock to NULL afterwards
939 if (lastDock_before_it_was_null->type == WM_DRAWER) {
940 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
941 } */
944 ondock = 0;
946 break;
948 case ButtonPress:
949 break;
951 case ButtonRelease:
952 if (ev.xbutton.button != clickButton)
953 break;
954 XUngrabPointer(dpy, CurrentTime);
956 Bool docked = False;
957 if (ondock) {
958 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
959 XUnmapWindow(dpy, scr->dock_shadow);
960 if (originalDock == NULL) { // docking an undocked appicon
961 docked = wDockAttachIcon(lastDock, aicon, ix, iy, False);
962 if (!docked) {
963 /* AppIcon got rejected (happens only when we can't get the
964 command for that appicon, and the user cancels the
965 wInputDialog asking for one). Make the rejection obvious by
966 sliding the icon to its old position */
967 if (lastDock->type == WM_DRAWER) {
968 // Also fill the gap left in the drawer
969 wDrawerFillTheGap(lastDock, aicon, False);
971 SlideWindow(icon->core->window, x, y, oldX, oldY);
974 else { // moving a docked appicon to a dock
975 if (originalDock == lastDock) {
976 docked = True;
977 wDockReattachIcon(originalDock, aicon, ix, iy);
979 else {
980 docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy);
981 if (!docked) {
982 /* Possible scenario: user moved an auto-attracted appicon
983 from the clip to the dock, and cancelled the wInputDialog
984 asking for a command */
985 if (lastDock->type == WM_DRAWER) {
986 wDrawerFillTheGap(lastDock, aicon, False);
988 /* If aicon comes from a drawer, make some room to reattach it */
989 if (originalDock->type == WM_DRAWER) {
990 WAppIcon *aiconsToShift[ originalDock->icon_count ];
991 int j = 0;
993 for (i = 0; i < originalDock->max_icons; i++) {
994 WAppIcon *ai = originalDock->icon_array[ i ];
995 if (ai && ai != aicon &&
996 abs(ai->xindex) >= abs(aicon->xindex))
997 aiconsToShift[j++] = ai;
999 if (j != originalDock->icon_count - abs(aicon->xindex) - 1)
1000 // Trust this never happens?
1001 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1002 j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex);
1003 wSlideAppicons(aiconsToShift, j, originalDock->on_right_side);
1004 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1007 SlideWindow(icon->core->window, x, y, oldX, oldY);
1008 wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex);
1010 else {
1011 if (originalDock->auto_collapse && !originalDock->collapsed) {
1012 originalDock->collapsed = 1;
1013 wDockHideIcons(originalDock);
1015 if (originalDock->auto_raise_lower)
1016 wDockLower(originalDock);
1020 // No matter what happened above, check to lower lastDock
1021 // Don't see why I commented out the following 2 lines
1022 /* if (lastDock->auto_raise_lower)
1023 wDockLower(lastDock); */
1024 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1025 * collapsed, so that wHandleAppIconMove doesn't collapse it
1026 * right away (the timer will take care of it) */
1027 if (lastDock->auto_collapse)
1028 collapsed = 0;
1030 else {
1031 if (originalDock != NULL) { /* Detaching a docked appicon */
1032 if (superfluous) {
1033 if (!aicon->running && !wPreferences.no_animations) {
1034 /* We need to deselect it, even if is deselected in
1035 * wDockDetach(), because else DoKaboom() will fail.
1037 if (aicon->icon->selected)
1038 wIconSelect(aicon->icon);
1039 DoKaboom(scr, aicon->icon->core->window, x, y);
1042 wDockDetach(originalDock, aicon);
1043 if (originalDock->auto_collapse && !originalDock->collapsed) {
1044 originalDock->collapsed = 1;
1045 wDockHideIcons(originalDock);
1047 if (originalDock->auto_raise_lower)
1048 wDockLower(originalDock);
1051 // Can't remember why the icon hiding is better done above than below (commented out)
1052 // Also, lastDock is quite different from originalDock
1054 if (collapsed) {
1055 lastDock->collapsed = 1;
1056 wDockHideIcons(lastDock);
1057 collapsed = 0;
1060 if (superfluous) {
1061 if (ghost != None)
1062 XFreePixmap(dpy, ghost);
1063 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
1065 if (showed_all_clips) {
1066 int i;
1067 for (i = 0; i < scr->workspace_count; i++) {
1068 if (i == scr->current_workspace)
1069 continue;
1070 wDockHideIcons(scr->workspaces[i]->clip);
1073 if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked))
1074 /* Need to rearrange unless moving from dock to dock */
1075 wArrangeIcons(scr, True);
1076 return hasMoved;
1079 return False; /* Never reached */
1082 /* This function save the application icon and store the path in the Dictionary */
1083 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const char *wm_class)
1085 WMPropList *dict = WDWindowAttributes->dictionary;
1086 WMPropList *adict, *key, *iconk;
1087 WMPropList *val;
1088 char *tmp;
1090 tmp = get_name_for_instance_class(wm_instance, wm_class);
1091 key = WMCreatePLString(tmp);
1092 wfree(tmp);
1094 adict = WMGetFromPLDictionary(dict, key);
1095 iconk = WMCreatePLString("Icon");
1097 if (adict) {
1098 val = WMGetFromPLDictionary(adict, iconk);
1099 } else {
1100 /* no dictionary for app, so create one */
1101 adict = WMCreatePLDictionary(NULL, NULL);
1102 WMPutInPLDictionary(dict, key, adict);
1103 WMReleasePropList(adict);
1104 val = NULL;
1107 if (!val) {
1108 val = WMCreatePLString(iconPath);
1109 WMPutInPLDictionary(adict, iconk, val);
1110 WMReleasePropList(val);
1111 } else {
1112 val = NULL;
1115 WMReleasePropList(key);
1116 WMReleasePropList(iconk);
1118 if (val && !wPreferences.flags.noupdates)
1119 UpdateDomainFile(WDWindowAttributes);
1122 static WAppIcon *findDockIconFor(WDock *dock, Window main_window)
1124 WAppIcon *aicon = NULL;
1126 aicon = wDockFindIconForWindow(dock, main_window);
1127 if (!aicon) {
1128 wDockTrackWindowLaunch(dock, main_window);
1129 aicon = wDockFindIconForWindow(dock, main_window);
1131 return aicon;
1134 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window)
1136 WScreen *scr = wwin->screen_ptr;
1137 wapp->app_icon = NULL;
1139 if (scr->last_dock)
1140 wapp->app_icon = findDockIconFor(scr->last_dock, main_window);
1142 /* check main dock if we did not find it in last dock */
1143 if (!wapp->app_icon && scr->dock)
1144 wapp->app_icon = findDockIconFor(scr->dock, main_window);
1146 /* check clips */
1147 if (!wapp->app_icon) {
1148 int i;
1149 for (i = 0; i < scr->workspace_count; i++) {
1150 WDock *dock = scr->workspaces[i]->clip;
1151 if (dock)
1152 wapp->app_icon = findDockIconFor(dock, main_window);
1153 if (wapp->app_icon)
1154 break;
1158 /* Finally check drawers */
1159 if (!wapp->app_icon) {
1160 WDrawerChain *dc;
1161 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
1162 wapp->app_icon = findDockIconFor(dc->adrawer, main_window);
1163 if (wapp->app_icon)
1164 break;
1168 /* If created, then set some flags */
1169 if (wapp->app_icon) {
1170 WWindow *mainw = wapp->main_window_desc;
1172 wapp->app_icon->running = 1;
1173 wapp->app_icon->icon->owner = mainw;
1174 if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
1175 wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
1177 /* Update the icon images */
1178 wIconUpdate(wapp->app_icon->icon);
1180 /* Paint it */
1181 wAppIconPaint(wapp->app_icon);
1182 save_appicon(wapp->app_icon, True);
1186 /* Add the appicon to the appiconlist */
1187 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon)
1189 appicon->prev = NULL;
1190 appicon->next = scr->app_icon_list;
1191 if (scr->app_icon_list)
1192 scr->app_icon_list->prev = appicon;
1194 scr->app_icon_list = appicon;
1197 /* Remove the appicon from the appiconlist */
1198 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon)
1200 if (appicon == scr->app_icon_list) {
1201 if (appicon->next)
1202 appicon->next->prev = NULL;
1203 scr->app_icon_list = appicon->next;
1204 } else {
1205 if (appicon->next)
1206 appicon->next->prev = appicon->prev;
1207 if (appicon->prev)
1208 appicon->prev->next = appicon->next;
1211 appicon->prev = NULL;
1212 appicon->next = NULL;
1215 /* Return the AppIcon associated with a given (Xlib) Window. */
1216 WAppIcon *wAppIconFor(Window window)
1218 WObjDescriptor *desc;
1220 if (window == None)
1221 return NULL;
1223 if (XFindContext(dpy, window, w_global.context.client_win, (XPointer *) & desc) == XCNOENT)
1224 return NULL;
1226 if (desc->parent_type == WCLASS_APPICON || desc->parent_type == WCLASS_DOCK_ICON)
1227 return desc->parent;
1229 return NULL;