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(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
;
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(scr
, aicon
);
121 aicon
->command
= wstrdup(command
);
124 aicon
->wm_class
= wstrdup(wm_class
);
127 aicon
->wm_instance
= wstrdup(wm_instance
);
129 if (wPreferences
.flags
.clip_merged_in_dock
&& wm_class
!= NULL
&& strcmp(wm_class
, "WMDock") == 0)
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
);
164 void unpaint_app_icon(WApplication
*wapp
)
170 if (!wapp
|| !wapp
->app_icon
)
173 aicon
= wapp
->app_icon
;
175 /* If the icon is docked, don't continue */
179 scr
= wapp
->main_window_desc
->screen_ptr
;
180 clip
= scr
->workspaces
[scr
->current_workspace
]->clip
;
182 if (!clip
|| !aicon
->attracted
|| !clip
->collapsed
)
183 XUnmapWindow(dpy
, aicon
->icon
->core
->window
);
185 /* We want to avoid having it on the list because otherwise
186 * there will be a hole when the icons are arranged with
188 remove_from_appicon_list(scr
, aicon
);
190 if (wPreferences
.auto_arrange_icons
&& !aicon
->attracted
)
191 wArrangeIcons(scr
, True
);
194 void paint_app_icon(WApplication
*wapp
)
198 WDock
*attracting_dock
;
200 Bool update_icon
= False
;
202 if (!wapp
|| !wapp
->app_icon
|| !wapp
->main_window_desc
)
205 icon
= wapp
->app_icon
->icon
;
206 scr
= wapp
->main_window_desc
->screen_ptr
;
207 wapp
->app_icon
->main_window
= wapp
->main_window
;
209 /* If the icon is docked, don't continue */
210 if (wapp
->app_icon
->docked
)
213 attracting_dock
= scr
->attracting_drawer
!= NULL
?
214 scr
->attracting_drawer
:
215 scr
->workspaces
[scr
->current_workspace
]->clip
;
216 if (attracting_dock
&& attracting_dock
->attract_icons
&&
217 wDockFindFreeSlot(attracting_dock
, &x
, &y
)) {
218 wapp
->app_icon
->attracted
= 1;
219 if (!icon
->shadowed
) {
223 wDockAttachIcon(attracting_dock
, wapp
->app_icon
, x
, y
, update_icon
);
225 /* We must know if the icon is painted in the screen,
226 * because if painted, then PlaceIcon will return the next
227 * space on the screen, and the icon will move */
228 if (wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
) {
229 PlaceIcon(scr
, &x
, &y
, wGetHeadForWindow(wapp
->main_window_desc
));
230 wAppIconMove(wapp
->app_icon
, x
, y
);
231 wLowerFrame(icon
->core
);
235 /* If we want appicon (no_appicon is not set) and the icon is not
236 * in the appicon_list, we must add it. Else, we want to avoid
237 * having it on the list */
238 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
) &&
239 wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
)
240 add_to_appicon_list(scr
, wapp
->app_icon
);
242 if (!attracting_dock
|| !wapp
->app_icon
->attracted
|| !attracting_dock
->collapsed
)
243 XMapWindow(dpy
, icon
->core
->window
);
245 if (wPreferences
.auto_arrange_icons
&& !wapp
->app_icon
->attracted
)
246 wArrangeIcons(scr
, True
);
249 void removeAppIconFor(WApplication
*wapp
)
254 if (wPreferences
.highlight_active_app
)
255 wIconSetHighlited(wapp
->app_icon
->icon
, False
);
256 if (wapp
->app_icon
->docked
&& !wapp
->app_icon
->attracted
) {
257 wapp
->app_icon
->running
= 0;
258 /* since we keep it, we don't care if it was attracted or not */
259 wapp
->app_icon
->attracted
= 0;
260 wapp
->app_icon
->icon
->shadowed
= 0;
261 wapp
->app_icon
->main_window
= None
;
262 wapp
->app_icon
->pid
= 0;
263 wapp
->app_icon
->icon
->owner
= NULL
;
264 wapp
->app_icon
->icon
->icon_win
= None
;
266 /* Set the icon image */
267 set_icon_image_from_database(wapp
->app_icon
->icon
, wapp
->app_icon
->wm_instance
,
268 wapp
->app_icon
->wm_class
, wapp
->app_icon
->command
);
270 /* Update the icon, because wapp->app_icon->icon could be NULL */
271 wIconUpdate(wapp
->app_icon
->icon
);
274 wAppIconPaint(wapp
->app_icon
);
275 } else if (wapp
->app_icon
->docked
) {
276 wapp
->app_icon
->running
= 0;
277 if (wapp
->app_icon
->dock
->type
== WM_DRAWER
) {
278 wDrawerFillTheGap(wapp
->app_icon
->dock
, wapp
->app_icon
, True
);
280 wDockDetach(wapp
->app_icon
->dock
, wapp
->app_icon
);
282 wAppIconDestroy(wapp
->app_icon
);
285 wapp
->app_icon
= NULL
;
287 if (wPreferences
.auto_arrange_icons
)
288 wArrangeIcons(wapp
->main_window_desc
->screen_ptr
, True
);
291 static WAppIcon
*wAppIconCreate(WWindow
*leader_win
)
295 aicon
= wmalloc(sizeof(WAppIcon
));
302 if (leader_win
->wm_class
)
303 aicon
->wm_class
= wstrdup(leader_win
->wm_class
);
305 if (leader_win
->wm_instance
)
306 aicon
->wm_instance
= wstrdup(leader_win
->wm_instance
);
308 aicon
->icon
= icon_create_for_wwindow(leader_win
);
310 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
313 /* will be overriden if docked */
314 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
315 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
316 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
317 aicon
->icon
->core
->descriptor
.parent
= aicon
;
318 AddToStackList(aicon
->icon
->core
);
319 aicon
->icon
->show_title
= 0;
324 void wAppIconDestroy(WAppIcon
* aicon
)
326 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
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(scr
, 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 ]", scr
->current_workspace
+ 1, ((scr
->current_workspace
/ 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 (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
)
443 if (!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
);
537 if (!icon
->destroyed
) {
538 if (!wIconChangeImageFile(icon
->icon
, file
)) {
539 wMessageDialog(scr
, _("Error"),
540 _("Could not open specified icon file"),
541 _("OK"), NULL
, NULL
);
543 wDefaultChangeIcon(icon
->wm_instance
, icon
->wm_class
, file
);
554 static void killCallback(WMenu
* menu
, WMenuEntry
* entry
)
556 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
557 WFakeGroupLeader
*fPtr
;
561 if (!WCHECK_STATE(WSTATE_NORMAL
))
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
) {
581 WWindow
*wwin
, *twin
;
583 wwin
= wapp
->main_window_desc
->screen_ptr
->focused_window
;
586 if (wwin
->fake_group
== fPtr
)
590 } else if (!wapp
->main_window_desc
->flags
.destroyed
) {
591 wClientKill(wapp
->main_window_desc
);
594 wrelease(wapp
->main_window_desc
);
596 WCHANGE_STATE(WSTATE_NORMAL
);
599 static WMenu
*createApplicationMenu(WScreen
*scr
)
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
);
613 static void openApplicationMenu(WApplication
* wapp
, int x
, int y
)
616 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
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");
629 menu
->entries
[1]->text
= _("Hide");
631 menu
->flags
.realized
= 0;
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
;
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 /* Parameter not used, but tell the compiler that it is ok */
655 wAppIconPaint(desc
->parent
);
658 static void iconDblClick(WObjDescriptor
*desc
, XEvent
*event
)
660 WAppIcon
*aicon
= desc
->parent
;
662 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
665 assert(aicon
->icon
->owner
!= NULL
);
667 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
669 if (event
->xbutton
.state
& ControlMask
) {
670 relaunchApplication(wapp
);
674 unhideHere
= (event
->xbutton
.state
& ShiftMask
);
675 /* go to the last workspace that the user worked on the app */
676 if (!unhideHere
&& wapp
->last_workspace
!= scr
->current_workspace
)
677 wWorkspaceChange(scr
, wapp
->last_workspace
);
679 wUnhideApplication(wapp
, event
->xbutton
.button
== Button2
, unhideHere
);
681 if (event
->xbutton
.state
& MOD_MASK
)
682 wHideOtherApplications(aicon
->icon
->owner
);
685 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
)
687 WAppIcon
*aicon
= desc
->parent
;
688 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
691 if (aicon
->editing
|| WCHECK_STATE(WSTATE_MODAL
))
694 if (IsDoubleClick(scr
, event
)) {
695 /* Middle or right mouse actions were handled on first click */
696 if (event
->xbutton
.button
== Button1
)
697 iconDblClick(desc
, event
);
701 if (event
->xbutton
.button
== Button2
) {
702 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
705 relaunchApplication(wapp
);
710 if (event
->xbutton
.button
== Button3
) {
711 WObjDescriptor
*desc
;
712 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
717 if (event
->xbutton
.send_event
&&
718 XGrabPointer(dpy
, aicon
->icon
->core
->window
, True
, ButtonMotionMask
719 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
720 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
721 wwarning("pointer grab failed for appicon menu");
725 openApplicationMenu(wapp
, event
->xbutton
.x_root
, event
->xbutton
.y_root
);
727 /* allow drag select of menu */
728 desc
= &scr
->icon_menu
->menu
->descriptor
;
729 event
->xbutton
.send_event
= True
;
730 (*desc
->handle_mousedown
) (desc
, event
);
734 hasMoved
= wHandleAppIconMove(aicon
, event
);
735 if (wPreferences
.single_click
&& !hasMoved
&& aicon
->dock
!= NULL
)
737 iconDblClick(desc
, event
);
741 Bool
wHandleAppIconMove(WAppIcon
*aicon
, XEvent
*event
)
743 WIcon
*icon
= aicon
->icon
;
744 WScreen
*scr
= icon
->core
->screen_ptr
;
745 WDock
*originalDock
= aicon
->dock
; /* can be NULL */
746 WDock
*lastDock
= originalDock
;
747 WDock
*allDocks
[scr
->drawer_count
+ 2]; /* clip, dock and drawers (order determined at runtime) */
749 Bool dockable
, ondock
;
750 Bool grabbed
= False
;
751 Bool collapsed
= False
; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
752 int superfluous
= wPreferences
.superfluous
; /* we cache it to avoid problems */
753 int omnipresent
= aicon
->omnipresent
; /* this must be cached */
754 Bool showed_all_clips
= False
;
756 int clickButton
= event
->xbutton
.button
;
758 Window wins
[2]; /* Managing shadow window */
761 int x
= aicon
->x_pos
, y
= aicon
->y_pos
;
762 int ofs_x
= event
->xbutton
.x
, ofs_y
= event
->xbutton
.y
;
763 int shad_x
= x
, shad_y
= y
;
764 int ix
= aicon
->xindex
, iy
= aicon
->yindex
;
768 Bool hasMoved
= False
;
770 if (wPreferences
.flags
.noupdates
&& originalDock
!= NULL
)
773 if (!(event
->xbutton
.state
& MOD_MASK
))
774 wRaiseFrame(icon
->core
);
776 /* If Mod is pressed for an docked appicon, assume it is to undock it,
777 * so don't lower it */
778 if (originalDock
== NULL
)
779 wLowerFrame(icon
->core
);
782 if (XGrabPointer(dpy
, icon
->core
->window
, True
, ButtonMotionMask
783 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
784 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
785 wwarning("Pointer grab failed in wHandleAppIconMove");
788 if (originalDock
!= NULL
) {
794 if (wPreferences
.flags
.nodock
&& wPreferences
.flags
.noclip
&& wPreferences
.flags
.nodrawer
)
797 dockable
= canBeDocked(icon
->owner
);
800 /* We try the various docks in that order:
801 * - First, the dock the appicon comes from, if any
802 * - Then, the drawers
803 * - Then, the "dock" (WM_DOCK)
804 * - Finally, the clip
807 if (originalDock
!= NULL
)
808 allDocks
[ i
++ ] = originalDock
;
809 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
810 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
811 if (dc
->adrawer
!= originalDock
)
812 allDocks
[ i
++ ] = dc
->adrawer
;
814 if (!wPreferences
.flags
.nodock
&& scr
->dock
!= originalDock
)
815 allDocks
[ i
++ ] = scr
->dock
;
817 if (!wPreferences
.flags
.noclip
&&
818 originalDock
!= scr
->workspaces
[scr
->current_workspace
]->clip
)
819 allDocks
[ i
++ ] = scr
->workspaces
[scr
->current_workspace
]->clip
;
821 for ( ; i
< scr
->drawer_count
+ 2; i
++) /* In case the clip, the dock, or both, are disabled */
822 allDocks
[ i
] = NULL
;
824 wins
[0] = icon
->core
->window
;
825 wins
[1] = scr
->dock_shadow
;
826 XRestackWindows(dpy
, wins
, 2);
827 XMoveResizeWindow(dpy
, scr
->dock_shadow
, aicon
->x_pos
, aicon
->y_pos
, ICON_SIZE
, ICON_SIZE
);
829 if (icon
->pixmap
!= None
)
830 ghost
= MakeGhostIcon(scr
, icon
->pixmap
);
832 ghost
= MakeGhostIcon(scr
, icon
->core
->window
);
833 XSetWindowBackgroundPixmap(dpy
, scr
->dock_shadow
, ghost
);
834 XClearWindow(dpy
, scr
->dock_shadow
);
837 XMapWindow(dpy
, scr
->dock_shadow
);
840 WMMaskEvent(dpy
, PointerMotionMask
| ButtonReleaseMask
| ButtonPressMask
841 | ButtonMotionMask
| ExposureMask
| EnterWindowMask
, &ev
);
848 /* It means the cursor moved so fast that it entered
849 * something else (if moving slowly, it would have
850 * stayed in the appIcon that is being moved. Ignore
851 * such "spurious" EnterNotifiy's */
857 if (abs(ofs_x
- ev
.xmotion
.x
) >= MOVE_THRESHOLD
858 || abs(ofs_y
- ev
.xmotion
.y
) >= MOVE_THRESHOLD
) {
859 XChangeActivePointerGrab(dpy
, ButtonMotionMask
860 | ButtonReleaseMask
| ButtonPressMask
,
861 wPreferences
.cursor
[WCUR_MOVE
], CurrentTime
);
868 if (omnipresent
&& !showed_all_clips
) {
870 for (i
= 0; i
< scr
->workspace_count
; i
++) {
871 if (i
== scr
->current_workspace
)
874 wDockShowIcons(scr
->workspaces
[i
]->clip
);
875 /* Note: if dock is collapsed (for instance, because it
876 auto-collapses), its icons still won't show up */
878 showed_all_clips
= True
; /* To prevent flickering */
881 x
= ev
.xmotion
.x_root
- ofs_x
;
882 y
= ev
.xmotion
.y_root
- ofs_y
;
883 wAppIconMove(aicon
, x
, y
);
885 WDock
*theNewDock
= NULL
;
886 if (!(ev
.xmotion
.state
& MOD_MASK
) || aicon
->launching
|| aicon
->lock
|| originalDock
== NULL
) {
887 for (i
= 0; dockable
&& i
< scr
->drawer_count
+ 2; i
++) {
888 WDock
*theDock
= allDocks
[i
];
891 if (wDockSnapIcon(theDock
, aicon
, x
, y
, &ix
, &iy
, (theDock
== originalDock
))) {
892 theNewDock
= theDock
;
896 if (originalDock
!= NULL
&& theNewDock
== NULL
&&
897 (aicon
->launching
|| aicon
->lock
|| aicon
->running
)) {
898 /* In those cases, stay in lastDock if no dock really wants us */
899 theNewDock
= lastDock
;
902 if (lastDock
!= NULL
&& lastDock
!= theNewDock
) {
903 /* Leave lastDock in the state we found it */
904 if (lastDock
->type
== WM_DRAWER
) {
905 wDrawerFillTheGap(lastDock
, aicon
, (lastDock
== originalDock
));
908 lastDock
->collapsed
= 1;
909 wDockHideIcons(lastDock
);
912 if (lastDock
->auto_raise_lower
) {
913 wDockLower(lastDock
);
916 if (theNewDock
!= NULL
) {
917 if (lastDock
!= theNewDock
) {
918 collapsed
= theNewDock
->collapsed
;
920 theNewDock
->collapsed
= 0;
921 wDockShowIcons(theNewDock
);
923 if (theNewDock
->auto_raise_lower
) {
924 wDockRaise(theNewDock
);
925 /* And raise the moving tile above it */
926 wRaiseFrame(aicon
->icon
->core
);
928 lastDock
= theNewDock
;
931 shad_x
= lastDock
->x_pos
+ ix
*wPreferences
.icon_size
;
932 shad_y
= lastDock
->y_pos
+ iy
*wPreferences
.icon_size
;
934 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
937 XMapWindow(dpy
, scr
->dock_shadow
);
941 lastDock
= theNewDock
; // i.e., NULL
943 XUnmapWindow(dpy
, scr
->dock_shadow
);
945 * Leaving that weird comment for now.
946 * But if we see no gap, there is no need to fill one!
947 * We could test ondock first and the lastDock to NULL afterwards
948 if (lastDock_before_it_was_null->type == WM_DRAWER) {
949 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
961 if (ev
.xbutton
.button
!= clickButton
)
963 XUngrabPointer(dpy
, CurrentTime
);
967 slide_window(icon
->core
->window
, x
, y
, shad_x
, shad_y
);
968 XUnmapWindow(dpy
, scr
->dock_shadow
);
969 if (originalDock
== NULL
) { // docking an undocked appicon
970 docked
= wDockAttachIcon(lastDock
, aicon
, ix
, iy
, False
);
972 /* AppIcon got rejected (happens only when we can't get the
973 command for that appicon, and the user cancels the
974 wInputDialog asking for one). Make the rejection obvious by
975 sliding the icon to its old position */
976 if (lastDock
->type
== WM_DRAWER
) {
977 // Also fill the gap left in the drawer
978 wDrawerFillTheGap(lastDock
, aicon
, False
);
980 slide_window(icon
->core
->window
, x
, y
, oldX
, oldY
);
983 else { // moving a docked appicon to a dock
984 if (originalDock
== lastDock
) {
986 wDockReattachIcon(originalDock
, aicon
, ix
, iy
);
989 docked
= wDockMoveIconBetweenDocks(originalDock
, lastDock
, aicon
, ix
, iy
);
991 /* Possible scenario: user moved an auto-attracted appicon
992 from the clip to the dock, and cancelled the wInputDialog
993 asking for a command */
994 if (lastDock
->type
== WM_DRAWER
) {
995 wDrawerFillTheGap(lastDock
, aicon
, False
);
997 /* If aicon comes from a drawer, make some room to reattach it */
998 if (originalDock
->type
== WM_DRAWER
) {
999 WAppIcon
*aiconsToShift
[ originalDock
->icon_count
];
1002 for (i
= 0; i
< originalDock
->max_icons
; i
++) {
1003 WAppIcon
*ai
= originalDock
->icon_array
[ i
];
1004 if (ai
&& ai
!= aicon
&&
1005 abs(ai
->xindex
) >= abs(aicon
->xindex
))
1006 aiconsToShift
[j
++] = ai
;
1008 if (j
!= originalDock
->icon_count
- abs(aicon
->xindex
) - 1)
1009 // Trust this never happens?
1010 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1011 j
, originalDock
->icon_count
- abs(aicon
->xindex
) - 1, aicon
->xindex
);
1012 wSlideAppicons(aiconsToShift
, j
, originalDock
->on_right_side
);
1013 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1016 slide_window(icon
->core
->window
, x
, y
, oldX
, oldY
);
1017 wDockReattachIcon(originalDock
, aicon
, aicon
->xindex
, aicon
->yindex
);
1020 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1021 originalDock
->collapsed
= 1;
1022 wDockHideIcons(originalDock
);
1024 if (originalDock
->auto_raise_lower
)
1025 wDockLower(originalDock
);
1029 // No matter what happened above, check to lower lastDock
1030 // Don't see why I commented out the following 2 lines
1031 /* if (lastDock->auto_raise_lower)
1032 wDockLower(lastDock); */
1033 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1034 * collapsed, so that wHandleAppIconMove doesn't collapse it
1035 * right away (the timer will take care of it) */
1036 if (lastDock
->auto_collapse
)
1040 if (originalDock
!= NULL
) { /* Detaching a docked appicon */
1042 if (!aicon
->running
&& !wPreferences
.no_animations
) {
1043 /* We need to deselect it, even if is deselected in
1044 * wDockDetach(), because else DoKaboom() will fail.
1046 if (aicon
->icon
->selected
)
1047 wIconSelect(aicon
->icon
);
1048 DoKaboom(scr
, aicon
->icon
->core
->window
, x
, y
);
1051 wDockDetach(originalDock
, aicon
);
1052 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1053 originalDock
->collapsed
= 1;
1054 wDockHideIcons(originalDock
);
1056 if (originalDock
->auto_raise_lower
)
1057 wDockLower(originalDock
);
1060 // Can't remember why the icon hiding is better done above than below (commented out)
1061 // Also, lastDock is quite different from originalDock
1064 lastDock->collapsed = 1;
1065 wDockHideIcons(lastDock);
1071 XFreePixmap(dpy
, ghost
);
1072 XSetWindowBackground(dpy
, scr
->dock_shadow
, scr
->white_pixel
);
1074 if (showed_all_clips
) {
1076 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1077 if (i
== scr
->current_workspace
)
1080 wDockHideIcons(scr
->workspaces
[i
]->clip
);
1083 if (wPreferences
.auto_arrange_icons
&& !(originalDock
!= NULL
&& docked
))
1084 /* Need to rearrange unless moving from dock to dock */
1085 wArrangeIcons(scr
, True
);
1091 /* This function save the application icon and store the path in the Dictionary */
1092 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
)
1094 WMPropList
*dict
= w_global
.domain
.window_attr
->dictionary
;
1095 WMPropList
*adict
, *key
, *iconk
;
1099 tmp
= get_name_for_instance_class(wm_instance
, wm_class
);
1100 key
= WMCreatePLString(tmp
);
1103 adict
= WMGetFromPLDictionary(dict
, key
);
1104 iconk
= WMCreatePLString("Icon");
1107 val
= WMGetFromPLDictionary(adict
, iconk
);
1109 /* no dictionary for app, so create one */
1110 adict
= WMCreatePLDictionary(NULL
, NULL
);
1111 WMPutInPLDictionary(dict
, key
, adict
);
1112 WMReleasePropList(adict
);
1117 val
= WMCreatePLString(iconPath
);
1118 WMPutInPLDictionary(adict
, iconk
, val
);
1119 WMReleasePropList(val
);
1124 WMReleasePropList(key
);
1125 WMReleasePropList(iconk
);
1127 if (val
&& !wPreferences
.flags
.noupdates
)
1128 UpdateDomainFile(w_global
.domain
.window_attr
);
1131 static WAppIcon
*findDockIconFor(WDock
*dock
, Window main_window
)
1133 WAppIcon
*aicon
= NULL
;
1135 aicon
= wDockFindIconForWindow(dock
, main_window
);
1137 wDockTrackWindowLaunch(dock
, main_window
);
1138 aicon
= wDockFindIconForWindow(dock
, main_window
);
1143 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
)
1145 WScreen
*scr
= wwin
->screen_ptr
;
1146 wapp
->app_icon
= NULL
;
1149 wapp
->app_icon
= findDockIconFor(scr
->last_dock
, main_window
);
1151 /* check main dock if we did not find it in last dock */
1152 if (!wapp
->app_icon
&& scr
->dock
)
1153 wapp
->app_icon
= findDockIconFor(scr
->dock
, main_window
);
1156 if (!wapp
->app_icon
) {
1158 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1159 WDock
*dock
= scr
->workspaces
[i
]->clip
;
1162 wapp
->app_icon
= findDockIconFor(dock
, main_window
);
1169 /* Finally check drawers */
1170 if (!wapp
->app_icon
) {
1172 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
1173 wapp
->app_icon
= findDockIconFor(dc
->adrawer
, main_window
);
1179 /* If created, then set some flags */
1180 if (wapp
->app_icon
&& !WFLAGP(wapp
->main_window_desc
, no_appicon
)) {
1181 WWindow
*mainw
= wapp
->main_window_desc
;
1183 wapp
->app_icon
->running
= 1;
1184 wapp
->app_icon
->icon
->owner
= mainw
;
1185 if (mainw
->wm_hints
&& (mainw
->wm_hints
->flags
& IconWindowHint
))
1186 wapp
->app_icon
->icon
->icon_win
= mainw
->wm_hints
->icon_window
;
1188 /* Update the icon images */
1189 wIconUpdate(wapp
->app_icon
->icon
);
1192 wAppIconPaint(wapp
->app_icon
);
1196 /* Add the appicon to the appiconlist */
1197 static void add_to_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1199 appicon
->prev
= NULL
;
1200 appicon
->next
= scr
->app_icon_list
;
1201 if (scr
->app_icon_list
)
1202 scr
->app_icon_list
->prev
= appicon
;
1204 scr
->app_icon_list
= appicon
;
1207 /* Remove the appicon from the appiconlist */
1208 static void remove_from_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1210 if (appicon
== scr
->app_icon_list
) {
1212 appicon
->next
->prev
= NULL
;
1213 scr
->app_icon_list
= appicon
->next
;
1216 appicon
->next
->prev
= appicon
->prev
;
1218 appicon
->prev
->next
= appicon
->next
;
1221 appicon
->prev
= NULL
;
1222 appicon
->next
= NULL
;
1225 /* Return the AppIcon associated with a given (Xlib) Window. */
1226 WAppIcon
*wAppIconFor(Window window
)
1228 WObjDescriptor
*desc
;
1233 if (XFindContext(dpy
, window
, w_global
.context
.client_win
, (XPointer
*) & desc
) == XCNOENT
)
1236 if (desc
->parent_type
== WCLASS_APPICON
|| desc
->parent_type
== WCLASS_DOCK_ICON
)
1237 return desc
->parent
;