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>
33 #include "WindowMaker.h"
36 #include "application.h"
43 #include "workspace.h"
44 #include "superfluous.h"
55 * icon_file for the dock is got from the preferences file by
56 * using the classname/instancename
59 /**** Global variables ****/
60 extern Cursor wCursor
[WCUR_LAST
];
61 extern WPreferences wPreferences
;
62 extern WDDomain
*WDWindowAttributes
;
63 extern XContext wWinContext
;
65 #define MOD_MASK wPreferences.modifier_mask
66 #define ICON_SIZE wPreferences.icon_size
68 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
);
69 static void iconDblClick(WObjDescriptor
* desc
, XEvent
* event
);
70 static void iconExpose(WObjDescriptor
* desc
, XEvent
* event
);
71 static void wApplicationSaveIconPathFor(char *iconPath
, char *wm_instance
, char *wm_class
);
72 static WAppIcon
*wAppIconCreate(WWindow
* leader_win
);
73 static void add_to_appicon_list(WScreen
*scr
, WAppIcon
*appicon
);
74 static void remove_from_appicon_list(WScreen
*scr
, WAppIcon
*appicon
);
75 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
);
77 /* This function is used if the application is a .app. It checks if it has an icon in it
78 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
80 void wApplicationExtractDirPackIcon(WScreen
* scr
, char *path
, char *wm_instance
, char *wm_class
)
82 char *iconPath
= NULL
;
85 if (strstr(path
, ".app")) {
86 tmp
= wmalloc(strlen(path
) + 16);
88 if (scr
->flags
.supports_tiff
) {
91 if (access(tmp
, R_OK
) == 0)
98 if (access(tmp
, R_OK
) == 0)
106 wApplicationSaveIconPathFor(iconPath
, wm_instance
, wm_class
);
112 WAppIcon
*wAppIconCreateForDock(WScreen
*scr
, char *command
, char *wm_instance
, char *wm_class
, int tile
)
116 aicon
= wmalloc(sizeof(WAppIcon
));
121 add_to_appicon_list(scr
, aicon
);
124 aicon
->command
= wstrdup(command
);
127 aicon
->wm_class
= wstrdup(wm_class
);
130 aicon
->wm_instance
= wstrdup(wm_instance
);
132 aicon
->icon
= icon_create_for_dock(scr
, command
, wm_instance
, wm_class
, tile
);
135 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
138 /* will be overriden by dock */
139 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
140 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
141 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
142 aicon
->icon
->core
->descriptor
.parent
= aicon
;
143 AddToStackList(aicon
->icon
->core
);
148 void create_appicon_for_application(WApplication
*wapp
, WWindow
*wwin
)
150 /* Try to create an icon from the dock or clip */
151 create_appicon_from_dock(wwin
, wapp
, wapp
->main_window
);
153 /* If app_icon was not found, create it */
154 if (!wapp
->app_icon
) {
155 /* Create the icon */
156 wapp
->app_icon
= wAppIconCreate(wapp
->main_window_desc
);
157 wIconUpdate(wapp
->app_icon
->icon
, NULL
);
159 /* Now, paint the icon */
160 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
))
161 paint_app_icon(wapp
);
164 /* Save the app_icon in a file */
165 save_appicon(wapp
->app_icon
, False
);
168 void unpaint_app_icon(WApplication
*wapp
)
174 if (!wapp
|| !wapp
->app_icon
)
177 aicon
= wapp
->app_icon
;
179 /* If the icon is docked, don't continue */
183 scr
= wapp
->main_window_desc
->screen_ptr
;
184 clip
= scr
->workspaces
[scr
->current_workspace
]->clip
;
186 if (!clip
|| !aicon
->attracted
|| !clip
->collapsed
)
187 XUnmapWindow(dpy
, aicon
->icon
->core
->window
);
189 /* We want to avoid having it on the list because otherwise
190 * there will be a hole when the icons are arranged with
192 remove_from_appicon_list(scr
, aicon
);
194 if (wPreferences
.auto_arrange_icons
&& !aicon
->attracted
)
195 wArrangeIcons(scr
, True
);
198 void paint_app_icon(WApplication
*wapp
)
201 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
202 WDock
*attracting_dock
;
204 Bool update_icon
= False
;
206 if (!wapp
|| !wapp
->app_icon
)
209 icon
= wapp
->app_icon
->icon
;
210 wapp
->app_icon
->main_window
= wapp
->main_window
;
212 /* If the icon is docked, don't continue */
213 if (wapp
->app_icon
->docked
)
216 attracting_dock
= scr
->attracting_drawer
!= NULL
?
217 scr
->attracting_drawer
:
218 scr
->workspaces
[scr
->current_workspace
]->clip
;
219 if (attracting_dock
&& attracting_dock
->attract_icons
&&
220 wDockFindFreeSlot(attracting_dock
, &x
, &y
)) {
221 wapp
->app_icon
->attracted
= 1;
222 if (!icon
->shadowed
) {
226 wDockAttachIcon(attracting_dock
, wapp
->app_icon
, x
, y
, update_icon
);
228 /* We must know if the icon is painted in the screen,
229 * because if painted, then PlaceIcon will return the next
230 * space on the screen, and the icon will move */
231 if (wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
) {
232 PlaceIcon(scr
, &x
, &y
, wGetHeadForWindow(wapp
->main_window_desc
));
233 wAppIconMove(wapp
->app_icon
, x
, y
);
234 wLowerFrame(icon
->core
);
238 /* If we want appicon (no_appicon is not set) and the icon is not
239 * in the appicon_list, we must add it. Else, we want to avoid
240 * having it on the list */
241 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
) &&
242 wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
)
243 add_to_appicon_list(scr
, wapp
->app_icon
);
245 if (!attracting_dock
|| !wapp
->app_icon
->attracted
|| !attracting_dock
->collapsed
)
246 XMapWindow(dpy
, icon
->core
->window
);
248 if (wPreferences
.auto_arrange_icons
&& !wapp
->app_icon
->attracted
)
249 wArrangeIcons(scr
, True
);
252 void removeAppIconFor(WApplication
*wapp
)
257 if (wPreferences
.highlight_active_app
)
258 wIconSetHighlited(wapp
->app_icon
->icon
, False
);
259 if (wapp
->app_icon
->docked
&& !wapp
->app_icon
->attracted
) {
260 wapp
->app_icon
->running
= 0;
261 /* since we keep it, we don't care if it was attracted or not */
262 wapp
->app_icon
->attracted
= 0;
263 wapp
->app_icon
->icon
->shadowed
= 0;
264 wapp
->app_icon
->main_window
= None
;
265 wapp
->app_icon
->pid
= 0;
266 wapp
->app_icon
->icon
->owner
= NULL
;
267 wapp
->app_icon
->icon
->icon_win
= None
;
269 /* Set the icon image */
270 set_icon_image_from_database(wapp
->app_icon
->icon
, wapp
->app_icon
->wm_instance
,
271 wapp
->app_icon
->wm_class
, wapp
->app_icon
->command
);
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 (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 relaunchApplication(wapp
);
489 static void hideCallback(WMenu
* menu
, WMenuEntry
* entry
)
491 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
493 if (wapp
->flags
.hidden
) {
494 wWorkspaceChange(menu
->menu
->screen_ptr
, wapp
->last_workspace
);
495 wUnhideApplication(wapp
, False
, False
);
497 wHideApplication(wapp
);
501 static void unhideHereCallback(WMenu
* menu
, WMenuEntry
* entry
)
503 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
505 wUnhideApplication(wapp
, False
, True
);
508 static void setIconCallback(WMenu
*menu
, WMenuEntry
*entry
)
510 WAppIcon
*icon
= ((WApplication
*) entry
->clientdata
)->app_icon
;
515 assert(icon
!= NULL
);
521 scr
= icon
->icon
->core
->screen_ptr
;
525 result
= wIconChooserDialog(scr
, &file
, icon
->wm_instance
, icon
->wm_class
);
527 if (result
&& !icon
->destroyed
) {
528 if (file
&& *file
== 0) {
532 if (!wIconChangeImageFile(icon
->icon
, file
)) {
533 wMessageDialog(scr
, _("Error"),
534 _("Could not open specified icon file"), _("OK"), NULL
, NULL
);
536 wDefaultChangeIcon(scr
, icon
->wm_instance
, icon
->wm_class
, file
);
546 static void killCallback(WMenu
* menu
, WMenuEntry
* entry
)
548 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
549 WFakeGroupLeader
*fPtr
;
552 char *basename(const char *shortname
);
554 if (!WCHECK_STATE(WSTATE_NORMAL
))
557 WCHANGE_STATE(WSTATE_MODAL
);
559 assert(entry
->clientdata
!= NULL
);
561 shortname
= basename(wapp
->app_icon
->wm_instance
);
563 buffer
= wstrconcat(wapp
->app_icon
? shortname
: NULL
,
564 _(" will be forcibly closed.\n"
565 "Any unsaved changes will be lost.\n" "Please confirm."));
567 fPtr
= wapp
->main_window_desc
->fake_group
;
569 wretain(wapp
->main_window_desc
);
570 if (wPreferences
.dont_confirm_kill
571 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
572 buffer
, _("Yes"), _("No"), NULL
) == WAPRDefault
) {
574 WWindow
*wwin
, *twin
;
576 wwin
= wapp
->main_window_desc
->screen_ptr
->focused_window
;
579 if (wwin
->fake_group
== fPtr
)
583 } else if (!wapp
->main_window_desc
->flags
.destroyed
) {
584 wClientKill(wapp
->main_window_desc
);
587 wrelease(wapp
->main_window_desc
);
589 WCHANGE_STATE(WSTATE_NORMAL
);
592 static WMenu
*createApplicationMenu(WScreen
*scr
)
596 menu
= wMenuCreate(scr
, NULL
, False
);
597 wMenuAddCallback(menu
, _("Unhide Here"), unhideHereCallback
, NULL
);
598 wMenuAddCallback(menu
, _("Hide"), hideCallback
, NULL
);
599 wMenuAddCallback(menu
, _("Launch"), relaunchCallback
, NULL
);
600 wMenuAddCallback(menu
, _("Set Icon..."), setIconCallback
, NULL
);
601 wMenuAddCallback(menu
, _("Kill"), killCallback
, NULL
);
606 static void openApplicationMenu(WApplication
* wapp
, int x
, int y
)
609 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
612 if (!scr
->icon_menu
) {
613 scr
->icon_menu
= createApplicationMenu(scr
);
614 wfree(scr
->icon_menu
->entries
[1]->text
);
617 menu
= scr
->icon_menu
;
619 if (wapp
->flags
.hidden
)
620 menu
->entries
[1]->text
= _("Unhide");
622 menu
->entries
[1]->text
= _("Hide");
624 menu
->flags
.realized
= 0;
627 x
-= menu
->frame
->core
->width
/ 2;
628 if (x
+ menu
->frame
->core
->width
> scr
->scr_width
)
629 x
= scr
->scr_width
- menu
->frame
->core
->width
;
634 /* set client data */
635 for (i
= 0; i
< menu
->entry_no
; i
++)
636 menu
->entries
[i
]->clientdata
= wapp
;
638 wMenuMapAt(menu
, x
, y
, False
);
641 /******************************************************************/
643 static void iconExpose(WObjDescriptor
*desc
, XEvent
*event
)
645 wAppIconPaint(desc
->parent
);
648 static void iconDblClick(WObjDescriptor
*desc
, XEvent
*event
)
650 WAppIcon
*aicon
= desc
->parent
;
652 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
655 assert(aicon
->icon
->owner
!= NULL
);
657 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
659 if (event
->xbutton
.state
& ControlMask
) {
660 relaunchApplication(wapp
);
664 unhideHere
= (event
->xbutton
.state
& ShiftMask
);
665 /* go to the last workspace that the user worked on the app */
666 if (!unhideHere
&& wapp
->last_workspace
!= scr
->current_workspace
)
667 wWorkspaceChange(scr
, wapp
->last_workspace
);
669 wUnhideApplication(wapp
, event
->xbutton
.button
== Button2
, unhideHere
);
671 if (event
->xbutton
.state
& MOD_MASK
)
672 wHideOtherApplications(aicon
->icon
->owner
);
675 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
)
677 WAppIcon
*aicon
= desc
->parent
;
678 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
681 if (aicon
->editing
|| WCHECK_STATE(WSTATE_MODAL
))
684 if (IsDoubleClick(scr
, event
)) {
685 /* Middle or right mouse actions were handled on first click */
686 if (event
->xbutton
.button
== Button1
)
687 iconDblClick(desc
, event
);
691 if (event
->xbutton
.button
== Button2
) {
692 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
695 relaunchApplication(wapp
);
700 if (event
->xbutton
.button
== Button3
) {
701 WObjDescriptor
*desc
;
702 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
707 if (event
->xbutton
.send_event
&&
708 XGrabPointer(dpy
, aicon
->icon
->core
->window
, True
, ButtonMotionMask
709 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
710 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
711 wwarning("pointer grab failed for appicon menu");
715 openApplicationMenu(wapp
, event
->xbutton
.x_root
, event
->xbutton
.y_root
);
717 /* allow drag select of menu */
718 desc
= &scr
->icon_menu
->menu
->descriptor
;
719 event
->xbutton
.send_event
= True
;
720 (*desc
->handle_mousedown
) (desc
, event
);
724 hasMoved
= wHandleAppIconMove(aicon
, event
);
725 if (wPreferences
.single_click
&& !hasMoved
&& aicon
->dock
!= NULL
)
727 iconDblClick(desc
, event
);
731 Bool
wHandleAppIconMove(WAppIcon
*aicon
, XEvent
*event
)
733 WIcon
*icon
= aicon
->icon
;
734 WScreen
*scr
= icon
->core
->screen_ptr
;
735 WDock
*originalDock
= aicon
->dock
; /* can be NULL */
736 WDock
*lastDock
= originalDock
;
737 WDock
*allDocks
[scr
->drawer_count
+ 2]; /* clip, dock and drawers (order determined at runtime) */
739 Bool done
= False
, dockable
, ondock
;
740 Bool grabbed
= False
;
741 Bool collapsed
= False
; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
742 int superfluous
= wPreferences
.superfluous
; /* we cache it to avoid problems */
743 int omnipresent
= aicon
->omnipresent
; /* this must be cached */
744 Bool showed_all_clips
= False
;
746 int clickButton
= event
->xbutton
.button
;
748 Window wins
[2]; /* Managing shadow window */
751 int x
= aicon
->x_pos
, y
= aicon
->y_pos
;
752 int ofs_x
= event
->xbutton
.x
, ofs_y
= event
->xbutton
.y
;
753 int shad_x
= x
, shad_y
= y
;
754 int ix
= aicon
->xindex
, iy
= aicon
->yindex
;
758 Bool hasMoved
= False
;
760 if (wPreferences
.flags
.noupdates
&& originalDock
!= NULL
)
763 if (!(event
->xbutton
.state
& MOD_MASK
))
764 wRaiseFrame(icon
->core
);
766 /* If Mod is pressed for an docked appicon, assume it is to undock it,
767 * so don't lower it */
768 if (originalDock
== NULL
)
769 wLowerFrame(icon
->core
);
772 if (XGrabPointer(dpy
, icon
->core
->window
, True
, ButtonMotionMask
773 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
774 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
775 wwarning("Pointer grab failed in wHandleAppIconMove");
778 if (originalDock
!= NULL
) {
784 if (wPreferences
.flags
.nodock
&& wPreferences
.flags
.noclip
&& wPreferences
.flags
.nodrawer
)
787 dockable
= canBeDocked(icon
->owner
);
790 /* We try the various docks in that order:
791 * - First, the dock the appicon comes from, if any
792 * - Then, the drawers
793 * - Then, the "dock" (WM_DOCK)
794 * - Finally, the clip
797 if (originalDock
!= NULL
)
798 allDocks
[ i
++ ] = originalDock
;
799 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
800 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
801 if (dc
->adrawer
!= originalDock
)
802 allDocks
[ i
++ ] = dc
->adrawer
;
804 if (!wPreferences
.flags
.nodock
&& scr
->dock
!= originalDock
)
805 allDocks
[ i
++ ] = scr
->dock
;
806 if (!wPreferences
.flags
.noclip
&&
807 originalDock
!= scr
->workspaces
[scr
->current_workspace
]->clip
)
808 allDocks
[ i
++ ] = scr
->workspaces
[scr
->current_workspace
]->clip
;
809 for ( ; i
< scr
->drawer_count
+ 2; i
++) /* In case the clip, the dock, or both, are disabled */
810 allDocks
[ i
] = NULL
;
812 wins
[0] = icon
->core
->window
;
813 wins
[1] = scr
->dock_shadow
;
814 XRestackWindows(dpy
, wins
, 2);
815 XMoveResizeWindow(dpy
, scr
->dock_shadow
, aicon
->x_pos
, aicon
->y_pos
, ICON_SIZE
, ICON_SIZE
);
817 if (icon
->pixmap
!= None
)
818 ghost
= MakeGhostIcon(scr
, icon
->pixmap
);
820 ghost
= MakeGhostIcon(scr
, icon
->core
->window
);
821 XSetWindowBackgroundPixmap(dpy
, scr
->dock_shadow
, ghost
);
822 XClearWindow(dpy
, scr
->dock_shadow
);
825 XMapWindow(dpy
, scr
->dock_shadow
);
828 WMMaskEvent(dpy
, PointerMotionMask
| ButtonReleaseMask
| ButtonPressMask
829 | ButtonMotionMask
| ExposureMask
| EnterWindowMask
, &ev
);
836 /* It means the cursor moved so fast that it entered
837 * something else (if moving slowly, it would have
838 * stayed in the appIcon that is being moved. Ignore
839 * such "spurious" EnterNotifiy's */
845 if (abs(ofs_x
- ev
.xmotion
.x
) >= MOVE_THRESHOLD
846 || abs(ofs_y
- ev
.xmotion
.y
) >= MOVE_THRESHOLD
) {
847 XChangeActivePointerGrab(dpy
, ButtonMotionMask
848 | ButtonReleaseMask
| ButtonPressMask
,
849 wCursor
[WCUR_MOVE
], CurrentTime
);
856 if (omnipresent
&& !showed_all_clips
) {
858 for (i
= 0; i
< scr
->workspace_count
; i
++) {
859 if (i
== scr
->current_workspace
)
861 wDockShowIcons(scr
->workspaces
[i
]->clip
);
862 /* Note: if dock is collapsed (for instance, because it
863 auto-collapses), its icons still won't show up */
865 showed_all_clips
= True
; /* To prevent flickering */
868 x
= ev
.xmotion
.x_root
- ofs_x
;
869 y
= ev
.xmotion
.y_root
- ofs_y
;
870 wAppIconMove(aicon
, x
, y
);
872 WDock
*theNewDock
= NULL
;
873 if (!(ev
.xmotion
.state
& MOD_MASK
) || aicon
->launching
|| aicon
->lock
) {
874 for (i
= 0; dockable
&& i
< scr
->drawer_count
+ 2; i
++) {
875 WDock
*theDock
= allDocks
[i
];
878 if (wDockSnapIcon(theDock
, aicon
, x
, y
, &ix
, &iy
, (theDock
== originalDock
))) {
879 theNewDock
= theDock
;
883 if (originalDock
!= NULL
&& theNewDock
== NULL
&&
884 (aicon
->launching
|| aicon
->lock
|| aicon
->running
)) {
885 /* In those cases, stay in lastDock if no dock really wants us */
886 theNewDock
= lastDock
;
889 if (lastDock
!= NULL
&& lastDock
!= theNewDock
) {
890 /* Leave lastDock in the state we found it */
891 if (lastDock
->type
== WM_DRAWER
) {
892 wDrawerFillTheGap(lastDock
, aicon
, (lastDock
== originalDock
));
895 lastDock
->collapsed
= 1;
896 wDockHideIcons(lastDock
);
899 if (lastDock
->auto_raise_lower
) {
900 wDockLower(lastDock
);
903 if (theNewDock
!= NULL
) {
904 if (lastDock
!= theNewDock
) {
905 collapsed
= theNewDock
->collapsed
;
907 theNewDock
->collapsed
= 0;
908 wDockShowIcons(theNewDock
);
910 if (theNewDock
->auto_raise_lower
) {
911 wDockRaise(theNewDock
);
912 /* And raise the moving tile above it */
913 wRaiseFrame(aicon
->icon
->core
);
915 lastDock
= theNewDock
;
918 shad_x
= lastDock
->x_pos
+ ix
*wPreferences
.icon_size
;
919 shad_y
= lastDock
->y_pos
+ iy
*wPreferences
.icon_size
;
921 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
924 XMapWindow(dpy
, scr
->dock_shadow
);
928 lastDock
= theNewDock
; // i.e., NULL
930 XUnmapWindow(dpy
, scr
->dock_shadow
);
932 * Leaving that weird comment for now.
933 * But if we see no gap, there is no need to fill one!
934 * We could test ondock first and the lastDock to NULL afterwards
935 if (lastDock_before_it_was_null->type == WM_DRAWER) {
936 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
948 if (ev
.xbutton
.button
!= clickButton
)
950 XUngrabPointer(dpy
, CurrentTime
);
954 SlideWindow(icon
->core
->window
, x
, y
, shad_x
, shad_y
);
955 XUnmapWindow(dpy
, scr
->dock_shadow
);
956 if (originalDock
== NULL
) { // docking an undocked appicon
957 docked
= wDockAttachIcon(lastDock
, aicon
, ix
, iy
, False
);
959 /* AppIcon got rejected (happens only when we can't get the
960 command for that appicon, and the user cancels the
961 wInputDialog asking for one). Make the rejection obvious by
962 sliding the icon to its old position */
963 if (lastDock
->type
== WM_DRAWER
) {
964 // Also fill the gap left in the drawer
965 wDrawerFillTheGap(lastDock
, aicon
, False
);
967 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
970 else { // moving a docked appicon to a dock
971 if (originalDock
== lastDock
) {
973 wDockReattachIcon(originalDock
, aicon
, ix
, iy
);
976 docked
= wDockMoveIconBetweenDocks(originalDock
, lastDock
, aicon
, ix
, iy
);
978 /* Possible scenario: user moved an auto-attracted appicon
979 from the clip to the dock, and cancelled the wInputDialog
980 asking for a command */
981 if (lastDock
->type
== WM_DRAWER
) {
982 wDrawerFillTheGap(lastDock
, aicon
, False
);
984 /* If aicon comes from a drawer, make some room to reattach it */
985 if (originalDock
->type
== WM_DRAWER
) {
986 WAppIcon
*aiconsToShift
[ originalDock
->icon_count
];
989 for (i
= 0; i
< originalDock
->max_icons
; i
++) {
990 WAppIcon
*ai
= originalDock
->icon_array
[ i
];
991 if (ai
&& ai
!= aicon
&&
992 abs(ai
->xindex
) >= abs(aicon
->xindex
))
993 aiconsToShift
[j
++] = ai
;
995 if (j
!= originalDock
->icon_count
- abs(aicon
->xindex
) - 1)
996 // Trust this never happens?
997 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
998 j
, originalDock
->icon_count
- abs(aicon
->xindex
) - 1, aicon
->xindex
);
999 wSlideAppicons(aiconsToShift
, j
, originalDock
->on_right_side
);
1000 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1003 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
1004 wDockReattachIcon(originalDock
, aicon
, aicon
->xindex
, aicon
->yindex
);
1007 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1008 originalDock
->collapsed
= 1;
1009 wDockHideIcons(originalDock
);
1011 if (originalDock
->auto_raise_lower
)
1012 wDockLower(originalDock
);
1016 // No matter what happened above, check to lower lastDock
1017 // Don't see why I commented out the following 2 lines
1018 /* if (lastDock->auto_raise_lower)
1019 wDockLower(lastDock); */
1020 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1021 * collapsed, so that wHandleAppIconMove doesn't collapse it
1022 * right away (the timer will take care of it) */
1023 if (lastDock
->auto_collapse
)
1027 if (originalDock
!= NULL
) { /* Detaching a docked appicon */
1029 if (!aicon
->running
&& !wPreferences
.no_animations
) {
1030 /* We need to deselect it, even if is deselected in
1031 * wDockDetach(), because else DoKaboom() will fail.
1033 if (aicon
->icon
->selected
)
1034 wIconSelect(aicon
->icon
);
1035 DoKaboom(scr
, aicon
->icon
->core
->window
, x
, y
);
1038 wDockDetach(originalDock
, aicon
);
1039 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1040 originalDock
->collapsed
= 1;
1041 wDockHideIcons(originalDock
);
1043 if (originalDock
->auto_raise_lower
)
1044 wDockLower(originalDock
);
1047 // Can't remember why the icon hiding is better done above than below (commented out)
1048 // Also, lastDock is quite different from originalDock
1051 lastDock->collapsed = 1;
1052 wDockHideIcons(lastDock);
1058 XFreePixmap(dpy
, ghost
);
1059 XSetWindowBackground(dpy
, scr
->dock_shadow
, scr
->white_pixel
);
1061 if (showed_all_clips
) {
1063 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1064 if (i
== scr
->current_workspace
)
1066 wDockHideIcons(scr
->workspaces
[i
]->clip
);
1069 if (wPreferences
.auto_arrange_icons
&& !(originalDock
!= NULL
&& docked
))
1070 /* Need to rearrange unless moving from dock to dock */
1071 wArrangeIcons(scr
, True
);
1075 return False
; /* Never reached */
1078 /* This function save the application icon and store the path in the Dictionary */
1079 static void wApplicationSaveIconPathFor(char *iconPath
, char *wm_instance
, char *wm_class
)
1081 WMPropList
*dict
= WDWindowAttributes
->dictionary
;
1082 WMPropList
*adict
, *key
, *iconk
;
1086 tmp
= get_name_for_instance_class(wm_instance
, wm_class
);
1087 key
= WMCreatePLString(tmp
);
1090 adict
= WMGetFromPLDictionary(dict
, key
);
1091 iconk
= WMCreatePLString("Icon");
1094 val
= WMGetFromPLDictionary(adict
, iconk
);
1096 /* no dictionary for app, so create one */
1097 adict
= WMCreatePLDictionary(NULL
, NULL
);
1098 WMPutInPLDictionary(dict
, key
, adict
);
1099 WMReleasePropList(adict
);
1104 val
= WMCreatePLString(iconPath
);
1105 WMPutInPLDictionary(adict
, iconk
, val
);
1106 WMReleasePropList(val
);
1111 WMReleasePropList(key
);
1112 WMReleasePropList(iconk
);
1114 if (val
&& !wPreferences
.flags
.noupdates
)
1115 UpdateDomainFile(WDWindowAttributes
);
1118 static WAppIcon
*findDockIconFor(WDock
*dock
, Window main_window
)
1120 WAppIcon
*aicon
= NULL
;
1122 aicon
= wDockFindIconForWindow(dock
, main_window
);
1124 wDockTrackWindowLaunch(dock
, main_window
);
1125 aicon
= wDockFindIconForWindow(dock
, main_window
);
1130 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
)
1132 WScreen
*scr
= wwin
->screen_ptr
;
1133 wapp
->app_icon
= NULL
;
1136 wapp
->app_icon
= findDockIconFor(scr
->last_dock
, main_window
);
1138 /* check main dock if we did not find it in last dock */
1139 if (!wapp
->app_icon
&& scr
->dock
)
1140 wapp
->app_icon
= findDockIconFor(scr
->dock
, main_window
);
1143 if (!wapp
->app_icon
) {
1145 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1146 WDock
*dock
= scr
->workspaces
[i
]->clip
;
1148 wapp
->app_icon
= findDockIconFor(dock
, main_window
);
1154 /* Finally check drawers */
1155 if (!wapp
->app_icon
) {
1157 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
1158 wapp
->app_icon
= findDockIconFor(dc
->adrawer
, main_window
);
1164 /* If created, then set some flags */
1165 if (wapp
->app_icon
) {
1166 WWindow
*mainw
= wapp
->main_window_desc
;
1168 wapp
->app_icon
->running
= 1;
1169 wapp
->app_icon
->icon
->owner
= mainw
;
1170 if (mainw
->wm_hints
&& (mainw
->wm_hints
->flags
& IconWindowHint
))
1171 wapp
->app_icon
->icon
->icon_win
= mainw
->wm_hints
->icon_window
;
1173 /* Update the icon images */
1174 wIconUpdate(wapp
->app_icon
->icon
, NULL
);
1177 wAppIconPaint(wapp
->app_icon
);
1178 save_appicon(wapp
->app_icon
, True
);
1182 /* Add the appicon to the appiconlist */
1183 static void add_to_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1185 appicon
->prev
= NULL
;
1186 appicon
->next
= scr
->app_icon_list
;
1187 if (scr
->app_icon_list
)
1188 scr
->app_icon_list
->prev
= appicon
;
1190 scr
->app_icon_list
= appicon
;
1193 /* Remove the appicon from the appiconlist */
1194 static void remove_from_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1196 if (appicon
== scr
->app_icon_list
) {
1198 appicon
->next
->prev
= NULL
;
1199 scr
->app_icon_list
= appicon
->next
;
1202 appicon
->next
->prev
= appicon
->prev
;
1204 appicon
->prev
->next
= appicon
->next
;
1207 appicon
->prev
= NULL
;
1208 appicon
->next
= NULL
;
1211 /* Return the AppIcon associated with a given (Xlib) Window. */
1212 WAppIcon
*wAppIconFor(Window window
)
1214 WObjDescriptor
*desc
;
1219 if (XFindContext(dpy
, window
, wWinContext
, (XPointer
*) & desc
) == XCNOENT
)
1222 if (desc
->parent_type
== WCLASS_APPICON
|| desc
->parent_type
== WCLASS_DOCK_ICON
)
1223 return desc
->parent
;