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.
26 #include <X11/Xutil.h>
34 #include "WindowMaker.h"
37 #include "application.h"
44 #include "workspace.h"
45 #include "superfluous.h"
51 #include "placement.h"
59 * icon_file for the dock is got from the preferences file by
60 * using the classname/instancename
63 #define MOD_MASK wPreferences.modifier_mask
64 #define ICON_SIZE wPreferences.icon_size
66 static void iconDblClick(WObjDescriptor
* desc
, XEvent
* event
);
67 static void iconExpose(WObjDescriptor
* desc
, XEvent
* event
);
68 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
);
69 static WAppIcon
*wAppIconCreate(WWindow
* leader_win
);
70 static void add_to_appicon_list(WAppIcon
*appicon
);
71 static void remove_from_appicon_list(WAppIcon
*appicon
);
72 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
);
74 /* This function is used if the application is a .app. It checks if it has an icon in it
75 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
77 void wApplicationExtractDirPackIcon(const char *path
, const char *wm_instance
, const char *wm_class
)
79 char *iconPath
= NULL
;
82 if (strstr(path
, ".app")) {
83 tmp
= wmalloc(strlen(path
) + 16);
85 if (wPreferences
.supports_tiff
) {
88 if (access(tmp
, R_OK
) == 0)
95 if (access(tmp
, R_OK
) == 0)
103 wApplicationSaveIconPathFor(iconPath
, wm_instance
, wm_class
);
109 WAppIcon
*wAppIconCreateForDock(WScreen
*scr
, const char *command
, const char *wm_instance
, const char *wm_class
, int tile
)
113 aicon
= wmalloc(sizeof(WAppIcon
));
118 add_to_appicon_list(aicon
);
121 aicon
->command
= wstrdup(command
);
124 aicon
->wm_class
= wstrdup(wm_class
);
127 aicon
->wm_instance
= wstrdup(wm_instance
);
129 if (strcmp(wm_class
, "WMDock") == 0 && wPreferences
.flags
.clip_merged_in_dock
)
131 aicon
->icon
= icon_create_for_dock(scr
, command
, wm_instance
, wm_class
, tile
);
134 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
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
);
147 void create_appicon_for_application(WApplication
*wapp
, WWindow
*wwin
)
149 /* Try to create an icon from the dock or clip */
150 create_appicon_from_dock(wwin
, wapp
, wapp
->main_window
);
152 /* If app_icon was not found, create it */
153 if (!wapp
->app_icon
) {
154 /* Create the icon */
155 wapp
->app_icon
= wAppIconCreate(wapp
->main_window_desc
);
156 wIconUpdate(wapp
->app_icon
->icon
);
158 /* Now, paint the icon */
159 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
))
160 paint_app_icon(wapp
);
163 /* Save the app_icon in a file */
164 save_appicon(wapp
->app_icon
, False
);
167 void unpaint_app_icon(WApplication
*wapp
)
173 if (!wapp
|| !wapp
->app_icon
)
176 aicon
= wapp
->app_icon
;
178 /* If the icon is docked, don't continue */
182 scr
= wapp
->main_window_desc
->screen_ptr
;
183 clip
= w_global
.workspace
.array
[w_global
.workspace
.current
]->clip
;
185 if (!clip
|| !aicon
->attracted
|| !clip
->collapsed
)
186 XUnmapWindow(dpy
, aicon
->icon
->core
->window
);
188 /* We want to avoid having it on the list because otherwise
189 * there will be a hole when the icons are arranged with
191 remove_from_appicon_list(aicon
);
193 if (wPreferences
.auto_arrange_icons
&& !aicon
->attracted
)
194 wArrangeIcons(scr
, True
);
197 void paint_app_icon(WApplication
*wapp
)
200 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
201 WDock
*attracting_dock
;
203 Bool update_icon
= False
;
205 if (!wapp
|| !wapp
->app_icon
)
208 icon
= wapp
->app_icon
->icon
;
209 wapp
->app_icon
->main_window
= wapp
->main_window
;
211 /* If the icon is docked, don't continue */
212 if (wapp
->app_icon
->docked
)
215 attracting_dock
= scr
->attracting_drawer
!= NULL
?
216 scr
->attracting_drawer
:
217 w_global
.workspace
.array
[w_global
.workspace
.current
]->clip
;
218 if (attracting_dock
&& attracting_dock
->attract_icons
&&
219 wDockFindFreeSlot(attracting_dock
, &x
, &y
)) {
220 wapp
->app_icon
->attracted
= 1;
221 if (!icon
->shadowed
) {
225 wDockAttachIcon(attracting_dock
, wapp
->app_icon
, x
, y
, update_icon
);
227 /* We must know if the icon is painted in the screen,
228 * because if painted, then PlaceIcon will return the next
229 * space on the screen, and the icon will move */
230 if (wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
) {
231 PlaceIcon(scr
, &x
, &y
, wGetHeadForWindow(wapp
->main_window_desc
));
232 wAppIconMove(wapp
->app_icon
, x
, y
);
233 wLowerFrame(icon
->core
);
237 /* If we want appicon (no_appicon is not set) and the icon is not
238 * in the appicon_list, we must add it. Else, we want to avoid
239 * having it on the list */
240 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
) &&
241 wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
)
242 add_to_appicon_list(wapp
->app_icon
);
244 if (!attracting_dock
|| !wapp
->app_icon
->attracted
|| !attracting_dock
->collapsed
)
245 XMapWindow(dpy
, icon
->core
->window
);
247 if (wPreferences
.auto_arrange_icons
&& !wapp
->app_icon
->attracted
)
248 wArrangeIcons(scr
, True
);
251 void removeAppIconFor(WApplication
*wapp
)
256 if (wPreferences
.highlight_active_app
)
257 wIconSetHighlited(wapp
->app_icon
->icon
, False
);
258 if (wapp
->app_icon
->docked
&& !wapp
->app_icon
->attracted
) {
259 wapp
->app_icon
->running
= 0;
260 /* since we keep it, we don't care if it was attracted or not */
261 wapp
->app_icon
->attracted
= 0;
262 wapp
->app_icon
->icon
->shadowed
= 0;
263 wapp
->app_icon
->main_window
= None
;
264 wapp
->app_icon
->pid
= 0;
265 wapp
->app_icon
->icon
->owner
= NULL
;
266 wapp
->app_icon
->icon
->icon_win
= None
;
268 /* Set the icon image */
269 set_icon_image_from_database(wapp
->app_icon
->icon
, wapp
->app_icon
->wm_instance
,
270 wapp
->app_icon
->wm_class
, wapp
->app_icon
->command
);
272 /* Update the icon, because wapp->app_icon->icon could be NULL */
273 wIconUpdate(wapp
->app_icon
->icon
);
276 wAppIconPaint(wapp
->app_icon
);
277 } else if (wapp
->app_icon
->docked
) {
278 wapp
->app_icon
->running
= 0;
279 if (wapp
->app_icon
->dock
->type
== WM_DRAWER
) {
280 wDrawerFillTheGap(wapp
->app_icon
->dock
, wapp
->app_icon
, True
);
282 wDockDetach(wapp
->app_icon
->dock
, wapp
->app_icon
);
284 wAppIconDestroy(wapp
->app_icon
);
287 wapp
->app_icon
= NULL
;
289 if (wPreferences
.auto_arrange_icons
)
290 wArrangeIcons(wapp
->main_window_desc
->screen_ptr
, True
);
293 static WAppIcon
*wAppIconCreate(WWindow
*leader_win
)
297 aicon
= wmalloc(sizeof(WAppIcon
));
304 if (leader_win
->wm_class
)
305 aicon
->wm_class
= wstrdup(leader_win
->wm_class
);
307 if (leader_win
->wm_instance
)
308 aicon
->wm_instance
= wstrdup(leader_win
->wm_instance
);
310 aicon
->icon
= icon_create_for_wwindow(leader_win
);
312 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
315 /* will be overriden if docked */
316 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
317 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
318 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
319 aicon
->icon
->core
->descriptor
.parent
= aicon
;
320 AddToStackList(aicon
->icon
->core
);
321 aicon
->icon
->show_title
= 0;
326 void wAppIconDestroy(WAppIcon
*aicon
)
328 RemoveFromStackList(aicon
->icon
->core
);
329 wIconDestroy(aicon
->icon
);
331 wfree(aicon
->command
);
333 if (aicon
->dnd_command
)
334 wfree(aicon
->dnd_command
);
336 if (aicon
->wm_instance
)
337 wfree(aicon
->wm_instance
);
340 wfree(aicon
->wm_class
);
342 remove_from_appicon_list(aicon
);
344 aicon
->destroyed
= 1;
348 static void drawCorner(WIcon
* icon
)
350 WScreen
*scr
= icon
->core
->screen_ptr
;
359 XFillPolygon(dpy
, icon
->core
->window
, scr
->icon_title_texture
->normal_gc
,
360 points
, 3, Convex
, CoordModeOrigin
);
361 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 0, 12);
362 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 12, 0);
365 void wAppIconMove(WAppIcon
* aicon
, int x
, int y
)
367 XMoveWindow(dpy
, aicon
->icon
->core
->window
, x
, y
);
373 static void updateDockNumbers(WScreen
*scr
)
377 WAppIcon
*dicon
= scr
->dock
->icon_array
[0];
379 ws_numbers
= wmalloc(20);
380 snprintf(ws_numbers
, 20, "%i [ %i ]", w_global
.workspace
.current
+ 1, ((w_global
.workspace
.current
/ 10) + 1));
381 length
= strlen(ws_numbers
);
383 XClearArea(dpy
, dicon
->icon
->core
->window
, 2, 2, 50, WMFontHeight(scr
->icon_title_font
) + 1, False
);
385 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->black
,
386 scr
->icon_title_font
, 4, 3, ws_numbers
, length
);
388 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->white
,
389 scr
->icon_title_font
, 3, 2, ws_numbers
, length
);
393 #endif /* WS_INDICATOR */
395 void wAppIconPaint(WAppIcon
*aicon
)
398 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
400 if (aicon
->icon
->owner
)
401 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
405 wIconPaint(aicon
->icon
);
408 if (aicon
->docked
&& scr
->dock
&& scr
->dock
== aicon
->dock
&& aicon
->yindex
== 0)
409 updateDockNumbers(scr
);
411 if (scr
->dock_dots
&& aicon
->docked
&& !aicon
->running
&& aicon
->command
!= NULL
) {
412 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
413 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
414 XCopyArea(dpy
, scr
->dock_dots
->image
, aicon
->icon
->core
->window
,
415 scr
->copy_gc
, 0, 0, scr
->dock_dots
->width
, scr
->dock_dots
->height
, 0, 0);
418 if (wapp
&& wapp
->flags
.hidden
) {
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
,
422 aicon
->icon
->core
->window
, scr
->copy_gc
, 0, 0, 7, scr
->dock_dots
->height
, 0, 0);
424 #endif /* HIDDENDOT */
426 if (aicon
->omnipresent
)
427 drawCorner(aicon
->icon
);
429 XSetClipMask(dpy
, scr
->copy_gc
, None
);
430 if (aicon
->launching
)
431 XFillRectangle(dpy
, aicon
->icon
->core
->window
, scr
->stipple_gc
,
432 0, 0, wPreferences
.icon_size
, wPreferences
.icon_size
);
435 /* Save the application icon, if it's a dockapp then use it with dock = True */
436 void save_appicon(WAppIcon
*aicon
, Bool dock
)
443 if (dock
&& (!aicon
->docked
|| aicon
->attracted
))
446 path
= wIconStore(aicon
->icon
);
450 wApplicationSaveIconPathFor(path
, aicon
->wm_instance
, aicon
->wm_class
);
454 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
456 /* main_window may not have the full command line; try to find one which does */
457 static void relaunchApplication(WApplication
*wapp
)
460 WWindow
*wlist
, *next
;
462 scr
= wapp
->main_window_desc
->screen_ptr
;
463 wlist
= scr
->focused_window
;
473 if (wlist
->main_window
== wapp
->main_window
) {
474 if (RelaunchWindow(wlist
))
482 static void relaunchCallback(WMenu
* menu
, WMenuEntry
* entry
)
484 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
486 /* Parameter not used, but tell the compiler that it is ok */
489 relaunchApplication(wapp
);
492 static void hideCallback(WMenu
* menu
, WMenuEntry
* entry
)
494 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
496 if (wapp
->flags
.hidden
) {
497 wWorkspaceChange(menu
->menu
->screen_ptr
, wapp
->last_workspace
);
498 wUnhideApplication(wapp
, False
, False
);
500 wHideApplication(wapp
);
504 static void unhideHereCallback(WMenu
* menu
, WMenuEntry
* entry
)
506 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
508 /* Parameter not used, but tell the compiler that it is ok */
511 wUnhideApplication(wapp
, False
, True
);
514 static void setIconCallback(WMenu
*menu
, WMenuEntry
*entry
)
516 WAppIcon
*icon
= ((WApplication
*) entry
->clientdata
)->app_icon
;
521 /* Parameter not used, but tell the compiler that it is ok */
524 assert(icon
!= NULL
);
530 scr
= icon
->icon
->core
->screen_ptr
;
534 result
= wIconChooserDialog(scr
, &file
, icon
->wm_instance
, icon
->wm_class
);
536 if (result
&& !icon
->destroyed
) {
537 if (file
&& *file
== 0) {
541 if (!wIconChangeImageFile(icon
->icon
, file
)) {
542 wMessageDialog(scr
, _("Error"),
543 _("Could not open specified icon file"), _("OK"), NULL
, NULL
);
545 wDefaultChangeIcon(icon
->wm_instance
, icon
->wm_class
, file
);
555 static void killCallback(WMenu
* menu
, WMenuEntry
* entry
)
557 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
558 WFakeGroupLeader
*fPtr
;
562 if (!WCHECK_STATE(WSTATE_NORMAL
))
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
) {
582 WWindow
*wwin
, *twin
;
584 wwin
= wapp
->main_window_desc
->screen_ptr
->focused_window
;
587 if (wwin
->fake_group
== fPtr
)
591 } else if (!wapp
->main_window_desc
->flags
.destroyed
) {
592 wClientKill(wapp
->main_window_desc
);
595 wrelease(wapp
->main_window_desc
);
597 WCHANGE_STATE(WSTATE_NORMAL
);
600 static WMenu
*createApplicationMenu(WScreen
*scr
)
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
);
614 static void openApplicationMenu(WApplication
* wapp
, int x
, int y
)
617 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
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");
630 menu
->entries
[1]->text
= _("Hide");
632 menu
->flags
.realized
= 0;
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
;
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 /* Parameter not used, but tell the compiler that it is ok */
656 wAppIconPaint(desc
->parent
);
659 static void iconDblClick(WObjDescriptor
*desc
, XEvent
*event
)
661 WAppIcon
*aicon
= desc
->parent
;
663 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
666 assert(aicon
->icon
->owner
!= NULL
);
668 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
670 if (event
->xbutton
.state
& ControlMask
) {
671 relaunchApplication(wapp
);
675 unhideHere
= (event
->xbutton
.state
& ShiftMask
);
676 /* go to the last workspace that the user worked on the app */
677 if (!unhideHere
&& wapp
->last_workspace
!= w_global
.workspace
.current
)
678 wWorkspaceChange(scr
, wapp
->last_workspace
);
680 wUnhideApplication(wapp
, event
->xbutton
.button
== Button2
, unhideHere
);
682 if (event
->xbutton
.state
& MOD_MASK
)
683 wHideOtherApplications(aicon
->icon
->owner
);
686 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
)
688 WAppIcon
*aicon
= desc
->parent
;
689 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
692 if (aicon
->editing
|| WCHECK_STATE(WSTATE_MODAL
))
695 if (IsDoubleClick(scr
, event
)) {
696 /* Middle or right mouse actions were handled on first click */
697 if (event
->xbutton
.button
== Button1
)
698 iconDblClick(desc
, event
);
702 if (event
->xbutton
.button
== Button2
) {
703 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
706 relaunchApplication(wapp
);
711 if (event
->xbutton
.button
== Button3
) {
712 WObjDescriptor
*desc
;
713 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
718 if (event
->xbutton
.send_event
&&
719 XGrabPointer(dpy
, aicon
->icon
->core
->window
, True
, ButtonMotionMask
720 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
721 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
722 wwarning("pointer grab failed for appicon menu");
726 openApplicationMenu(wapp
, event
->xbutton
.x_root
, event
->xbutton
.y_root
);
728 /* allow drag select of menu */
729 desc
= &scr
->icon_menu
->menu
->descriptor
;
730 event
->xbutton
.send_event
= True
;
731 (*desc
->handle_mousedown
) (desc
, event
);
735 hasMoved
= wHandleAppIconMove(aicon
, event
);
736 if (wPreferences
.single_click
&& !hasMoved
&& aicon
->dock
!= NULL
)
738 iconDblClick(desc
, event
);
742 Bool
wHandleAppIconMove(WAppIcon
*aicon
, XEvent
*event
)
744 WIcon
*icon
= aicon
->icon
;
745 WScreen
*scr
= icon
->core
->screen_ptr
;
746 WDock
*originalDock
= aicon
->dock
; /* can be NULL */
747 WDock
*lastDock
= originalDock
;
748 WDock
*allDocks
[scr
->drawer_count
+ 2]; /* clip, dock and drawers (order determined at runtime) */
750 Bool done
= False
, dockable
, ondock
;
751 Bool grabbed
= False
;
752 Bool collapsed
= False
; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
753 int superfluous
= wPreferences
.superfluous
; /* we cache it to avoid problems */
754 int omnipresent
= aicon
->omnipresent
; /* this must be cached */
755 Bool showed_all_clips
= False
;
757 int clickButton
= event
->xbutton
.button
;
759 Window wins
[2]; /* Managing shadow window */
762 int x
= aicon
->x_pos
, y
= aicon
->y_pos
;
763 int ofs_x
= event
->xbutton
.x
, ofs_y
= event
->xbutton
.y
;
764 int shad_x
= x
, shad_y
= y
;
765 int ix
= aicon
->xindex
, iy
= aicon
->yindex
;
769 Bool hasMoved
= False
;
771 if (wPreferences
.flags
.noupdates
&& originalDock
!= NULL
)
774 if (!(event
->xbutton
.state
& MOD_MASK
))
775 wRaiseFrame(icon
->core
);
777 /* If Mod is pressed for an docked appicon, assume it is to undock it,
778 * so don't lower it */
779 if (originalDock
== NULL
)
780 wLowerFrame(icon
->core
);
783 if (XGrabPointer(dpy
, icon
->core
->window
, True
, ButtonMotionMask
784 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
785 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
786 wwarning("Pointer grab failed in wHandleAppIconMove");
789 if (originalDock
!= NULL
) {
795 if (wPreferences
.flags
.nodock
&& wPreferences
.flags
.noclip
&& wPreferences
.flags
.nodrawer
)
798 dockable
= canBeDocked(icon
->owner
);
801 /* We try the various docks in that order:
802 * - First, the dock the appicon comes from, if any
803 * - Then, the drawers
804 * - Then, the "dock" (WM_DOCK)
805 * - Finally, the clip
808 if (originalDock
!= NULL
)
809 allDocks
[ i
++ ] = originalDock
;
810 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
811 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
812 if (dc
->adrawer
!= originalDock
)
813 allDocks
[ i
++ ] = dc
->adrawer
;
815 if (!wPreferences
.flags
.nodock
&& scr
->dock
!= originalDock
)
816 allDocks
[ i
++ ] = scr
->dock
;
818 if (!wPreferences
.flags
.noclip
&&
819 originalDock
!= w_global
.workspace
.array
[w_global
.workspace
.current
]->clip
)
820 allDocks
[i
++] = w_global
.workspace
.array
[w_global
.workspace
.current
]->clip
;
822 for ( ; i
< scr
->drawer_count
+ 2; i
++) /* In case the clip, the dock, or both, are disabled */
823 allDocks
[ i
] = NULL
;
825 wins
[0] = icon
->core
->window
;
826 wins
[1] = scr
->dock_shadow
;
827 XRestackWindows(dpy
, wins
, 2);
828 XMoveResizeWindow(dpy
, scr
->dock_shadow
, aicon
->x_pos
, aicon
->y_pos
, ICON_SIZE
, ICON_SIZE
);
830 if (icon
->pixmap
!= None
)
831 ghost
= MakeGhostIcon(scr
, icon
->pixmap
);
833 ghost
= MakeGhostIcon(scr
, icon
->core
->window
);
834 XSetWindowBackgroundPixmap(dpy
, scr
->dock_shadow
, ghost
);
835 XClearWindow(dpy
, scr
->dock_shadow
);
838 XMapWindow(dpy
, scr
->dock_shadow
);
841 WMMaskEvent(dpy
, PointerMotionMask
| ButtonReleaseMask
| ButtonPressMask
842 | ButtonMotionMask
| ExposureMask
| EnterWindowMask
, &ev
);
849 /* It means the cursor moved so fast that it entered
850 * something else (if moving slowly, it would have
851 * stayed in the appIcon that is being moved. Ignore
852 * such "spurious" EnterNotifiy's */
858 if (abs(ofs_x
- ev
.xmotion
.x
) >= MOVE_THRESHOLD
859 || abs(ofs_y
- ev
.xmotion
.y
) >= MOVE_THRESHOLD
) {
860 XChangeActivePointerGrab(dpy
, ButtonMotionMask
861 | ButtonReleaseMask
| ButtonPressMask
,
862 wPreferences
.cursor
[WCUR_MOVE
], CurrentTime
);
869 if (omnipresent
&& !showed_all_clips
) {
871 for (i
= 0; i
< w_global
.workspace
.count
; i
++) {
872 if (i
== w_global
.workspace
.current
)
875 wDockShowIcons(w_global
.workspace
.array
[i
]->clip
);
876 /* Note: if dock is collapsed (for instance, because it
877 auto-collapses), its icons still won't show up */
879 showed_all_clips
= True
; /* To prevent flickering */
882 x
= ev
.xmotion
.x_root
- ofs_x
;
883 y
= ev
.xmotion
.y_root
- ofs_y
;
884 wAppIconMove(aicon
, x
, y
);
886 WDock
*theNewDock
= NULL
;
887 if (!(ev
.xmotion
.state
& MOD_MASK
) || aicon
->launching
|| aicon
->lock
|| originalDock
== NULL
) {
888 for (i
= 0; dockable
&& i
< scr
->drawer_count
+ 2; i
++) {
889 WDock
*theDock
= allDocks
[i
];
892 if (wDockSnapIcon(theDock
, aicon
, x
, y
, &ix
, &iy
, (theDock
== originalDock
))) {
893 theNewDock
= theDock
;
897 if (originalDock
!= NULL
&& theNewDock
== NULL
&&
898 (aicon
->launching
|| aicon
->lock
|| aicon
->running
)) {
899 /* In those cases, stay in lastDock if no dock really wants us */
900 theNewDock
= lastDock
;
903 if (lastDock
!= NULL
&& lastDock
!= theNewDock
) {
904 /* Leave lastDock in the state we found it */
905 if (lastDock
->type
== WM_DRAWER
) {
906 wDrawerFillTheGap(lastDock
, aicon
, (lastDock
== originalDock
));
909 lastDock
->collapsed
= 1;
910 wDockHideIcons(lastDock
);
913 if (lastDock
->auto_raise_lower
) {
914 wDockLower(lastDock
);
917 if (theNewDock
!= NULL
) {
918 if (lastDock
!= theNewDock
) {
919 collapsed
= theNewDock
->collapsed
;
921 theNewDock
->collapsed
= 0;
922 wDockShowIcons(theNewDock
);
924 if (theNewDock
->auto_raise_lower
) {
925 wDockRaise(theNewDock
);
926 /* And raise the moving tile above it */
927 wRaiseFrame(aicon
->icon
->core
);
929 lastDock
= theNewDock
;
932 shad_x
= lastDock
->x_pos
+ ix
*wPreferences
.icon_size
;
933 shad_y
= lastDock
->y_pos
+ iy
*wPreferences
.icon_size
;
935 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
938 XMapWindow(dpy
, scr
->dock_shadow
);
942 lastDock
= theNewDock
; // i.e., NULL
944 XUnmapWindow(dpy
, scr
->dock_shadow
);
946 * Leaving that weird comment for now.
947 * But if we see no gap, there is no need to fill one!
948 * We could test ondock first and the lastDock to NULL afterwards
949 if (lastDock_before_it_was_null->type == WM_DRAWER) {
950 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
962 if (ev
.xbutton
.button
!= clickButton
)
964 XUngrabPointer(dpy
, CurrentTime
);
968 SlideWindow(icon
->core
->window
, x
, y
, shad_x
, shad_y
);
969 XUnmapWindow(dpy
, scr
->dock_shadow
);
970 if (originalDock
== NULL
) { // docking an undocked appicon
971 docked
= wDockAttachIcon(lastDock
, aicon
, ix
, iy
, False
);
973 /* AppIcon got rejected (happens only when we can't get the
974 command for that appicon, and the user cancels the
975 wInputDialog asking for one). Make the rejection obvious by
976 sliding the icon to its old position */
977 if (lastDock
->type
== WM_DRAWER
) {
978 // Also fill the gap left in the drawer
979 wDrawerFillTheGap(lastDock
, aicon
, False
);
981 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
984 else { // moving a docked appicon to a dock
985 if (originalDock
== lastDock
) {
987 wDockReattachIcon(originalDock
, aicon
, ix
, iy
);
990 docked
= wDockMoveIconBetweenDocks(originalDock
, lastDock
, aicon
, ix
, iy
);
992 /* Possible scenario: user moved an auto-attracted appicon
993 from the clip to the dock, and cancelled the wInputDialog
994 asking for a command */
995 if (lastDock
->type
== WM_DRAWER
) {
996 wDrawerFillTheGap(lastDock
, aicon
, False
);
998 /* If aicon comes from a drawer, make some room to reattach it */
999 if (originalDock
->type
== WM_DRAWER
) {
1000 WAppIcon
*aiconsToShift
[ originalDock
->icon_count
];
1003 for (i
= 0; i
< originalDock
->max_icons
; i
++) {
1004 WAppIcon
*ai
= originalDock
->icon_array
[ i
];
1005 if (ai
&& ai
!= aicon
&&
1006 abs(ai
->xindex
) >= abs(aicon
->xindex
))
1007 aiconsToShift
[j
++] = ai
;
1009 if (j
!= originalDock
->icon_count
- abs(aicon
->xindex
) - 1)
1010 // Trust this never happens?
1011 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1012 j
, originalDock
->icon_count
- abs(aicon
->xindex
) - 1, aicon
->xindex
);
1013 wSlideAppicons(aiconsToShift
, j
, originalDock
->on_right_side
);
1014 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1017 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
1018 wDockReattachIcon(originalDock
, aicon
, aicon
->xindex
, aicon
->yindex
);
1021 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1022 originalDock
->collapsed
= 1;
1023 wDockHideIcons(originalDock
);
1025 if (originalDock
->auto_raise_lower
)
1026 wDockLower(originalDock
);
1030 // No matter what happened above, check to lower lastDock
1031 // Don't see why I commented out the following 2 lines
1032 /* if (lastDock->auto_raise_lower)
1033 wDockLower(lastDock); */
1034 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1035 * collapsed, so that wHandleAppIconMove doesn't collapse it
1036 * right away (the timer will take care of it) */
1037 if (lastDock
->auto_collapse
)
1041 if (originalDock
!= NULL
) { /* Detaching a docked appicon */
1043 if (!aicon
->running
&& !wPreferences
.no_animations
) {
1044 /* We need to deselect it, even if is deselected in
1045 * wDockDetach(), because else DoKaboom() will fail.
1047 if (aicon
->icon
->selected
)
1048 wIconSelect(aicon
->icon
);
1049 DoKaboom(scr
, aicon
->icon
->core
->window
, x
, y
);
1052 wDockDetach(originalDock
, aicon
);
1053 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1054 originalDock
->collapsed
= 1;
1055 wDockHideIcons(originalDock
);
1057 if (originalDock
->auto_raise_lower
)
1058 wDockLower(originalDock
);
1061 // Can't remember why the icon hiding is better done above than below (commented out)
1062 // Also, lastDock is quite different from originalDock
1065 lastDock->collapsed = 1;
1066 wDockHideIcons(lastDock);
1072 XFreePixmap(dpy
, ghost
);
1073 XSetWindowBackground(dpy
, scr
->dock_shadow
, scr
->white_pixel
);
1075 if (showed_all_clips
) {
1077 for (i
= 0; i
< w_global
.workspace
.count
; i
++) {
1078 if (i
== w_global
.workspace
.current
)
1081 wDockHideIcons(w_global
.workspace
.array
[i
]->clip
);
1084 if (wPreferences
.auto_arrange_icons
&& !(originalDock
!= NULL
&& docked
))
1085 /* Need to rearrange unless moving from dock to dock */
1086 wArrangeIcons(scr
, True
);
1090 return False
; /* Never reached */
1093 /* This function save the application icon and store the path in the Dictionary */
1094 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
)
1096 WMPropList
*dict
= w_global
.domain
.window_attr
->dictionary
;
1097 WMPropList
*adict
, *key
, *iconk
;
1101 tmp
= get_name_for_instance_class(wm_instance
, wm_class
);
1102 key
= WMCreatePLString(tmp
);
1105 adict
= WMGetFromPLDictionary(dict
, key
);
1106 iconk
= WMCreatePLString("Icon");
1109 val
= WMGetFromPLDictionary(adict
, iconk
);
1111 /* no dictionary for app, so create one */
1112 adict
= WMCreatePLDictionary(NULL
, NULL
);
1113 WMPutInPLDictionary(dict
, key
, adict
);
1114 WMReleasePropList(adict
);
1119 val
= WMCreatePLString(iconPath
);
1120 WMPutInPLDictionary(adict
, iconk
, val
);
1121 WMReleasePropList(val
);
1126 WMReleasePropList(key
);
1127 WMReleasePropList(iconk
);
1129 if (val
&& !wPreferences
.flags
.noupdates
)
1130 UpdateDomainFile(w_global
.domain
.window_attr
);
1133 static WAppIcon
*findDockIconFor(WDock
*dock
, Window main_window
)
1135 WAppIcon
*aicon
= NULL
;
1137 aicon
= wDockFindIconForWindow(dock
, main_window
);
1139 wDockTrackWindowLaunch(dock
, main_window
);
1140 aicon
= wDockFindIconForWindow(dock
, main_window
);
1145 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
)
1147 WScreen
*scr
= wwin
->screen_ptr
;
1148 wapp
->app_icon
= NULL
;
1151 wapp
->app_icon
= findDockIconFor(scr
->last_dock
, main_window
);
1153 /* check main dock if we did not find it in last dock */
1154 if (!wapp
->app_icon
&& scr
->dock
)
1155 wapp
->app_icon
= findDockIconFor(scr
->dock
, main_window
);
1158 if (!wapp
->app_icon
) {
1160 for (i
= 0; i
< w_global
.workspace
.count
; i
++) {
1161 WDock
*dock
= w_global
.workspace
.array
[i
]->clip
;
1164 wapp
->app_icon
= findDockIconFor(dock
, main_window
);
1171 /* Finally check drawers */
1172 if (!wapp
->app_icon
) {
1174 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
1175 wapp
->app_icon
= findDockIconFor(dc
->adrawer
, main_window
);
1181 /* If created, then set some flags */
1182 if (wapp
->app_icon
) {
1183 WWindow
*mainw
= wapp
->main_window_desc
;
1185 wapp
->app_icon
->running
= 1;
1186 wapp
->app_icon
->icon
->owner
= mainw
;
1187 if (mainw
->wm_hints
&& (mainw
->wm_hints
->flags
& IconWindowHint
))
1188 wapp
->app_icon
->icon
->icon_win
= mainw
->wm_hints
->icon_window
;
1190 /* Update the icon images */
1191 wIconUpdate(wapp
->app_icon
->icon
);
1194 wAppIconPaint(wapp
->app_icon
);
1195 save_appicon(wapp
->app_icon
, True
);
1199 /* Add the appicon to the appiconlist */
1200 static void add_to_appicon_list(WAppIcon
*appicon
)
1202 appicon
->prev
= NULL
;
1203 appicon
->next
= w_global
.app_icon_list
;
1204 if (w_global
.app_icon_list
)
1205 w_global
.app_icon_list
->prev
= appicon
;
1207 w_global
.app_icon_list
= appicon
;
1210 /* Remove the appicon from the appiconlist */
1211 static void remove_from_appicon_list(WAppIcon
*appicon
)
1213 if (appicon
== w_global
.app_icon_list
) {
1215 appicon
->next
->prev
= NULL
;
1216 w_global
.app_icon_list
= appicon
->next
;
1219 appicon
->next
->prev
= appicon
->prev
;
1221 appicon
->prev
->next
= appicon
->next
;
1224 appicon
->prev
= NULL
;
1225 appicon
->next
= NULL
;
1228 /* Return the AppIcon associated with a given (Xlib) Window. */
1229 WAppIcon
*wAppIconFor(Window window
)
1231 WObjDescriptor
*desc
;
1236 if (XFindContext(dpy
, window
, w_global
.context
.client_win
, (XPointer
*) & desc
) == XCNOENT
)
1239 if (desc
->parent_type
== WCLASS_APPICON
|| desc
->parent_type
== WCLASS_DOCK_ICON
)
1240 return desc
->parent
;