Merge branch 'wmdrawer' into next
[wmaker-crm.git] / src / appicon.c
blob72fb3d00fe7a01bd8b066ad51b235fc9944875e9
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 #include "placement.h"
51 #ifdef XDND
52 #include "xdnd.h"
53 #endif
56 * icon_file for the dock is got from the preferences file by
57 * using the classname/instancename
60 /**** Global variables ****/
61 extern Cursor wCursor[WCUR_LAST];
62 extern WPreferences wPreferences;
63 extern WDDomain *WDWindowAttributes;
64 extern XContext wWinContext;
66 #define MOD_MASK wPreferences.modifier_mask
67 #define ICON_SIZE wPreferences.icon_size
69 void appIconMouseDown(WObjDescriptor * desc, XEvent * event);
70 static void iconDblClick(WObjDescriptor * desc, XEvent * event);
71 static void iconExpose(WObjDescriptor * desc, XEvent * event);
72 static void wApplicationSaveIconPathFor(char *iconPath, char *wm_instance, char *wm_class);
73 static WAppIcon *wAppIconCreate(WWindow * leader_win);
74 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon);
75 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon);
76 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window);
78 /* This function is used if the application is a .app. It checks if it has an icon in it
79 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
81 void wApplicationExtractDirPackIcon(WScreen * scr, char *path, char *wm_instance, char *wm_class)
83 char *iconPath = NULL;
84 char *tmp = NULL;
86 if (strstr(path, ".app")) {
87 tmp = wmalloc(strlen(path) + 16);
89 if (scr->flags.supports_tiff) {
90 strcpy(tmp, path);
91 strcat(tmp, ".tiff");
92 if (access(tmp, R_OK) == 0)
93 iconPath = tmp;
96 if (!iconPath) {
97 strcpy(tmp, path);
98 strcat(tmp, ".xpm");
99 if (access(tmp, R_OK) == 0)
100 iconPath = tmp;
103 if (!iconPath)
104 wfree(tmp);
106 if (iconPath) {
107 wApplicationSaveIconPathFor(iconPath, wm_instance, wm_class);
108 wfree(iconPath);
113 WAppIcon *wAppIconCreateForDock(WScreen *scr, char *command, char *wm_instance, char *wm_class, int tile)
115 WAppIcon *aicon;
117 aicon = wmalloc(sizeof(WAppIcon));
118 wretain(aicon);
119 aicon->yindex = -1;
120 aicon->xindex = -1;
122 add_to_appicon_list(scr, aicon);
124 if (command)
125 aicon->command = wstrdup(command);
127 if (wm_class)
128 aicon->wm_class = wstrdup(wm_class);
130 if (wm_instance)
131 aicon->wm_instance = wstrdup(wm_instance);
133 if (strcmp(wm_class, "WMDock") == 0 && wPreferences.flags.clip_merged_in_dock)
134 tile = TILE_CLIP;
135 aicon->icon = icon_create_for_dock(scr, command, wm_instance, wm_class, tile);
137 #ifdef XDND
138 wXDNDMakeAwareness(aicon->icon->core->window);
139 #endif
141 /* will be overriden by dock */
142 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
143 aicon->icon->core->descriptor.handle_expose = iconExpose;
144 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
145 aicon->icon->core->descriptor.parent = aicon;
146 AddToStackList(aicon->icon->core);
148 return aicon;
151 void create_appicon_for_application(WApplication *wapp, WWindow *wwin)
153 /* Try to create an icon from the dock or clip */
154 create_appicon_from_dock(wwin, wapp, wapp->main_window);
156 /* If app_icon was not found, create it */
157 if (!wapp->app_icon) {
158 /* Create the icon */
159 wapp->app_icon = wAppIconCreate(wapp->main_window_desc);
160 wIconUpdate(wapp->app_icon->icon);
162 /* Now, paint the icon */
163 if (!WFLAGP(wapp->main_window_desc, no_appicon))
164 paint_app_icon(wapp);
167 /* Save the app_icon in a file */
168 save_appicon(wapp->app_icon, False);
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 = wapp->main_window_desc->screen_ptr;
205 WDock *attracting_dock;
206 int x = 0, y = 0;
207 Bool update_icon = False;
209 if (!wapp || !wapp->app_icon)
210 return;
212 icon = wapp->app_icon->icon;
213 wapp->app_icon->main_window = wapp->main_window;
215 /* If the icon is docked, don't continue */
216 if (wapp->app_icon->docked)
217 return;
219 attracting_dock = scr->attracting_drawer != NULL ?
220 scr->attracting_drawer :
221 scr->workspaces[scr->current_workspace]->clip;
222 if (attracting_dock && attracting_dock->attract_icons &&
223 wDockFindFreeSlot(attracting_dock, &x, &y)) {
224 wapp->app_icon->attracted = 1;
225 if (!icon->shadowed) {
226 icon->shadowed = 1;
227 update_icon = True;
229 wDockAttachIcon(attracting_dock, wapp->app_icon, x, y, update_icon);
230 } else {
231 /* We must know if the icon is painted in the screen,
232 * because if painted, then PlaceIcon will return the next
233 * space on the screen, and the icon will move */
234 if (wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL) {
235 PlaceIcon(scr, &x, &y, wGetHeadForWindow(wapp->main_window_desc));
236 wAppIconMove(wapp->app_icon, x, y);
237 wLowerFrame(icon->core);
241 /* If we want appicon (no_appicon is not set) and the icon is not
242 * in the appicon_list, we must add it. Else, we want to avoid
243 * having it on the list */
244 if (!WFLAGP(wapp->main_window_desc, no_appicon) &&
245 wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL)
246 add_to_appicon_list(scr, wapp->app_icon);
248 if (!attracting_dock || !wapp->app_icon->attracted || !attracting_dock->collapsed)
249 XMapWindow(dpy, icon->core->window);
251 if (wPreferences.auto_arrange_icons && !wapp->app_icon->attracted)
252 wArrangeIcons(scr, True);
255 void removeAppIconFor(WApplication *wapp)
257 if (!wapp->app_icon)
258 return;
260 if (wPreferences.highlight_active_app)
261 wIconSetHighlited(wapp->app_icon->icon, False);
262 if (wapp->app_icon->docked && !wapp->app_icon->attracted) {
263 wapp->app_icon->running = 0;
264 /* since we keep it, we don't care if it was attracted or not */
265 wapp->app_icon->attracted = 0;
266 wapp->app_icon->icon->shadowed = 0;
267 wapp->app_icon->main_window = None;
268 wapp->app_icon->pid = 0;
269 wapp->app_icon->icon->owner = NULL;
270 wapp->app_icon->icon->icon_win = None;
272 /* Set the icon image */
273 set_icon_image_from_database(wapp->app_icon->icon, wapp->app_icon->wm_instance,
274 wapp->app_icon->wm_class, wapp->app_icon->command);
276 /* Update the icon, because wapp->app_icon->icon could be NULL */
277 wIconUpdate(wapp->app_icon->icon);
279 /* Paint it */
280 wAppIconPaint(wapp->app_icon);
281 } else if (wapp->app_icon->docked) {
282 wapp->app_icon->running = 0;
283 if (wapp->app_icon->dock->type == WM_DRAWER) {
284 wDrawerFillTheGap(wapp->app_icon->dock, wapp->app_icon, True);
286 wDockDetach(wapp->app_icon->dock, wapp->app_icon);
287 } else {
288 wAppIconDestroy(wapp->app_icon);
291 wapp->app_icon = NULL;
293 if (wPreferences.auto_arrange_icons)
294 wArrangeIcons(wapp->main_window_desc->screen_ptr, True);
297 static WAppIcon *wAppIconCreate(WWindow *leader_win)
299 WAppIcon *aicon;
301 aicon = wmalloc(sizeof(WAppIcon));
302 wretain(aicon);
303 aicon->yindex = -1;
304 aicon->xindex = -1;
305 aicon->prev = NULL;
306 aicon->next = NULL;
308 if (leader_win->wm_class)
309 aicon->wm_class = wstrdup(leader_win->wm_class);
311 if (leader_win->wm_instance)
312 aicon->wm_instance = wstrdup(leader_win->wm_instance);
314 aicon->icon = icon_create_for_wwindow(leader_win);
315 #ifdef XDND
316 wXDNDMakeAwareness(aicon->icon->core->window);
317 #endif
319 /* will be overriden if docked */
320 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
321 aicon->icon->core->descriptor.handle_expose = iconExpose;
322 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
323 aicon->icon->core->descriptor.parent = aicon;
324 AddToStackList(aicon->icon->core);
325 aicon->icon->show_title = 0;
327 return aicon;
330 void wAppIconDestroy(WAppIcon * aicon)
332 WScreen *scr = aicon->icon->core->screen_ptr;
334 RemoveFromStackList(aicon->icon->core);
335 wIconDestroy(aicon->icon);
336 if (aicon->command)
337 wfree(aicon->command);
338 #ifdef XDND
339 if (aicon->dnd_command)
340 wfree(aicon->dnd_command);
341 #endif
342 if (aicon->wm_instance)
343 wfree(aicon->wm_instance);
345 if (aicon->wm_class)
346 wfree(aicon->wm_class);
348 remove_from_appicon_list(scr, aicon);
350 aicon->destroyed = 1;
351 wrelease(aicon);
354 static void drawCorner(WIcon * icon)
356 WScreen *scr = icon->core->screen_ptr;
357 XPoint points[3];
359 points[0].x = 1;
360 points[0].y = 1;
361 points[1].x = 12;
362 points[1].y = 1;
363 points[2].x = 1;
364 points[2].y = 12;
365 XFillPolygon(dpy, icon->core->window, scr->icon_title_texture->normal_gc,
366 points, 3, Convex, CoordModeOrigin);
367 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 0, 12);
368 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 12, 0);
371 void wAppIconMove(WAppIcon * aicon, int x, int y)
373 XMoveWindow(dpy, aicon->icon->core->window, x, y);
374 aicon->x_pos = x;
375 aicon->y_pos = y;
378 #ifdef WS_INDICATOR
379 static void updateDockNumbers(WScreen * scr)
381 int length;
382 char *ws_numbers;
383 WAppIcon *dicon = scr->dock->icon_array[0];
385 ws_numbers = wmalloc(20);
386 snprintf(ws_numbers, 20, "%i [ %i ]", scr->current_workspace + 1, ((scr->current_workspace / 10) + 1));
387 length = strlen(ws_numbers);
389 XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, WMFontHeight(scr->icon_title_font) + 1, False);
391 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->black,
392 scr->icon_title_font, 4, 3, ws_numbers, length);
394 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->white,
395 scr->icon_title_font, 3, 2, ws_numbers, length);
397 wfree(ws_numbers);
399 #endif /* WS_INDICATOR */
401 void wAppIconPaint(WAppIcon *aicon)
403 WApplication *wapp;
404 WScreen *scr = aicon->icon->core->screen_ptr;
406 if (aicon->icon->owner)
407 wapp = wApplicationOf(aicon->icon->owner->main_window);
408 else
409 wapp = NULL;
411 wIconPaint(aicon->icon);
413 # ifdef WS_INDICATOR
414 if (aicon->docked && scr->dock && scr->dock == aicon->dock && aicon->yindex == 0)
415 updateDockNumbers(scr);
416 # endif
417 if (scr->dock_dots && aicon->docked && !aicon->running && aicon->command != NULL) {
418 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
419 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
420 XCopyArea(dpy, scr->dock_dots->image, aicon->icon->core->window,
421 scr->copy_gc, 0, 0, scr->dock_dots->width, scr->dock_dots->height, 0, 0);
423 #ifdef HIDDENDOT
424 if (wapp && wapp->flags.hidden) {
425 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
426 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
427 XCopyArea(dpy, scr->dock_dots->image,
428 aicon->icon->core->window, scr->copy_gc, 0, 0, 7, scr->dock_dots->height, 0, 0);
430 #endif /* HIDDENDOT */
432 if (aicon->omnipresent)
433 drawCorner(aicon->icon);
435 XSetClipMask(dpy, scr->copy_gc, None);
436 if (aicon->launching)
437 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
438 0, 0, wPreferences.icon_size, wPreferences.icon_size);
441 /* Save the application icon, if it's a dockapp then use it with dock = True */
442 void save_appicon(WAppIcon *aicon, Bool dock)
444 char *path;
446 if (!aicon)
447 return;
449 if (dock && (!aicon->docked || aicon->attracted))
450 return;
452 path = wIconStore(aicon->icon);
453 if (!path)
454 return;
456 wApplicationSaveIconPathFor(path, aicon->wm_instance, aicon->wm_class);
457 wfree(path);
460 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
462 /* main_window may not have the full command line; try to find one which does */
463 static void relaunchApplication(WApplication *wapp)
465 WScreen *scr;
466 WWindow *wlist, *next;
468 scr = wapp->main_window_desc->screen_ptr;
469 wlist = scr->focused_window;
470 if (! wlist)
471 return;
473 while (wlist->prev)
474 wlist = wlist->prev;
476 while (wlist) {
477 next = wlist->next;
479 if (wlist->main_window == wapp->main_window) {
480 if (RelaunchWindow(wlist))
481 return;
484 wlist = next;
488 static void relaunchCallback(WMenu * menu, WMenuEntry * entry)
490 WApplication *wapp = (WApplication *) entry->clientdata;
492 relaunchApplication(wapp);
495 static void hideCallback(WMenu * menu, WMenuEntry * entry)
497 WApplication *wapp = (WApplication *) entry->clientdata;
499 if (wapp->flags.hidden) {
500 wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
501 wUnhideApplication(wapp, False, False);
502 } else {
503 wHideApplication(wapp);
507 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
509 WApplication *wapp = (WApplication *) entry->clientdata;
511 wUnhideApplication(wapp, False, True);
514 static void setIconCallback(WMenu *menu, WMenuEntry *entry)
516 WAppIcon *icon = ((WApplication *) entry->clientdata)->app_icon;
517 char *file = NULL;
518 WScreen *scr;
519 int result;
521 assert(icon != NULL);
523 if (icon->editing)
524 return;
526 icon->editing = 1;
527 scr = icon->icon->core->screen_ptr;
529 wretain(icon);
531 result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
533 if (result && !icon->destroyed) {
534 if (file && *file == 0) {
535 wfree(file);
536 file = NULL;
538 if (!wIconChangeImageFile(icon->icon, file)) {
539 wMessageDialog(scr, _("Error"),
540 _("Could not open specified icon file"), _("OK"), NULL, NULL);
541 } else {
542 wDefaultChangeIcon(scr, icon->wm_instance, icon->wm_class, file);
543 wAppIconPaint(icon);
545 if (file)
546 wfree(file);
548 icon->editing = 0;
549 wrelease(icon);
552 static void killCallback(WMenu * menu, WMenuEntry * entry)
554 WApplication *wapp = (WApplication *) entry->clientdata;
555 WFakeGroupLeader *fPtr;
556 char *buffer;
557 char *shortname;
558 char *basename(const char *shortname);
560 if (!WCHECK_STATE(WSTATE_NORMAL))
561 return;
563 WCHANGE_STATE(WSTATE_MODAL);
565 assert(entry->clientdata != NULL);
567 shortname = basename(wapp->app_icon->wm_instance);
569 buffer = wstrconcat(wapp->app_icon ? shortname : NULL,
570 _(" will be forcibly closed.\n"
571 "Any unsaved changes will be lost.\n" "Please confirm."));
573 fPtr = wapp->main_window_desc->fake_group;
575 wretain(wapp->main_window_desc);
576 if (wPreferences.dont_confirm_kill
577 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
578 buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
579 if (fPtr != NULL) {
580 WWindow *wwin, *twin;
582 wwin = wapp->main_window_desc->screen_ptr->focused_window;
583 while (wwin) {
584 twin = wwin->prev;
585 if (wwin->fake_group == fPtr)
586 wClientKill(wwin);
587 wwin = twin;
589 } else if (!wapp->main_window_desc->flags.destroyed) {
590 wClientKill(wapp->main_window_desc);
593 wrelease(wapp->main_window_desc);
594 wfree(buffer);
595 WCHANGE_STATE(WSTATE_NORMAL);
598 static WMenu *createApplicationMenu(WScreen *scr)
600 WMenu *menu;
602 menu = wMenuCreate(scr, NULL, False);
603 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
604 wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
605 wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
606 wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
607 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
609 return menu;
612 static void openApplicationMenu(WApplication * wapp, int x, int y)
614 WMenu *menu;
615 WScreen *scr = wapp->main_window_desc->screen_ptr;
616 int i;
618 if (!scr->icon_menu) {
619 scr->icon_menu = createApplicationMenu(scr);
620 wfree(scr->icon_menu->entries[1]->text);
623 menu = scr->icon_menu;
625 if (wapp->flags.hidden)
626 menu->entries[1]->text = _("Unhide");
627 else
628 menu->entries[1]->text = _("Hide");
630 menu->flags.realized = 0;
631 wMenuRealize(menu);
633 x -= menu->frame->core->width / 2;
634 if (x + menu->frame->core->width > scr->scr_width)
635 x = scr->scr_width - menu->frame->core->width;
637 if (x < 0)
638 x = 0;
640 /* set client data */
641 for (i = 0; i < menu->entry_no; i++)
642 menu->entries[i]->clientdata = wapp;
644 wMenuMapAt(menu, x, y, False);
647 /******************************************************************/
649 static void iconExpose(WObjDescriptor *desc, XEvent *event)
651 wAppIconPaint(desc->parent);
654 static void iconDblClick(WObjDescriptor *desc, XEvent *event)
656 WAppIcon *aicon = desc->parent;
657 WApplication *wapp;
658 WScreen *scr = aicon->icon->core->screen_ptr;
659 int unhideHere;
661 assert(aicon->icon->owner != NULL);
663 wapp = wApplicationOf(aicon->icon->owner->main_window);
665 if (event->xbutton.state & ControlMask) {
666 relaunchApplication(wapp);
667 return;
670 unhideHere = (event->xbutton.state & ShiftMask);
671 /* go to the last workspace that the user worked on the app */
672 if (!unhideHere && wapp->last_workspace != scr->current_workspace)
673 wWorkspaceChange(scr, wapp->last_workspace);
675 wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
677 if (event->xbutton.state & MOD_MASK)
678 wHideOtherApplications(aicon->icon->owner);
681 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
683 WAppIcon *aicon = desc->parent;
684 WScreen *scr = aicon->icon->core->screen_ptr;
685 Bool hasMoved;
687 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
688 return;
690 if (IsDoubleClick(scr, event)) {
691 /* Middle or right mouse actions were handled on first click */
692 if (event->xbutton.button == Button1)
693 iconDblClick(desc, event);
694 return;
697 if (event->xbutton.button == Button2) {
698 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
700 if (wapp)
701 relaunchApplication(wapp);
703 return;
706 if (event->xbutton.button == Button3) {
707 WObjDescriptor *desc;
708 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
710 if (!wapp)
711 return;
713 if (event->xbutton.send_event &&
714 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
715 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
716 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
717 wwarning("pointer grab failed for appicon menu");
718 return;
721 openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
723 /* allow drag select of menu */
724 desc = &scr->icon_menu->menu->descriptor;
725 event->xbutton.send_event = True;
726 (*desc->handle_mousedown) (desc, event);
727 return;
730 hasMoved = wHandleAppIconMove(aicon, event);
731 if (wPreferences.single_click && !hasMoved && aicon->dock != NULL)
733 iconDblClick(desc, event);
737 Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event)
739 WIcon *icon = aicon->icon;
740 WScreen *scr = icon->core->screen_ptr;
741 WDock *originalDock = aicon->dock; /* can be NULL */
742 WDock *lastDock = originalDock;
743 WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */
744 WDrawerChain *dc;
745 Bool done = False, dockable, ondock;
746 Bool grabbed = False;
747 Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
748 int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */
749 int omnipresent = aicon->omnipresent; /* this must be cached */
750 Bool showed_all_clips = False;
752 int clickButton = event->xbutton.button;
753 Pixmap ghost = None;
754 Window wins[2]; /* Managing shadow window */
755 XEvent ev;
757 int x = aicon->x_pos, y = aicon->y_pos;
758 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
759 int shad_x = x, shad_y = y;
760 int ix = aicon->xindex, iy = aicon->yindex;
761 int i;
762 int oldX = x;
763 int oldY = y;
764 Bool hasMoved = False;
766 if (wPreferences.flags.noupdates && originalDock != NULL)
767 return False;
769 if (!(event->xbutton.state & MOD_MASK))
770 wRaiseFrame(icon->core);
771 else {
772 /* If Mod is pressed for an docked appicon, assume it is to undock it,
773 * so don't lower it */
774 if (originalDock == NULL)
775 wLowerFrame(icon->core);
778 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
779 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
780 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
781 wwarning("Pointer grab failed in wHandleAppIconMove");
784 if (originalDock != NULL) {
785 dockable = True;
786 ondock = True;
788 else {
789 ondock = False;
790 if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer)
791 dockable = 0;
792 else
793 dockable = canBeDocked(icon->owner);
796 /* We try the various docks in that order:
797 * - First, the dock the appicon comes from, if any
798 * - Then, the drawers
799 * - Then, the "dock" (WM_DOCK)
800 * - Finally, the clip
802 i = 0;
803 if (originalDock != NULL)
804 allDocks[ i++ ] = originalDock;
805 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
806 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
807 if (dc->adrawer != originalDock)
808 allDocks[ i++ ] = dc->adrawer;
810 if (!wPreferences.flags.nodock && scr->dock != originalDock)
811 allDocks[ i++ ] = scr->dock;
812 if (!wPreferences.flags.noclip &&
813 originalDock != scr->workspaces[scr->current_workspace]->clip)
814 allDocks[ i++ ] = scr->workspaces[scr->current_workspace]->clip;
815 for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */
816 allDocks[ i ] = NULL;
818 wins[0] = icon->core->window;
819 wins[1] = scr->dock_shadow;
820 XRestackWindows(dpy, wins, 2);
821 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
822 if (superfluous) {
823 if (icon->pixmap != None)
824 ghost = MakeGhostIcon(scr, icon->pixmap);
825 else
826 ghost = MakeGhostIcon(scr, icon->core->window);
827 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
828 XClearWindow(dpy, scr->dock_shadow);
830 if (ondock)
831 XMapWindow(dpy, scr->dock_shadow);
833 while (!done) {
834 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
835 | ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
836 switch (ev.type) {
837 case Expose:
838 WMHandleEvent(&ev);
839 break;
841 case EnterNotify:
842 /* It means the cursor moved so fast that it entered
843 * something else (if moving slowly, it would have
844 * stayed in the appIcon that is being moved. Ignore
845 * such "spurious" EnterNotifiy's */
846 break;
848 case MotionNotify:
849 hasMoved = True;
850 if (!grabbed) {
851 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
852 || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
853 XChangeActivePointerGrab(dpy, ButtonMotionMask
854 | ButtonReleaseMask | ButtonPressMask,
855 wCursor[WCUR_MOVE], CurrentTime);
856 grabbed = 1;
857 } else {
858 break;
862 if (omnipresent && !showed_all_clips) {
863 int i;
864 for (i = 0; i < scr->workspace_count; i++) {
865 if (i == scr->current_workspace)
866 continue;
867 wDockShowIcons(scr->workspaces[i]->clip);
868 /* Note: if dock is collapsed (for instance, because it
869 auto-collapses), its icons still won't show up */
871 showed_all_clips = True; /* To prevent flickering */
874 x = ev.xmotion.x_root - ofs_x;
875 y = ev.xmotion.y_root - ofs_y;
876 wAppIconMove(aicon, x, y);
878 WDock *theNewDock = NULL;
879 if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock) {
880 for (i = 0; dockable && i < scr->drawer_count + 2; i++) {
881 WDock *theDock = allDocks[i];
882 if (theDock == NULL)
883 break;
884 if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) {
885 theNewDock = theDock;
886 break;
889 if (originalDock != NULL && theNewDock == NULL &&
890 (aicon->launching || aicon->lock || aicon->running)) {
891 /* In those cases, stay in lastDock if no dock really wants us */
892 theNewDock = lastDock;
895 if (lastDock != NULL && lastDock != theNewDock) {
896 /* Leave lastDock in the state we found it */
897 if (lastDock->type == WM_DRAWER) {
898 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
900 if (collapsed) {
901 lastDock->collapsed = 1;
902 wDockHideIcons(lastDock);
903 collapsed = False;
905 if (lastDock->auto_raise_lower) {
906 wDockLower(lastDock);
909 if (theNewDock != NULL) {
910 if (lastDock != theNewDock) {
911 collapsed = theNewDock->collapsed;
912 if (collapsed) {
913 theNewDock->collapsed = 0;
914 wDockShowIcons(theNewDock);
916 if (theNewDock->auto_raise_lower) {
917 wDockRaise(theNewDock);
918 /* And raise the moving tile above it */
919 wRaiseFrame(aicon->icon->core);
921 lastDock = theNewDock;
924 shad_x = lastDock->x_pos + ix*wPreferences.icon_size;
925 shad_y = lastDock->y_pos + iy*wPreferences.icon_size;
927 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
929 if (!ondock) {
930 XMapWindow(dpy, scr->dock_shadow);
932 ondock = 1;
933 } else {
934 lastDock = theNewDock; // i.e., NULL
935 if (ondock) {
936 XUnmapWindow(dpy, scr->dock_shadow);
938 * Leaving that weird comment for now.
939 * But if we see no gap, there is no need to fill one!
940 * We could test ondock first and the lastDock to NULL afterwards
941 if (lastDock_before_it_was_null->type == WM_DRAWER) {
942 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
943 } */
946 ondock = 0;
948 break;
950 case ButtonPress:
951 break;
953 case ButtonRelease:
954 if (ev.xbutton.button != clickButton)
955 break;
956 XUngrabPointer(dpy, CurrentTime);
958 Bool docked = False;
959 if (ondock) {
960 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
961 XUnmapWindow(dpy, scr->dock_shadow);
962 if (originalDock == NULL) { // docking an undocked appicon
963 docked = wDockAttachIcon(lastDock, aicon, ix, iy, False);
964 if (!docked) {
965 /* AppIcon got rejected (happens only when we can't get the
966 command for that appicon, and the user cancels the
967 wInputDialog asking for one). Make the rejection obvious by
968 sliding the icon to its old position */
969 if (lastDock->type == WM_DRAWER) {
970 // Also fill the gap left in the drawer
971 wDrawerFillTheGap(lastDock, aicon, False);
973 SlideWindow(icon->core->window, x, y, oldX, oldY);
976 else { // moving a docked appicon to a dock
977 if (originalDock == lastDock) {
978 docked = True;
979 wDockReattachIcon(originalDock, aicon, ix, iy);
981 else {
982 docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy);
983 if (!docked) {
984 /* Possible scenario: user moved an auto-attracted appicon
985 from the clip to the dock, and cancelled the wInputDialog
986 asking for a command */
987 if (lastDock->type == WM_DRAWER) {
988 wDrawerFillTheGap(lastDock, aicon, False);
990 /* If aicon comes from a drawer, make some room to reattach it */
991 if (originalDock->type == WM_DRAWER) {
992 WAppIcon *aiconsToShift[ originalDock->icon_count ];
993 int j = 0;
995 for (i = 0; i < originalDock->max_icons; i++) {
996 WAppIcon *ai = originalDock->icon_array[ i ];
997 if (ai && ai != aicon &&
998 abs(ai->xindex) >= abs(aicon->xindex))
999 aiconsToShift[j++] = ai;
1001 if (j != originalDock->icon_count - abs(aicon->xindex) - 1)
1002 // Trust this never happens?
1003 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1004 j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex);
1005 wSlideAppicons(aiconsToShift, j, originalDock->on_right_side);
1006 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1009 SlideWindow(icon->core->window, x, y, oldX, oldY);
1010 wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex);
1012 else {
1013 if (originalDock->auto_collapse && !originalDock->collapsed) {
1014 originalDock->collapsed = 1;
1015 wDockHideIcons(originalDock);
1017 if (originalDock->auto_raise_lower)
1018 wDockLower(originalDock);
1022 // No matter what happened above, check to lower lastDock
1023 // Don't see why I commented out the following 2 lines
1024 /* if (lastDock->auto_raise_lower)
1025 wDockLower(lastDock); */
1026 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1027 * collapsed, so that wHandleAppIconMove doesn't collapse it
1028 * right away (the timer will take care of it) */
1029 if (lastDock->auto_collapse)
1030 collapsed = 0;
1032 else {
1033 if (originalDock != NULL) { /* Detaching a docked appicon */
1034 if (superfluous) {
1035 if (!aicon->running && !wPreferences.no_animations) {
1036 /* We need to deselect it, even if is deselected in
1037 * wDockDetach(), because else DoKaboom() will fail.
1039 if (aicon->icon->selected)
1040 wIconSelect(aicon->icon);
1041 DoKaboom(scr, aicon->icon->core->window, x, y);
1044 wDockDetach(originalDock, aicon);
1045 if (originalDock->auto_collapse && !originalDock->collapsed) {
1046 originalDock->collapsed = 1;
1047 wDockHideIcons(originalDock);
1049 if (originalDock->auto_raise_lower)
1050 wDockLower(originalDock);
1053 // Can't remember why the icon hiding is better done above than below (commented out)
1054 // Also, lastDock is quite different from originalDock
1056 if (collapsed) {
1057 lastDock->collapsed = 1;
1058 wDockHideIcons(lastDock);
1059 collapsed = 0;
1062 if (superfluous) {
1063 if (ghost != None)
1064 XFreePixmap(dpy, ghost);
1065 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
1067 if (showed_all_clips) {
1068 int i;
1069 for (i = 0; i < scr->workspace_count; i++) {
1070 if (i == scr->current_workspace)
1071 continue;
1072 wDockHideIcons(scr->workspaces[i]->clip);
1075 if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked))
1076 /* Need to rearrange unless moving from dock to dock */
1077 wArrangeIcons(scr, True);
1078 return hasMoved;
1081 return False; /* Never reached */
1084 /* This function save the application icon and store the path in the Dictionary */
1085 static void wApplicationSaveIconPathFor(char *iconPath, char *wm_instance, char *wm_class)
1087 WMPropList *dict = WDWindowAttributes->dictionary;
1088 WMPropList *adict, *key, *iconk;
1089 WMPropList *val;
1090 char *tmp;
1092 tmp = get_name_for_instance_class(wm_instance, wm_class);
1093 key = WMCreatePLString(tmp);
1094 wfree(tmp);
1096 adict = WMGetFromPLDictionary(dict, key);
1097 iconk = WMCreatePLString("Icon");
1099 if (adict) {
1100 val = WMGetFromPLDictionary(adict, iconk);
1101 } else {
1102 /* no dictionary for app, so create one */
1103 adict = WMCreatePLDictionary(NULL, NULL);
1104 WMPutInPLDictionary(dict, key, adict);
1105 WMReleasePropList(adict);
1106 val = NULL;
1109 if (!val) {
1110 val = WMCreatePLString(iconPath);
1111 WMPutInPLDictionary(adict, iconk, val);
1112 WMReleasePropList(val);
1113 } else {
1114 val = NULL;
1117 WMReleasePropList(key);
1118 WMReleasePropList(iconk);
1120 if (val && !wPreferences.flags.noupdates)
1121 UpdateDomainFile(WDWindowAttributes);
1124 static WAppIcon *findDockIconFor(WDock *dock, Window main_window)
1126 WAppIcon *aicon = NULL;
1128 aicon = wDockFindIconForWindow(dock, main_window);
1129 if (!aicon) {
1130 wDockTrackWindowLaunch(dock, main_window);
1131 aicon = wDockFindIconForWindow(dock, main_window);
1133 return aicon;
1136 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window)
1138 WScreen *scr = wwin->screen_ptr;
1139 wapp->app_icon = NULL;
1141 if (scr->last_dock)
1142 wapp->app_icon = findDockIconFor(scr->last_dock, main_window);
1144 /* check main dock if we did not find it in last dock */
1145 if (!wapp->app_icon && scr->dock)
1146 wapp->app_icon = findDockIconFor(scr->dock, main_window);
1148 /* check clips */
1149 if (!wapp->app_icon) {
1150 int i;
1151 for (i = 0; i < scr->workspace_count; i++) {
1152 WDock *dock = scr->workspaces[i]->clip;
1153 if (dock)
1154 wapp->app_icon = findDockIconFor(dock, main_window);
1155 if (wapp->app_icon)
1156 break;
1160 /* Finally check drawers */
1161 if (!wapp->app_icon) {
1162 WDrawerChain *dc;
1163 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
1164 wapp->app_icon = findDockIconFor(dc->adrawer, main_window);
1165 if (wapp->app_icon)
1166 break;
1170 /* If created, then set some flags */
1171 if (wapp->app_icon) {
1172 WWindow *mainw = wapp->main_window_desc;
1174 wapp->app_icon->running = 1;
1175 wapp->app_icon->icon->owner = mainw;
1176 if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
1177 wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
1179 /* Update the icon images */
1180 wIconUpdate(wapp->app_icon->icon);
1182 /* Paint it */
1183 wAppIconPaint(wapp->app_icon);
1184 save_appicon(wapp->app_icon, True);
1188 /* Add the appicon to the appiconlist */
1189 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon)
1191 appicon->prev = NULL;
1192 appicon->next = scr->app_icon_list;
1193 if (scr->app_icon_list)
1194 scr->app_icon_list->prev = appicon;
1196 scr->app_icon_list = appicon;
1199 /* Remove the appicon from the appiconlist */
1200 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon)
1202 if (appicon == scr->app_icon_list) {
1203 if (appicon->next)
1204 appicon->next->prev = NULL;
1205 scr->app_icon_list = appicon->next;
1206 } else {
1207 if (appicon->next)
1208 appicon->next->prev = appicon->prev;
1209 if (appicon->prev)
1210 appicon->prev->next = appicon->next;
1213 appicon->prev = NULL;
1214 appicon->next = NULL;
1217 /* Return the AppIcon associated with a given (Xlib) Window. */
1218 WAppIcon *wAppIconFor(Window window)
1220 WObjDescriptor *desc;
1222 if (window == None)
1223 return NULL;
1225 if (XFindContext(dpy, window, wWinContext, (XPointer *) & desc) == XCNOENT)
1226 return NULL;
1228 if (desc->parent_type == WCLASS_APPICON || desc->parent_type == WCLASS_DOCK_ICON)
1229 return desc->parent;
1231 return NULL;