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