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