wmaker: Replaced local 'extern' definition of wPreferences by proper header usage
[wmaker-crm.git] / src / appicon.c
blob41e52e731379996e919106e31a191abc809198b6
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 WDDomain *WDWindowAttributes;
65 extern XContext wWinContext;
67 #define MOD_MASK wPreferences.modifier_mask
68 #define ICON_SIZE wPreferences.icon_size
70 void appIconMouseDown(WObjDescriptor * desc, XEvent * event);
71 static void iconDblClick(WObjDescriptor * desc, XEvent * event);
72 static void iconExpose(WObjDescriptor * desc, XEvent * event);
73 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const char *wm_class);
74 static WAppIcon *wAppIconCreate(WWindow * leader_win);
75 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon);
76 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon);
77 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window);
79 /* This function is used if the application is a .app. It checks if it has an icon in it
80 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
82 void wApplicationExtractDirPackIcon(WScreen * scr, const char *path, const char *wm_instance, const char *wm_class)
84 char *iconPath = NULL;
85 char *tmp = NULL;
87 if (strstr(path, ".app")) {
88 tmp = wmalloc(strlen(path) + 16);
90 if (scr->flags.supports_tiff) {
91 strcpy(tmp, path);
92 strcat(tmp, ".tiff");
93 if (access(tmp, R_OK) == 0)
94 iconPath = tmp;
97 if (!iconPath) {
98 strcpy(tmp, path);
99 strcat(tmp, ".xpm");
100 if (access(tmp, R_OK) == 0)
101 iconPath = tmp;
104 if (!iconPath)
105 wfree(tmp);
107 if (iconPath) {
108 wApplicationSaveIconPathFor(iconPath, wm_instance, wm_class);
109 wfree(iconPath);
114 WAppIcon *wAppIconCreateForDock(WScreen *scr, const char *command, const char *wm_instance, const char *wm_class, int tile)
116 WAppIcon *aicon;
118 aicon = wmalloc(sizeof(WAppIcon));
119 wretain(aicon);
120 aicon->yindex = -1;
121 aicon->xindex = -1;
123 add_to_appicon_list(scr, aicon);
125 if (command)
126 aicon->command = wstrdup(command);
128 if (wm_class)
129 aicon->wm_class = wstrdup(wm_class);
131 if (wm_instance)
132 aicon->wm_instance = wstrdup(wm_instance);
134 if (strcmp(wm_class, "WMDock") == 0 && wPreferences.flags.clip_merged_in_dock)
135 tile = TILE_CLIP;
136 aicon->icon = icon_create_for_dock(scr, command, wm_instance, wm_class, tile);
138 #ifdef XDND
139 wXDNDMakeAwareness(aicon->icon->core->window);
140 #endif
142 /* will be overriden by dock */
143 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
144 aicon->icon->core->descriptor.handle_expose = iconExpose;
145 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
146 aicon->icon->core->descriptor.parent = aicon;
147 AddToStackList(aicon->icon->core);
149 return aicon;
152 void create_appicon_for_application(WApplication *wapp, WWindow *wwin)
154 /* Try to create an icon from the dock or clip */
155 create_appicon_from_dock(wwin, wapp, wapp->main_window);
157 /* If app_icon was not found, create it */
158 if (!wapp->app_icon) {
159 /* Create the icon */
160 wapp->app_icon = wAppIconCreate(wapp->main_window_desc);
161 wIconUpdate(wapp->app_icon->icon);
163 /* Now, paint the icon */
164 if (!WFLAGP(wapp->main_window_desc, no_appicon))
165 paint_app_icon(wapp);
168 /* Save the app_icon in a file */
169 save_appicon(wapp->app_icon, False);
172 void unpaint_app_icon(WApplication *wapp)
174 WAppIcon *aicon;
175 WScreen *scr;
176 WDock *clip;
178 if (!wapp || !wapp->app_icon)
179 return;
181 aicon = wapp->app_icon;
183 /* If the icon is docked, don't continue */
184 if (aicon->docked)
185 return;
187 scr = wapp->main_window_desc->screen_ptr;
188 clip = scr->workspaces[scr->current_workspace]->clip;
190 if (!clip || !aicon->attracted || !clip->collapsed)
191 XUnmapWindow(dpy, aicon->icon->core->window);
193 /* We want to avoid having it on the list because otherwise
194 * there will be a hole when the icons are arranged with
195 * wArrangeIcons() */
196 remove_from_appicon_list(scr, aicon);
198 if (wPreferences.auto_arrange_icons && !aicon->attracted)
199 wArrangeIcons(scr, True);
202 void paint_app_icon(WApplication *wapp)
204 WIcon *icon;
205 WScreen *scr = wapp->main_window_desc->screen_ptr;
206 WDock *attracting_dock;
207 int x = 0, y = 0;
208 Bool update_icon = False;
210 if (!wapp || !wapp->app_icon)
211 return;
213 icon = wapp->app_icon->icon;
214 wapp->app_icon->main_window = wapp->main_window;
216 /* If the icon is docked, don't continue */
217 if (wapp->app_icon->docked)
218 return;
220 attracting_dock = scr->attracting_drawer != NULL ?
221 scr->attracting_drawer :
222 scr->workspaces[scr->current_workspace]->clip;
223 if (attracting_dock && attracting_dock->attract_icons &&
224 wDockFindFreeSlot(attracting_dock, &x, &y)) {
225 wapp->app_icon->attracted = 1;
226 if (!icon->shadowed) {
227 icon->shadowed = 1;
228 update_icon = True;
230 wDockAttachIcon(attracting_dock, wapp->app_icon, x, y, update_icon);
231 } else {
232 /* We must know if the icon is painted in the screen,
233 * because if painted, then PlaceIcon will return the next
234 * space on the screen, and the icon will move */
235 if (wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL) {
236 PlaceIcon(scr, &x, &y, wGetHeadForWindow(wapp->main_window_desc));
237 wAppIconMove(wapp->app_icon, x, y);
238 wLowerFrame(icon->core);
242 /* If we want appicon (no_appicon is not set) and the icon is not
243 * in the appicon_list, we must add it. Else, we want to avoid
244 * having it on the list */
245 if (!WFLAGP(wapp->main_window_desc, no_appicon) &&
246 wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL)
247 add_to_appicon_list(scr, wapp->app_icon);
249 if (!attracting_dock || !wapp->app_icon->attracted || !attracting_dock->collapsed)
250 XMapWindow(dpy, icon->core->window);
252 if (wPreferences.auto_arrange_icons && !wapp->app_icon->attracted)
253 wArrangeIcons(scr, True);
256 void removeAppIconFor(WApplication *wapp)
258 if (!wapp->app_icon)
259 return;
261 if (wPreferences.highlight_active_app)
262 wIconSetHighlited(wapp->app_icon->icon, False);
263 if (wapp->app_icon->docked && !wapp->app_icon->attracted) {
264 wapp->app_icon->running = 0;
265 /* since we keep it, we don't care if it was attracted or not */
266 wapp->app_icon->attracted = 0;
267 wapp->app_icon->icon->shadowed = 0;
268 wapp->app_icon->main_window = None;
269 wapp->app_icon->pid = 0;
270 wapp->app_icon->icon->owner = NULL;
271 wapp->app_icon->icon->icon_win = None;
273 /* Set the icon image */
274 set_icon_image_from_database(wapp->app_icon->icon, wapp->app_icon->wm_instance,
275 wapp->app_icon->wm_class, wapp->app_icon->command);
277 /* Update the icon, because wapp->app_icon->icon could be NULL */
278 wIconUpdate(wapp->app_icon->icon);
280 /* Paint it */
281 wAppIconPaint(wapp->app_icon);
282 } else if (wapp->app_icon->docked) {
283 wapp->app_icon->running = 0;
284 if (wapp->app_icon->dock->type == WM_DRAWER) {
285 wDrawerFillTheGap(wapp->app_icon->dock, wapp->app_icon, True);
287 wDockDetach(wapp->app_icon->dock, wapp->app_icon);
288 } else {
289 wAppIconDestroy(wapp->app_icon);
292 wapp->app_icon = NULL;
294 if (wPreferences.auto_arrange_icons)
295 wArrangeIcons(wapp->main_window_desc->screen_ptr, True);
298 static WAppIcon *wAppIconCreate(WWindow *leader_win)
300 WAppIcon *aicon;
302 aicon = wmalloc(sizeof(WAppIcon));
303 wretain(aicon);
304 aicon->yindex = -1;
305 aicon->xindex = -1;
306 aicon->prev = NULL;
307 aicon->next = NULL;
309 if (leader_win->wm_class)
310 aicon->wm_class = wstrdup(leader_win->wm_class);
312 if (leader_win->wm_instance)
313 aicon->wm_instance = wstrdup(leader_win->wm_instance);
315 aicon->icon = icon_create_for_wwindow(leader_win);
316 #ifdef XDND
317 wXDNDMakeAwareness(aicon->icon->core->window);
318 #endif
320 /* will be overriden if docked */
321 aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
322 aicon->icon->core->descriptor.handle_expose = iconExpose;
323 aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
324 aicon->icon->core->descriptor.parent = aicon;
325 AddToStackList(aicon->icon->core);
326 aicon->icon->show_title = 0;
328 return aicon;
331 void wAppIconDestroy(WAppIcon * aicon)
333 WScreen *scr = aicon->icon->core->screen_ptr;
335 RemoveFromStackList(aicon->icon->core);
336 wIconDestroy(aicon->icon);
337 if (aicon->command)
338 wfree(aicon->command);
339 #ifdef XDND
340 if (aicon->dnd_command)
341 wfree(aicon->dnd_command);
342 #endif
343 if (aicon->wm_instance)
344 wfree(aicon->wm_instance);
346 if (aicon->wm_class)
347 wfree(aicon->wm_class);
349 remove_from_appicon_list(scr, aicon);
351 aicon->destroyed = 1;
352 wrelease(aicon);
355 static void drawCorner(WIcon * icon)
357 WScreen *scr = icon->core->screen_ptr;
358 XPoint points[3];
360 points[0].x = 1;
361 points[0].y = 1;
362 points[1].x = 12;
363 points[1].y = 1;
364 points[2].x = 1;
365 points[2].y = 12;
366 XFillPolygon(dpy, icon->core->window, scr->icon_title_texture->normal_gc,
367 points, 3, Convex, CoordModeOrigin);
368 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 0, 12);
369 XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 12, 0);
372 void wAppIconMove(WAppIcon * aicon, int x, int y)
374 XMoveWindow(dpy, aicon->icon->core->window, x, y);
375 aicon->x_pos = x;
376 aicon->y_pos = y;
379 #ifdef WS_INDICATOR
380 static void updateDockNumbers(WScreen * scr)
382 int length;
383 char *ws_numbers;
384 WAppIcon *dicon = scr->dock->icon_array[0];
386 ws_numbers = wmalloc(20);
387 snprintf(ws_numbers, 20, "%i [ %i ]", scr->current_workspace + 1, ((scr->current_workspace / 10) + 1));
388 length = strlen(ws_numbers);
390 XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, WMFontHeight(scr->icon_title_font) + 1, False);
392 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->black,
393 scr->icon_title_font, 4, 3, ws_numbers, length);
395 WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->white,
396 scr->icon_title_font, 3, 2, ws_numbers, length);
398 wfree(ws_numbers);
400 #endif /* WS_INDICATOR */
402 void wAppIconPaint(WAppIcon *aicon)
404 WApplication *wapp;
405 WScreen *scr = aicon->icon->core->screen_ptr;
407 if (aicon->icon->owner)
408 wapp = wApplicationOf(aicon->icon->owner->main_window);
409 else
410 wapp = NULL;
412 wIconPaint(aicon->icon);
414 # ifdef WS_INDICATOR
415 if (aicon->docked && scr->dock && scr->dock == aicon->dock && aicon->yindex == 0)
416 updateDockNumbers(scr);
417 # endif
418 if (scr->dock_dots && aicon->docked && !aicon->running && aicon->command != NULL) {
419 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
420 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
421 XCopyArea(dpy, scr->dock_dots->image, aicon->icon->core->window,
422 scr->copy_gc, 0, 0, scr->dock_dots->width, scr->dock_dots->height, 0, 0);
424 #ifdef HIDDENDOT
425 if (wapp && wapp->flags.hidden) {
426 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
427 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
428 XCopyArea(dpy, scr->dock_dots->image,
429 aicon->icon->core->window, scr->copy_gc, 0, 0, 7, scr->dock_dots->height, 0, 0);
431 #endif /* HIDDENDOT */
433 if (aicon->omnipresent)
434 drawCorner(aicon->icon);
436 XSetClipMask(dpy, scr->copy_gc, None);
437 if (aicon->launching)
438 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
439 0, 0, wPreferences.icon_size, wPreferences.icon_size);
442 /* Save the application icon, if it's a dockapp then use it with dock = True */
443 void save_appicon(WAppIcon *aicon, Bool dock)
445 char *path;
447 if (!aicon)
448 return;
450 if (dock && (!aicon->docked || aicon->attracted))
451 return;
453 path = wIconStore(aicon->icon);
454 if (!path)
455 return;
457 wApplicationSaveIconPathFor(path, aicon->wm_instance, aicon->wm_class);
458 wfree(path);
461 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
463 /* main_window may not have the full command line; try to find one which does */
464 static void relaunchApplication(WApplication *wapp)
466 WScreen *scr;
467 WWindow *wlist, *next;
469 scr = wapp->main_window_desc->screen_ptr;
470 wlist = scr->focused_window;
471 if (! wlist)
472 return;
474 while (wlist->prev)
475 wlist = wlist->prev;
477 while (wlist) {
478 next = wlist->next;
480 if (wlist->main_window == wapp->main_window) {
481 if (RelaunchWindow(wlist))
482 return;
485 wlist = next;
489 static void relaunchCallback(WMenu * menu, WMenuEntry * entry)
491 WApplication *wapp = (WApplication *) entry->clientdata;
493 relaunchApplication(wapp);
496 static void hideCallback(WMenu * menu, WMenuEntry * entry)
498 WApplication *wapp = (WApplication *) entry->clientdata;
500 if (wapp->flags.hidden) {
501 wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
502 wUnhideApplication(wapp, False, False);
503 } else {
504 wHideApplication(wapp);
508 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
510 WApplication *wapp = (WApplication *) entry->clientdata;
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 assert(icon != NULL);
524 if (icon->editing)
525 return;
527 icon->editing = 1;
528 scr = icon->icon->core->screen_ptr;
530 wretain(icon);
532 result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
534 if (result && !icon->destroyed) {
535 if (file && *file == 0) {
536 wfree(file);
537 file = NULL;
539 if (!wIconChangeImageFile(icon->icon, file)) {
540 wMessageDialog(scr, _("Error"),
541 _("Could not open specified icon file"), _("OK"), NULL, NULL);
542 } else {
543 wDefaultChangeIcon(scr, icon->wm_instance, icon->wm_class, file);
544 wAppIconPaint(icon);
546 if (file)
547 wfree(file);
549 icon->editing = 0;
550 wrelease(icon);
553 static void killCallback(WMenu * menu, WMenuEntry * entry)
555 WApplication *wapp = (WApplication *) entry->clientdata;
556 WFakeGroupLeader *fPtr;
557 char *buffer;
558 char *shortname;
559 char *basename(const char *shortname);
561 if (!WCHECK_STATE(WSTATE_NORMAL))
562 return;
564 WCHANGE_STATE(WSTATE_MODAL);
566 assert(entry->clientdata != NULL);
568 shortname = basename(wapp->app_icon->wm_instance);
570 buffer = wstrconcat(wapp->app_icon ? shortname : NULL,
571 _(" will be forcibly closed.\n"
572 "Any unsaved changes will be lost.\n" "Please confirm."));
574 fPtr = wapp->main_window_desc->fake_group;
576 wretain(wapp->main_window_desc);
577 if (wPreferences.dont_confirm_kill
578 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
579 buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
580 if (fPtr != NULL) {
581 WWindow *wwin, *twin;
583 wwin = wapp->main_window_desc->screen_ptr->focused_window;
584 while (wwin) {
585 twin = wwin->prev;
586 if (wwin->fake_group == fPtr)
587 wClientKill(wwin);
588 wwin = twin;
590 } else if (!wapp->main_window_desc->flags.destroyed) {
591 wClientKill(wapp->main_window_desc);
594 wrelease(wapp->main_window_desc);
595 wfree(buffer);
596 WCHANGE_STATE(WSTATE_NORMAL);
599 static WMenu *createApplicationMenu(WScreen *scr)
601 WMenu *menu;
603 menu = wMenuCreate(scr, NULL, False);
604 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
605 wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
606 wMenuAddCallback(menu, _("Launch"), relaunchCallback, NULL);
607 wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
608 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
610 return menu;
613 static void openApplicationMenu(WApplication * wapp, int x, int y)
615 WMenu *menu;
616 WScreen *scr = wapp->main_window_desc->screen_ptr;
617 int i;
619 if (!scr->icon_menu) {
620 scr->icon_menu = createApplicationMenu(scr);
621 wfree(scr->icon_menu->entries[1]->text);
624 menu = scr->icon_menu;
626 if (wapp->flags.hidden)
627 menu->entries[1]->text = _("Unhide");
628 else
629 menu->entries[1]->text = _("Hide");
631 menu->flags.realized = 0;
632 wMenuRealize(menu);
634 x -= menu->frame->core->width / 2;
635 if (x + menu->frame->core->width > scr->scr_width)
636 x = scr->scr_width - menu->frame->core->width;
638 if (x < 0)
639 x = 0;
641 /* set client data */
642 for (i = 0; i < menu->entry_no; i++)
643 menu->entries[i]->clientdata = wapp;
645 wMenuMapAt(menu, x, y, False);
648 /******************************************************************/
650 static void iconExpose(WObjDescriptor *desc, XEvent *event)
652 wAppIconPaint(desc->parent);
655 static void iconDblClick(WObjDescriptor *desc, XEvent *event)
657 WAppIcon *aicon = desc->parent;
658 WApplication *wapp;
659 WScreen *scr = aicon->icon->core->screen_ptr;
660 int unhideHere;
662 assert(aicon->icon->owner != NULL);
664 wapp = wApplicationOf(aicon->icon->owner->main_window);
666 if (event->xbutton.state & ControlMask) {
667 relaunchApplication(wapp);
668 return;
671 unhideHere = (event->xbutton.state & ShiftMask);
672 /* go to the last workspace that the user worked on the app */
673 if (!unhideHere && wapp->last_workspace != scr->current_workspace)
674 wWorkspaceChange(scr, wapp->last_workspace);
676 wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
678 if (event->xbutton.state & MOD_MASK)
679 wHideOtherApplications(aicon->icon->owner);
682 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
684 WAppIcon *aicon = desc->parent;
685 WScreen *scr = aicon->icon->core->screen_ptr;
686 Bool hasMoved;
688 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
689 return;
691 if (IsDoubleClick(scr, event)) {
692 /* Middle or right mouse actions were handled on first click */
693 if (event->xbutton.button == Button1)
694 iconDblClick(desc, event);
695 return;
698 if (event->xbutton.button == Button2) {
699 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
701 if (wapp)
702 relaunchApplication(wapp);
704 return;
707 if (event->xbutton.button == Button3) {
708 WObjDescriptor *desc;
709 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
711 if (!wapp)
712 return;
714 if (event->xbutton.send_event &&
715 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
716 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
717 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
718 wwarning("pointer grab failed for appicon menu");
719 return;
722 openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
724 /* allow drag select of menu */
725 desc = &scr->icon_menu->menu->descriptor;
726 event->xbutton.send_event = True;
727 (*desc->handle_mousedown) (desc, event);
728 return;
731 hasMoved = wHandleAppIconMove(aicon, event);
732 if (wPreferences.single_click && !hasMoved && aicon->dock != NULL)
734 iconDblClick(desc, event);
738 Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event)
740 WIcon *icon = aicon->icon;
741 WScreen *scr = icon->core->screen_ptr;
742 WDock *originalDock = aicon->dock; /* can be NULL */
743 WDock *lastDock = originalDock;
744 WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */
745 WDrawerChain *dc;
746 Bool done = False, dockable, ondock;
747 Bool grabbed = False;
748 Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
749 int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */
750 int omnipresent = aicon->omnipresent; /* this must be cached */
751 Bool showed_all_clips = False;
753 int clickButton = event->xbutton.button;
754 Pixmap ghost = None;
755 Window wins[2]; /* Managing shadow window */
756 XEvent ev;
758 int x = aicon->x_pos, y = aicon->y_pos;
759 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
760 int shad_x = x, shad_y = y;
761 int ix = aicon->xindex, iy = aicon->yindex;
762 int i;
763 int oldX = x;
764 int oldY = y;
765 Bool hasMoved = False;
767 if (wPreferences.flags.noupdates && originalDock != NULL)
768 return False;
770 if (!(event->xbutton.state & MOD_MASK))
771 wRaiseFrame(icon->core);
772 else {
773 /* If Mod is pressed for an docked appicon, assume it is to undock it,
774 * so don't lower it */
775 if (originalDock == NULL)
776 wLowerFrame(icon->core);
779 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
780 | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
781 GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
782 wwarning("Pointer grab failed in wHandleAppIconMove");
785 if (originalDock != NULL) {
786 dockable = True;
787 ondock = True;
789 else {
790 ondock = False;
791 if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer)
792 dockable = 0;
793 else
794 dockable = canBeDocked(icon->owner);
797 /* We try the various docks in that order:
798 * - First, the dock the appicon comes from, if any
799 * - Then, the drawers
800 * - Then, the "dock" (WM_DOCK)
801 * - Finally, the clip
803 i = 0;
804 if (originalDock != NULL)
805 allDocks[ i++ ] = originalDock;
806 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
807 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
808 if (dc->adrawer != originalDock)
809 allDocks[ i++ ] = dc->adrawer;
811 if (!wPreferences.flags.nodock && scr->dock != originalDock)
812 allDocks[ i++ ] = scr->dock;
813 if (!wPreferences.flags.noclip &&
814 originalDock != scr->workspaces[scr->current_workspace]->clip)
815 allDocks[ i++ ] = scr->workspaces[scr->current_workspace]->clip;
816 for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */
817 allDocks[ i ] = NULL;
819 wins[0] = icon->core->window;
820 wins[1] = scr->dock_shadow;
821 XRestackWindows(dpy, wins, 2);
822 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
823 if (superfluous) {
824 if (icon->pixmap != None)
825 ghost = MakeGhostIcon(scr, icon->pixmap);
826 else
827 ghost = MakeGhostIcon(scr, icon->core->window);
828 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
829 XClearWindow(dpy, scr->dock_shadow);
831 if (ondock)
832 XMapWindow(dpy, scr->dock_shadow);
834 while (!done) {
835 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
836 | ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
837 switch (ev.type) {
838 case Expose:
839 WMHandleEvent(&ev);
840 break;
842 case EnterNotify:
843 /* It means the cursor moved so fast that it entered
844 * something else (if moving slowly, it would have
845 * stayed in the appIcon that is being moved. Ignore
846 * such "spurious" EnterNotifiy's */
847 break;
849 case MotionNotify:
850 hasMoved = True;
851 if (!grabbed) {
852 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
853 || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
854 XChangeActivePointerGrab(dpy, ButtonMotionMask
855 | ButtonReleaseMask | ButtonPressMask,
856 wCursor[WCUR_MOVE], CurrentTime);
857 grabbed = 1;
858 } else {
859 break;
863 if (omnipresent && !showed_all_clips) {
864 int i;
865 for (i = 0; i < scr->workspace_count; i++) {
866 if (i == scr->current_workspace)
867 continue;
868 wDockShowIcons(scr->workspaces[i]->clip);
869 /* Note: if dock is collapsed (for instance, because it
870 auto-collapses), its icons still won't show up */
872 showed_all_clips = True; /* To prevent flickering */
875 x = ev.xmotion.x_root - ofs_x;
876 y = ev.xmotion.y_root - ofs_y;
877 wAppIconMove(aicon, x, y);
879 WDock *theNewDock = NULL;
880 if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock) {
881 for (i = 0; dockable && i < scr->drawer_count + 2; i++) {
882 WDock *theDock = allDocks[i];
883 if (theDock == NULL)
884 break;
885 if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) {
886 theNewDock = theDock;
887 break;
890 if (originalDock != NULL && theNewDock == NULL &&
891 (aicon->launching || aicon->lock || aicon->running)) {
892 /* In those cases, stay in lastDock if no dock really wants us */
893 theNewDock = lastDock;
896 if (lastDock != NULL && lastDock != theNewDock) {
897 /* Leave lastDock in the state we found it */
898 if (lastDock->type == WM_DRAWER) {
899 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
901 if (collapsed) {
902 lastDock->collapsed = 1;
903 wDockHideIcons(lastDock);
904 collapsed = False;
906 if (lastDock->auto_raise_lower) {
907 wDockLower(lastDock);
910 if (theNewDock != NULL) {
911 if (lastDock != theNewDock) {
912 collapsed = theNewDock->collapsed;
913 if (collapsed) {
914 theNewDock->collapsed = 0;
915 wDockShowIcons(theNewDock);
917 if (theNewDock->auto_raise_lower) {
918 wDockRaise(theNewDock);
919 /* And raise the moving tile above it */
920 wRaiseFrame(aicon->icon->core);
922 lastDock = theNewDock;
925 shad_x = lastDock->x_pos + ix*wPreferences.icon_size;
926 shad_y = lastDock->y_pos + iy*wPreferences.icon_size;
928 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
930 if (!ondock) {
931 XMapWindow(dpy, scr->dock_shadow);
933 ondock = 1;
934 } else {
935 lastDock = theNewDock; // i.e., NULL
936 if (ondock) {
937 XUnmapWindow(dpy, scr->dock_shadow);
939 * Leaving that weird comment for now.
940 * But if we see no gap, there is no need to fill one!
941 * We could test ondock first and the lastDock to NULL afterwards
942 if (lastDock_before_it_was_null->type == WM_DRAWER) {
943 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
944 } */
947 ondock = 0;
949 break;
951 case ButtonPress:
952 break;
954 case ButtonRelease:
955 if (ev.xbutton.button != clickButton)
956 break;
957 XUngrabPointer(dpy, CurrentTime);
959 Bool docked = False;
960 if (ondock) {
961 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
962 XUnmapWindow(dpy, scr->dock_shadow);
963 if (originalDock == NULL) { // docking an undocked appicon
964 docked = wDockAttachIcon(lastDock, aicon, ix, iy, False);
965 if (!docked) {
966 /* AppIcon got rejected (happens only when we can't get the
967 command for that appicon, and the user cancels the
968 wInputDialog asking for one). Make the rejection obvious by
969 sliding the icon to its old position */
970 if (lastDock->type == WM_DRAWER) {
971 // Also fill the gap left in the drawer
972 wDrawerFillTheGap(lastDock, aicon, False);
974 SlideWindow(icon->core->window, x, y, oldX, oldY);
977 else { // moving a docked appicon to a dock
978 if (originalDock == lastDock) {
979 docked = True;
980 wDockReattachIcon(originalDock, aicon, ix, iy);
982 else {
983 docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy);
984 if (!docked) {
985 /* Possible scenario: user moved an auto-attracted appicon
986 from the clip to the dock, and cancelled the wInputDialog
987 asking for a command */
988 if (lastDock->type == WM_DRAWER) {
989 wDrawerFillTheGap(lastDock, aicon, False);
991 /* If aicon comes from a drawer, make some room to reattach it */
992 if (originalDock->type == WM_DRAWER) {
993 WAppIcon *aiconsToShift[ originalDock->icon_count ];
994 int j = 0;
996 for (i = 0; i < originalDock->max_icons; i++) {
997 WAppIcon *ai = originalDock->icon_array[ i ];
998 if (ai && ai != aicon &&
999 abs(ai->xindex) >= abs(aicon->xindex))
1000 aiconsToShift[j++] = ai;
1002 if (j != originalDock->icon_count - abs(aicon->xindex) - 1)
1003 // Trust this never happens?
1004 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1005 j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex);
1006 wSlideAppicons(aiconsToShift, j, originalDock->on_right_side);
1007 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1010 SlideWindow(icon->core->window, x, y, oldX, oldY);
1011 wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex);
1013 else {
1014 if (originalDock->auto_collapse && !originalDock->collapsed) {
1015 originalDock->collapsed = 1;
1016 wDockHideIcons(originalDock);
1018 if (originalDock->auto_raise_lower)
1019 wDockLower(originalDock);
1023 // No matter what happened above, check to lower lastDock
1024 // Don't see why I commented out the following 2 lines
1025 /* if (lastDock->auto_raise_lower)
1026 wDockLower(lastDock); */
1027 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1028 * collapsed, so that wHandleAppIconMove doesn't collapse it
1029 * right away (the timer will take care of it) */
1030 if (lastDock->auto_collapse)
1031 collapsed = 0;
1033 else {
1034 if (originalDock != NULL) { /* Detaching a docked appicon */
1035 if (superfluous) {
1036 if (!aicon->running && !wPreferences.no_animations) {
1037 /* We need to deselect it, even if is deselected in
1038 * wDockDetach(), because else DoKaboom() will fail.
1040 if (aicon->icon->selected)
1041 wIconSelect(aicon->icon);
1042 DoKaboom(scr, aicon->icon->core->window, x, y);
1045 wDockDetach(originalDock, aicon);
1046 if (originalDock->auto_collapse && !originalDock->collapsed) {
1047 originalDock->collapsed = 1;
1048 wDockHideIcons(originalDock);
1050 if (originalDock->auto_raise_lower)
1051 wDockLower(originalDock);
1054 // Can't remember why the icon hiding is better done above than below (commented out)
1055 // Also, lastDock is quite different from originalDock
1057 if (collapsed) {
1058 lastDock->collapsed = 1;
1059 wDockHideIcons(lastDock);
1060 collapsed = 0;
1063 if (superfluous) {
1064 if (ghost != None)
1065 XFreePixmap(dpy, ghost);
1066 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
1068 if (showed_all_clips) {
1069 int i;
1070 for (i = 0; i < scr->workspace_count; i++) {
1071 if (i == scr->current_workspace)
1072 continue;
1073 wDockHideIcons(scr->workspaces[i]->clip);
1076 if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked))
1077 /* Need to rearrange unless moving from dock to dock */
1078 wArrangeIcons(scr, True);
1079 return hasMoved;
1082 return False; /* Never reached */
1085 /* This function save the application icon and store the path in the Dictionary */
1086 static void wApplicationSaveIconPathFor(const char *iconPath, const char *wm_instance, const char *wm_class)
1088 WMPropList *dict = WDWindowAttributes->dictionary;
1089 WMPropList *adict, *key, *iconk;
1090 WMPropList *val;
1091 char *tmp;
1093 tmp = get_name_for_instance_class(wm_instance, wm_class);
1094 key = WMCreatePLString(tmp);
1095 wfree(tmp);
1097 adict = WMGetFromPLDictionary(dict, key);
1098 iconk = WMCreatePLString("Icon");
1100 if (adict) {
1101 val = WMGetFromPLDictionary(adict, iconk);
1102 } else {
1103 /* no dictionary for app, so create one */
1104 adict = WMCreatePLDictionary(NULL, NULL);
1105 WMPutInPLDictionary(dict, key, adict);
1106 WMReleasePropList(adict);
1107 val = NULL;
1110 if (!val) {
1111 val = WMCreatePLString(iconPath);
1112 WMPutInPLDictionary(adict, iconk, val);
1113 WMReleasePropList(val);
1114 } else {
1115 val = NULL;
1118 WMReleasePropList(key);
1119 WMReleasePropList(iconk);
1121 if (val && !wPreferences.flags.noupdates)
1122 UpdateDomainFile(WDWindowAttributes);
1125 static WAppIcon *findDockIconFor(WDock *dock, Window main_window)
1127 WAppIcon *aicon = NULL;
1129 aicon = wDockFindIconForWindow(dock, main_window);
1130 if (!aicon) {
1131 wDockTrackWindowLaunch(dock, main_window);
1132 aicon = wDockFindIconForWindow(dock, main_window);
1134 return aicon;
1137 static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window main_window)
1139 WScreen *scr = wwin->screen_ptr;
1140 wapp->app_icon = NULL;
1142 if (scr->last_dock)
1143 wapp->app_icon = findDockIconFor(scr->last_dock, main_window);
1145 /* check main dock if we did not find it in last dock */
1146 if (!wapp->app_icon && scr->dock)
1147 wapp->app_icon = findDockIconFor(scr->dock, main_window);
1149 /* check clips */
1150 if (!wapp->app_icon) {
1151 int i;
1152 for (i = 0; i < scr->workspace_count; i++) {
1153 WDock *dock = scr->workspaces[i]->clip;
1154 if (dock)
1155 wapp->app_icon = findDockIconFor(dock, main_window);
1156 if (wapp->app_icon)
1157 break;
1161 /* Finally check drawers */
1162 if (!wapp->app_icon) {
1163 WDrawerChain *dc;
1164 for (dc = scr->drawers; dc != NULL; dc = dc->next) {
1165 wapp->app_icon = findDockIconFor(dc->adrawer, main_window);
1166 if (wapp->app_icon)
1167 break;
1171 /* If created, then set some flags */
1172 if (wapp->app_icon) {
1173 WWindow *mainw = wapp->main_window_desc;
1175 wapp->app_icon->running = 1;
1176 wapp->app_icon->icon->owner = mainw;
1177 if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
1178 wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
1180 /* Update the icon images */
1181 wIconUpdate(wapp->app_icon->icon);
1183 /* Paint it */
1184 wAppIconPaint(wapp->app_icon);
1185 save_appicon(wapp->app_icon, True);
1189 /* Add the appicon to the appiconlist */
1190 static void add_to_appicon_list(WScreen *scr, WAppIcon *appicon)
1192 appicon->prev = NULL;
1193 appicon->next = scr->app_icon_list;
1194 if (scr->app_icon_list)
1195 scr->app_icon_list->prev = appicon;
1197 scr->app_icon_list = appicon;
1200 /* Remove the appicon from the appiconlist */
1201 static void remove_from_appicon_list(WScreen *scr, WAppIcon *appicon)
1203 if (appicon == scr->app_icon_list) {
1204 if (appicon->next)
1205 appicon->next->prev = NULL;
1206 scr->app_icon_list = appicon->next;
1207 } else {
1208 if (appicon->next)
1209 appicon->next->prev = appicon->prev;
1210 if (appicon->prev)
1211 appicon->prev->next = appicon->next;
1214 appicon->prev = NULL;
1215 appicon->next = NULL;
1218 /* Return the AppIcon associated with a given (Xlib) Window. */
1219 WAppIcon *wAppIconFor(Window window)
1221 WObjDescriptor *desc;
1223 if (window == None)
1224 return NULL;
1226 if (XFindContext(dpy, window, wWinContext, (XPointer *) & desc) == XCNOENT)
1227 return NULL;
1229 if (desc->parent_type == WCLASS_APPICON || desc->parent_type == WCLASS_DOCK_ICON)
1230 return desc->parent;
1232 return NULL;