1 /* appicon.c- icon for applications (not mini-window)
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <X11/Xutil.h>
34 #include "WindowMaker.h"
37 #include "application.h"
44 #include "workspace.h"
45 #include "superfluous.h"
51 #include "placement.h"
59 * icon_file for the dock is got from the preferences file by
60 * using the classname/instancename
63 #define MOD_MASK wPreferences.modifier_mask
64 #define ICON_SIZE wPreferences.icon_size
66 static void iconDblClick(WObjDescriptor
* desc
, XEvent
* event
);
67 static void iconExpose(WObjDescriptor
* desc
, XEvent
* event
);
68 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
);
69 static WAppIcon
*wAppIconCreate(WWindow
* leader_win
);
70 static void add_to_appicon_list(WScreen
*scr
, WAppIcon
*appicon
);
71 static void remove_from_appicon_list(WScreen
*scr
, WAppIcon
*appicon
);
72 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
);
74 /* This function is used if the application is a .app. It checks if it has an icon in it
75 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
77 void wApplicationExtractDirPackIcon(const char *path
, const char *wm_instance
, const char *wm_class
)
79 char *iconPath
= NULL
;
82 if (strstr(path
, ".app")) {
83 tmp
= wmalloc(strlen(path
) + 16);
85 if (wPreferences
.supports_tiff
) {
88 if (access(tmp
, R_OK
) == 0)
95 if (access(tmp
, R_OK
) == 0)
103 wApplicationSaveIconPathFor(iconPath
, wm_instance
, wm_class
);
109 WAppIcon
*wAppIconCreateForDock(WScreen
*scr
, const char *command
, const char *wm_instance
, const char *wm_class
, int tile
)
113 aicon
= wmalloc(sizeof(WAppIcon
));
118 add_to_appicon_list(scr
, aicon
);
121 aicon
->command
= wstrdup(command
);
124 aicon
->wm_class
= wstrdup(wm_class
);
127 aicon
->wm_instance
= wstrdup(wm_instance
);
129 if (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
= scr
->workspaces
[scr
->current_workspace
]->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(scr
, 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 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
);
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 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
331 RemoveFromStackList(aicon
->icon
->core
);
332 wIconDestroy(aicon
->icon
);
334 wfree(aicon
->command
);
336 if (aicon
->dnd_command
)
337 wfree(aicon
->dnd_command
);
339 if (aicon
->wm_instance
)
340 wfree(aicon
->wm_instance
);
343 wfree(aicon
->wm_class
);
345 remove_from_appicon_list(scr
, aicon
);
347 aicon
->destroyed
= 1;
351 static void drawCorner(WIcon
* icon
)
353 WScreen
*scr
= icon
->core
->screen_ptr
;
362 XFillPolygon(dpy
, icon
->core
->window
, scr
->icon_title_texture
->normal_gc
,
363 points
, 3, Convex
, CoordModeOrigin
);
364 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 0, 12);
365 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 12, 0);
368 void wAppIconMove(WAppIcon
* aicon
, int x
, int y
)
370 XMoveWindow(dpy
, aicon
->icon
->core
->window
, x
, y
);
376 static void updateDockNumbers(WScreen
*scr
)
380 WAppIcon
*dicon
= scr
->dock
->icon_array
[0];
382 ws_numbers
= wmalloc(20);
383 snprintf(ws_numbers
, 20, "%i [ %i ]", scr
->current_workspace
+ 1, ((scr
->current_workspace
/ 10) + 1));
384 length
= strlen(ws_numbers
);
386 XClearArea(dpy
, dicon
->icon
->core
->window
, 2, 2, 50, WMFontHeight(scr
->icon_title_font
) + 1, False
);
388 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->black
,
389 scr
->icon_title_font
, 4, 3, ws_numbers
, length
);
391 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->white
,
392 scr
->icon_title_font
, 3, 2, ws_numbers
, length
);
396 #endif /* WS_INDICATOR */
398 void wAppIconPaint(WAppIcon
*aicon
)
401 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
403 if (aicon
->icon
->owner
)
404 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
408 wIconPaint(aicon
->icon
);
411 if (aicon
->docked
&& scr
->dock
&& scr
->dock
== aicon
->dock
&& aicon
->yindex
== 0)
412 updateDockNumbers(scr
);
414 if (scr
->dock_dots
&& aicon
->docked
&& !aicon
->running
&& aicon
->command
!= NULL
) {
415 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
416 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
417 XCopyArea(dpy
, scr
->dock_dots
->image
, aicon
->icon
->core
->window
,
418 scr
->copy_gc
, 0, 0, scr
->dock_dots
->width
, scr
->dock_dots
->height
, 0, 0);
421 if (wapp
&& wapp
->flags
.hidden
) {
422 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
423 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
424 XCopyArea(dpy
, scr
->dock_dots
->image
,
425 aicon
->icon
->core
->window
, scr
->copy_gc
, 0, 0, 7, scr
->dock_dots
->height
, 0, 0);
427 #endif /* HIDDENDOT */
429 if (aicon
->omnipresent
)
430 drawCorner(aicon
->icon
);
432 XSetClipMask(dpy
, scr
->copy_gc
, None
);
433 if (aicon
->launching
)
434 XFillRectangle(dpy
, aicon
->icon
->core
->window
, scr
->stipple_gc
,
435 0, 0, wPreferences
.icon_size
, wPreferences
.icon_size
);
438 /* Save the application icon, if it's a dockapp then use it with dock = True */
439 void save_appicon(WAppIcon
*aicon
, Bool dock
)
446 if (dock
&& (!aicon
->docked
|| aicon
->attracted
))
449 path
= wIconStore(aicon
->icon
);
453 wApplicationSaveIconPathFor(path
, aicon
->wm_instance
, aicon
->wm_class
);
457 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
459 /* main_window may not have the full command line; try to find one which does */
460 static void relaunchApplication(WApplication
*wapp
)
463 WWindow
*wlist
, *next
;
465 scr
= wapp
->main_window_desc
->screen_ptr
;
466 wlist
= scr
->focused_window
;
476 if (wlist
->main_window
== wapp
->main_window
) {
477 if (RelaunchWindow(wlist
))
485 static void relaunchCallback(WMenu
* menu
, WMenuEntry
* entry
)
487 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
489 /* Parameter not used, but tell the compiler that it is ok */
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 /* Parameter not used, but tell the compiler that it is ok */
514 wUnhideApplication(wapp
, False
, True
);
517 static void setIconCallback(WMenu
*menu
, WMenuEntry
*entry
)
519 WAppIcon
*icon
= ((WApplication
*) entry
->clientdata
)->app_icon
;
524 /* Parameter not used, but tell the compiler that it is ok */
527 assert(icon
!= NULL
);
533 scr
= icon
->icon
->core
->screen_ptr
;
537 result
= wIconChooserDialog(scr
, &file
, icon
->wm_instance
, icon
->wm_class
);
540 if (!icon
->destroyed
) {
541 if (!wIconChangeImageFile(icon
->icon
, file
)) {
542 wMessageDialog(scr
, _("Error"),
543 _("Could not open specified icon file"),
544 _("OK"), NULL
, NULL
);
546 wDefaultChangeIcon(icon
->wm_instance
, icon
->wm_class
, file
);
557 static void killCallback(WMenu
* menu
, WMenuEntry
* entry
)
559 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
560 WFakeGroupLeader
*fPtr
;
564 if (!WCHECK_STATE(WSTATE_NORMAL
))
567 WCHANGE_STATE(WSTATE_MODAL
);
569 assert(entry
->clientdata
!= NULL
);
571 shortname
= basename(wapp
->app_icon
->wm_instance
);
573 buffer
= wstrconcat(wapp
->app_icon
? shortname
: NULL
,
574 _(" will be forcibly closed.\n"
575 "Any unsaved changes will be lost.\n" "Please confirm."));
577 fPtr
= wapp
->main_window_desc
->fake_group
;
579 wretain(wapp
->main_window_desc
);
580 if (wPreferences
.dont_confirm_kill
581 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
582 buffer
, _("Yes"), _("No"), NULL
) == WAPRDefault
) {
584 WWindow
*wwin
, *twin
;
586 wwin
= wapp
->main_window_desc
->screen_ptr
->focused_window
;
589 if (wwin
->fake_group
== fPtr
)
593 } else if (!wapp
->main_window_desc
->flags
.destroyed
) {
594 wClientKill(wapp
->main_window_desc
);
597 wrelease(wapp
->main_window_desc
);
599 WCHANGE_STATE(WSTATE_NORMAL
);
602 static WMenu
*createApplicationMenu(WScreen
*scr
)
606 menu
= wMenuCreate(scr
, NULL
, False
);
607 wMenuAddCallback(menu
, _("Unhide Here"), unhideHereCallback
, NULL
);
608 wMenuAddCallback(menu
, _("Hide"), hideCallback
, NULL
);
609 wMenuAddCallback(menu
, _("Launch"), relaunchCallback
, NULL
);
610 wMenuAddCallback(menu
, _("Set Icon..."), setIconCallback
, NULL
);
611 wMenuAddCallback(menu
, _("Kill"), killCallback
, NULL
);
616 static void openApplicationMenu(WApplication
* wapp
, int x
, int y
)
619 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
622 if (!scr
->icon_menu
) {
623 scr
->icon_menu
= createApplicationMenu(scr
);
624 wfree(scr
->icon_menu
->entries
[1]->text
);
627 menu
= scr
->icon_menu
;
629 if (wapp
->flags
.hidden
)
630 menu
->entries
[1]->text
= _("Unhide");
632 menu
->entries
[1]->text
= _("Hide");
634 menu
->flags
.realized
= 0;
637 x
-= menu
->frame
->core
->width
/ 2;
638 if (x
+ menu
->frame
->core
->width
> scr
->scr_width
)
639 x
= scr
->scr_width
- menu
->frame
->core
->width
;
644 /* set client data */
645 for (i
= 0; i
< menu
->entry_no
; i
++)
646 menu
->entries
[i
]->clientdata
= wapp
;
648 wMenuMapAt(menu
, x
, y
, False
);
651 /******************************************************************/
653 static void iconExpose(WObjDescriptor
*desc
, XEvent
*event
)
655 /* Parameter not used, but tell the compiler that it is ok */
658 wAppIconPaint(desc
->parent
);
661 static void iconDblClick(WObjDescriptor
*desc
, XEvent
*event
)
663 WAppIcon
*aicon
= desc
->parent
;
665 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
668 assert(aicon
->icon
->owner
!= NULL
);
670 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
672 if (event
->xbutton
.state
& ControlMask
) {
673 relaunchApplication(wapp
);
677 unhideHere
= (event
->xbutton
.state
& ShiftMask
);
678 /* go to the last workspace that the user worked on the app */
679 if (!unhideHere
&& wapp
->last_workspace
!= scr
->current_workspace
)
680 wWorkspaceChange(scr
, wapp
->last_workspace
);
682 wUnhideApplication(wapp
, event
->xbutton
.button
== Button2
, unhideHere
);
684 if (event
->xbutton
.state
& MOD_MASK
)
685 wHideOtherApplications(aicon
->icon
->owner
);
688 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
)
690 WAppIcon
*aicon
= desc
->parent
;
691 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
694 if (aicon
->editing
|| WCHECK_STATE(WSTATE_MODAL
))
697 if (IsDoubleClick(scr
, event
)) {
698 /* Middle or right mouse actions were handled on first click */
699 if (event
->xbutton
.button
== Button1
)
700 iconDblClick(desc
, event
);
704 if (event
->xbutton
.button
== Button2
) {
705 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
708 relaunchApplication(wapp
);
713 if (event
->xbutton
.button
== Button3
) {
714 WObjDescriptor
*desc
;
715 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
720 if (event
->xbutton
.send_event
&&
721 XGrabPointer(dpy
, aicon
->icon
->core
->window
, True
, ButtonMotionMask
722 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
723 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
724 wwarning("pointer grab failed for appicon menu");
728 openApplicationMenu(wapp
, event
->xbutton
.x_root
, event
->xbutton
.y_root
);
730 /* allow drag select of menu */
731 desc
= &scr
->icon_menu
->menu
->descriptor
;
732 event
->xbutton
.send_event
= True
;
733 (*desc
->handle_mousedown
) (desc
, event
);
737 hasMoved
= wHandleAppIconMove(aicon
, event
);
738 if (wPreferences
.single_click
&& !hasMoved
&& aicon
->dock
!= NULL
)
740 iconDblClick(desc
, event
);
744 Bool
wHandleAppIconMove(WAppIcon
*aicon
, XEvent
*event
)
746 WIcon
*icon
= aicon
->icon
;
747 WScreen
*scr
= icon
->core
->screen_ptr
;
748 WDock
*originalDock
= aicon
->dock
; /* can be NULL */
749 WDock
*lastDock
= originalDock
;
750 WDock
*allDocks
[scr
->drawer_count
+ 2]; /* clip, dock and drawers (order determined at runtime) */
752 Bool dockable
, ondock
;
753 Bool grabbed
= False
;
754 Bool collapsed
= False
; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
755 int superfluous
= wPreferences
.superfluous
; /* we cache it to avoid problems */
756 int omnipresent
= aicon
->omnipresent
; /* this must be cached */
757 Bool showed_all_clips
= False
;
759 int clickButton
= event
->xbutton
.button
;
761 Window wins
[2]; /* Managing shadow window */
764 int x
= aicon
->x_pos
, y
= aicon
->y_pos
;
765 int ofs_x
= event
->xbutton
.x
, ofs_y
= event
->xbutton
.y
;
766 int shad_x
= x
, shad_y
= y
;
767 int ix
= aicon
->xindex
, iy
= aicon
->yindex
;
771 Bool hasMoved
= False
;
773 if (wPreferences
.flags
.noupdates
&& originalDock
!= NULL
)
776 if (!(event
->xbutton
.state
& MOD_MASK
))
777 wRaiseFrame(icon
->core
);
779 /* If Mod is pressed for an docked appicon, assume it is to undock it,
780 * so don't lower it */
781 if (originalDock
== NULL
)
782 wLowerFrame(icon
->core
);
785 if (XGrabPointer(dpy
, icon
->core
->window
, True
, ButtonMotionMask
786 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
787 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
788 wwarning("Pointer grab failed in wHandleAppIconMove");
791 if (originalDock
!= NULL
) {
797 if (wPreferences
.flags
.nodock
&& wPreferences
.flags
.noclip
&& wPreferences
.flags
.nodrawer
)
800 dockable
= canBeDocked(icon
->owner
);
803 /* We try the various docks in that order:
804 * - First, the dock the appicon comes from, if any
805 * - Then, the drawers
806 * - Then, the "dock" (WM_DOCK)
807 * - Finally, the clip
810 if (originalDock
!= NULL
)
811 allDocks
[ i
++ ] = originalDock
;
812 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
813 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
814 if (dc
->adrawer
!= originalDock
)
815 allDocks
[ i
++ ] = dc
->adrawer
;
817 if (!wPreferences
.flags
.nodock
&& scr
->dock
!= originalDock
)
818 allDocks
[ i
++ ] = scr
->dock
;
820 if (!wPreferences
.flags
.noclip
&&
821 originalDock
!= scr
->workspaces
[scr
->current_workspace
]->clip
)
822 allDocks
[ i
++ ] = scr
->workspaces
[scr
->current_workspace
]->clip
;
824 for ( ; i
< scr
->drawer_count
+ 2; i
++) /* In case the clip, the dock, or both, are disabled */
825 allDocks
[ i
] = NULL
;
827 wins
[0] = icon
->core
->window
;
828 wins
[1] = scr
->dock_shadow
;
829 XRestackWindows(dpy
, wins
, 2);
830 XMoveResizeWindow(dpy
, scr
->dock_shadow
, aicon
->x_pos
, aicon
->y_pos
, ICON_SIZE
, ICON_SIZE
);
832 if (icon
->pixmap
!= None
)
833 ghost
= MakeGhostIcon(scr
, icon
->pixmap
);
835 ghost
= MakeGhostIcon(scr
, icon
->core
->window
);
836 XSetWindowBackgroundPixmap(dpy
, scr
->dock_shadow
, ghost
);
837 XClearWindow(dpy
, scr
->dock_shadow
);
840 XMapWindow(dpy
, scr
->dock_shadow
);
843 WMMaskEvent(dpy
, PointerMotionMask
| ButtonReleaseMask
| ButtonPressMask
844 | ButtonMotionMask
| ExposureMask
| EnterWindowMask
, &ev
);
851 /* It means the cursor moved so fast that it entered
852 * something else (if moving slowly, it would have
853 * stayed in the appIcon that is being moved. Ignore
854 * such "spurious" EnterNotifiy's */
860 if (abs(ofs_x
- ev
.xmotion
.x
) >= MOVE_THRESHOLD
861 || abs(ofs_y
- ev
.xmotion
.y
) >= MOVE_THRESHOLD
) {
862 XChangeActivePointerGrab(dpy
, ButtonMotionMask
863 | ButtonReleaseMask
| ButtonPressMask
,
864 wPreferences
.cursor
[WCUR_MOVE
], CurrentTime
);
871 if (omnipresent
&& !showed_all_clips
) {
873 for (i
= 0; i
< scr
->workspace_count
; i
++) {
874 if (i
== scr
->current_workspace
)
877 wDockShowIcons(scr
->workspaces
[i
]->clip
);
878 /* Note: if dock is collapsed (for instance, because it
879 auto-collapses), its icons still won't show up */
881 showed_all_clips
= True
; /* To prevent flickering */
884 x
= ev
.xmotion
.x_root
- ofs_x
;
885 y
= ev
.xmotion
.y_root
- ofs_y
;
886 wAppIconMove(aicon
, x
, y
);
888 WDock
*theNewDock
= NULL
;
889 if (!(ev
.xmotion
.state
& MOD_MASK
) || aicon
->launching
|| aicon
->lock
|| originalDock
== NULL
) {
890 for (i
= 0; dockable
&& i
< scr
->drawer_count
+ 2; i
++) {
891 WDock
*theDock
= allDocks
[i
];
894 if (wDockSnapIcon(theDock
, aicon
, x
, y
, &ix
, &iy
, (theDock
== originalDock
))) {
895 theNewDock
= theDock
;
899 if (originalDock
!= NULL
&& theNewDock
== NULL
&&
900 (aicon
->launching
|| aicon
->lock
|| aicon
->running
)) {
901 /* In those cases, stay in lastDock if no dock really wants us */
902 theNewDock
= lastDock
;
905 if (lastDock
!= NULL
&& lastDock
!= theNewDock
) {
906 /* Leave lastDock in the state we found it */
907 if (lastDock
->type
== WM_DRAWER
) {
908 wDrawerFillTheGap(lastDock
, aicon
, (lastDock
== originalDock
));
911 lastDock
->collapsed
= 1;
912 wDockHideIcons(lastDock
);
915 if (lastDock
->auto_raise_lower
) {
916 wDockLower(lastDock
);
919 if (theNewDock
!= NULL
) {
920 if (lastDock
!= theNewDock
) {
921 collapsed
= theNewDock
->collapsed
;
923 theNewDock
->collapsed
= 0;
924 wDockShowIcons(theNewDock
);
926 if (theNewDock
->auto_raise_lower
) {
927 wDockRaise(theNewDock
);
928 /* And raise the moving tile above it */
929 wRaiseFrame(aicon
->icon
->core
);
931 lastDock
= theNewDock
;
934 shad_x
= lastDock
->x_pos
+ ix
*wPreferences
.icon_size
;
935 shad_y
= lastDock
->y_pos
+ iy
*wPreferences
.icon_size
;
937 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
940 XMapWindow(dpy
, scr
->dock_shadow
);
944 lastDock
= theNewDock
; // i.e., NULL
946 XUnmapWindow(dpy
, scr
->dock_shadow
);
948 * Leaving that weird comment for now.
949 * But if we see no gap, there is no need to fill one!
950 * We could test ondock first and the lastDock to NULL afterwards
951 if (lastDock_before_it_was_null->type == WM_DRAWER) {
952 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
964 if (ev
.xbutton
.button
!= clickButton
)
966 XUngrabPointer(dpy
, CurrentTime
);
970 SlideWindow(icon
->core
->window
, x
, y
, shad_x
, shad_y
);
971 XUnmapWindow(dpy
, scr
->dock_shadow
);
972 if (originalDock
== NULL
) { // docking an undocked appicon
973 docked
= wDockAttachIcon(lastDock
, aicon
, ix
, iy
, False
);
975 /* AppIcon got rejected (happens only when we can't get the
976 command for that appicon, and the user cancels the
977 wInputDialog asking for one). Make the rejection obvious by
978 sliding the icon to its old position */
979 if (lastDock
->type
== WM_DRAWER
) {
980 // Also fill the gap left in the drawer
981 wDrawerFillTheGap(lastDock
, aicon
, False
);
983 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
986 else { // moving a docked appicon to a dock
987 if (originalDock
== lastDock
) {
989 wDockReattachIcon(originalDock
, aicon
, ix
, iy
);
992 docked
= wDockMoveIconBetweenDocks(originalDock
, lastDock
, aicon
, ix
, iy
);
994 /* Possible scenario: user moved an auto-attracted appicon
995 from the clip to the dock, and cancelled the wInputDialog
996 asking for a command */
997 if (lastDock
->type
== WM_DRAWER
) {
998 wDrawerFillTheGap(lastDock
, aicon
, False
);
1000 /* If aicon comes from a drawer, make some room to reattach it */
1001 if (originalDock
->type
== WM_DRAWER
) {
1002 WAppIcon
*aiconsToShift
[ originalDock
->icon_count
];
1005 for (i
= 0; i
< originalDock
->max_icons
; i
++) {
1006 WAppIcon
*ai
= originalDock
->icon_array
[ i
];
1007 if (ai
&& ai
!= aicon
&&
1008 abs(ai
->xindex
) >= abs(aicon
->xindex
))
1009 aiconsToShift
[j
++] = ai
;
1011 if (j
!= originalDock
->icon_count
- abs(aicon
->xindex
) - 1)
1012 // Trust this never happens?
1013 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1014 j
, originalDock
->icon_count
- abs(aicon
->xindex
) - 1, aicon
->xindex
);
1015 wSlideAppicons(aiconsToShift
, j
, originalDock
->on_right_side
);
1016 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1019 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
1020 wDockReattachIcon(originalDock
, aicon
, aicon
->xindex
, aicon
->yindex
);
1023 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1024 originalDock
->collapsed
= 1;
1025 wDockHideIcons(originalDock
);
1027 if (originalDock
->auto_raise_lower
)
1028 wDockLower(originalDock
);
1032 // No matter what happened above, check to lower lastDock
1033 // Don't see why I commented out the following 2 lines
1034 /* if (lastDock->auto_raise_lower)
1035 wDockLower(lastDock); */
1036 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1037 * collapsed, so that wHandleAppIconMove doesn't collapse it
1038 * right away (the timer will take care of it) */
1039 if (lastDock
->auto_collapse
)
1043 if (originalDock
!= NULL
) { /* Detaching a docked appicon */
1045 if (!aicon
->running
&& !wPreferences
.no_animations
) {
1046 /* We need to deselect it, even if is deselected in
1047 * wDockDetach(), because else DoKaboom() will fail.
1049 if (aicon
->icon
->selected
)
1050 wIconSelect(aicon
->icon
);
1051 DoKaboom(scr
, aicon
->icon
->core
->window
, x
, y
);
1054 wDockDetach(originalDock
, aicon
);
1055 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1056 originalDock
->collapsed
= 1;
1057 wDockHideIcons(originalDock
);
1059 if (originalDock
->auto_raise_lower
)
1060 wDockLower(originalDock
);
1063 // Can't remember why the icon hiding is better done above than below (commented out)
1064 // Also, lastDock is quite different from originalDock
1067 lastDock->collapsed = 1;
1068 wDockHideIcons(lastDock);
1074 XFreePixmap(dpy
, ghost
);
1075 XSetWindowBackground(dpy
, scr
->dock_shadow
, scr
->white_pixel
);
1077 if (showed_all_clips
) {
1079 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1080 if (i
== scr
->current_workspace
)
1083 wDockHideIcons(scr
->workspaces
[i
]->clip
);
1086 if (wPreferences
.auto_arrange_icons
&& !(originalDock
!= NULL
&& docked
))
1087 /* Need to rearrange unless moving from dock to dock */
1088 wArrangeIcons(scr
, True
);
1094 /* This function save the application icon and store the path in the Dictionary */
1095 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
)
1097 WMPropList
*dict
= w_global
.domain
.window_attr
->dictionary
;
1098 WMPropList
*adict
, *key
, *iconk
;
1102 tmp
= get_name_for_instance_class(wm_instance
, wm_class
);
1103 key
= WMCreatePLString(tmp
);
1106 adict
= WMGetFromPLDictionary(dict
, key
);
1107 iconk
= WMCreatePLString("Icon");
1110 val
= WMGetFromPLDictionary(adict
, iconk
);
1112 /* no dictionary for app, so create one */
1113 adict
= WMCreatePLDictionary(NULL
, NULL
);
1114 WMPutInPLDictionary(dict
, key
, adict
);
1115 WMReleasePropList(adict
);
1120 val
= WMCreatePLString(iconPath
);
1121 WMPutInPLDictionary(adict
, iconk
, val
);
1122 WMReleasePropList(val
);
1127 WMReleasePropList(key
);
1128 WMReleasePropList(iconk
);
1130 if (val
&& !wPreferences
.flags
.noupdates
)
1131 UpdateDomainFile(w_global
.domain
.window_attr
);
1134 static WAppIcon
*findDockIconFor(WDock
*dock
, Window main_window
)
1136 WAppIcon
*aicon
= NULL
;
1138 aicon
= wDockFindIconForWindow(dock
, main_window
);
1140 wDockTrackWindowLaunch(dock
, main_window
);
1141 aicon
= wDockFindIconForWindow(dock
, main_window
);
1146 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
)
1148 WScreen
*scr
= wwin
->screen_ptr
;
1149 wapp
->app_icon
= NULL
;
1152 wapp
->app_icon
= findDockIconFor(scr
->last_dock
, main_window
);
1154 /* check main dock if we did not find it in last dock */
1155 if (!wapp
->app_icon
&& scr
->dock
)
1156 wapp
->app_icon
= findDockIconFor(scr
->dock
, main_window
);
1159 if (!wapp
->app_icon
) {
1161 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1162 WDock
*dock
= scr
->workspaces
[i
]->clip
;
1165 wapp
->app_icon
= findDockIconFor(dock
, main_window
);
1172 /* Finally check drawers */
1173 if (!wapp
->app_icon
) {
1175 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
1176 wapp
->app_icon
= findDockIconFor(dc
->adrawer
, main_window
);
1182 /* If created, then set some flags */
1183 if (wapp
->app_icon
) {
1184 WWindow
*mainw
= wapp
->main_window_desc
;
1186 wapp
->app_icon
->running
= 1;
1187 wapp
->app_icon
->icon
->owner
= mainw
;
1188 if (mainw
->wm_hints
&& (mainw
->wm_hints
->flags
& IconWindowHint
))
1189 wapp
->app_icon
->icon
->icon_win
= mainw
->wm_hints
->icon_window
;
1191 /* Update the icon images */
1192 wIconUpdate(wapp
->app_icon
->icon
);
1195 wAppIconPaint(wapp
->app_icon
);
1196 save_appicon(wapp
->app_icon
, True
);
1200 /* Add the appicon to the appiconlist */
1201 static void add_to_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1203 appicon
->prev
= NULL
;
1204 appicon
->next
= scr
->app_icon_list
;
1205 if (scr
->app_icon_list
)
1206 scr
->app_icon_list
->prev
= appicon
;
1208 scr
->app_icon_list
= appicon
;
1211 /* Remove the appicon from the appiconlist */
1212 static void remove_from_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1214 if (appicon
== scr
->app_icon_list
) {
1216 appicon
->next
->prev
= NULL
;
1217 scr
->app_icon_list
= appicon
->next
;
1220 appicon
->next
->prev
= appicon
->prev
;
1222 appicon
->prev
->next
= appicon
->next
;
1225 appicon
->prev
= NULL
;
1226 appicon
->next
= NULL
;
1229 /* Return the AppIcon associated with a given (Xlib) Window. */
1230 WAppIcon
*wAppIconFor(Window window
)
1232 WObjDescriptor
*desc
;
1237 if (XFindContext(dpy
, window
, w_global
.context
.client_win
, (XPointer
*) & desc
) == XCNOENT
)
1240 if (desc
->parent_type
== WCLASS_APPICON
|| desc
->parent_type
== WCLASS_DOCK_ICON
)
1241 return desc
->parent
;