Wrap appicons when dock is moved up and down
[wmaker-crm.git] / src / appicon.c
blobd4384d7f201616f187d3b49a38cbb1ce2764bc7d
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 aicon->icon = icon_create_for_dock(scr, command, wm_instance, wm_class, tile);
134 #ifdef XDND
135 wXDNDMakeAwareness(aicon->icon->core->window);
136 #endif
138 /* will be overriden by dock */
139 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
140 aicon->icon->core->descriptor.handle_expose = iconExpose;
141 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
142 aicon->icon->core->descriptor.parent = aicon;
143 AddToStackList(aicon->icon->core);
145 return aicon;
148 void create_appicon_for_application(WApplication *wapp, WWindow *wwin)
150 /* Try to create an icon from the dock or clip */
151 create_appicon_from_dock(wwin, wapp, wapp->main_window);
153 /* If app_icon was not found, create it */
154 if (!wapp->app_icon) {
155 /* Create the icon */
156 wapp->app_icon = wAppIconCreate(wapp->main_window_desc);
157 wIconUpdate(wapp->app_icon->icon, NULL);
159 /* Now, paint the icon */
160 if (!WFLAGP(wapp->main_window_desc, no_appicon))
161 paint_app_icon(wapp);
164 /* Save the app_icon in a file */
165 save_appicon(wapp->app_icon, False);
168 void unpaint_app_icon(WApplication *wapp)
170 WAppIcon *aicon;
171 WScreen *scr;
172 WDock *clip;
174 if (!wapp || !wapp->app_icon)
175 return;
177 aicon = wapp->app_icon;
179 /* If the icon is docked, don't continue */
180 if (aicon->docked)
181 return;
183 scr = wapp->main_window_desc->screen_ptr;
184 clip = scr->workspaces[scr->current_workspace]->clip;
186 if (!clip || !aicon->attracted || !clip->collapsed)
187 XUnmapWindow(dpy, aicon->icon->core->window);
189 /* We want to avoid having it on the list because otherwise
190 * there will be a hole when the icons are arranged with
191 * wArrangeIcons() */
192 remove_from_appicon_list(scr, aicon);
194 if (wPreferences.auto_arrange_icons && !aicon->attracted)
195 wArrangeIcons(scr, True);
198 void paint_app_icon(WApplication *wapp)
200 WIcon *icon;
201 WScreen *scr = wapp->main_window_desc->screen_ptr;
202 WDock *attracting_dock;
203 int x = 0, y = 0;
204 Bool update_icon = False;
206 if (!wapp || !wapp->app_icon)
207 return;
209 icon = wapp->app_icon->icon;
210 wapp->app_icon->main_window = wapp->main_window;
212 /* If the icon is docked, don't continue */
213 if (wapp->app_icon->docked)
214 return;
216 attracting_dock = scr->attracting_drawer != NULL ?
217 scr->attracting_drawer :
218 scr->workspaces[scr->current_workspace]->clip;
219 if (attracting_dock && attracting_dock->attract_icons &&
220 wDockFindFreeSlot(attracting_dock, &x, &y)) {
221 wapp->app_icon->attracted = 1;
222 if (!icon->shadowed) {
223 icon->shadowed = 1;
224 update_icon = True;
226 wDockAttachIcon(attracting_dock, wapp->app_icon, x, y, update_icon);
227 } else {
228 /* We must know if the icon is painted in the screen,
229 * because if painted, then PlaceIcon will return the next
230 * space on the screen, and the icon will move */
231 if (wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL) {
232 PlaceIcon(scr, &x, &y, wGetHeadForWindow(wapp->main_window_desc));
233 wAppIconMove(wapp->app_icon, x, y);
234 wLowerFrame(icon->core);
238 /* If we want appicon (no_appicon is not set) and the icon is not
239 * in the appicon_list, we must add it. Else, we want to avoid
240 * having it on the list */
241 if (!WFLAGP(wapp->main_window_desc, no_appicon) &&
242 wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL)
243 add_to_appicon_list(scr, wapp->app_icon);
245 if (!attracting_dock || !wapp->app_icon->attracted || !attracting_dock->collapsed)
246 XMapWindow(dpy, icon->core->window);
248 if (wPreferences.auto_arrange_icons && !wapp->app_icon->attracted)
249 wArrangeIcons(scr, True);
252 void removeAppIconFor(WApplication *wapp)
254 if (!wapp->app_icon)
255 return;
257 if (wPreferences.highlight_active_app)
258 wIconSetHighlited(wapp->app_icon->icon, False);
259 if (wapp->app_icon->docked && !wapp->app_icon->attracted) {
260 wapp->app_icon->running = 0;
261 /* since we keep it, we don't care if it was attracted or not */
262 wapp->app_icon->attracted = 0;
263 wapp->app_icon->icon->shadowed = 0;
264 wapp->app_icon->main_window = None;
265 wapp->app_icon->pid = 0;
266 wapp->app_icon->icon->owner = NULL;
267 wapp->app_icon->icon->icon_win = None;
269 /* Set the icon image */
270 set_icon_image_from_database(wapp->app_icon->icon, wapp->app_icon->wm_instance,
271 wapp->app_icon->wm_class, wapp->app_icon->command);
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 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 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 (scr->dock_dots && 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 relaunchApplication(wapp);
489 static void hideCallback(WMenu * menu, WMenuEntry * entry)
491 WApplication *wapp = (WApplication *) entry->clientdata;
493 if (wapp->flags.hidden) {
494 wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
495 wUnhideApplication(wapp, False, False);
496 } else {
497 wHideApplication(wapp);
501 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
503 WApplication *wapp = (WApplication *) entry->clientdata;
505 wUnhideApplication(wapp, False, True);
508 static void setIconCallback(WMenu *menu, WMenuEntry *entry)
510 WAppIcon *icon = ((WApplication *) entry->clientdata)->app_icon;
511 char *file = NULL;
512 WScreen *scr;
513 int result;
515 assert(icon != NULL);
517 if (icon->editing)
518 return;
520 icon->editing = 1;
521 scr = icon->icon->core->screen_ptr;
523 wretain(icon);
525 result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
527 if (result && !icon->destroyed) {
528 if (file && *file == 0) {
529 wfree(file);
530 file = NULL;
532 if (!wIconChangeImageFile(icon->icon, file)) {
533 wMessageDialog(scr, _("Error"),
534 _("Could not open specified icon file"), _("OK"), NULL, NULL);
535 } else {
536 wDefaultChangeIcon(scr, icon->wm_instance, icon->wm_class, file);
537 wAppIconPaint(icon);
539 if (file)
540 wfree(file);
542 icon->editing = 0;
543 wrelease(icon);
546 static void killCallback(WMenu * menu, WMenuEntry * entry)
548 WApplication *wapp = (WApplication *) entry->clientdata;
549 WFakeGroupLeader *fPtr;
550 char *buffer;
551 char *shortname;
552 char *basename(const char *shortname);
554 if (!WCHECK_STATE(WSTATE_NORMAL))
555 return;
557 WCHANGE_STATE(WSTATE_MODAL);
559 assert(entry->clientdata != NULL);
561 shortname = basename(wapp->app_icon->wm_instance);
563 buffer = wstrconcat(wapp->app_icon ? shortname : NULL,
564 _(" will be forcibly closed.\n"
565 "Any unsaved changes will be lost.\n" "Please confirm."));
567 fPtr = wapp->main_window_desc->fake_group;
569 wretain(wapp->main_window_desc);
570 if (wPreferences.dont_confirm_kill
571 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
572 buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
573 if (fPtr != NULL) {
574 WWindow *wwin, *twin;
576 wwin = wapp->main_window_desc->screen_ptr->focused_window;
577 while (wwin) {
578 twin = wwin->prev;
579 if (wwin->fake_group == fPtr)
580 wClientKill(wwin);
581 wwin = twin;
583 } else if (!wapp->main_window_desc->flags.destroyed) {
584 wClientKill(wapp->main_window_desc);
587 wrelease(wapp->main_window_desc);
588 wfree(buffer);
589 WCHANGE_STATE(WSTATE_NORMAL);
592 static WMenu *createApplicationMenu(WScreen *scr)
594 WMenu *menu;
596 menu = wMenuCreate(scr, NULL, False);
597 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
598 wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
599 wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
600 wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
601 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
603 return menu;
606 static void openApplicationMenu(WApplication * wapp, int x, int y)
608 WMenu *menu;
609 WScreen *scr = wapp->main_window_desc->screen_ptr;
610 int i;
612 if (!scr->icon_menu) {
613 scr->icon_menu = createApplicationMenu(scr);
614 wfree(scr->icon_menu->entries[1]->text);
617 menu = scr->icon_menu;
619 if (wapp->flags.hidden)
620 menu->entries[1]->text = _("Unhide");
621 else
622 menu->entries[1]->text = _("Hide");
624 menu->flags.realized = 0;
625 wMenuRealize(menu);
627 x -= menu->frame->core->width / 2;
628 if (x + menu->frame->core->width > scr->scr_width)
629 x = scr->scr_width - menu->frame->core->width;
631 if (x < 0)
632 x = 0;
634 /* set client data */
635 for (i = 0; i < menu->entry_no; i++)
636 menu->entries[i]->clientdata = wapp;
638 wMenuMapAt(menu, x, y, False);
641 /******************************************************************/
643 static void iconExpose(WObjDescriptor *desc, XEvent *event)
645 wAppIconPaint(desc->parent);
648 static void iconDblClick(WObjDescriptor *desc, XEvent *event)
650 WAppIcon *aicon = desc->parent;
651 WApplication *wapp;
652 WScreen *scr = aicon->icon->core->screen_ptr;
653 int unhideHere;
655 assert(aicon->icon->owner != NULL);
657 wapp = wApplicationOf(aicon->icon->owner->main_window);
659 if (event->xbutton.state & ControlMask) {
660 relaunchApplication(wapp);
661 return;
664 unhideHere = (event->xbutton.state & ShiftMask);
665 /* go to the last workspace that the user worked on the app */
666 if (!unhideHere && wapp->last_workspace != scr->current_workspace)
667 wWorkspaceChange(scr, wapp->last_workspace);
669 wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
671 if (event->xbutton.state & MOD_MASK)
672 wHideOtherApplications(aicon->icon->owner);
675 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
677 WAppIcon *aicon = desc->parent;
678 WScreen *scr = aicon->icon->core->screen_ptr;
679 Bool hasMoved;
681 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
682 return;
684 if (IsDoubleClick(scr, event)) {
685 /* Middle or right mouse actions were handled on first click */
686 if (event->xbutton.button == Button1)
687 iconDblClick(desc, event);
688 return;
691 if (event->xbutton.button == Button2) {
692 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
694 if (wapp)
695 relaunchApplication(wapp);
697 return;
700 if (event->xbutton.button == Button3) {
701 WObjDescriptor *desc;
702 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
704 if (!wapp)
705 return;
707 if (event->xbutton.send_event &&
708 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
709 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
710 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
711 wwarning("pointer grab failed for appicon menu");
712 return;
715 openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
717 /* allow drag select of menu */
718 desc = &scr->icon_menu->menu->descriptor;
719 event->xbutton.send_event = True;
720 (*desc->handle_mousedown) (desc, event);
721 return;
724 hasMoved = wHandleAppIconMove(aicon, event);
725 if (wPreferences.single_click && !hasMoved && aicon->dock != NULL)
727 iconDblClick(desc, event);
731 Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event)
733 WIcon *icon = aicon->icon;
734 WScreen *scr = icon->core->screen_ptr;
735 WDock *originalDock = aicon->dock; /* can be NULL */
736 WDock *lastDock = originalDock;
737 WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */
738 WDrawerChain *dc;
739 Bool done = False, dockable, ondock;
740 Bool grabbed = False;
741 Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
742 int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */
743 int omnipresent = aicon->omnipresent; /* this must be cached */
744 Bool showed_all_clips = False;
746 int clickButton = event->xbutton.button;
747 Pixmap ghost = None;
748 Window wins[2]; /* Managing shadow window */
749 XEvent ev;
751 int x = aicon->x_pos, y = aicon->y_pos;
752 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
753 int shad_x = x, shad_y = y;
754 int ix = aicon->xindex, iy = aicon->yindex;
755 int i;
756 int oldX = x;
757 int oldY = y;
758 Bool hasMoved = False;
760 if (wPreferences.flags.noupdates && originalDock != NULL)
761 return False;
763 if (!(event->xbutton.state & MOD_MASK))
764 wRaiseFrame(icon->core);
765 else {
766 /* If Mod is pressed for an docked appicon, assume it is to undock it,
767 * so don't lower it */
768 if (originalDock == NULL)
769 wLowerFrame(icon->core);
772 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
773 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
774 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
775 wwarning("Pointer grab failed in wHandleAppIconMove");
778 if (originalDock != NULL) {
779 dockable = True;
780 ondock = True;
782 else {
783 ondock = False;
784 if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer)
785 dockable = 0;
786 else
787 dockable = canBeDocked(icon->owner);
790 /* We try the various docks in that order:
791 * - First, the dock the appicon comes from, if any
792 * - Then, the drawers
793 * - Then, the "dock" (WM_DOCK)
794 * - Finally, the clip
796 i = 0;
797 if (originalDock != NULL)
798 allDocks[ i++ ] = originalDock;
799 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
800 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
801 if (dc->adrawer != originalDock)
802 allDocks[ i++ ] = dc->adrawer;
804 if (!wPreferences.flags.nodock && scr->dock != originalDock)
805 allDocks[ i++ ] = scr->dock;
806 if (!wPreferences.flags.noclip &&
807 originalDock != scr->workspaces[scr->current_workspace]->clip)
808 allDocks[ i++ ] = scr->workspaces[scr->current_workspace]->clip;
809 for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */
810 allDocks[ i ] = NULL;
812 wins[0] = icon->core->window;
813 wins[1] = scr->dock_shadow;
814 XRestackWindows(dpy, wins, 2);
815 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
816 if (superfluous) {
817 if (icon->pixmap != None)
818 ghost = MakeGhostIcon(scr, icon->pixmap);
819 else
820 ghost = MakeGhostIcon(scr, icon->core->window);
821 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
822 XClearWindow(dpy, scr->dock_shadow);
824 if (ondock)
825 XMapWindow(dpy, scr->dock_shadow);
827 while (!done) {
828 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
829 | ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
830 switch (ev.type) {
831 case Expose:
832 WMHandleEvent(&ev);
833 break;
835 case EnterNotify:
836 /* It means the cursor moved so fast that it entered
837 * something else (if moving slowly, it would have
838 * stayed in the appIcon that is being moved. Ignore
839 * such "spurious" EnterNotifiy's */
840 break;
842 case MotionNotify:
843 hasMoved = True;
844 if (!grabbed) {
845 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
846 || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
847 XChangeActivePointerGrab(dpy, ButtonMotionMask
848 | ButtonReleaseMask | ButtonPressMask,
849 wCursor[WCUR_MOVE], CurrentTime);
850 grabbed = 1;
851 } else {
852 break;
856 if (omnipresent && !showed_all_clips) {
857 int i;
858 for (i = 0; i < scr->workspace_count; i++) {
859 if (i == scr->current_workspace)
860 continue;
861 wDockShowIcons(scr->workspaces[i]->clip);
862 /* Note: if dock is collapsed (for instance, because it
863 auto-collapses), its icons still won't show up */
865 showed_all_clips = True; /* To prevent flickering */
868 x = ev.xmotion.x_root - ofs_x;
869 y = ev.xmotion.y_root - ofs_y;
870 wAppIconMove(aicon, x, y);
872 WDock *theNewDock = NULL;
873 if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock) {
874 for (i = 0; dockable && i < scr->drawer_count + 2; i++) {
875 WDock *theDock = allDocks[i];
876 if (theDock == NULL)
877 break;
878 if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) {
879 theNewDock = theDock;
880 break;
883 if (originalDock != NULL && theNewDock == NULL &&
884 (aicon->launching || aicon->lock || aicon->running)) {
885 /* In those cases, stay in lastDock if no dock really wants us */
886 theNewDock = lastDock;
889 if (lastDock != NULL && lastDock != theNewDock) {
890 /* Leave lastDock in the state we found it */
891 if (lastDock->type == WM_DRAWER) {
892 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
894 if (collapsed) {
895 lastDock->collapsed = 1;
896 wDockHideIcons(lastDock);
897 collapsed = False;
899 if (lastDock->auto_raise_lower) {
900 wDockLower(lastDock);
903 if (theNewDock != NULL) {
904 if (lastDock != theNewDock) {
905 collapsed = theNewDock->collapsed;
906 if (collapsed) {
907 theNewDock->collapsed = 0;
908 wDockShowIcons(theNewDock);
910 if (theNewDock->auto_raise_lower) {
911 wDockRaise(theNewDock);
912 /* And raise the moving tile above it */
913 wRaiseFrame(aicon->icon->core);
915 lastDock = theNewDock;
918 shad_x = lastDock->x_pos + ix*wPreferences.icon_size;
919 shad_y = lastDock->y_pos + iy*wPreferences.icon_size;
921 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
923 if (!ondock) {
924 XMapWindow(dpy, scr->dock_shadow);
926 ondock = 1;
927 } else {
928 lastDock = theNewDock; // i.e., NULL
929 if (ondock) {
930 XUnmapWindow(dpy, scr->dock_shadow);
932 * Leaving that weird comment for now.
933 * But if we see no gap, there is no need to fill one!
934 * We could test ondock first and the lastDock to NULL afterwards
935 if (lastDock_before_it_was_null->type == WM_DRAWER) {
936 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
937 } */
940 ondock = 0;
942 break;
944 case ButtonPress:
945 break;
947 case ButtonRelease:
948 if (ev.xbutton.button != clickButton)
949 break;
950 XUngrabPointer(dpy, CurrentTime);
952 Bool docked = False;
953 if (ondock) {
954 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
955 XUnmapWindow(dpy, scr->dock_shadow);
956 if (originalDock == NULL) { // docking an undocked appicon
957 docked = wDockAttachIcon(lastDock, aicon, ix, iy, False);
958 if (!docked) {
959 /* AppIcon got rejected (happens only when we can't get the
960 command for that appicon, and the user cancels the
961 wInputDialog asking for one). Make the rejection obvious by
962 sliding the icon to its old position */
963 if (lastDock->type == WM_DRAWER) {
964 // Also fill the gap left in the drawer
965 wDrawerFillTheGap(lastDock, aicon, False);
967 SlideWindow(icon->core->window, x, y, oldX, oldY);
970 else { // moving a docked appicon to a dock
971 if (originalDock == lastDock) {
972 docked = True;
973 wDockReattachIcon(originalDock, aicon, ix, iy);
975 else {
976 docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy);
977 if (!docked) {
978 /* Possible scenario: user moved an auto-attracted appicon
979 from the clip to the dock, and cancelled the wInputDialog
980 asking for a command */
981 if (lastDock->type == WM_DRAWER) {
982 wDrawerFillTheGap(lastDock, aicon, False);
984 /* If aicon comes from a drawer, make some room to reattach it */
985 if (originalDock->type == WM_DRAWER) {
986 WAppIcon *aiconsToShift[ originalDock->icon_count ];
987 int j = 0;
989 for (i = 0; i < originalDock->max_icons; i++) {
990 WAppIcon *ai = originalDock->icon_array[ i ];
991 if (ai && ai != aicon &&
992 abs(ai->xindex) >= abs(aicon->xindex))
993 aiconsToShift[j++] = ai;
995 if (j != originalDock->icon_count - abs(aicon->xindex) - 1)
996 // Trust this never happens?
997 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
998 j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex);
999 wSlideAppicons(aiconsToShift, j, originalDock->on_right_side);
1000 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1003 SlideWindow(icon->core->window, x, y, oldX, oldY);
1004 wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex);
1006 else {
1007 if (originalDock->auto_collapse && !originalDock->collapsed) {
1008 originalDock->collapsed = 1;
1009 wDockHideIcons(originalDock);
1011 if (originalDock->auto_raise_lower)
1012 wDockLower(originalDock);
1016 // No matter what happened above, check to lower lastDock
1017 // Don't see why I commented out the following 2 lines
1018 /* if (lastDock->auto_raise_lower)
1019 wDockLower(lastDock); */
1020 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1021 * collapsed, so that wHandleAppIconMove doesn't collapse it
1022 * right away (the timer will take care of it) */
1023 if (lastDock->auto_collapse)
1024 collapsed = 0;
1026 else {
1027 if (originalDock != NULL) { /* Detaching a docked appicon */
1028 if (superfluous) {
1029 if (!aicon->running && !wPreferences.no_animations) {
1030 /* We need to deselect it, even if is deselected in
1031 * wDockDetach(), because else DoKaboom() will fail.
1033 if (aicon->icon->selected)
1034 wIconSelect(aicon->icon);
1035 DoKaboom(scr, aicon->icon->core->window, x, y);
1038 wDockDetach(originalDock, aicon);
1039 if (originalDock->auto_collapse && !originalDock->collapsed) {
1040 originalDock->collapsed = 1;
1041 wDockHideIcons(originalDock);
1043 if (originalDock->auto_raise_lower)
1044 wDockLower(originalDock);
1047 // Can't remember why the icon hiding is better done above than below (commented out)
1048 // Also, lastDock is quite different from originalDock
1050 if (collapsed) {
1051 lastDock->collapsed = 1;
1052 wDockHideIcons(lastDock);
1053 collapsed = 0;
1056 if (superfluous) {
1057 if (ghost != None)
1058 XFreePixmap(dpy, ghost);
1059 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
1061 if (showed_all_clips) {
1062 int i;
1063 for (i = 0; i < scr->workspace_count; i++) {
1064 if (i == scr->current_workspace)
1065 continue;
1066 wDockHideIcons(scr->workspaces[i]->clip);
1069 if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked))
1070 /* Need to rearrange unless moving from dock to dock */
1071 wArrangeIcons(scr, True);
1072 return hasMoved;
1075 return False; /* Never reached */
1078 /* This function save the application icon and store the path in the Dictionary */
1079 static void wApplicationSaveIconPathFor(char *iconPath, char *wm_instance, char *wm_class)
1081 WMPropList *dict = WDWindowAttributes->dictionary;
1082 WMPropList *adict, *key, *iconk;
1083 WMPropList *val;
1084 char *tmp;
1086 tmp = get_name_for_instance_class(wm_instance, wm_class);
1087 key = WMCreatePLString(tmp);
1088 wfree(tmp);
1090 adict = WMGetFromPLDictionary(dict, key);
1091 iconk = WMCreatePLString("Icon");
1093 if (adict) {
1094 val = WMGetFromPLDictionary(adict, iconk);
1095 } else {
1096 /* no dictionary for app, so create one */
1097 adict = WMCreatePLDictionary(NULL, NULL);
1098 WMPutInPLDictionary(dict, key, adict);
1099 WMReleasePropList(adict);
1100 val = NULL;
1103 if (!val) {
1104 val = WMCreatePLString(iconPath);
1105 WMPutInPLDictionary(adict, iconk, val);
1106 WMReleasePropList(val);
1107 } else {
1108 val = NULL;
1111 WMReleasePropList(key);
1112 WMReleasePropList(iconk);
1114 if (val && !wPreferences.flags.noupdates)
1115 UpdateDomainFile(WDWindowAttributes);
1118 static WAppIcon *findDockIconFor(WDock *dock, Window main_window)
1120 WAppIcon *aicon = NULL;
1122 aicon = wDockFindIconForWindow(dock, main_window);
1123 if (!aicon) {
1124 wDockTrackWindowLaunch(dock, main_window);
1125 aicon = wDockFindIconForWindow(dock, main_window);
1127 return aicon;
1130 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window)
1132 WScreen *scr = wwin->screen_ptr;
1133 wapp->app_icon = NULL;
1135 if (scr->last_dock)
1136 wapp->app_icon = findDockIconFor(scr->last_dock, main_window);
1138 /* check main dock if we did not find it in last dock */
1139 if (!wapp->app_icon && scr->dock)
1140 wapp->app_icon = findDockIconFor(scr->dock, main_window);
1142 /* check clips */
1143 if (!wapp->app_icon) {
1144 int i;
1145 for (i = 0; i < scr->workspace_count; i++) {
1146 WDock *dock = scr->workspaces[i]->clip;
1147 if (dock)
1148 wapp->app_icon = findDockIconFor(dock, main_window);
1149 if (wapp->app_icon)
1150 break;
1154 /* Finally check drawers */
1155 if (!wapp->app_icon) {
1156 WDrawerChain *dc;
1157 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
1158 wapp->app_icon = findDockIconFor(dc->adrawer, main_window);
1159 if (wapp->app_icon)
1160 break;
1164 /* If created, then set some flags */
1165 if (wapp->app_icon) {
1166 WWindow *mainw = wapp->main_window_desc;
1168 wapp->app_icon->running = 1;
1169 wapp->app_icon->icon->owner = mainw;
1170 if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
1171 wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
1173 /* Update the icon images */
1174 wIconUpdate(wapp->app_icon->icon, NULL);
1176 /* Paint it */
1177 wAppIconPaint(wapp->app_icon);
1178 save_appicon(wapp->app_icon, True);
1182 /* Add the appicon to the appiconlist */
1183 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon)
1185 appicon->prev = NULL;
1186 appicon->next = scr->app_icon_list;
1187 if (scr->app_icon_list)
1188 scr->app_icon_list->prev = appicon;
1190 scr->app_icon_list = appicon;
1193 /* Remove the appicon from the appiconlist */
1194 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon)
1196 if (appicon == scr->app_icon_list) {
1197 if (appicon->next)
1198 appicon->next->prev = NULL;
1199 scr->app_icon_list = appicon->next;
1200 } else {
1201 if (appicon->next)
1202 appicon->next->prev = appicon->prev;
1203 if (appicon->prev)
1204 appicon->prev->next = appicon->next;
1207 appicon->prev = NULL;
1208 appicon->next = NULL;
1211 /* Return the AppIcon associated with a given (Xlib) Window. */
1212 WAppIcon *wAppIconFor(Window window)
1214 WObjDescriptor *desc;
1216 if (window == None)
1217 return NULL;
1219 if (XFindContext(dpy, window, wWinContext, (XPointer *) & desc) == XCNOENT)
1220 return NULL;
1222 if (desc->parent_type == WCLASS_APPICON || desc->parent_type == WCLASS_DOCK_ICON)
1223 return desc->parent;
1225 return NULL;