wmaker: Changed math on floating point by integer operation
[wmaker-crm.git] / src / appicon.c
blobe1fada1357f74e6efe23ab4f2d85a3286d127a91
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 #include "misc.h"
52 #include "event.h"
53 #ifdef XDND
54 #include "xdnd.h"
55 #endif
58 * icon_file for the dock is got from the preferences file by
59 * using the classname/instancename
62 /**** Global variables ****/
63 extern Cursor wCursor[WCUR_LAST];
64 extern WPreferences wPreferences;
65 extern WDDomain *WDWindowAttributes;
66 extern XContext wWinContext;
68 #define MOD_MASK wPreferences.modifier_mask
69 #define ICON_SIZE wPreferences.icon_size
71 void appIconMouseDown(WObjDescriptor * desc, XEvent * event);
72 static void iconDblClick(WObjDescriptor * desc, XEvent * event);
73 static void iconExpose(WObjDescriptor * desc, XEvent * event);
74 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const char *wm_class);
75 static WAppIcon *wAppIconCreate(WWindow * leader_win);
76 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon);
77 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon);
78 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window);
80 /* This function is used if the application is a .app. It checks if it has an icon in it
81 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
83 void wApplicationExtractDirPackIcon(WScreen * scr, const char *path, const char *wm_instance, const char *wm_class)
85 char *iconPath = NULL;
86 char *tmp = NULL;
88 if (strstr(path, ".app")) {
89 tmp = wmalloc(strlen(path) + 16);
91 if (scr->flags.supports_tiff) {
92 strcpy(tmp, path);
93 strcat(tmp, ".tiff");
94 if (access(tmp, R_OK) == 0)
95 iconPath = tmp;
98 if (!iconPath) {
99 strcpy(tmp, path);
100 strcat(tmp, ".xpm");
101 if (access(tmp, R_OK) == 0)
102 iconPath = tmp;
105 if (!iconPath)
106 wfree(tmp);
108 if (iconPath) {
109 wApplicationSaveIconPathFor(iconPath, wm_instance, wm_class);
110 wfree(iconPath);
115 WAppIcon *wAppIconCreateForDock(WScreen *scr, const char *command, const char *wm_instance, const char *wm_class, int tile)
117 WAppIcon *aicon;
119 aicon = wmalloc(sizeof(WAppIcon));
120 wretain(aicon);
121 aicon->yindex = -1;
122 aicon->xindex = -1;
124 add_to_appicon_list(scr, aicon);
126 if (command)
127 aicon->command = wstrdup(command);
129 if (wm_class)
130 aicon->wm_class = wstrdup(wm_class);
132 if (wm_instance)
133 aicon->wm_instance = wstrdup(wm_instance);
135 if (strcmp(wm_class, "WMDock") == 0 && wPreferences.flags.clip_merged_in_dock)
136 tile = TILE_CLIP;
137 aicon->icon = icon_create_for_dock(scr, command, wm_instance, wm_class, tile);
139 #ifdef XDND
140 wXDNDMakeAwareness(aicon->icon->core->window);
141 #endif
143 /* will be overriden by dock */
144 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
145 aicon->icon->core->descriptor.handle_expose = iconExpose;
146 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
147 aicon->icon->core->descriptor.parent = aicon;
148 AddToStackList(aicon->icon->core);
150 return aicon;
153 void create_appicon_for_application(WApplication *wapp, WWindow *wwin)
155 /* Try to create an icon from the dock or clip */
156 create_appicon_from_dock(wwin, wapp, wapp->main_window);
158 /* If app_icon was not found, create it */
159 if (!wapp->app_icon) {
160 /* Create the icon */
161 wapp->app_icon = wAppIconCreate(wapp->main_window_desc);
162 wIconUpdate(wapp->app_icon->icon);
164 /* Now, paint the icon */
165 if (!WFLAGP(wapp->main_window_desc, no_appicon))
166 paint_app_icon(wapp);
169 /* Save the app_icon in a file */
170 save_appicon(wapp->app_icon, False);
173 void unpaint_app_icon(WApplication *wapp)
175 WAppIcon *aicon;
176 WScreen *scr;
177 WDock *clip;
179 if (!wapp || !wapp->app_icon)
180 return;
182 aicon = wapp->app_icon;
184 /* If the icon is docked, don't continue */
185 if (aicon->docked)
186 return;
188 scr = wapp->main_window_desc->screen_ptr;
189 clip = scr->workspaces[scr->current_workspace]->clip;
191 if (!clip || !aicon->attracted || !clip->collapsed)
192 XUnmapWindow(dpy, aicon->icon->core->window);
194 /* We want to avoid having it on the list because otherwise
195 * there will be a hole when the icons are arranged with
196 * wArrangeIcons() */
197 remove_from_appicon_list(scr, aicon);
199 if (wPreferences.auto_arrange_icons && !aicon->attracted)
200 wArrangeIcons(scr, True);
203 void paint_app_icon(WApplication *wapp)
205 WIcon *icon;
206 WScreen *scr = wapp->main_window_desc->screen_ptr;
207 WDock *attracting_dock;
208 int x = 0, y = 0;
209 Bool update_icon = False;
211 if (!wapp || !wapp->app_icon)
212 return;
214 icon = wapp->app_icon->icon;
215 wapp->app_icon->main_window = wapp->main_window;
217 /* If the icon is docked, don't continue */
218 if (wapp->app_icon->docked)
219 return;
221 attracting_dock = scr->attracting_drawer != NULL ?
222 scr->attracting_drawer :
223 scr->workspaces[scr->current_workspace]->clip;
224 if (attracting_dock && attracting_dock->attract_icons &&
225 wDockFindFreeSlot(attracting_dock, &x, &y)) {
226 wapp->app_icon->attracted = 1;
227 if (!icon->shadowed) {
228 icon->shadowed = 1;
229 update_icon = True;
231 wDockAttachIcon(attracting_dock, wapp->app_icon, x, y, update_icon);
232 } else {
233 /* We must know if the icon is painted in the screen,
234 * because if painted, then PlaceIcon will return the next
235 * space on the screen, and the icon will move */
236 if (wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL) {
237 PlaceIcon(scr, &x, &y, wGetHeadForWindow(wapp->main_window_desc));
238 wAppIconMove(wapp->app_icon, x, y);
239 wLowerFrame(icon->core);
243 /* If we want appicon (no_appicon is not set) and the icon is not
244 * in the appicon_list, we must add it. Else, we want to avoid
245 * having it on the list */
246 if (!WFLAGP(wapp->main_window_desc, no_appicon) &&
247 wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL)
248 add_to_appicon_list(scr, wapp->app_icon);
250 if (!attracting_dock || !wapp->app_icon->attracted || !attracting_dock->collapsed)
251 XMapWindow(dpy, icon->core->window);
253 if (wPreferences.auto_arrange_icons && !wapp->app_icon->attracted)
254 wArrangeIcons(scr, True);
257 void removeAppIconFor(WApplication *wapp)
259 if (!wapp->app_icon)
260 return;
262 if (wPreferences.highlight_active_app)
263 wIconSetHighlited(wapp->app_icon->icon, False);
264 if (wapp->app_icon->docked && !wapp->app_icon->attracted) {
265 wapp->app_icon->running = 0;
266 /* since we keep it, we don't care if it was attracted or not */
267 wapp->app_icon->attracted = 0;
268 wapp->app_icon->icon->shadowed = 0;
269 wapp->app_icon->main_window = None;
270 wapp->app_icon->pid = 0;
271 wapp->app_icon->icon->owner = NULL;
272 wapp->app_icon->icon->icon_win = None;
274 /* Set the icon image */
275 set_icon_image_from_database(wapp->app_icon->icon, wapp->app_icon->wm_instance,
276 wapp->app_icon->wm_class, wapp->app_icon->command);
278 /* Update the icon, because wapp->app_icon->icon could be NULL */
279 wIconUpdate(wapp->app_icon->icon);
281 /* Paint it */
282 wAppIconPaint(wapp->app_icon);
283 } else if (wapp->app_icon->docked) {
284 wapp->app_icon->running = 0;
285 if (wapp->app_icon->dock->type == WM_DRAWER) {
286 wDrawerFillTheGap(wapp->app_icon->dock, wapp->app_icon, True);
288 wDockDetach(wapp->app_icon->dock, wapp->app_icon);
289 } else {
290 wAppIconDestroy(wapp->app_icon);
293 wapp->app_icon = NULL;
295 if (wPreferences.auto_arrange_icons)
296 wArrangeIcons(wapp->main_window_desc->screen_ptr, True);
299 static WAppIcon *wAppIconCreate(WWindow *leader_win)
301 WAppIcon *aicon;
303 aicon = wmalloc(sizeof(WAppIcon));
304 wretain(aicon);
305 aicon->yindex = -1;
306 aicon->xindex = -1;
307 aicon->prev = NULL;
308 aicon->next = NULL;
310 if (leader_win->wm_class)
311 aicon->wm_class = wstrdup(leader_win->wm_class);
313 if (leader_win->wm_instance)
314 aicon->wm_instance = wstrdup(leader_win->wm_instance);
316 aicon->icon = icon_create_for_wwindow(leader_win);
317 #ifdef XDND
318 wXDNDMakeAwareness(aicon->icon->core->window);
319 #endif
321 /* will be overriden if docked */
322 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
323 aicon->icon->core->descriptor.handle_expose = iconExpose;
324 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
325 aicon->icon->core->descriptor.parent = aicon;
326 AddToStackList(aicon->icon->core);
327 aicon->icon->show_title = 0;
329 return aicon;
332 void wAppIconDestroy(WAppIcon * aicon)
334 WScreen *scr = aicon->icon->core->screen_ptr;
336 RemoveFromStackList(aicon->icon->core);
337 wIconDestroy(aicon->icon);
338 if (aicon->command)
339 wfree(aicon->command);
340 #ifdef XDND
341 if (aicon->dnd_command)
342 wfree(aicon->dnd_command);
343 #endif
344 if (aicon->wm_instance)
345 wfree(aicon->wm_instance);
347 if (aicon->wm_class)
348 wfree(aicon->wm_class);
350 remove_from_appicon_list(scr, aicon);
352 aicon->destroyed = 1;
353 wrelease(aicon);
356 static void drawCorner(WIcon * icon)
358 WScreen *scr = icon->core->screen_ptr;
359 XPoint points[3];
361 points[0].x = 1;
362 points[0].y = 1;
363 points[1].x = 12;
364 points[1].y = 1;
365 points[2].x = 1;
366 points[2].y = 12;
367 XFillPolygon(dpy, icon->core->window, scr->icon_title_texture->normal_gc,
368 points, 3, Convex, CoordModeOrigin);
369 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 0, 12);
370 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 12, 0);
373 void wAppIconMove(WAppIcon * aicon, int x, int y)
375 XMoveWindow(dpy, aicon->icon->core->window, x, y);
376 aicon->x_pos = x;
377 aicon->y_pos = y;
380 #ifdef WS_INDICATOR
381 static void updateDockNumbers(WScreen * scr)
383 int length;
384 char *ws_numbers;
385 WAppIcon *dicon = scr->dock->icon_array[0];
387 ws_numbers = wmalloc(20);
388 snprintf(ws_numbers, 20, "%i [ %i ]", scr->current_workspace + 1, ((scr->current_workspace / 10) + 1));
389 length = strlen(ws_numbers);
391 XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, WMFontHeight(scr->icon_title_font) + 1, False);
393 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->black,
394 scr->icon_title_font, 4, 3, ws_numbers, length);
396 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->white,
397 scr->icon_title_font, 3, 2, ws_numbers, length);
399 wfree(ws_numbers);
401 #endif /* WS_INDICATOR */
403 void wAppIconPaint(WAppIcon *aicon)
405 WApplication *wapp;
406 WScreen *scr = aicon->icon->core->screen_ptr;
408 if (aicon->icon->owner)
409 wapp = wApplicationOf(aicon->icon->owner->main_window);
410 else
411 wapp = NULL;
413 wIconPaint(aicon->icon);
415 # ifdef WS_INDICATOR
416 if (aicon->docked && scr->dock && scr->dock == aicon->dock && aicon->yindex == 0)
417 updateDockNumbers(scr);
418 # endif
419 if (scr->dock_dots && aicon->docked && !aicon->running && aicon->command != NULL) {
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, aicon->icon->core->window,
423 scr->copy_gc, 0, 0, scr->dock_dots->width, scr->dock_dots->height, 0, 0);
425 #ifdef HIDDENDOT
426 if (wapp && wapp->flags.hidden) {
427 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
428 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
429 XCopyArea(dpy, scr->dock_dots->image,
430 aicon->icon->core->window, scr->copy_gc, 0, 0, 7, scr->dock_dots->height, 0, 0);
432 #endif /* HIDDENDOT */
434 if (aicon->omnipresent)
435 drawCorner(aicon->icon);
437 XSetClipMask(dpy, scr->copy_gc, None);
438 if (aicon->launching)
439 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
440 0, 0, wPreferences.icon_size, wPreferences.icon_size);
443 /* Save the application icon, if it's a dockapp then use it with dock = True */
444 void save_appicon(WAppIcon *aicon, Bool dock)
446 char *path;
448 if (!aicon)
449 return;
451 if (dock && (!aicon->docked || aicon->attracted))
452 return;
454 path = wIconStore(aicon->icon);
455 if (!path)
456 return;
458 wApplicationSaveIconPathFor(path, aicon->wm_instance, aicon->wm_class);
459 wfree(path);
462 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
464 /* main_window may not have the full command line; try to find one which does */
465 static void relaunchApplication(WApplication *wapp)
467 WScreen *scr;
468 WWindow *wlist, *next;
470 scr = wapp->main_window_desc->screen_ptr;
471 wlist = scr->focused_window;
472 if (! wlist)
473 return;
475 while (wlist->prev)
476 wlist = wlist->prev;
478 while (wlist) {
479 next = wlist->next;
481 if (wlist->main_window == wapp->main_window) {
482 if (RelaunchWindow(wlist))
483 return;
486 wlist = next;
490 static void relaunchCallback(WMenu * menu, WMenuEntry * entry)
492 WApplication *wapp = (WApplication *) entry->clientdata;
494 relaunchApplication(wapp);
497 static void hideCallback(WMenu * menu, WMenuEntry * entry)
499 WApplication *wapp = (WApplication *) entry->clientdata;
501 if (wapp->flags.hidden) {
502 wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
503 wUnhideApplication(wapp, False, False);
504 } else {
505 wHideApplication(wapp);
509 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
511 WApplication *wapp = (WApplication *) entry->clientdata;
513 wUnhideApplication(wapp, False, True);
516 static void setIconCallback(WMenu *menu, WMenuEntry *entry)
518 WAppIcon *icon = ((WApplication *) entry->clientdata)->app_icon;
519 char *file = NULL;
520 WScreen *scr;
521 int result;
523 assert(icon != NULL);
525 if (icon->editing)
526 return;
528 icon->editing = 1;
529 scr = icon->icon->core->screen_ptr;
531 wretain(icon);
533 result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
535 if (result && !icon->destroyed) {
536 if (file && *file == 0) {
537 wfree(file);
538 file = NULL;
540 if (!wIconChangeImageFile(icon->icon, file)) {
541 wMessageDialog(scr, _("Error"),
542 _("Could not open specified icon file"), _("OK"), NULL, NULL);
543 } else {
544 wDefaultChangeIcon(scr, icon->wm_instance, icon->wm_class, file);
545 wAppIconPaint(icon);
547 if (file)
548 wfree(file);
550 icon->editing = 0;
551 wrelease(icon);
554 static void killCallback(WMenu * menu, WMenuEntry * entry)
556 WApplication *wapp = (WApplication *) entry->clientdata;
557 WFakeGroupLeader *fPtr;
558 char *buffer;
559 char *shortname;
560 char *basename(const char *shortname);
562 if (!WCHECK_STATE(WSTATE_NORMAL))
563 return;
565 WCHANGE_STATE(WSTATE_MODAL);
567 assert(entry->clientdata != NULL);
569 shortname = basename(wapp->app_icon->wm_instance);
571 buffer = wstrconcat(wapp->app_icon ? shortname : NULL,
572 _(" will be forcibly closed.\n"
573 "Any unsaved changes will be lost.\n" "Please confirm."));
575 fPtr = wapp->main_window_desc->fake_group;
577 wretain(wapp->main_window_desc);
578 if (wPreferences.dont_confirm_kill
579 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
580 buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
581 if (fPtr != NULL) {
582 WWindow *wwin, *twin;
584 wwin = wapp->main_window_desc->screen_ptr->focused_window;
585 while (wwin) {
586 twin = wwin->prev;
587 if (wwin->fake_group == fPtr)
588 wClientKill(wwin);
589 wwin = twin;
591 } else if (!wapp->main_window_desc->flags.destroyed) {
592 wClientKill(wapp->main_window_desc);
595 wrelease(wapp->main_window_desc);
596 wfree(buffer);
597 WCHANGE_STATE(WSTATE_NORMAL);
600 static WMenu *createApplicationMenu(WScreen *scr)
602 WMenu *menu;
604 menu = wMenuCreate(scr, NULL, False);
605 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
606 wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
607 wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
608 wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
609 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
611 return menu;
614 static void openApplicationMenu(WApplication * wapp, int x, int y)
616 WMenu *menu;
617 WScreen *scr = wapp->main_window_desc->screen_ptr;
618 int i;
620 if (!scr->icon_menu) {
621 scr->icon_menu = createApplicationMenu(scr);
622 wfree(scr->icon_menu->entries[1]->text);
625 menu = scr->icon_menu;
627 if (wapp->flags.hidden)
628 menu->entries[1]->text = _("Unhide");
629 else
630 menu->entries[1]->text = _("Hide");
632 menu->flags.realized = 0;
633 wMenuRealize(menu);
635 x -= menu->frame->core->width / 2;
636 if (x + menu->frame->core->width > scr->scr_width)
637 x = scr->scr_width - menu->frame->core->width;
639 if (x < 0)
640 x = 0;
642 /* set client data */
643 for (i = 0; i < menu->entry_no; i++)
644 menu->entries[i]->clientdata = wapp;
646 wMenuMapAt(menu, x, y, False);
649 /******************************************************************/
651 static void iconExpose(WObjDescriptor *desc, XEvent *event)
653 wAppIconPaint(desc->parent);
656 static void iconDblClick(WObjDescriptor *desc, XEvent *event)
658 WAppIcon *aicon = desc->parent;
659 WApplication *wapp;
660 WScreen *scr = aicon->icon->core->screen_ptr;
661 int unhideHere;
663 assert(aicon->icon->owner != NULL);
665 wapp = wApplicationOf(aicon->icon->owner->main_window);
667 if (event->xbutton.state & ControlMask) {
668 relaunchApplication(wapp);
669 return;
672 unhideHere = (event->xbutton.state & ShiftMask);
673 /* go to the last workspace that the user worked on the app */
674 if (!unhideHere && wapp->last_workspace != scr->current_workspace)
675 wWorkspaceChange(scr, wapp->last_workspace);
677 wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
679 if (event->xbutton.state & MOD_MASK)
680 wHideOtherApplications(aicon->icon->owner);
683 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
685 WAppIcon *aicon = desc->parent;
686 WScreen *scr = aicon->icon->core->screen_ptr;
687 Bool hasMoved;
689 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
690 return;
692 if (IsDoubleClick(scr, event)) {
693 /* Middle or right mouse actions were handled on first click */
694 if (event->xbutton.button == Button1)
695 iconDblClick(desc, event);
696 return;
699 if (event->xbutton.button == Button2) {
700 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
702 if (wapp)
703 relaunchApplication(wapp);
705 return;
708 if (event->xbutton.button == Button3) {
709 WObjDescriptor *desc;
710 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
712 if (!wapp)
713 return;
715 if (event->xbutton.send_event &&
716 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
717 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
718 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
719 wwarning("pointer grab failed for appicon menu");
720 return;
723 openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
725 /* allow drag select of menu */
726 desc = &scr->icon_menu->menu->descriptor;
727 event->xbutton.send_event = True;
728 (*desc->handle_mousedown) (desc, event);
729 return;
732 hasMoved = wHandleAppIconMove(aicon, event);
733 if (wPreferences.single_click && !hasMoved && aicon->dock != NULL)
735 iconDblClick(desc, event);
739 Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event)
741 WIcon *icon = aicon->icon;
742 WScreen *scr = icon->core->screen_ptr;
743 WDock *originalDock = aicon->dock; /* can be NULL */
744 WDock *lastDock = originalDock;
745 WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */
746 WDrawerChain *dc;
747 Bool done = False, dockable, ondock;
748 Bool grabbed = False;
749 Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
750 int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */
751 int omnipresent = aicon->omnipresent; /* this must be cached */
752 Bool showed_all_clips = False;
754 int clickButton = event->xbutton.button;
755 Pixmap ghost = None;
756 Window wins[2]; /* Managing shadow window */
757 XEvent ev;
759 int x = aicon->x_pos, y = aicon->y_pos;
760 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
761 int shad_x = x, shad_y = y;
762 int ix = aicon->xindex, iy = aicon->yindex;
763 int i;
764 int oldX = x;
765 int oldY = y;
766 Bool hasMoved = False;
768 if (wPreferences.flags.noupdates && originalDock != NULL)
769 return False;
771 if (!(event->xbutton.state & MOD_MASK))
772 wRaiseFrame(icon->core);
773 else {
774 /* If Mod is pressed for an docked appicon, assume it is to undock it,
775 * so don't lower it */
776 if (originalDock == NULL)
777 wLowerFrame(icon->core);
780 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
781 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
782 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
783 wwarning("Pointer grab failed in wHandleAppIconMove");
786 if (originalDock != NULL) {
787 dockable = True;
788 ondock = True;
790 else {
791 ondock = False;
792 if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer)
793 dockable = 0;
794 else
795 dockable = canBeDocked(icon->owner);
798 /* We try the various docks in that order:
799 * - First, the dock the appicon comes from, if any
800 * - Then, the drawers
801 * - Then, the "dock" (WM_DOCK)
802 * - Finally, the clip
804 i = 0;
805 if (originalDock != NULL)
806 allDocks[ i++ ] = originalDock;
807 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
808 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
809 if (dc->adrawer != originalDock)
810 allDocks[ i++ ] = dc->adrawer;
812 if (!wPreferences.flags.nodock && scr->dock != originalDock)
813 allDocks[ i++ ] = scr->dock;
814 if (!wPreferences.flags.noclip &&
815 originalDock != scr->workspaces[scr->current_workspace]->clip)
816 allDocks[ i++ ] = scr->workspaces[scr->current_workspace]->clip;
817 for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */
818 allDocks[ i ] = NULL;
820 wins[0] = icon->core->window;
821 wins[1] = scr->dock_shadow;
822 XRestackWindows(dpy, wins, 2);
823 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
824 if (superfluous) {
825 if (icon->pixmap != None)
826 ghost = MakeGhostIcon(scr, icon->pixmap);
827 else
828 ghost = MakeGhostIcon(scr, icon->core->window);
829 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
830 XClearWindow(dpy, scr->dock_shadow);
832 if (ondock)
833 XMapWindow(dpy, scr->dock_shadow);
835 while (!done) {
836 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
837 | ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
838 switch (ev.type) {
839 case Expose:
840 WMHandleEvent(&ev);
841 break;
843 case EnterNotify:
844 /* It means the cursor moved so fast that it entered
845 * something else (if moving slowly, it would have
846 * stayed in the appIcon that is being moved. Ignore
847 * such "spurious" EnterNotifiy's */
848 break;
850 case MotionNotify:
851 hasMoved = True;
852 if (!grabbed) {
853 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
854 || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
855 XChangeActivePointerGrab(dpy, ButtonMotionMask
856 | ButtonReleaseMask | ButtonPressMask,
857 wCursor[WCUR_MOVE], CurrentTime);
858 grabbed = 1;
859 } else {
860 break;
864 if (omnipresent && !showed_all_clips) {
865 int i;
866 for (i = 0; i < scr->workspace_count; i++) {
867 if (i == scr->current_workspace)
868 continue;
869 wDockShowIcons(scr->workspaces[i]->clip);
870 /* Note: if dock is collapsed (for instance, because it
871 auto-collapses), its icons still won't show up */
873 showed_all_clips = True; /* To prevent flickering */
876 x = ev.xmotion.x_root - ofs_x;
877 y = ev.xmotion.y_root - ofs_y;
878 wAppIconMove(aicon, x, y);
880 WDock *theNewDock = NULL;
881 if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock) {
882 for (i = 0; dockable && i < scr->drawer_count + 2; i++) {
883 WDock *theDock = allDocks[i];
884 if (theDock == NULL)
885 break;
886 if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) {
887 theNewDock = theDock;
888 break;
891 if (originalDock != NULL && theNewDock == NULL &&
892 (aicon->launching || aicon->lock || aicon->running)) {
893 /* In those cases, stay in lastDock if no dock really wants us */
894 theNewDock = lastDock;
897 if (lastDock != NULL && lastDock != theNewDock) {
898 /* Leave lastDock in the state we found it */
899 if (lastDock->type == WM_DRAWER) {
900 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
902 if (collapsed) {
903 lastDock->collapsed = 1;
904 wDockHideIcons(lastDock);
905 collapsed = False;
907 if (lastDock->auto_raise_lower) {
908 wDockLower(lastDock);
911 if (theNewDock != NULL) {
912 if (lastDock != theNewDock) {
913 collapsed = theNewDock->collapsed;
914 if (collapsed) {
915 theNewDock->collapsed = 0;
916 wDockShowIcons(theNewDock);
918 if (theNewDock->auto_raise_lower) {
919 wDockRaise(theNewDock);
920 /* And raise the moving tile above it */
921 wRaiseFrame(aicon->icon->core);
923 lastDock = theNewDock;
926 shad_x = lastDock->x_pos + ix*wPreferences.icon_size;
927 shad_y = lastDock->y_pos + iy*wPreferences.icon_size;
929 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
931 if (!ondock) {
932 XMapWindow(dpy, scr->dock_shadow);
934 ondock = 1;
935 } else {
936 lastDock = theNewDock; // i.e., NULL
937 if (ondock) {
938 XUnmapWindow(dpy, scr->dock_shadow);
940 * Leaving that weird comment for now.
941 * But if we see no gap, there is no need to fill one!
942 * We could test ondock first and the lastDock to NULL afterwards
943 if (lastDock_before_it_was_null->type == WM_DRAWER) {
944 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
945 } */
948 ondock = 0;
950 break;
952 case ButtonPress:
953 break;
955 case ButtonRelease:
956 if (ev.xbutton.button != clickButton)
957 break;
958 XUngrabPointer(dpy, CurrentTime);
960 Bool docked = False;
961 if (ondock) {
962 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
963 XUnmapWindow(dpy, scr->dock_shadow);
964 if (originalDock == NULL) { // docking an undocked appicon
965 docked = wDockAttachIcon(lastDock, aicon, ix, iy, False);
966 if (!docked) {
967 /* AppIcon got rejected (happens only when we can't get the
968 command for that appicon, and the user cancels the
969 wInputDialog asking for one). Make the rejection obvious by
970 sliding the icon to its old position */
971 if (lastDock->type == WM_DRAWER) {
972 // Also fill the gap left in the drawer
973 wDrawerFillTheGap(lastDock, aicon, False);
975 SlideWindow(icon->core->window, x, y, oldX, oldY);
978 else { // moving a docked appicon to a dock
979 if (originalDock == lastDock) {
980 docked = True;
981 wDockReattachIcon(originalDock, aicon, ix, iy);
983 else {
984 docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy);
985 if (!docked) {
986 /* Possible scenario: user moved an auto-attracted appicon
987 from the clip to the dock, and cancelled the wInputDialog
988 asking for a command */
989 if (lastDock->type == WM_DRAWER) {
990 wDrawerFillTheGap(lastDock, aicon, False);
992 /* If aicon comes from a drawer, make some room to reattach it */
993 if (originalDock->type == WM_DRAWER) {
994 WAppIcon *aiconsToShift[ originalDock->icon_count ];
995 int j = 0;
997 for (i = 0; i < originalDock->max_icons; i++) {
998 WAppIcon *ai = originalDock->icon_array[ i ];
999 if (ai && ai != aicon &&
1000 abs(ai->xindex) >= abs(aicon->xindex))
1001 aiconsToShift[j++] = ai;
1003 if (j != originalDock->icon_count - abs(aicon->xindex) - 1)
1004 // Trust this never happens?
1005 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1006 j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex);
1007 wSlideAppicons(aiconsToShift, j, originalDock->on_right_side);
1008 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1011 SlideWindow(icon->core->window, x, y, oldX, oldY);
1012 wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex);
1014 else {
1015 if (originalDock->auto_collapse && !originalDock->collapsed) {
1016 originalDock->collapsed = 1;
1017 wDockHideIcons(originalDock);
1019 if (originalDock->auto_raise_lower)
1020 wDockLower(originalDock);
1024 // No matter what happened above, check to lower lastDock
1025 // Don't see why I commented out the following 2 lines
1026 /* if (lastDock->auto_raise_lower)
1027 wDockLower(lastDock); */
1028 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1029 * collapsed, so that wHandleAppIconMove doesn't collapse it
1030 * right away (the timer will take care of it) */
1031 if (lastDock->auto_collapse)
1032 collapsed = 0;
1034 else {
1035 if (originalDock != NULL) { /* Detaching a docked appicon */
1036 if (superfluous) {
1037 if (!aicon->running && !wPreferences.no_animations) {
1038 /* We need to deselect it, even if is deselected in
1039 * wDockDetach(), because else DoKaboom() will fail.
1041 if (aicon->icon->selected)
1042 wIconSelect(aicon->icon);
1043 DoKaboom(scr, aicon->icon->core->window, x, y);
1046 wDockDetach(originalDock, aicon);
1047 if (originalDock->auto_collapse && !originalDock->collapsed) {
1048 originalDock->collapsed = 1;
1049 wDockHideIcons(originalDock);
1051 if (originalDock->auto_raise_lower)
1052 wDockLower(originalDock);
1055 // Can't remember why the icon hiding is better done above than below (commented out)
1056 // Also, lastDock is quite different from originalDock
1058 if (collapsed) {
1059 lastDock->collapsed = 1;
1060 wDockHideIcons(lastDock);
1061 collapsed = 0;
1064 if (superfluous) {
1065 if (ghost != None)
1066 XFreePixmap(dpy, ghost);
1067 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
1069 if (showed_all_clips) {
1070 int i;
1071 for (i = 0; i < scr->workspace_count; i++) {
1072 if (i == scr->current_workspace)
1073 continue;
1074 wDockHideIcons(scr->workspaces[i]->clip);
1077 if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked))
1078 /* Need to rearrange unless moving from dock to dock */
1079 wArrangeIcons(scr, True);
1080 return hasMoved;
1083 return False; /* Never reached */
1086 /* This function save the application icon and store the path in the Dictionary */
1087 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const char *wm_class)
1089 WMPropList *dict = WDWindowAttributes->dictionary;
1090 WMPropList *adict, *key, *iconk;
1091 WMPropList *val;
1092 char *tmp;
1094 tmp = get_name_for_instance_class(wm_instance, wm_class);
1095 key = WMCreatePLString(tmp);
1096 wfree(tmp);
1098 adict = WMGetFromPLDictionary(dict, key);
1099 iconk = WMCreatePLString("Icon");
1101 if (adict) {
1102 val = WMGetFromPLDictionary(adict, iconk);
1103 } else {
1104 /* no dictionary for app, so create one */
1105 adict = WMCreatePLDictionary(NULL, NULL);
1106 WMPutInPLDictionary(dict, key, adict);
1107 WMReleasePropList(adict);
1108 val = NULL;
1111 if (!val) {
1112 val = WMCreatePLString(iconPath);
1113 WMPutInPLDictionary(adict, iconk, val);
1114 WMReleasePropList(val);
1115 } else {
1116 val = NULL;
1119 WMReleasePropList(key);
1120 WMReleasePropList(iconk);
1122 if (val && !wPreferences.flags.noupdates)
1123 UpdateDomainFile(WDWindowAttributes);
1126 static WAppIcon *findDockIconFor(WDock *dock, Window main_window)
1128 WAppIcon *aicon = NULL;
1130 aicon = wDockFindIconForWindow(dock, main_window);
1131 if (!aicon) {
1132 wDockTrackWindowLaunch(dock, main_window);
1133 aicon = wDockFindIconForWindow(dock, main_window);
1135 return aicon;
1138 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window)
1140 WScreen *scr = wwin->screen_ptr;
1141 wapp->app_icon = NULL;
1143 if (scr->last_dock)
1144 wapp->app_icon = findDockIconFor(scr->last_dock, main_window);
1146 /* check main dock if we did not find it in last dock */
1147 if (!wapp->app_icon && scr->dock)
1148 wapp->app_icon = findDockIconFor(scr->dock, main_window);
1150 /* check clips */
1151 if (!wapp->app_icon) {
1152 int i;
1153 for (i = 0; i < scr->workspace_count; i++) {
1154 WDock *dock = scr->workspaces[i]->clip;
1155 if (dock)
1156 wapp->app_icon = findDockIconFor(dock, main_window);
1157 if (wapp->app_icon)
1158 break;
1162 /* Finally check drawers */
1163 if (!wapp->app_icon) {
1164 WDrawerChain *dc;
1165 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
1166 wapp->app_icon = findDockIconFor(dc->adrawer, main_window);
1167 if (wapp->app_icon)
1168 break;
1172 /* If created, then set some flags */
1173 if (wapp->app_icon) {
1174 WWindow *mainw = wapp->main_window_desc;
1176 wapp->app_icon->running = 1;
1177 wapp->app_icon->icon->owner = mainw;
1178 if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
1179 wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
1181 /* Update the icon images */
1182 wIconUpdate(wapp->app_icon->icon);
1184 /* Paint it */
1185 wAppIconPaint(wapp->app_icon);
1186 save_appicon(wapp->app_icon, True);
1190 /* Add the appicon to the appiconlist */
1191 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon)
1193 appicon->prev = NULL;
1194 appicon->next = scr->app_icon_list;
1195 if (scr->app_icon_list)
1196 scr->app_icon_list->prev = appicon;
1198 scr->app_icon_list = appicon;
1201 /* Remove the appicon from the appiconlist */
1202 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon)
1204 if (appicon == scr->app_icon_list) {
1205 if (appicon->next)
1206 appicon->next->prev = NULL;
1207 scr->app_icon_list = appicon->next;
1208 } else {
1209 if (appicon->next)
1210 appicon->next->prev = appicon->prev;
1211 if (appicon->prev)
1212 appicon->prev->next = appicon->next;
1215 appicon->prev = NULL;
1216 appicon->next = NULL;
1219 /* Return the AppIcon associated with a given (Xlib) Window. */
1220 WAppIcon *wAppIconFor(Window window)
1222 WObjDescriptor *desc;
1224 if (window == None)
1225 return NULL;
1227 if (XFindContext(dpy, window, wWinContext, (XPointer *) & desc) == XCNOENT)
1228 return NULL;
1230 if (desc->parent_type == WCLASS_APPICON || desc->parent_type == WCLASS_DOCK_ICON)
1231 return desc->parent;
1233 return NULL;