1 /* appicon.c- icon for applications (not mini-window)
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <X11/Xutil.h>
34 #include "WindowMaker.h"
37 #include "application.h"
44 #include "workspace.h"
45 #include "superfluous.h"
51 #include "placement.h"
59 * icon_file for the dock is got from the preferences file by
60 * using the classname/instancename
63 #define MOD_MASK wPreferences.modifier_mask
64 #define ICON_SIZE wPreferences.icon_size
66 static void iconDblClick(WObjDescriptor
* desc
, XEvent
* event
);
67 static void iconExpose(WObjDescriptor
* desc
, XEvent
* event
);
68 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
);
69 static WAppIcon
*wAppIconCreate(WWindow
* leader_win
);
70 static void add_to_appicon_list(WAppIcon
*appicon
);
71 static void remove_from_appicon_list(WAppIcon
*appicon
);
72 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
);
74 /* This function is used if the application is a .app. It checks if it has an icon in it
75 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
77 void wApplicationExtractDirPackIcon(const char *path
, const char *wm_instance
, const char *wm_class
)
79 char *iconPath
= NULL
;
82 if (strstr(path
, ".app")) {
83 tmp
= wmalloc(strlen(path
) + 16);
85 if (wPreferences
.supports_tiff
) {
88 if (access(tmp
, R_OK
) == 0)
95 if (access(tmp
, R_OK
) == 0)
103 wApplicationSaveIconPathFor(iconPath
, wm_instance
, wm_class
);
109 WAppIcon
*wAppIconCreateForDock(WScreen
*scr
, const char *command
, const char *wm_instance
, const char *wm_class
, int tile
)
113 aicon
= wmalloc(sizeof(WAppIcon
));
118 add_to_appicon_list(aicon
);
121 aicon
->command
= wstrdup(command
);
124 aicon
->wm_class
= wstrdup(wm_class
);
127 aicon
->wm_instance
= wstrdup(wm_instance
);
129 if (strcmp(wm_class
, "WMDock") == 0 && wPreferences
.flags
.clip_merged_in_dock
)
131 aicon
->icon
= icon_create_for_dock(scr
, command
, wm_instance
, wm_class
, tile
);
134 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
137 /* will be overriden by dock */
138 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
139 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
140 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
141 aicon
->icon
->core
->descriptor
.parent
= aicon
;
142 AddToStackList(aicon
->icon
->core
);
147 void create_appicon_for_application(WApplication
*wapp
, WWindow
*wwin
)
149 /* Try to create an icon from the dock or clip */
150 create_appicon_from_dock(wwin
, wapp
, wapp
->main_window
);
152 /* If app_icon was not found, create it */
153 if (!wapp
->app_icon
) {
154 /* Create the icon */
155 wapp
->app_icon
= wAppIconCreate(wapp
->main_window_desc
);
156 wIconUpdate(wapp
->app_icon
->icon
);
158 /* Now, paint the icon */
159 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
))
160 paint_app_icon(wapp
);
163 /* Save the app_icon in a file */
164 save_appicon(wapp
->app_icon
, False
);
167 void unpaint_app_icon(WApplication
*wapp
)
173 if (!wapp
|| !wapp
->app_icon
)
176 aicon
= wapp
->app_icon
;
178 /* If the icon is docked, don't continue */
182 scr
= wapp
->main_window_desc
->screen_ptr
;
183 clip
= w_global
.workspace
.array
[w_global
.workspace
.current
]->clip
;
185 if (!clip
|| !aicon
->attracted
|| !clip
->collapsed
)
186 XUnmapWindow(dpy
, aicon
->icon
->core
->window
);
188 /* We want to avoid having it on the list because otherwise
189 * there will be a hole when the icons are arranged with
191 remove_from_appicon_list(aicon
);
193 if (wPreferences
.auto_arrange_icons
&& !aicon
->attracted
)
194 wArrangeIcons(scr
, True
);
197 void paint_app_icon(WApplication
*wapp
)
201 WDock
*attracting_dock
;
203 Bool update_icon
= False
;
205 if (!wapp
|| !wapp
->app_icon
|| !wapp
->main_window_desc
)
208 icon
= wapp
->app_icon
->icon
;
209 scr
= wapp
->main_window_desc
->screen_ptr
;
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 w_global
.workspace
.array
[w_global
.workspace
.current
]->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(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
);
273 /* Update the icon, because wapp->app_icon->icon could be NULL */
274 wIconUpdate(wapp
->app_icon
->icon
);
277 wAppIconPaint(wapp
->app_icon
);
278 } else if (wapp
->app_icon
->docked
) {
279 wapp
->app_icon
->running
= 0;
280 if (wapp
->app_icon
->dock
->type
== WM_DRAWER
) {
281 wDrawerFillTheGap(wapp
->app_icon
->dock
, wapp
->app_icon
, True
);
283 wDockDetach(wapp
->app_icon
->dock
, wapp
->app_icon
);
285 wAppIconDestroy(wapp
->app_icon
);
288 wapp
->app_icon
= NULL
;
290 if (wPreferences
.auto_arrange_icons
)
291 wArrangeIcons(wapp
->main_window_desc
->screen_ptr
, True
);
294 static WAppIcon
*wAppIconCreate(WWindow
*leader_win
)
298 aicon
= wmalloc(sizeof(WAppIcon
));
305 if (leader_win
->wm_class
)
306 aicon
->wm_class
= wstrdup(leader_win
->wm_class
);
308 if (leader_win
->wm_instance
)
309 aicon
->wm_instance
= wstrdup(leader_win
->wm_instance
);
311 aicon
->icon
= icon_create_for_wwindow(leader_win
);
313 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
316 /* will be overriden if docked */
317 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
318 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
319 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
320 aicon
->icon
->core
->descriptor
.parent
= aicon
;
321 AddToStackList(aicon
->icon
->core
);
322 aicon
->icon
->show_title
= 0;
327 void wAppIconDestroy(WAppIcon
*aicon
)
329 RemoveFromStackList(aicon
->icon
->core
);
330 wIconDestroy(aicon
->icon
);
332 wfree(aicon
->command
);
334 if (aicon
->dnd_command
)
335 wfree(aicon
->dnd_command
);
337 if (aicon
->wm_instance
)
338 wfree(aicon
->wm_instance
);
341 wfree(aicon
->wm_class
);
343 remove_from_appicon_list(aicon
);
345 aicon
->destroyed
= 1;
349 static void drawCorner(WIcon
* icon
)
351 WScreen
*scr
= icon
->core
->screen_ptr
;
360 XFillPolygon(dpy
, icon
->core
->window
, scr
->icon_title_texture
->normal_gc
,
361 points
, 3, Convex
, CoordModeOrigin
);
362 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 0, 12);
363 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 12, 0);
366 void wAppIconMove(WAppIcon
* aicon
, int x
, int y
)
368 XMoveWindow(dpy
, aicon
->icon
->core
->window
, x
, y
);
374 static void updateDockNumbers(WScreen
*scr
)
378 WAppIcon
*dicon
= scr
->dock
->icon_array
[0];
380 ws_numbers
= wmalloc(20);
381 snprintf(ws_numbers
, 20, "%i [ %i ]", w_global
.workspace
.current
+ 1, ((w_global
.workspace
.current
/ 10) + 1));
382 length
= strlen(ws_numbers
);
384 XClearArea(dpy
, dicon
->icon
->core
->window
, 2, 2, 50, WMFontHeight(scr
->icon_title_font
) + 1, False
);
386 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->black
,
387 scr
->icon_title_font
, 4, 3, ws_numbers
, length
);
389 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->white
,
390 scr
->icon_title_font
, 3, 2, ws_numbers
, length
);
394 #endif /* WS_INDICATOR */
396 void wAppIconPaint(WAppIcon
*aicon
)
399 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
401 if (aicon
->icon
->owner
)
402 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
406 wIconPaint(aicon
->icon
);
409 if (aicon
->docked
&& scr
->dock
&& scr
->dock
== aicon
->dock
&& aicon
->yindex
== 0)
410 updateDockNumbers(scr
);
412 if (scr
->dock_dots
&& aicon
->docked
&& !aicon
->running
&& aicon
->command
!= NULL
) {
413 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
414 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
415 XCopyArea(dpy
, scr
->dock_dots
->image
, aicon
->icon
->core
->window
,
416 scr
->copy_gc
, 0, 0, scr
->dock_dots
->width
, scr
->dock_dots
->height
, 0, 0);
419 if (wapp
&& wapp
->flags
.hidden
) {
420 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
421 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
422 XCopyArea(dpy
, scr
->dock_dots
->image
,
423 aicon
->icon
->core
->window
, scr
->copy_gc
, 0, 0, 7, scr
->dock_dots
->height
, 0, 0);
425 #endif /* HIDDENDOT */
427 if (aicon
->omnipresent
)
428 drawCorner(aicon
->icon
);
430 XSetClipMask(dpy
, scr
->copy_gc
, None
);
431 if (aicon
->launching
)
432 XFillRectangle(dpy
, aicon
->icon
->core
->window
, scr
->stipple_gc
,
433 0, 0, wPreferences
.icon_size
, wPreferences
.icon_size
);
436 /* Save the application icon, if it's a dockapp then use it with dock = True */
437 void save_appicon(WAppIcon
*aicon
, Bool dock
)
444 if (dock
&& (!aicon
->docked
|| aicon
->attracted
))
447 path
= wIconStore(aicon
->icon
);
451 wApplicationSaveIconPathFor(path
, aicon
->wm_instance
, aicon
->wm_class
);
455 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
457 /* main_window may not have the full command line; try to find one which does */
458 static void relaunchApplication(WApplication
*wapp
)
461 WWindow
*wlist
, *next
;
463 scr
= wapp
->main_window_desc
->screen_ptr
;
464 wlist
= scr
->focused_window
;
474 if (wlist
->main_window
== wapp
->main_window
) {
475 if (RelaunchWindow(wlist
))
483 static void relaunchCallback(WMenu
* menu
, WMenuEntry
* entry
)
485 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
487 /* Parameter not used, but tell the compiler that it is ok */
490 relaunchApplication(wapp
);
493 static void hideCallback(WMenu
* menu
, WMenuEntry
* entry
)
495 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
497 if (wapp
->flags
.hidden
) {
498 wWorkspaceChange(menu
->menu
->screen_ptr
, wapp
->last_workspace
);
499 wUnhideApplication(wapp
, False
, False
);
501 wHideApplication(wapp
);
505 static void unhideHereCallback(WMenu
* menu
, WMenuEntry
* entry
)
507 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
509 /* Parameter not used, but tell the compiler that it is ok */
512 wUnhideApplication(wapp
, False
, True
);
515 static void setIconCallback(WMenu
*menu
, WMenuEntry
*entry
)
517 WAppIcon
*icon
= ((WApplication
*) entry
->clientdata
)->app_icon
;
522 /* Parameter not used, but tell the compiler that it is ok */
525 assert(icon
!= NULL
);
531 scr
= icon
->icon
->core
->screen_ptr
;
535 result
= wIconChooserDialog(scr
, &file
, icon
->wm_instance
, icon
->wm_class
);
538 if (!icon
->destroyed
) {
539 if (!wIconChangeImageFile(icon
->icon
, file
)) {
540 wMessageDialog(scr
, _("Error"),
541 _("Could not open specified icon file"),
542 _("OK"), NULL
, NULL
);
544 wDefaultChangeIcon(icon
->wm_instance
, icon
->wm_class
, file
);
555 static void killCallback(WMenu
* menu
, WMenuEntry
* entry
)
557 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
558 WFakeGroupLeader
*fPtr
;
562 if (!WCHECK_STATE(WSTATE_NORMAL
))
565 WCHANGE_STATE(WSTATE_MODAL
);
567 assert(entry
->clientdata
!= NULL
);
569 shortname
= basename(wapp
->app_icon
->wm_instance
);
571 buffer
= wstrconcat(wapp
->app_icon
? shortname
: NULL
,
572 _(" will be forcibly closed.\n"
573 "Any unsaved changes will be lost.\n" "Please confirm."));
575 fPtr
= wapp
->main_window_desc
->fake_group
;
577 wretain(wapp
->main_window_desc
);
578 if (wPreferences
.dont_confirm_kill
579 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
580 buffer
, _("Yes"), _("No"), NULL
) == WAPRDefault
) {
582 WWindow
*wwin
, *twin
;
584 wwin
= wapp
->main_window_desc
->screen_ptr
->focused_window
;
587 if (wwin
->fake_group
== fPtr
)
591 } else if (!wapp
->main_window_desc
->flags
.destroyed
) {
592 wClientKill(wapp
->main_window_desc
);
595 wrelease(wapp
->main_window_desc
);
597 WCHANGE_STATE(WSTATE_NORMAL
);
600 static WMenu
*createApplicationMenu(WScreen
*scr
)
604 menu
= wMenuCreate(scr
, NULL
, False
);
605 wMenuAddCallback(menu
, _("Unhide Here"), unhideHereCallback
, NULL
);
606 wMenuAddCallback(menu
, _("Hide"), hideCallback
, NULL
);
607 wMenuAddCallback(menu
, _("Launch"), relaunchCallback
, NULL
);
608 wMenuAddCallback(menu
, _("Set Icon..."), setIconCallback
, NULL
);
609 wMenuAddCallback(menu
, _("Kill"), killCallback
, NULL
);
614 static void openApplicationMenu(WApplication
* wapp
, int x
, int y
)
617 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
620 if (!scr
->icon_menu
) {
621 scr
->icon_menu
= createApplicationMenu(scr
);
622 wfree(scr
->icon_menu
->entries
[1]->text
);
625 menu
= scr
->icon_menu
;
627 if (wapp
->flags
.hidden
)
628 menu
->entries
[1]->text
= _("Unhide");
630 menu
->entries
[1]->text
= _("Hide");
632 menu
->flags
.realized
= 0;
635 x
-= menu
->frame
->core
->width
/ 2;
636 if (x
+ menu
->frame
->core
->width
> scr
->scr_width
)
637 x
= scr
->scr_width
- menu
->frame
->core
->width
;
642 /* set client data */
643 for (i
= 0; i
< menu
->entry_no
; i
++)
644 menu
->entries
[i
]->clientdata
= wapp
;
646 wMenuMapAt(menu
, x
, y
, False
);
649 /******************************************************************/
651 static void iconExpose(WObjDescriptor
*desc
, XEvent
*event
)
653 /* Parameter not used, but tell the compiler that it is ok */
656 wAppIconPaint(desc
->parent
);
659 static void iconDblClick(WObjDescriptor
*desc
, XEvent
*event
)
661 WAppIcon
*aicon
= desc
->parent
;
663 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
666 assert(aicon
->icon
->owner
!= NULL
);
668 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
670 if (event
->xbutton
.state
& ControlMask
) {
671 relaunchApplication(wapp
);
675 unhideHere
= (event
->xbutton
.state
& ShiftMask
);
676 /* go to the last workspace that the user worked on the app */
677 if (!unhideHere
&& wapp
->last_workspace
!= w_global
.workspace
.current
)
678 wWorkspaceChange(scr
, wapp
->last_workspace
);
680 wUnhideApplication(wapp
, event
->xbutton
.button
== Button2
, unhideHere
);
682 if (event
->xbutton
.state
& MOD_MASK
)
683 wHideOtherApplications(aicon
->icon
->owner
);
686 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
)
688 WAppIcon
*aicon
= desc
->parent
;
689 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
692 if (aicon
->editing
|| WCHECK_STATE(WSTATE_MODAL
))
695 if (IsDoubleClick(scr
, event
)) {
696 /* Middle or right mouse actions were handled on first click */
697 if (event
->xbutton
.button
== Button1
)
698 iconDblClick(desc
, event
);
702 if (event
->xbutton
.button
== Button2
) {
703 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
706 relaunchApplication(wapp
);
711 if (event
->xbutton
.button
== Button3
) {
712 WObjDescriptor
*desc
;
713 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
718 if (event
->xbutton
.send_event
&&
719 XGrabPointer(dpy
, aicon
->icon
->core
->window
, True
, ButtonMotionMask
720 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
721 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
722 wwarning("pointer grab failed for appicon menu");
726 openApplicationMenu(wapp
, event
->xbutton
.x_root
, event
->xbutton
.y_root
);
728 /* allow drag select of menu */
729 desc
= &scr
->icon_menu
->menu
->descriptor
;
730 event
->xbutton
.send_event
= True
;
731 (*desc
->handle_mousedown
) (desc
, event
);
735 hasMoved
= wHandleAppIconMove(aicon
, event
);
736 if (wPreferences
.single_click
&& !hasMoved
&& aicon
->dock
!= NULL
)
738 iconDblClick(desc
, event
);
742 Bool
wHandleAppIconMove(WAppIcon
*aicon
, XEvent
*event
)
744 WIcon
*icon
= aicon
->icon
;
745 WScreen
*scr
= icon
->core
->screen_ptr
;
746 WDock
*originalDock
= aicon
->dock
; /* can be NULL */
747 WDock
*lastDock
= originalDock
;
748 WDock
*allDocks
[scr
->drawer_count
+ 2]; /* clip, dock and drawers (order determined at runtime) */
750 Bool dockable
, ondock
;
751 Bool grabbed
= False
;
752 Bool collapsed
= False
; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
753 int superfluous
= wPreferences
.superfluous
; /* we cache it to avoid problems */
754 int omnipresent
= aicon
->omnipresent
; /* this must be cached */
755 Bool showed_all_clips
= False
;
757 int clickButton
= event
->xbutton
.button
;
759 Window wins
[2]; /* Managing shadow window */
762 int x
= aicon
->x_pos
, y
= aicon
->y_pos
;
763 int ofs_x
= event
->xbutton
.x
, ofs_y
= event
->xbutton
.y
;
764 int shad_x
= x
, shad_y
= y
;
765 int ix
= aicon
->xindex
, iy
= aicon
->yindex
;
769 Bool hasMoved
= False
;
771 if (wPreferences
.flags
.noupdates
&& originalDock
!= NULL
)
774 if (!(event
->xbutton
.state
& MOD_MASK
))
775 wRaiseFrame(icon
->core
);
777 /* If Mod is pressed for an docked appicon, assume it is to undock it,
778 * so don't lower it */
779 if (originalDock
== NULL
)
780 wLowerFrame(icon
->core
);
783 if (XGrabPointer(dpy
, icon
->core
->window
, True
, ButtonMotionMask
784 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
785 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
786 wwarning("Pointer grab failed in wHandleAppIconMove");
789 if (originalDock
!= NULL
) {
795 if (wPreferences
.flags
.nodock
&& wPreferences
.flags
.noclip
&& wPreferences
.flags
.nodrawer
)
798 dockable
= canBeDocked(icon
->owner
);
801 /* We try the various docks in that order:
802 * - First, the dock the appicon comes from, if any
803 * - Then, the drawers
804 * - Then, the "dock" (WM_DOCK)
805 * - Finally, the clip
808 if (originalDock
!= NULL
)
809 allDocks
[ i
++ ] = originalDock
;
810 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
811 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
812 if (dc
->adrawer
!= originalDock
)
813 allDocks
[ i
++ ] = dc
->adrawer
;
815 if (!wPreferences
.flags
.nodock
&& scr
->dock
!= originalDock
)
816 allDocks
[ i
++ ] = scr
->dock
;
818 if (!wPreferences
.flags
.noclip
&&
819 originalDock
!= w_global
.workspace
.array
[w_global
.workspace
.current
]->clip
)
820 allDocks
[i
++] = w_global
.workspace
.array
[w_global
.workspace
.current
]->clip
;
822 for ( ; i
< scr
->drawer_count
+ 2; i
++) /* In case the clip, the dock, or both, are disabled */
823 allDocks
[ i
] = NULL
;
825 wins
[0] = icon
->core
->window
;
826 wins
[1] = scr
->dock_shadow
;
827 XRestackWindows(dpy
, wins
, 2);
828 XMoveResizeWindow(dpy
, scr
->dock_shadow
, aicon
->x_pos
, aicon
->y_pos
, ICON_SIZE
, ICON_SIZE
);
830 if (icon
->pixmap
!= None
)
831 ghost
= MakeGhostIcon(scr
, icon
->pixmap
);
833 ghost
= MakeGhostIcon(scr
, icon
->core
->window
);
834 XSetWindowBackgroundPixmap(dpy
, scr
->dock_shadow
, ghost
);
835 XClearWindow(dpy
, scr
->dock_shadow
);
838 XMapWindow(dpy
, scr
->dock_shadow
);
841 WMMaskEvent(dpy
, PointerMotionMask
| ButtonReleaseMask
| ButtonPressMask
842 | ButtonMotionMask
| ExposureMask
| EnterWindowMask
, &ev
);
849 /* It means the cursor moved so fast that it entered
850 * something else (if moving slowly, it would have
851 * stayed in the appIcon that is being moved. Ignore
852 * such "spurious" EnterNotifiy's */
858 if (abs(ofs_x
- ev
.xmotion
.x
) >= MOVE_THRESHOLD
859 || abs(ofs_y
- ev
.xmotion
.y
) >= MOVE_THRESHOLD
) {
860 XChangeActivePointerGrab(dpy
, ButtonMotionMask
861 | ButtonReleaseMask
| ButtonPressMask
,
862 wPreferences
.cursor
[WCUR_MOVE
], CurrentTime
);
869 if (omnipresent
&& !showed_all_clips
) {
871 for (i
= 0; i
< w_global
.workspace
.count
; i
++) {
872 if (i
== w_global
.workspace
.current
)
875 wDockShowIcons(w_global
.workspace
.array
[i
]->clip
);
876 /* Note: if dock is collapsed (for instance, because it
877 auto-collapses), its icons still won't show up */
879 showed_all_clips
= True
; /* To prevent flickering */
882 x
= ev
.xmotion
.x_root
- ofs_x
;
883 y
= ev
.xmotion
.y_root
- ofs_y
;
884 wAppIconMove(aicon
, x
, y
);
886 WDock
*theNewDock
= NULL
;
887 if (!(ev
.xmotion
.state
& MOD_MASK
) || aicon
->launching
|| aicon
->lock
|| originalDock
== NULL
) {
888 for (i
= 0; dockable
&& i
< scr
->drawer_count
+ 2; i
++) {
889 WDock
*theDock
= allDocks
[i
];
892 if (wDockSnapIcon(theDock
, aicon
, x
, y
, &ix
, &iy
, (theDock
== originalDock
))) {
893 theNewDock
= theDock
;
897 if (originalDock
!= NULL
&& theNewDock
== NULL
&&
898 (aicon
->launching
|| aicon
->lock
|| aicon
->running
)) {
899 /* In those cases, stay in lastDock if no dock really wants us */
900 theNewDock
= lastDock
;
903 if (lastDock
!= NULL
&& lastDock
!= theNewDock
) {
904 /* Leave lastDock in the state we found it */
905 if (lastDock
->type
== WM_DRAWER
) {
906 wDrawerFillTheGap(lastDock
, aicon
, (lastDock
== originalDock
));
909 lastDock
->collapsed
= 1;
910 wDockHideIcons(lastDock
);
913 if (lastDock
->auto_raise_lower
) {
914 wDockLower(lastDock
);
917 if (theNewDock
!= NULL
) {
918 if (lastDock
!= theNewDock
) {
919 collapsed
= theNewDock
->collapsed
;
921 theNewDock
->collapsed
= 0;
922 wDockShowIcons(theNewDock
);
924 if (theNewDock
->auto_raise_lower
) {
925 wDockRaise(theNewDock
);
926 /* And raise the moving tile above it */
927 wRaiseFrame(aicon
->icon
->core
);
929 lastDock
= theNewDock
;
932 shad_x
= lastDock
->x_pos
+ ix
*wPreferences
.icon_size
;
933 shad_y
= lastDock
->y_pos
+ iy
*wPreferences
.icon_size
;
935 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
938 XMapWindow(dpy
, scr
->dock_shadow
);
942 lastDock
= theNewDock
; // i.e., NULL
944 XUnmapWindow(dpy
, scr
->dock_shadow
);
946 * Leaving that weird comment for now.
947 * But if we see no gap, there is no need to fill one!
948 * We could test ondock first and the lastDock to NULL afterwards
949 if (lastDock_before_it_was_null->type == WM_DRAWER) {
950 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
962 if (ev
.xbutton
.button
!= clickButton
)
964 XUngrabPointer(dpy
, CurrentTime
);
968 SlideWindow(icon
->core
->window
, x
, y
, shad_x
, shad_y
);
969 XUnmapWindow(dpy
, scr
->dock_shadow
);
970 if (originalDock
== NULL
) { // docking an undocked appicon
971 docked
= wDockAttachIcon(lastDock
, aicon
, ix
, iy
, False
);
973 /* AppIcon got rejected (happens only when we can't get the
974 command for that appicon, and the user cancels the
975 wInputDialog asking for one). Make the rejection obvious by
976 sliding the icon to its old position */
977 if (lastDock
->type
== WM_DRAWER
) {
978 // Also fill the gap left in the drawer
979 wDrawerFillTheGap(lastDock
, aicon
, False
);
981 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
984 else { // moving a docked appicon to a dock
985 if (originalDock
== lastDock
) {
987 wDockReattachIcon(originalDock
, aicon
, ix
, iy
);
990 docked
= wDockMoveIconBetweenDocks(originalDock
, lastDock
, aicon
, ix
, iy
);
992 /* Possible scenario: user moved an auto-attracted appicon
993 from the clip to the dock, and cancelled the wInputDialog
994 asking for a command */
995 if (lastDock
->type
== WM_DRAWER
) {
996 wDrawerFillTheGap(lastDock
, aicon
, False
);
998 /* If aicon comes from a drawer, make some room to reattach it */
999 if (originalDock
->type
== WM_DRAWER
) {
1000 WAppIcon
*aiconsToShift
[ originalDock
->icon_count
];
1003 for (i
= 0; i
< originalDock
->max_icons
; i
++) {
1004 WAppIcon
*ai
= originalDock
->icon_array
[ i
];
1005 if (ai
&& ai
!= aicon
&&
1006 abs(ai
->xindex
) >= abs(aicon
->xindex
))
1007 aiconsToShift
[j
++] = ai
;
1009 if (j
!= originalDock
->icon_count
- abs(aicon
->xindex
) - 1)
1010 // Trust this never happens?
1011 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1012 j
, originalDock
->icon_count
- abs(aicon
->xindex
) - 1, aicon
->xindex
);
1013 wSlideAppicons(aiconsToShift
, j
, originalDock
->on_right_side
);
1014 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1017 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
1018 wDockReattachIcon(originalDock
, aicon
, aicon
->xindex
, aicon
->yindex
);
1021 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1022 originalDock
->collapsed
= 1;
1023 wDockHideIcons(originalDock
);
1025 if (originalDock
->auto_raise_lower
)
1026 wDockLower(originalDock
);
1030 // No matter what happened above, check to lower lastDock
1031 // Don't see why I commented out the following 2 lines
1032 /* if (lastDock->auto_raise_lower)
1033 wDockLower(lastDock); */
1034 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1035 * collapsed, so that wHandleAppIconMove doesn't collapse it
1036 * right away (the timer will take care of it) */
1037 if (lastDock
->auto_collapse
)
1041 if (originalDock
!= NULL
) { /* Detaching a docked appicon */
1043 if (!aicon
->running
&& !wPreferences
.no_animations
) {
1044 /* We need to deselect it, even if is deselected in
1045 * wDockDetach(), because else DoKaboom() will fail.
1047 if (aicon
->icon
->selected
)
1048 wIconSelect(aicon
->icon
);
1049 DoKaboom(scr
, aicon
->icon
->core
->window
, x
, y
);
1052 wDockDetach(originalDock
, aicon
);
1053 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1054 originalDock
->collapsed
= 1;
1055 wDockHideIcons(originalDock
);
1057 if (originalDock
->auto_raise_lower
)
1058 wDockLower(originalDock
);
1061 // Can't remember why the icon hiding is better done above than below (commented out)
1062 // Also, lastDock is quite different from originalDock
1065 lastDock->collapsed = 1;
1066 wDockHideIcons(lastDock);
1072 XFreePixmap(dpy
, ghost
);
1073 XSetWindowBackground(dpy
, scr
->dock_shadow
, scr
->white_pixel
);
1075 if (showed_all_clips
) {
1077 for (i
= 0; i
< w_global
.workspace
.count
; i
++) {
1078 if (i
== w_global
.workspace
.current
)
1081 wDockHideIcons(w_global
.workspace
.array
[i
]->clip
);
1084 if (wPreferences
.auto_arrange_icons
&& !(originalDock
!= NULL
&& docked
))
1085 /* Need to rearrange unless moving from dock to dock */
1086 wArrangeIcons(scr
, True
);
1092 /* This function save the application icon and store the path in the Dictionary */
1093 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
)
1095 WMPropList
*dict
= w_global
.domain
.window_attr
->dictionary
;
1096 WMPropList
*adict
, *key
, *iconk
;
1100 tmp
= get_name_for_instance_class(wm_instance
, wm_class
);
1101 key
= WMCreatePLString(tmp
);
1104 adict
= WMGetFromPLDictionary(dict
, key
);
1105 iconk
= WMCreatePLString("Icon");
1108 val
= WMGetFromPLDictionary(adict
, iconk
);
1110 /* no dictionary for app, so create one */
1111 adict
= WMCreatePLDictionary(NULL
, NULL
);
1112 WMPutInPLDictionary(dict
, key
, adict
);
1113 WMReleasePropList(adict
);
1118 val
= WMCreatePLString(iconPath
);
1119 WMPutInPLDictionary(adict
, iconk
, val
);
1120 WMReleasePropList(val
);
1125 WMReleasePropList(key
);
1126 WMReleasePropList(iconk
);
1128 if (val
&& !wPreferences
.flags
.noupdates
)
1129 UpdateDomainFile(w_global
.domain
.window_attr
);
1132 static WAppIcon
*findDockIconFor(WDock
*dock
, Window main_window
)
1134 WAppIcon
*aicon
= NULL
;
1136 aicon
= wDockFindIconForWindow(dock
, main_window
);
1138 wDockTrackWindowLaunch(dock
, main_window
);
1139 aicon
= wDockFindIconForWindow(dock
, main_window
);
1144 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
)
1146 WScreen
*scr
= wwin
->screen_ptr
;
1147 wapp
->app_icon
= NULL
;
1150 wapp
->app_icon
= findDockIconFor(scr
->last_dock
, main_window
);
1152 /* check main dock if we did not find it in last dock */
1153 if (!wapp
->app_icon
&& scr
->dock
)
1154 wapp
->app_icon
= findDockIconFor(scr
->dock
, main_window
);
1157 if (!wapp
->app_icon
) {
1159 for (i
= 0; i
< w_global
.workspace
.count
; i
++) {
1160 WDock
*dock
= w_global
.workspace
.array
[i
]->clip
;
1163 wapp
->app_icon
= findDockIconFor(dock
, main_window
);
1170 /* Finally check drawers */
1171 if (!wapp
->app_icon
) {
1173 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
1174 wapp
->app_icon
= findDockIconFor(dc
->adrawer
, main_window
);
1180 /* If created, then set some flags */
1181 if (wapp
->app_icon
) {
1182 WWindow
*mainw
= wapp
->main_window_desc
;
1184 wapp
->app_icon
->running
= 1;
1185 wapp
->app_icon
->icon
->owner
= mainw
;
1186 if (mainw
->wm_hints
&& (mainw
->wm_hints
->flags
& IconWindowHint
))
1187 wapp
->app_icon
->icon
->icon_win
= mainw
->wm_hints
->icon_window
;
1189 /* Update the icon images */
1190 wIconUpdate(wapp
->app_icon
->icon
);
1193 wAppIconPaint(wapp
->app_icon
);
1194 save_appicon(wapp
->app_icon
, True
);
1198 /* Add the appicon to the appiconlist */
1199 static void add_to_appicon_list(WAppIcon
*appicon
)
1201 appicon
->prev
= NULL
;
1202 appicon
->next
= w_global
.app_icon_list
;
1203 if (w_global
.app_icon_list
)
1204 w_global
.app_icon_list
->prev
= appicon
;
1206 w_global
.app_icon_list
= appicon
;
1209 /* Remove the appicon from the appiconlist */
1210 static void remove_from_appicon_list(WAppIcon
*appicon
)
1212 if (appicon
== w_global
.app_icon_list
) {
1214 appicon
->next
->prev
= NULL
;
1215 w_global
.app_icon_list
= appicon
->next
;
1218 appicon
->next
->prev
= appicon
->prev
;
1220 appicon
->prev
->next
= appicon
->next
;
1223 appicon
->prev
= NULL
;
1224 appicon
->next
= NULL
;
1227 /* Return the AppIcon associated with a given (Xlib) Window. */
1228 WAppIcon
*wAppIconFor(Window window
)
1230 WObjDescriptor
*desc
;
1235 if (XFindContext(dpy
, window
, w_global
.context
.client_win
, (XPointer
*) & desc
) == XCNOENT
)
1238 if (desc
->parent_type
== WCLASS_APPICON
|| desc
->parent_type
== WCLASS_DOCK_ICON
)
1239 return desc
->parent
;