src/appicon.c: updated paint_app_icon
[wmaker-crm.git] / src / appicon.c
blobbbf1455ffe9f2c373c41bd28ef7509434b7310bd
1 /* appicon.c- icon for applications (not mini-window)
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "wconfig.h"
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #include <stdlib.h>
28 #include <libgen.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
32 #include <errno.h>
34 #include "WindowMaker.h"
35 #include "window.h"
36 #include "icon.h"
37 #include "application.h"
38 #include "appicon.h"
39 #include "actions.h"
40 #include "stacking.h"
41 #include "dock.h"
42 #include "main.h"
43 #include "defaults.h"
44 #include "workspace.h"
45 #include "superfluous.h"
46 #include "menu.h"
47 #include "framewin.h"
48 #include "dialog.h"
49 #include "xinerama.h"
50 #include "client.h"
51 #include "placement.h"
52 #include "misc.h"
53 #include "event.h"
54 #ifdef XDND
55 #include "xdnd.h"
56 #endif
59 * icon_file for the dock is got from the preferences file by
60 * using the classname/instancename
63 #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(WAppIcon *appicon);
71 static void remove_from_appicon_list(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(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 (strcmp(wm_class, "WMDock") == 0 && wPreferences.flags.clip_merged_in_dock)
130 tile = TILE_CLIP;
131 aicon->icon = icon_create_for_dock(scr, command, wm_instance, wm_class, tile);
133 #ifdef 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 /* Save the app_icon in a file */
164 save_appicon(wapp->app_icon, False);
167 void unpaint_app_icon(WApplication *wapp)
169 WAppIcon *aicon;
170 WScreen *scr;
171 WDock *clip;
173 if (!wapp || !wapp->app_icon)
174 return;
176 aicon = wapp->app_icon;
178 /* If the icon is docked, don't continue */
179 if (aicon->docked)
180 return;
182 scr = wapp->main_window_desc->screen_ptr;
183 clip = w_global.workspace.array[w_global.workspace.current]->clip;
185 if (!clip || !aicon->attracted || !clip->collapsed)
186 XUnmapWindow(dpy, aicon->icon->core->window);
188 /* We want to avoid having it on the list because otherwise
189 * there will be a hole when the icons are arranged with
190 * wArrangeIcons() */
191 remove_from_appicon_list(aicon);
193 if (wPreferences.auto_arrange_icons && !aicon->attracted)
194 wArrangeIcons(scr, True);
197 void paint_app_icon(WApplication *wapp)
199 WIcon *icon;
200 WScreen *scr;
201 WDock *attracting_dock;
202 int x = 0, y = 0;
203 Bool update_icon = False;
205 if (!wapp || !wapp->app_icon || !wapp->main_window_desc)
206 return;
208 icon = wapp->app_icon->icon;
209 scr = wapp->main_window_desc->screen_ptr;
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 w_global.workspace.array[w_global.workspace.current]->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(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 /* Update the icon, because wapp->app_icon->icon could be NULL */
274 wIconUpdate(wapp->app_icon->icon);
276 /* Paint it */
277 wAppIconPaint(wapp->app_icon);
278 } else if (wapp->app_icon->docked) {
279 wapp->app_icon->running = 0;
280 if (wapp->app_icon->dock->type == WM_DRAWER) {
281 wDrawerFillTheGap(wapp->app_icon->dock, wapp->app_icon, True);
283 wDockDetach(wapp->app_icon->dock, wapp->app_icon);
284 } else {
285 wAppIconDestroy(wapp->app_icon);
288 wapp->app_icon = NULL;
290 if (wPreferences.auto_arrange_icons)
291 wArrangeIcons(wapp->main_window_desc->screen_ptr, True);
294 static WAppIcon *wAppIconCreate(WWindow *leader_win)
296 WAppIcon *aicon;
298 aicon = wmalloc(sizeof(WAppIcon));
299 wretain(aicon);
300 aicon->yindex = -1;
301 aicon->xindex = -1;
302 aicon->prev = NULL;
303 aicon->next = NULL;
305 if (leader_win->wm_class)
306 aicon->wm_class = wstrdup(leader_win->wm_class);
308 if (leader_win->wm_instance)
309 aicon->wm_instance = wstrdup(leader_win->wm_instance);
311 aicon->icon = icon_create_for_wwindow(leader_win);
312 #ifdef XDND
313 wXDNDMakeAwareness(aicon->icon->core->window);
314 #endif
316 /* will be overriden if docked */
317 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
318 aicon->icon->core->descriptor.handle_expose = iconExpose;
319 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
320 aicon->icon->core->descriptor.parent = aicon;
321 AddToStackList(aicon->icon->core);
322 aicon->icon->show_title = 0;
324 return aicon;
327 void wAppIconDestroy(WAppIcon *aicon)
329 RemoveFromStackList(aicon->icon->core);
330 wIconDestroy(aicon->icon);
331 if (aicon->command)
332 wfree(aicon->command);
333 #ifdef XDND
334 if (aicon->dnd_command)
335 wfree(aicon->dnd_command);
336 #endif
337 if (aicon->wm_instance)
338 wfree(aicon->wm_instance);
340 if (aicon->wm_class)
341 wfree(aicon->wm_class);
343 remove_from_appicon_list(aicon);
345 aicon->destroyed = 1;
346 wrelease(aicon);
349 static void drawCorner(WIcon * icon)
351 WScreen *scr = icon->core->screen_ptr;
352 XPoint points[3];
354 points[0].x = 1;
355 points[0].y = 1;
356 points[1].x = 12;
357 points[1].y = 1;
358 points[2].x = 1;
359 points[2].y = 12;
360 XFillPolygon(dpy, icon->core->window, scr->icon_title_texture->normal_gc,
361 points, 3, Convex, CoordModeOrigin);
362 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 0, 12);
363 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 12, 0);
366 void wAppIconMove(WAppIcon * aicon, int x, int y)
368 XMoveWindow(dpy, aicon->icon->core->window, x, y);
369 aicon->x_pos = x;
370 aicon->y_pos = y;
373 #ifdef WS_INDICATOR
374 static void updateDockNumbers(WScreen *scr)
376 int length;
377 char *ws_numbers;
378 WAppIcon *dicon = scr->dock->icon_array[0];
380 ws_numbers = wmalloc(20);
381 snprintf(ws_numbers, 20, "%i [ %i ]", w_global.workspace.current + 1, ((w_global.workspace.current / 10) + 1));
382 length = strlen(ws_numbers);
384 XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, WMFontHeight(scr->icon_title_font) + 1, False);
386 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->black,
387 scr->icon_title_font, 4, 3, ws_numbers, length);
389 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->white,
390 scr->icon_title_font, 3, 2, ws_numbers, length);
392 wfree(ws_numbers);
394 #endif /* WS_INDICATOR */
396 void wAppIconPaint(WAppIcon *aicon)
398 WApplication *wapp;
399 WScreen *scr = aicon->icon->core->screen_ptr;
401 if (aicon->icon->owner)
402 wapp = wApplicationOf(aicon->icon->owner->main_window);
403 else
404 wapp = NULL;
406 wIconPaint(aicon->icon);
408 # ifdef WS_INDICATOR
409 if (aicon->docked && scr->dock && scr->dock == aicon->dock && aicon->yindex == 0)
410 updateDockNumbers(scr);
411 # endif
412 if (scr->dock_dots && aicon->docked && !aicon->running && aicon->command != NULL) {
413 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
414 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
415 XCopyArea(dpy, scr->dock_dots->image, aicon->icon->core->window,
416 scr->copy_gc, 0, 0, scr->dock_dots->width, scr->dock_dots->height, 0, 0);
418 #ifdef HIDDENDOT
419 if (wapp && wapp->flags.hidden) {
420 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
421 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
422 XCopyArea(dpy, scr->dock_dots->image,
423 aicon->icon->core->window, scr->copy_gc, 0, 0, 7, scr->dock_dots->height, 0, 0);
425 #endif /* HIDDENDOT */
427 if (aicon->omnipresent)
428 drawCorner(aicon->icon);
430 XSetClipMask(dpy, scr->copy_gc, None);
431 if (aicon->launching)
432 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
433 0, 0, wPreferences.icon_size, wPreferences.icon_size);
436 /* Save the application icon, if it's a dockapp then use it with dock = True */
437 void save_appicon(WAppIcon *aicon, Bool dock)
439 char *path;
441 if (!aicon)
442 return;
444 if (dock && (!aicon->docked || aicon->attracted))
445 return;
447 path = wIconStore(aicon->icon);
448 if (!path)
449 return;
451 wApplicationSaveIconPathFor(path, aicon->wm_instance, aicon->wm_class);
452 wfree(path);
455 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
457 /* main_window may not have the full command line; try to find one which does */
458 static void relaunchApplication(WApplication *wapp)
460 WScreen *scr;
461 WWindow *wlist, *next;
463 scr = wapp->main_window_desc->screen_ptr;
464 wlist = scr->focused_window;
465 if (! wlist)
466 return;
468 while (wlist->prev)
469 wlist = wlist->prev;
471 while (wlist) {
472 next = wlist->next;
474 if (wlist->main_window == wapp->main_window) {
475 if (RelaunchWindow(wlist))
476 return;
479 wlist = next;
483 static void relaunchCallback(WMenu * menu, WMenuEntry * entry)
485 WApplication *wapp = (WApplication *) entry->clientdata;
487 /* Parameter not used, but tell the compiler that it is ok */
488 (void) menu;
490 relaunchApplication(wapp);
493 static void hideCallback(WMenu * menu, WMenuEntry * entry)
495 WApplication *wapp = (WApplication *) entry->clientdata;
497 if (wapp->flags.hidden) {
498 wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
499 wUnhideApplication(wapp, False, False);
500 } else {
501 wHideApplication(wapp);
505 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
507 WApplication *wapp = (WApplication *) entry->clientdata;
509 /* Parameter not used, but tell the compiler that it is ok */
510 (void) menu;
512 wUnhideApplication(wapp, False, True);
515 static void setIconCallback(WMenu *menu, WMenuEntry *entry)
517 WAppIcon *icon = ((WApplication *) entry->clientdata)->app_icon;
518 char *file = NULL;
519 WScreen *scr;
520 int result;
522 /* Parameter not used, but tell the compiler that it is ok */
523 (void) menu;
525 assert(icon != NULL);
527 if (icon->editing)
528 return;
530 icon->editing = 1;
531 scr = icon->icon->core->screen_ptr;
533 wretain(icon);
535 result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
537 if (result && !icon->destroyed) {
538 if (file && *file == 0) {
539 wfree(file);
540 file = NULL;
542 if (!wIconChangeImageFile(icon->icon, file)) {
543 wMessageDialog(scr, _("Error"),
544 _("Could not open specified icon file"), _("OK"), NULL, NULL);
545 } else {
546 wDefaultChangeIcon(icon->wm_instance, icon->wm_class, file);
547 wAppIconPaint(icon);
549 if (file)
550 wfree(file);
552 icon->editing = 0;
553 wrelease(icon);
556 static void killCallback(WMenu * menu, WMenuEntry * entry)
558 WApplication *wapp = (WApplication *) entry->clientdata;
559 WFakeGroupLeader *fPtr;
560 char *buffer;
561 char *shortname;
563 if (!WCHECK_STATE(WSTATE_NORMAL))
564 return;
566 WCHANGE_STATE(WSTATE_MODAL);
568 assert(entry->clientdata != NULL);
570 shortname = basename(wapp->app_icon->wm_instance);
572 buffer = wstrconcat(wapp->app_icon ? shortname : NULL,
573 _(" will be forcibly closed.\n"
574 "Any unsaved changes will be lost.\n" "Please confirm."));
576 fPtr = wapp->main_window_desc->fake_group;
578 wretain(wapp->main_window_desc);
579 if (wPreferences.dont_confirm_kill
580 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
581 buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
582 if (fPtr != NULL) {
583 WWindow *wwin, *twin;
585 wwin = wapp->main_window_desc->screen_ptr->focused_window;
586 while (wwin) {
587 twin = wwin->prev;
588 if (wwin->fake_group == fPtr)
589 wClientKill(wwin);
590 wwin = twin;
592 } else if (!wapp->main_window_desc->flags.destroyed) {
593 wClientKill(wapp->main_window_desc);
596 wrelease(wapp->main_window_desc);
597 wfree(buffer);
598 WCHANGE_STATE(WSTATE_NORMAL);
601 static WMenu *createApplicationMenu(WScreen *scr)
603 WMenu *menu;
605 menu = wMenuCreate(scr, NULL, False);
606 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
607 wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
608 wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
609 wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
610 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
612 return menu;
615 static void openApplicationMenu(WApplication * wapp, int x, int y)
617 WMenu *menu;
618 WScreen *scr = wapp->main_window_desc->screen_ptr;
619 int i;
621 if (!scr->icon_menu) {
622 scr->icon_menu = createApplicationMenu(scr);
623 wfree(scr->icon_menu->entries[1]->text);
626 menu = scr->icon_menu;
628 if (wapp->flags.hidden)
629 menu->entries[1]->text = _("Unhide");
630 else
631 menu->entries[1]->text = _("Hide");
633 menu->flags.realized = 0;
634 wMenuRealize(menu);
636 x -= menu->frame->core->width / 2;
637 if (x + menu->frame->core->width > scr->scr_width)
638 x = scr->scr_width - menu->frame->core->width;
640 if (x < 0)
641 x = 0;
643 /* set client data */
644 for (i = 0; i < menu->entry_no; i++)
645 menu->entries[i]->clientdata = wapp;
647 wMenuMapAt(menu, x, y, False);
650 /******************************************************************/
652 static void iconExpose(WObjDescriptor *desc, XEvent *event)
654 /* Parameter not used, but tell the compiler that it is ok */
655 (void) event;
657 wAppIconPaint(desc->parent);
660 static void iconDblClick(WObjDescriptor *desc, XEvent *event)
662 WAppIcon *aicon = desc->parent;
663 WApplication *wapp;
664 WScreen *scr = aicon->icon->core->screen_ptr;
665 int unhideHere;
667 assert(aicon->icon->owner != NULL);
669 wapp = wApplicationOf(aicon->icon->owner->main_window);
671 if (event->xbutton.state & ControlMask) {
672 relaunchApplication(wapp);
673 return;
676 unhideHere = (event->xbutton.state & ShiftMask);
677 /* go to the last workspace that the user worked on the app */
678 if (!unhideHere && wapp->last_workspace != w_global.workspace.current)
679 wWorkspaceChange(scr, wapp->last_workspace);
681 wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
683 if (event->xbutton.state & MOD_MASK)
684 wHideOtherApplications(aicon->icon->owner);
687 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
689 WAppIcon *aicon = desc->parent;
690 WScreen *scr = aicon->icon->core->screen_ptr;
691 Bool hasMoved;
693 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
694 return;
696 if (IsDoubleClick(scr, event)) {
697 /* Middle or right mouse actions were handled on first click */
698 if (event->xbutton.button == Button1)
699 iconDblClick(desc, event);
700 return;
703 if (event->xbutton.button == Button2) {
704 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
706 if (wapp)
707 relaunchApplication(wapp);
709 return;
712 if (event->xbutton.button == Button3) {
713 WObjDescriptor *desc;
714 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
716 if (!wapp)
717 return;
719 if (event->xbutton.send_event &&
720 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
721 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
722 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
723 wwarning("pointer grab failed for appicon menu");
724 return;
727 openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
729 /* allow drag select of menu */
730 desc = &scr->icon_menu->menu->descriptor;
731 event->xbutton.send_event = True;
732 (*desc->handle_mousedown) (desc, event);
733 return;
736 hasMoved = wHandleAppIconMove(aicon, event);
737 if (wPreferences.single_click && !hasMoved && aicon->dock != NULL)
739 iconDblClick(desc, event);
743 Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event)
745 WIcon *icon = aicon->icon;
746 WScreen *scr = icon->core->screen_ptr;
747 WDock *originalDock = aicon->dock; /* can be NULL */
748 WDock *lastDock = originalDock;
749 WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */
750 WDrawerChain *dc;
751 Bool done = False, dockable, ondock;
752 Bool grabbed = False;
753 Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
754 int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */
755 int omnipresent = aicon->omnipresent; /* this must be cached */
756 Bool showed_all_clips = False;
758 int clickButton = event->xbutton.button;
759 Pixmap ghost = None;
760 Window wins[2]; /* Managing shadow window */
761 XEvent ev;
763 int x = aicon->x_pos, y = aicon->y_pos;
764 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
765 int shad_x = x, shad_y = y;
766 int ix = aicon->xindex, iy = aicon->yindex;
767 int i;
768 int oldX = x;
769 int oldY = y;
770 Bool hasMoved = False;
772 if (wPreferences.flags.noupdates && originalDock != NULL)
773 return False;
775 if (!(event->xbutton.state & MOD_MASK))
776 wRaiseFrame(icon->core);
777 else {
778 /* If Mod is pressed for an docked appicon, assume it is to undock it,
779 * so don't lower it */
780 if (originalDock == NULL)
781 wLowerFrame(icon->core);
784 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
785 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
786 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
787 wwarning("Pointer grab failed in wHandleAppIconMove");
790 if (originalDock != NULL) {
791 dockable = True;
792 ondock = True;
794 else {
795 ondock = False;
796 if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer)
797 dockable = 0;
798 else
799 dockable = canBeDocked(icon->owner);
802 /* We try the various docks in that order:
803 * - First, the dock the appicon comes from, if any
804 * - Then, the drawers
805 * - Then, the "dock" (WM_DOCK)
806 * - Finally, the clip
808 i = 0;
809 if (originalDock != NULL)
810 allDocks[ i++ ] = originalDock;
811 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
812 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
813 if (dc->adrawer != originalDock)
814 allDocks[ i++ ] = dc->adrawer;
816 if (!wPreferences.flags.nodock && scr->dock != originalDock)
817 allDocks[ i++ ] = scr->dock;
819 if (!wPreferences.flags.noclip &&
820 originalDock != w_global.workspace.array[w_global.workspace.current]->clip)
821 allDocks[i++] = w_global.workspace.array[w_global.workspace.current]->clip;
823 for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */
824 allDocks[ i ] = NULL;
826 wins[0] = icon->core->window;
827 wins[1] = scr->dock_shadow;
828 XRestackWindows(dpy, wins, 2);
829 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
830 if (superfluous) {
831 if (icon->pixmap != None)
832 ghost = MakeGhostIcon(scr, icon->pixmap);
833 else
834 ghost = MakeGhostIcon(scr, icon->core->window);
835 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
836 XClearWindow(dpy, scr->dock_shadow);
838 if (ondock)
839 XMapWindow(dpy, scr->dock_shadow);
841 while (!done) {
842 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
843 | ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
844 switch (ev.type) {
845 case Expose:
846 WMHandleEvent(&ev);
847 break;
849 case EnterNotify:
850 /* It means the cursor moved so fast that it entered
851 * something else (if moving slowly, it would have
852 * stayed in the appIcon that is being moved. Ignore
853 * such "spurious" EnterNotifiy's */
854 break;
856 case MotionNotify:
857 hasMoved = True;
858 if (!grabbed) {
859 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
860 || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
861 XChangeActivePointerGrab(dpy, ButtonMotionMask
862 | ButtonReleaseMask | ButtonPressMask,
863 wPreferences.cursor[WCUR_MOVE], CurrentTime);
864 grabbed = 1;
865 } else {
866 break;
870 if (omnipresent && !showed_all_clips) {
871 int i;
872 for (i = 0; i < w_global.workspace.count; i++) {
873 if (i == w_global.workspace.current)
874 continue;
876 wDockShowIcons(w_global.workspace.array[i]->clip);
877 /* Note: if dock is collapsed (for instance, because it
878 auto-collapses), its icons still won't show up */
880 showed_all_clips = True; /* To prevent flickering */
883 x = ev.xmotion.x_root - ofs_x;
884 y = ev.xmotion.y_root - ofs_y;
885 wAppIconMove(aicon, x, y);
887 WDock *theNewDock = NULL;
888 if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock || originalDock == NULL) {
889 for (i = 0; dockable && i < scr->drawer_count + 2; i++) {
890 WDock *theDock = allDocks[i];
891 if (theDock == NULL)
892 break;
893 if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) {
894 theNewDock = theDock;
895 break;
898 if (originalDock != NULL && theNewDock == NULL &&
899 (aicon->launching || aicon->lock || aicon->running)) {
900 /* In those cases, stay in lastDock if no dock really wants us */
901 theNewDock = lastDock;
904 if (lastDock != NULL && lastDock != theNewDock) {
905 /* Leave lastDock in the state we found it */
906 if (lastDock->type == WM_DRAWER) {
907 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
909 if (collapsed) {
910 lastDock->collapsed = 1;
911 wDockHideIcons(lastDock);
912 collapsed = False;
914 if (lastDock->auto_raise_lower) {
915 wDockLower(lastDock);
918 if (theNewDock != NULL) {
919 if (lastDock != theNewDock) {
920 collapsed = theNewDock->collapsed;
921 if (collapsed) {
922 theNewDock->collapsed = 0;
923 wDockShowIcons(theNewDock);
925 if (theNewDock->auto_raise_lower) {
926 wDockRaise(theNewDock);
927 /* And raise the moving tile above it */
928 wRaiseFrame(aicon->icon->core);
930 lastDock = theNewDock;
933 shad_x = lastDock->x_pos + ix*wPreferences.icon_size;
934 shad_y = lastDock->y_pos + iy*wPreferences.icon_size;
936 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
938 if (!ondock) {
939 XMapWindow(dpy, scr->dock_shadow);
941 ondock = 1;
942 } else {
943 lastDock = theNewDock; // i.e., NULL
944 if (ondock) {
945 XUnmapWindow(dpy, scr->dock_shadow);
947 * Leaving that weird comment for now.
948 * But if we see no gap, there is no need to fill one!
949 * We could test ondock first and the lastDock to NULL afterwards
950 if (lastDock_before_it_was_null->type == WM_DRAWER) {
951 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
952 } */
955 ondock = 0;
957 break;
959 case ButtonPress:
960 break;
962 case ButtonRelease:
963 if (ev.xbutton.button != clickButton)
964 break;
965 XUngrabPointer(dpy, CurrentTime);
967 Bool docked = False;
968 if (ondock) {
969 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
970 XUnmapWindow(dpy, scr->dock_shadow);
971 if (originalDock == NULL) { // docking an undocked appicon
972 docked = wDockAttachIcon(lastDock, aicon, ix, iy, False);
973 if (!docked) {
974 /* AppIcon got rejected (happens only when we can't get the
975 command for that appicon, and the user cancels the
976 wInputDialog asking for one). Make the rejection obvious by
977 sliding the icon to its old position */
978 if (lastDock->type == WM_DRAWER) {
979 // Also fill the gap left in the drawer
980 wDrawerFillTheGap(lastDock, aicon, False);
982 SlideWindow(icon->core->window, x, y, oldX, oldY);
985 else { // moving a docked appicon to a dock
986 if (originalDock == lastDock) {
987 docked = True;
988 wDockReattachIcon(originalDock, aicon, ix, iy);
990 else {
991 docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy);
992 if (!docked) {
993 /* Possible scenario: user moved an auto-attracted appicon
994 from the clip to the dock, and cancelled the wInputDialog
995 asking for a command */
996 if (lastDock->type == WM_DRAWER) {
997 wDrawerFillTheGap(lastDock, aicon, False);
999 /* If aicon comes from a drawer, make some room to reattach it */
1000 if (originalDock->type == WM_DRAWER) {
1001 WAppIcon *aiconsToShift[ originalDock->icon_count ];
1002 int j = 0;
1004 for (i = 0; i < originalDock->max_icons; i++) {
1005 WAppIcon *ai = originalDock->icon_array[ i ];
1006 if (ai && ai != aicon &&
1007 abs(ai->xindex) >= abs(aicon->xindex))
1008 aiconsToShift[j++] = ai;
1010 if (j != originalDock->icon_count - abs(aicon->xindex) - 1)
1011 // Trust this never happens?
1012 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1013 j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex);
1014 wSlideAppicons(aiconsToShift, j, originalDock->on_right_side);
1015 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1018 SlideWindow(icon->core->window, x, y, oldX, oldY);
1019 wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex);
1021 else {
1022 if (originalDock->auto_collapse && !originalDock->collapsed) {
1023 originalDock->collapsed = 1;
1024 wDockHideIcons(originalDock);
1026 if (originalDock->auto_raise_lower)
1027 wDockLower(originalDock);
1031 // No matter what happened above, check to lower lastDock
1032 // Don't see why I commented out the following 2 lines
1033 /* if (lastDock->auto_raise_lower)
1034 wDockLower(lastDock); */
1035 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1036 * collapsed, so that wHandleAppIconMove doesn't collapse it
1037 * right away (the timer will take care of it) */
1038 if (lastDock->auto_collapse)
1039 collapsed = 0;
1041 else {
1042 if (originalDock != NULL) { /* Detaching a docked appicon */
1043 if (superfluous) {
1044 if (!aicon->running && !wPreferences.no_animations) {
1045 /* We need to deselect it, even if is deselected in
1046 * wDockDetach(), because else DoKaboom() will fail.
1048 if (aicon->icon->selected)
1049 wIconSelect(aicon->icon);
1050 DoKaboom(scr, aicon->icon->core->window, x, y);
1053 wDockDetach(originalDock, aicon);
1054 if (originalDock->auto_collapse && !originalDock->collapsed) {
1055 originalDock->collapsed = 1;
1056 wDockHideIcons(originalDock);
1058 if (originalDock->auto_raise_lower)
1059 wDockLower(originalDock);
1062 // Can't remember why the icon hiding is better done above than below (commented out)
1063 // Also, lastDock is quite different from originalDock
1065 if (collapsed) {
1066 lastDock->collapsed = 1;
1067 wDockHideIcons(lastDock);
1068 collapsed = 0;
1071 if (superfluous) {
1072 if (ghost != None)
1073 XFreePixmap(dpy, ghost);
1074 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
1076 if (showed_all_clips) {
1077 int i;
1078 for (i = 0; i < w_global.workspace.count; i++) {
1079 if (i == w_global.workspace.current)
1080 continue;
1082 wDockHideIcons(w_global.workspace.array[i]->clip);
1085 if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked))
1086 /* Need to rearrange unless moving from dock to dock */
1087 wArrangeIcons(scr, True);
1088 return hasMoved;
1091 return False; /* Never reached */
1094 /* This function save the application icon and store the path in the Dictionary */
1095 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const char *wm_class)
1097 WMPropList *dict = w_global.domain.window_attr->dictionary;
1098 WMPropList *adict, *key, *iconk;
1099 WMPropList *val;
1100 char *tmp;
1102 tmp = get_name_for_instance_class(wm_instance, wm_class);
1103 key = WMCreatePLString(tmp);
1104 wfree(tmp);
1106 adict = WMGetFromPLDictionary(dict, key);
1107 iconk = WMCreatePLString("Icon");
1109 if (adict) {
1110 val = WMGetFromPLDictionary(adict, iconk);
1111 } else {
1112 /* no dictionary for app, so create one */
1113 adict = WMCreatePLDictionary(NULL, NULL);
1114 WMPutInPLDictionary(dict, key, adict);
1115 WMReleasePropList(adict);
1116 val = NULL;
1119 if (!val) {
1120 val = WMCreatePLString(iconPath);
1121 WMPutInPLDictionary(adict, iconk, val);
1122 WMReleasePropList(val);
1123 } else {
1124 val = NULL;
1127 WMReleasePropList(key);
1128 WMReleasePropList(iconk);
1130 if (val && !wPreferences.flags.noupdates)
1131 UpdateDomainFile(w_global.domain.window_attr);
1134 static WAppIcon *findDockIconFor(WDock *dock, Window main_window)
1136 WAppIcon *aicon = NULL;
1138 aicon = wDockFindIconForWindow(dock, main_window);
1139 if (!aicon) {
1140 wDockTrackWindowLaunch(dock, main_window);
1141 aicon = wDockFindIconForWindow(dock, main_window);
1143 return aicon;
1146 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window)
1148 WScreen *scr = wwin->screen_ptr;
1149 wapp->app_icon = NULL;
1151 if (scr->last_dock)
1152 wapp->app_icon = findDockIconFor(scr->last_dock, main_window);
1154 /* check main dock if we did not find it in last dock */
1155 if (!wapp->app_icon && scr->dock)
1156 wapp->app_icon = findDockIconFor(scr->dock, main_window);
1158 /* check clips */
1159 if (!wapp->app_icon) {
1160 int i;
1161 for (i = 0; i < w_global.workspace.count; i++) {
1162 WDock *dock = w_global.workspace.array[i]->clip;
1164 if (dock)
1165 wapp->app_icon = findDockIconFor(dock, main_window);
1167 if (wapp->app_icon)
1168 break;
1172 /* Finally check drawers */
1173 if (!wapp->app_icon) {
1174 WDrawerChain *dc;
1175 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
1176 wapp->app_icon = findDockIconFor(dc->adrawer, main_window);
1177 if (wapp->app_icon)
1178 break;
1182 /* If created, then set some flags */
1183 if (wapp->app_icon) {
1184 WWindow *mainw = wapp->main_window_desc;
1186 wapp->app_icon->running = 1;
1187 wapp->app_icon->icon->owner = mainw;
1188 if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
1189 wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
1191 /* Update the icon images */
1192 wIconUpdate(wapp->app_icon->icon);
1194 /* Paint it */
1195 wAppIconPaint(wapp->app_icon);
1196 save_appicon(wapp->app_icon, True);
1200 /* Add the appicon to the appiconlist */
1201 static void add_to_appicon_list(WAppIcon *appicon)
1203 appicon->prev = NULL;
1204 appicon->next = w_global.app_icon_list;
1205 if (w_global.app_icon_list)
1206 w_global.app_icon_list->prev = appicon;
1208 w_global.app_icon_list = appicon;
1211 /* Remove the appicon from the appiconlist */
1212 static void remove_from_appicon_list(WAppIcon *appicon)
1214 if (appicon == w_global.app_icon_list) {
1215 if (appicon->next)
1216 appicon->next->prev = NULL;
1217 w_global.app_icon_list = appicon->next;
1218 } else {
1219 if (appicon->next)
1220 appicon->next->prev = appicon->prev;
1221 if (appicon->prev)
1222 appicon->prev->next = appicon->next;
1225 appicon->prev = NULL;
1226 appicon->next = NULL;
1229 /* Return the AppIcon associated with a given (Xlib) Window. */
1230 WAppIcon *wAppIconFor(Window window)
1232 WObjDescriptor *desc;
1234 if (window == None)
1235 return NULL;
1237 if (XFindContext(dpy, window, w_global.context.client_win, (XPointer *) & desc) == XCNOENT)
1238 return NULL;
1240 if (desc->parent_type == WCLASS_APPICON || desc->parent_type == WCLASS_DOCK_ICON)
1241 return desc->parent;
1243 return NULL;