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"
50 #include "placement.h"
56 * icon_file for the dock is got from the preferences file by
57 * using the classname/instancename
60 /**** Global variables ****/
61 extern Cursor wCursor
[WCUR_LAST
];
62 extern WPreferences wPreferences
;
63 extern WDDomain
*WDWindowAttributes
;
64 extern XContext wWinContext
;
66 #define MOD_MASK wPreferences.modifier_mask
67 #define ICON_SIZE wPreferences.icon_size
69 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
);
70 static void iconDblClick(WObjDescriptor
* desc
, XEvent
* event
);
71 static void iconExpose(WObjDescriptor
* desc
, XEvent
* event
);
72 static void wApplicationSaveIconPathFor(char *iconPath
, char *wm_instance
, char *wm_class
);
73 static WAppIcon
*wAppIconCreate(WWindow
* leader_win
);
74 static void add_to_appicon_list(WScreen
*scr
, WAppIcon
*appicon
);
75 static void remove_from_appicon_list(WScreen
*scr
, WAppIcon
*appicon
);
76 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
);
78 /* This function is used if the application is a .app. It checks if it has an icon in it
79 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
81 void wApplicationExtractDirPackIcon(WScreen
* scr
, char *path
, char *wm_instance
, char *wm_class
)
83 char *iconPath
= NULL
;
86 if (strstr(path
, ".app")) {
87 tmp
= wmalloc(strlen(path
) + 16);
89 if (scr
->flags
.supports_tiff
) {
92 if (access(tmp
, R_OK
) == 0)
99 if (access(tmp
, R_OK
) == 0)
107 wApplicationSaveIconPathFor(iconPath
, wm_instance
, wm_class
);
113 WAppIcon
*wAppIconCreateForDock(WScreen
*scr
, char *command
, char *wm_instance
, char *wm_class
, int tile
)
117 aicon
= wmalloc(sizeof(WAppIcon
));
122 add_to_appicon_list(scr
, aicon
);
125 aicon
->command
= wstrdup(command
);
128 aicon
->wm_class
= wstrdup(wm_class
);
131 aicon
->wm_instance
= wstrdup(wm_instance
);
133 if (strcmp(wm_class
, "WMDock") == 0 && wPreferences
.flags
.clip_merged_in_dock
)
135 aicon
->icon
= icon_create_for_dock(scr
, command
, wm_instance
, wm_class
, tile
);
138 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
141 /* will be overriden by dock */
142 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
143 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
144 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
145 aicon
->icon
->core
->descriptor
.parent
= aicon
;
146 AddToStackList(aicon
->icon
->core
);
151 void create_appicon_for_application(WApplication
*wapp
, WWindow
*wwin
)
153 /* Try to create an icon from the dock or clip */
154 create_appicon_from_dock(wwin
, wapp
, wapp
->main_window
);
156 /* If app_icon was not found, create it */
157 if (!wapp
->app_icon
) {
158 /* Create the icon */
159 wapp
->app_icon
= wAppIconCreate(wapp
->main_window_desc
);
160 wIconUpdate(wapp
->app_icon
->icon
);
162 /* Now, paint the icon */
163 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
))
164 paint_app_icon(wapp
);
167 /* Save the app_icon in a file */
168 save_appicon(wapp
->app_icon
, False
);
171 void unpaint_app_icon(WApplication
*wapp
)
177 if (!wapp
|| !wapp
->app_icon
)
180 aicon
= wapp
->app_icon
;
182 /* If the icon is docked, don't continue */
186 scr
= wapp
->main_window_desc
->screen_ptr
;
187 clip
= scr
->workspaces
[scr
->current_workspace
]->clip
;
189 if (!clip
|| !aicon
->attracted
|| !clip
->collapsed
)
190 XUnmapWindow(dpy
, aicon
->icon
->core
->window
);
192 /* We want to avoid having it on the list because otherwise
193 * there will be a hole when the icons are arranged with
195 remove_from_appicon_list(scr
, aicon
);
197 if (wPreferences
.auto_arrange_icons
&& !aicon
->attracted
)
198 wArrangeIcons(scr
, True
);
201 void paint_app_icon(WApplication
*wapp
)
204 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
205 WDock
*attracting_dock
;
207 Bool update_icon
= False
;
209 if (!wapp
|| !wapp
->app_icon
)
212 icon
= wapp
->app_icon
->icon
;
213 wapp
->app_icon
->main_window
= wapp
->main_window
;
215 /* If the icon is docked, don't continue */
216 if (wapp
->app_icon
->docked
)
219 attracting_dock
= scr
->attracting_drawer
!= NULL
?
220 scr
->attracting_drawer
:
221 scr
->workspaces
[scr
->current_workspace
]->clip
;
222 if (attracting_dock
&& attracting_dock
->attract_icons
&&
223 wDockFindFreeSlot(attracting_dock
, &x
, &y
)) {
224 wapp
->app_icon
->attracted
= 1;
225 if (!icon
->shadowed
) {
229 wDockAttachIcon(attracting_dock
, wapp
->app_icon
, x
, y
, update_icon
);
231 /* We must know if the icon is painted in the screen,
232 * because if painted, then PlaceIcon will return the next
233 * space on the screen, and the icon will move */
234 if (wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
) {
235 PlaceIcon(scr
, &x
, &y
, wGetHeadForWindow(wapp
->main_window_desc
));
236 wAppIconMove(wapp
->app_icon
, x
, y
);
237 wLowerFrame(icon
->core
);
241 /* If we want appicon (no_appicon is not set) and the icon is not
242 * in the appicon_list, we must add it. Else, we want to avoid
243 * having it on the list */
244 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
) &&
245 wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
)
246 add_to_appicon_list(scr
, wapp
->app_icon
);
248 if (!attracting_dock
|| !wapp
->app_icon
->attracted
|| !attracting_dock
->collapsed
)
249 XMapWindow(dpy
, icon
->core
->window
);
251 if (wPreferences
.auto_arrange_icons
&& !wapp
->app_icon
->attracted
)
252 wArrangeIcons(scr
, True
);
255 void removeAppIconFor(WApplication
*wapp
)
260 if (wPreferences
.highlight_active_app
)
261 wIconSetHighlited(wapp
->app_icon
->icon
, False
);
262 if (wapp
->app_icon
->docked
&& !wapp
->app_icon
->attracted
) {
263 wapp
->app_icon
->running
= 0;
264 /* since we keep it, we don't care if it was attracted or not */
265 wapp
->app_icon
->attracted
= 0;
266 wapp
->app_icon
->icon
->shadowed
= 0;
267 wapp
->app_icon
->main_window
= None
;
268 wapp
->app_icon
->pid
= 0;
269 wapp
->app_icon
->icon
->owner
= NULL
;
270 wapp
->app_icon
->icon
->icon_win
= None
;
272 /* Set the icon image */
273 set_icon_image_from_database(wapp
->app_icon
->icon
, wapp
->app_icon
->wm_instance
,
274 wapp
->app_icon
->wm_class
, wapp
->app_icon
->command
);
276 /* Update the icon, because wapp->app_icon->icon could be NULL */
277 wIconUpdate(wapp
->app_icon
->icon
);
280 wAppIconPaint(wapp
->app_icon
);
281 } else if (wapp
->app_icon
->docked
) {
282 wapp
->app_icon
->running
= 0;
283 if (wapp
->app_icon
->dock
->type
== WM_DRAWER
) {
284 wDrawerFillTheGap(wapp
->app_icon
->dock
, wapp
->app_icon
, True
);
286 wDockDetach(wapp
->app_icon
->dock
, wapp
->app_icon
);
288 wAppIconDestroy(wapp
->app_icon
);
291 wapp
->app_icon
= NULL
;
293 if (wPreferences
.auto_arrange_icons
)
294 wArrangeIcons(wapp
->main_window_desc
->screen_ptr
, True
);
297 static WAppIcon
*wAppIconCreate(WWindow
*leader_win
)
301 aicon
= wmalloc(sizeof(WAppIcon
));
308 if (leader_win
->wm_class
)
309 aicon
->wm_class
= wstrdup(leader_win
->wm_class
);
311 if (leader_win
->wm_instance
)
312 aicon
->wm_instance
= wstrdup(leader_win
->wm_instance
);
314 aicon
->icon
= icon_create_for_wwindow(leader_win
);
316 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
319 /* will be overriden if docked */
320 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
321 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
322 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
323 aicon
->icon
->core
->descriptor
.parent
= aicon
;
324 AddToStackList(aicon
->icon
->core
);
325 aicon
->icon
->show_title
= 0;
330 void wAppIconDestroy(WAppIcon
* aicon
)
332 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
334 RemoveFromStackList(aicon
->icon
->core
);
335 wIconDestroy(aicon
->icon
);
337 wfree(aicon
->command
);
339 if (aicon
->dnd_command
)
340 wfree(aicon
->dnd_command
);
342 if (aicon
->wm_instance
)
343 wfree(aicon
->wm_instance
);
346 wfree(aicon
->wm_class
);
348 remove_from_appicon_list(scr
, aicon
);
350 aicon
->destroyed
= 1;
354 static void drawCorner(WIcon
* icon
)
356 WScreen
*scr
= icon
->core
->screen_ptr
;
365 XFillPolygon(dpy
, icon
->core
->window
, scr
->icon_title_texture
->normal_gc
,
366 points
, 3, Convex
, CoordModeOrigin
);
367 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 0, 12);
368 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 12, 0);
371 void wAppIconMove(WAppIcon
* aicon
, int x
, int y
)
373 XMoveWindow(dpy
, aicon
->icon
->core
->window
, x
, y
);
379 static void updateDockNumbers(WScreen
* scr
)
383 WAppIcon
*dicon
= scr
->dock
->icon_array
[0];
385 ws_numbers
= wmalloc(20);
386 snprintf(ws_numbers
, 20, "%i [ %i ]", scr
->current_workspace
+ 1, ((scr
->current_workspace
/ 10) + 1));
387 length
= strlen(ws_numbers
);
389 XClearArea(dpy
, dicon
->icon
->core
->window
, 2, 2, 50, WMFontHeight(scr
->icon_title_font
) + 1, False
);
391 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->black
,
392 scr
->icon_title_font
, 4, 3, ws_numbers
, length
);
394 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->white
,
395 scr
->icon_title_font
, 3, 2, ws_numbers
, length
);
399 #endif /* WS_INDICATOR */
401 void wAppIconPaint(WAppIcon
*aicon
)
404 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
406 if (aicon
->icon
->owner
)
407 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
411 wIconPaint(aicon
->icon
);
414 if (aicon
->docked
&& scr
->dock
&& scr
->dock
== aicon
->dock
&& aicon
->yindex
== 0)
415 updateDockNumbers(scr
);
417 if (scr
->dock_dots
&& aicon
->docked
&& !aicon
->running
&& aicon
->command
!= NULL
) {
418 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
419 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
420 XCopyArea(dpy
, scr
->dock_dots
->image
, aicon
->icon
->core
->window
,
421 scr
->copy_gc
, 0, 0, scr
->dock_dots
->width
, scr
->dock_dots
->height
, 0, 0);
424 if (wapp
&& wapp
->flags
.hidden
) {
425 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
426 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
427 XCopyArea(dpy
, scr
->dock_dots
->image
,
428 aicon
->icon
->core
->window
, scr
->copy_gc
, 0, 0, 7, scr
->dock_dots
->height
, 0, 0);
430 #endif /* HIDDENDOT */
432 if (aicon
->omnipresent
)
433 drawCorner(aicon
->icon
);
435 XSetClipMask(dpy
, scr
->copy_gc
, None
);
436 if (aicon
->launching
)
437 XFillRectangle(dpy
, aicon
->icon
->core
->window
, scr
->stipple_gc
,
438 0, 0, wPreferences
.icon_size
, wPreferences
.icon_size
);
441 /* Save the application icon, if it's a dockapp then use it with dock = True */
442 void save_appicon(WAppIcon
*aicon
, Bool dock
)
449 if (dock
&& (!aicon
->docked
|| aicon
->attracted
))
452 path
= wIconStore(aicon
->icon
);
456 wApplicationSaveIconPathFor(path
, aicon
->wm_instance
, aicon
->wm_class
);
460 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
462 /* main_window may not have the full command line; try to find one which does */
463 static void relaunchApplication(WApplication
*wapp
)
466 WWindow
*wlist
, *next
;
468 scr
= wapp
->main_window_desc
->screen_ptr
;
469 wlist
= scr
->focused_window
;
479 if (wlist
->main_window
== wapp
->main_window
) {
480 if (RelaunchWindow(wlist
))
488 static void relaunchCallback(WMenu
* menu
, WMenuEntry
* entry
)
490 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
492 relaunchApplication(wapp
);
495 static void hideCallback(WMenu
* menu
, WMenuEntry
* entry
)
497 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
499 if (wapp
->flags
.hidden
) {
500 wWorkspaceChange(menu
->menu
->screen_ptr
, wapp
->last_workspace
);
501 wUnhideApplication(wapp
, False
, False
);
503 wHideApplication(wapp
);
507 static void unhideHereCallback(WMenu
* menu
, WMenuEntry
* entry
)
509 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
511 wUnhideApplication(wapp
, False
, True
);
514 static void setIconCallback(WMenu
*menu
, WMenuEntry
*entry
)
516 WAppIcon
*icon
= ((WApplication
*) entry
->clientdata
)->app_icon
;
521 assert(icon
!= NULL
);
527 scr
= icon
->icon
->core
->screen_ptr
;
531 result
= wIconChooserDialog(scr
, &file
, icon
->wm_instance
, icon
->wm_class
);
533 if (result
&& !icon
->destroyed
) {
534 if (file
&& *file
== 0) {
538 if (!wIconChangeImageFile(icon
->icon
, file
)) {
539 wMessageDialog(scr
, _("Error"),
540 _("Could not open specified icon file"), _("OK"), NULL
, NULL
);
542 wDefaultChangeIcon(scr
, icon
->wm_instance
, icon
->wm_class
, file
);
552 static void killCallback(WMenu
* menu
, WMenuEntry
* entry
)
554 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
555 WFakeGroupLeader
*fPtr
;
558 char *basename(const char *shortname
);
560 if (!WCHECK_STATE(WSTATE_NORMAL
))
563 WCHANGE_STATE(WSTATE_MODAL
);
565 assert(entry
->clientdata
!= NULL
);
567 shortname
= basename(wapp
->app_icon
->wm_instance
);
569 buffer
= wstrconcat(wapp
->app_icon
? shortname
: NULL
,
570 _(" will be forcibly closed.\n"
571 "Any unsaved changes will be lost.\n" "Please confirm."));
573 fPtr
= wapp
->main_window_desc
->fake_group
;
575 wretain(wapp
->main_window_desc
);
576 if (wPreferences
.dont_confirm_kill
577 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
578 buffer
, _("Yes"), _("No"), NULL
) == WAPRDefault
) {
580 WWindow
*wwin
, *twin
;
582 wwin
= wapp
->main_window_desc
->screen_ptr
->focused_window
;
585 if (wwin
->fake_group
== fPtr
)
589 } else if (!wapp
->main_window_desc
->flags
.destroyed
) {
590 wClientKill(wapp
->main_window_desc
);
593 wrelease(wapp
->main_window_desc
);
595 WCHANGE_STATE(WSTATE_NORMAL
);
598 static WMenu
*createApplicationMenu(WScreen
*scr
)
602 menu
= wMenuCreate(scr
, NULL
, False
);
603 wMenuAddCallback(menu
, _("Unhide Here"), unhideHereCallback
, NULL
);
604 wMenuAddCallback(menu
, _("Hide"), hideCallback
, NULL
);
605 wMenuAddCallback(menu
, _("Launch"), relaunchCallback
, NULL
);
606 wMenuAddCallback(menu
, _("Set Icon..."), setIconCallback
, NULL
);
607 wMenuAddCallback(menu
, _("Kill"), killCallback
, NULL
);
612 static void openApplicationMenu(WApplication
* wapp
, int x
, int y
)
615 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
618 if (!scr
->icon_menu
) {
619 scr
->icon_menu
= createApplicationMenu(scr
);
620 wfree(scr
->icon_menu
->entries
[1]->text
);
623 menu
= scr
->icon_menu
;
625 if (wapp
->flags
.hidden
)
626 menu
->entries
[1]->text
= _("Unhide");
628 menu
->entries
[1]->text
= _("Hide");
630 menu
->flags
.realized
= 0;
633 x
-= menu
->frame
->core
->width
/ 2;
634 if (x
+ menu
->frame
->core
->width
> scr
->scr_width
)
635 x
= scr
->scr_width
- menu
->frame
->core
->width
;
640 /* set client data */
641 for (i
= 0; i
< menu
->entry_no
; i
++)
642 menu
->entries
[i
]->clientdata
= wapp
;
644 wMenuMapAt(menu
, x
, y
, False
);
647 /******************************************************************/
649 static void iconExpose(WObjDescriptor
*desc
, XEvent
*event
)
651 wAppIconPaint(desc
->parent
);
654 static void iconDblClick(WObjDescriptor
*desc
, XEvent
*event
)
656 WAppIcon
*aicon
= desc
->parent
;
658 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
661 assert(aicon
->icon
->owner
!= NULL
);
663 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
665 if (event
->xbutton
.state
& ControlMask
) {
666 relaunchApplication(wapp
);
670 unhideHere
= (event
->xbutton
.state
& ShiftMask
);
671 /* go to the last workspace that the user worked on the app */
672 if (!unhideHere
&& wapp
->last_workspace
!= scr
->current_workspace
)
673 wWorkspaceChange(scr
, wapp
->last_workspace
);
675 wUnhideApplication(wapp
, event
->xbutton
.button
== Button2
, unhideHere
);
677 if (event
->xbutton
.state
& MOD_MASK
)
678 wHideOtherApplications(aicon
->icon
->owner
);
681 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
)
683 WAppIcon
*aicon
= desc
->parent
;
684 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
687 if (aicon
->editing
|| WCHECK_STATE(WSTATE_MODAL
))
690 if (IsDoubleClick(scr
, event
)) {
691 /* Middle or right mouse actions were handled on first click */
692 if (event
->xbutton
.button
== Button1
)
693 iconDblClick(desc
, event
);
697 if (event
->xbutton
.button
== Button2
) {
698 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
701 relaunchApplication(wapp
);
706 if (event
->xbutton
.button
== Button3
) {
707 WObjDescriptor
*desc
;
708 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
713 if (event
->xbutton
.send_event
&&
714 XGrabPointer(dpy
, aicon
->icon
->core
->window
, True
, ButtonMotionMask
715 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
716 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
717 wwarning("pointer grab failed for appicon menu");
721 openApplicationMenu(wapp
, event
->xbutton
.x_root
, event
->xbutton
.y_root
);
723 /* allow drag select of menu */
724 desc
= &scr
->icon_menu
->menu
->descriptor
;
725 event
->xbutton
.send_event
= True
;
726 (*desc
->handle_mousedown
) (desc
, event
);
730 hasMoved
= wHandleAppIconMove(aicon
, event
);
731 if (wPreferences
.single_click
&& !hasMoved
&& aicon
->dock
!= NULL
)
733 iconDblClick(desc
, event
);
737 Bool
wHandleAppIconMove(WAppIcon
*aicon
, XEvent
*event
)
739 WIcon
*icon
= aicon
->icon
;
740 WScreen
*scr
= icon
->core
->screen_ptr
;
741 WDock
*originalDock
= aicon
->dock
; /* can be NULL */
742 WDock
*lastDock
= originalDock
;
743 WDock
*allDocks
[scr
->drawer_count
+ 2]; /* clip, dock and drawers (order determined at runtime) */
745 Bool done
= False
, dockable
, ondock
;
746 Bool grabbed
= False
;
747 Bool collapsed
= False
; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
748 int superfluous
= wPreferences
.superfluous
; /* we cache it to avoid problems */
749 int omnipresent
= aicon
->omnipresent
; /* this must be cached */
750 Bool showed_all_clips
= False
;
752 int clickButton
= event
->xbutton
.button
;
754 Window wins
[2]; /* Managing shadow window */
757 int x
= aicon
->x_pos
, y
= aicon
->y_pos
;
758 int ofs_x
= event
->xbutton
.x
, ofs_y
= event
->xbutton
.y
;
759 int shad_x
= x
, shad_y
= y
;
760 int ix
= aicon
->xindex
, iy
= aicon
->yindex
;
764 Bool hasMoved
= False
;
766 if (wPreferences
.flags
.noupdates
&& originalDock
!= NULL
)
769 if (!(event
->xbutton
.state
& MOD_MASK
))
770 wRaiseFrame(icon
->core
);
772 /* If Mod is pressed for an docked appicon, assume it is to undock it,
773 * so don't lower it */
774 if (originalDock
== NULL
)
775 wLowerFrame(icon
->core
);
778 if (XGrabPointer(dpy
, icon
->core
->window
, True
, ButtonMotionMask
779 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
780 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
781 wwarning("Pointer grab failed in wHandleAppIconMove");
784 if (originalDock
!= NULL
) {
790 if (wPreferences
.flags
.nodock
&& wPreferences
.flags
.noclip
&& wPreferences
.flags
.nodrawer
)
793 dockable
= canBeDocked(icon
->owner
);
796 /* We try the various docks in that order:
797 * - First, the dock the appicon comes from, if any
798 * - Then, the drawers
799 * - Then, the "dock" (WM_DOCK)
800 * - Finally, the clip
803 if (originalDock
!= NULL
)
804 allDocks
[ i
++ ] = originalDock
;
805 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
806 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
807 if (dc
->adrawer
!= originalDock
)
808 allDocks
[ i
++ ] = dc
->adrawer
;
810 if (!wPreferences
.flags
.nodock
&& scr
->dock
!= originalDock
)
811 allDocks
[ i
++ ] = scr
->dock
;
812 if (!wPreferences
.flags
.noclip
&&
813 originalDock
!= scr
->workspaces
[scr
->current_workspace
]->clip
)
814 allDocks
[ i
++ ] = scr
->workspaces
[scr
->current_workspace
]->clip
;
815 for ( ; i
< scr
->drawer_count
+ 2; i
++) /* In case the clip, the dock, or both, are disabled */
816 allDocks
[ i
] = NULL
;
818 wins
[0] = icon
->core
->window
;
819 wins
[1] = scr
->dock_shadow
;
820 XRestackWindows(dpy
, wins
, 2);
821 XMoveResizeWindow(dpy
, scr
->dock_shadow
, aicon
->x_pos
, aicon
->y_pos
, ICON_SIZE
, ICON_SIZE
);
823 if (icon
->pixmap
!= None
)
824 ghost
= MakeGhostIcon(scr
, icon
->pixmap
);
826 ghost
= MakeGhostIcon(scr
, icon
->core
->window
);
827 XSetWindowBackgroundPixmap(dpy
, scr
->dock_shadow
, ghost
);
828 XClearWindow(dpy
, scr
->dock_shadow
);
831 XMapWindow(dpy
, scr
->dock_shadow
);
834 WMMaskEvent(dpy
, PointerMotionMask
| ButtonReleaseMask
| ButtonPressMask
835 | ButtonMotionMask
| ExposureMask
| EnterWindowMask
, &ev
);
842 /* It means the cursor moved so fast that it entered
843 * something else (if moving slowly, it would have
844 * stayed in the appIcon that is being moved. Ignore
845 * such "spurious" EnterNotifiy's */
851 if (abs(ofs_x
- ev
.xmotion
.x
) >= MOVE_THRESHOLD
852 || abs(ofs_y
- ev
.xmotion
.y
) >= MOVE_THRESHOLD
) {
853 XChangeActivePointerGrab(dpy
, ButtonMotionMask
854 | ButtonReleaseMask
| ButtonPressMask
,
855 wCursor
[WCUR_MOVE
], CurrentTime
);
862 if (omnipresent
&& !showed_all_clips
) {
864 for (i
= 0; i
< scr
->workspace_count
; i
++) {
865 if (i
== scr
->current_workspace
)
867 wDockShowIcons(scr
->workspaces
[i
]->clip
);
868 /* Note: if dock is collapsed (for instance, because it
869 auto-collapses), its icons still won't show up */
871 showed_all_clips
= True
; /* To prevent flickering */
874 x
= ev
.xmotion
.x_root
- ofs_x
;
875 y
= ev
.xmotion
.y_root
- ofs_y
;
876 wAppIconMove(aicon
, x
, y
);
878 WDock
*theNewDock
= NULL
;
879 if (!(ev
.xmotion
.state
& MOD_MASK
) || aicon
->launching
|| aicon
->lock
) {
880 for (i
= 0; dockable
&& i
< scr
->drawer_count
+ 2; i
++) {
881 WDock
*theDock
= allDocks
[i
];
884 if (wDockSnapIcon(theDock
, aicon
, x
, y
, &ix
, &iy
, (theDock
== originalDock
))) {
885 theNewDock
= theDock
;
889 if (originalDock
!= NULL
&& theNewDock
== NULL
&&
890 (aicon
->launching
|| aicon
->lock
|| aicon
->running
)) {
891 /* In those cases, stay in lastDock if no dock really wants us */
892 theNewDock
= lastDock
;
895 if (lastDock
!= NULL
&& lastDock
!= theNewDock
) {
896 /* Leave lastDock in the state we found it */
897 if (lastDock
->type
== WM_DRAWER
) {
898 wDrawerFillTheGap(lastDock
, aicon
, (lastDock
== originalDock
));
901 lastDock
->collapsed
= 1;
902 wDockHideIcons(lastDock
);
905 if (lastDock
->auto_raise_lower
) {
906 wDockLower(lastDock
);
909 if (theNewDock
!= NULL
) {
910 if (lastDock
!= theNewDock
) {
911 collapsed
= theNewDock
->collapsed
;
913 theNewDock
->collapsed
= 0;
914 wDockShowIcons(theNewDock
);
916 if (theNewDock
->auto_raise_lower
) {
917 wDockRaise(theNewDock
);
918 /* And raise the moving tile above it */
919 wRaiseFrame(aicon
->icon
->core
);
921 lastDock
= theNewDock
;
924 shad_x
= lastDock
->x_pos
+ ix
*wPreferences
.icon_size
;
925 shad_y
= lastDock
->y_pos
+ iy
*wPreferences
.icon_size
;
927 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
930 XMapWindow(dpy
, scr
->dock_shadow
);
934 lastDock
= theNewDock
; // i.e., NULL
936 XUnmapWindow(dpy
, scr
->dock_shadow
);
938 * Leaving that weird comment for now.
939 * But if we see no gap, there is no need to fill one!
940 * We could test ondock first and the lastDock to NULL afterwards
941 if (lastDock_before_it_was_null->type == WM_DRAWER) {
942 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
954 if (ev
.xbutton
.button
!= clickButton
)
956 XUngrabPointer(dpy
, CurrentTime
);
960 SlideWindow(icon
->core
->window
, x
, y
, shad_x
, shad_y
);
961 XUnmapWindow(dpy
, scr
->dock_shadow
);
962 if (originalDock
== NULL
) { // docking an undocked appicon
963 docked
= wDockAttachIcon(lastDock
, aicon
, ix
, iy
, False
);
965 /* AppIcon got rejected (happens only when we can't get the
966 command for that appicon, and the user cancels the
967 wInputDialog asking for one). Make the rejection obvious by
968 sliding the icon to its old position */
969 if (lastDock
->type
== WM_DRAWER
) {
970 // Also fill the gap left in the drawer
971 wDrawerFillTheGap(lastDock
, aicon
, False
);
973 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
976 else { // moving a docked appicon to a dock
977 if (originalDock
== lastDock
) {
979 wDockReattachIcon(originalDock
, aicon
, ix
, iy
);
982 docked
= wDockMoveIconBetweenDocks(originalDock
, lastDock
, aicon
, ix
, iy
);
984 /* Possible scenario: user moved an auto-attracted appicon
985 from the clip to the dock, and cancelled the wInputDialog
986 asking for a command */
987 if (lastDock
->type
== WM_DRAWER
) {
988 wDrawerFillTheGap(lastDock
, aicon
, False
);
990 /* If aicon comes from a drawer, make some room to reattach it */
991 if (originalDock
->type
== WM_DRAWER
) {
992 WAppIcon
*aiconsToShift
[ originalDock
->icon_count
];
995 for (i
= 0; i
< originalDock
->max_icons
; i
++) {
996 WAppIcon
*ai
= originalDock
->icon_array
[ i
];
997 if (ai
&& ai
!= aicon
&&
998 abs(ai
->xindex
) >= abs(aicon
->xindex
))
999 aiconsToShift
[j
++] = ai
;
1001 if (j
!= originalDock
->icon_count
- abs(aicon
->xindex
) - 1)
1002 // Trust this never happens?
1003 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1004 j
, originalDock
->icon_count
- abs(aicon
->xindex
) - 1, aicon
->xindex
);
1005 wSlideAppicons(aiconsToShift
, j
, originalDock
->on_right_side
);
1006 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1009 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
1010 wDockReattachIcon(originalDock
, aicon
, aicon
->xindex
, aicon
->yindex
);
1013 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1014 originalDock
->collapsed
= 1;
1015 wDockHideIcons(originalDock
);
1017 if (originalDock
->auto_raise_lower
)
1018 wDockLower(originalDock
);
1022 // No matter what happened above, check to lower lastDock
1023 // Don't see why I commented out the following 2 lines
1024 /* if (lastDock->auto_raise_lower)
1025 wDockLower(lastDock); */
1026 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1027 * collapsed, so that wHandleAppIconMove doesn't collapse it
1028 * right away (the timer will take care of it) */
1029 if (lastDock
->auto_collapse
)
1033 if (originalDock
!= NULL
) { /* Detaching a docked appicon */
1035 if (!aicon
->running
&& !wPreferences
.no_animations
) {
1036 /* We need to deselect it, even if is deselected in
1037 * wDockDetach(), because else DoKaboom() will fail.
1039 if (aicon
->icon
->selected
)
1040 wIconSelect(aicon
->icon
);
1041 DoKaboom(scr
, aicon
->icon
->core
->window
, x
, y
);
1044 wDockDetach(originalDock
, aicon
);
1045 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1046 originalDock
->collapsed
= 1;
1047 wDockHideIcons(originalDock
);
1049 if (originalDock
->auto_raise_lower
)
1050 wDockLower(originalDock
);
1053 // Can't remember why the icon hiding is better done above than below (commented out)
1054 // Also, lastDock is quite different from originalDock
1057 lastDock->collapsed = 1;
1058 wDockHideIcons(lastDock);
1064 XFreePixmap(dpy
, ghost
);
1065 XSetWindowBackground(dpy
, scr
->dock_shadow
, scr
->white_pixel
);
1067 if (showed_all_clips
) {
1069 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1070 if (i
== scr
->current_workspace
)
1072 wDockHideIcons(scr
->workspaces
[i
]->clip
);
1075 if (wPreferences
.auto_arrange_icons
&& !(originalDock
!= NULL
&& docked
))
1076 /* Need to rearrange unless moving from dock to dock */
1077 wArrangeIcons(scr
, True
);
1081 return False
; /* Never reached */
1084 /* This function save the application icon and store the path in the Dictionary */
1085 static void wApplicationSaveIconPathFor(char *iconPath
, char *wm_instance
, char *wm_class
)
1087 WMPropList
*dict
= WDWindowAttributes
->dictionary
;
1088 WMPropList
*adict
, *key
, *iconk
;
1092 tmp
= get_name_for_instance_class(wm_instance
, wm_class
);
1093 key
= WMCreatePLString(tmp
);
1096 adict
= WMGetFromPLDictionary(dict
, key
);
1097 iconk
= WMCreatePLString("Icon");
1100 val
= WMGetFromPLDictionary(adict
, iconk
);
1102 /* no dictionary for app, so create one */
1103 adict
= WMCreatePLDictionary(NULL
, NULL
);
1104 WMPutInPLDictionary(dict
, key
, adict
);
1105 WMReleasePropList(adict
);
1110 val
= WMCreatePLString(iconPath
);
1111 WMPutInPLDictionary(adict
, iconk
, val
);
1112 WMReleasePropList(val
);
1117 WMReleasePropList(key
);
1118 WMReleasePropList(iconk
);
1120 if (val
&& !wPreferences
.flags
.noupdates
)
1121 UpdateDomainFile(WDWindowAttributes
);
1124 static WAppIcon
*findDockIconFor(WDock
*dock
, Window main_window
)
1126 WAppIcon
*aicon
= NULL
;
1128 aicon
= wDockFindIconForWindow(dock
, main_window
);
1130 wDockTrackWindowLaunch(dock
, main_window
);
1131 aicon
= wDockFindIconForWindow(dock
, main_window
);
1136 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
)
1138 WScreen
*scr
= wwin
->screen_ptr
;
1139 wapp
->app_icon
= NULL
;
1142 wapp
->app_icon
= findDockIconFor(scr
->last_dock
, main_window
);
1144 /* check main dock if we did not find it in last dock */
1145 if (!wapp
->app_icon
&& scr
->dock
)
1146 wapp
->app_icon
= findDockIconFor(scr
->dock
, main_window
);
1149 if (!wapp
->app_icon
) {
1151 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1152 WDock
*dock
= scr
->workspaces
[i
]->clip
;
1154 wapp
->app_icon
= findDockIconFor(dock
, main_window
);
1160 /* Finally check drawers */
1161 if (!wapp
->app_icon
) {
1163 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
1164 wapp
->app_icon
= findDockIconFor(dc
->adrawer
, main_window
);
1170 /* If created, then set some flags */
1171 if (wapp
->app_icon
) {
1172 WWindow
*mainw
= wapp
->main_window_desc
;
1174 wapp
->app_icon
->running
= 1;
1175 wapp
->app_icon
->icon
->owner
= mainw
;
1176 if (mainw
->wm_hints
&& (mainw
->wm_hints
->flags
& IconWindowHint
))
1177 wapp
->app_icon
->icon
->icon_win
= mainw
->wm_hints
->icon_window
;
1179 /* Update the icon images */
1180 wIconUpdate(wapp
->app_icon
->icon
);
1183 wAppIconPaint(wapp
->app_icon
);
1184 save_appicon(wapp
->app_icon
, True
);
1188 /* Add the appicon to the appiconlist */
1189 static void add_to_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1191 appicon
->prev
= NULL
;
1192 appicon
->next
= scr
->app_icon_list
;
1193 if (scr
->app_icon_list
)
1194 scr
->app_icon_list
->prev
= appicon
;
1196 scr
->app_icon_list
= appicon
;
1199 /* Remove the appicon from the appiconlist */
1200 static void remove_from_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1202 if (appicon
== scr
->app_icon_list
) {
1204 appicon
->next
->prev
= NULL
;
1205 scr
->app_icon_list
= appicon
->next
;
1208 appicon
->next
->prev
= appicon
->prev
;
1210 appicon
->prev
->next
= appicon
->next
;
1213 appicon
->prev
= NULL
;
1214 appicon
->next
= NULL
;
1217 /* Return the AppIcon associated with a given (Xlib) Window. */
1218 WAppIcon
*wAppIconFor(Window window
)
1220 WObjDescriptor
*desc
;
1225 if (XFindContext(dpy
, window
, wWinContext
, (XPointer
*) & desc
) == XCNOENT
)
1228 if (desc
->parent_type
== WCLASS_APPICON
|| desc
->parent_type
== WCLASS_DOCK_ICON
)
1229 return desc
->parent
;