1 /* appicon.c- icon for applications (not mini-window)
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <X11/Xutil.h>
34 #include "WindowMaker.h"
37 #include "application.h"
44 #include "workspace.h"
45 #include "superfluous.h"
51 #include "placement.h"
59 * icon_file for the dock is got from the preferences file by
60 * using the classname/instancename
63 #define MOD_MASK wPreferences.modifier_mask
64 #define ICON_SIZE wPreferences.icon_size
66 static void iconDblClick(WObjDescriptor
* desc
, XEvent
* event
);
67 static void iconExpose(WObjDescriptor
* desc
, XEvent
* event
);
68 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
);
69 static WAppIcon
*wAppIconCreate(WWindow
* leader_win
);
70 static void add_to_appicon_list(WScreen
*scr
, WAppIcon
*appicon
);
71 static void remove_from_appicon_list(WScreen
*scr
, WAppIcon
*appicon
);
72 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
);
74 /* This function is used if the application is a .app. It checks if it has an icon in it
75 * like for example /usr/local/GNUstep/Applications/WPrefs.app/WPrefs.tiff
77 void wApplicationExtractDirPackIcon(const char *path
, const char *wm_instance
, const char *wm_class
)
79 char *iconPath
= NULL
;
82 if (strstr(path
, ".app")) {
83 tmp
= wmalloc(strlen(path
) + 16);
85 if (wPreferences
.supports_tiff
) {
88 if (access(tmp
, R_OK
) == 0)
95 if (access(tmp
, R_OK
) == 0)
103 wApplicationSaveIconPathFor(iconPath
, wm_instance
, wm_class
);
109 WAppIcon
*wAppIconCreateForDock(WScreen
*scr
, const char *command
, const char *wm_instance
, const char *wm_class
, int tile
)
113 aicon
= wmalloc(sizeof(WAppIcon
));
118 add_to_appicon_list(scr
, aicon
);
121 aicon
->command
= wstrdup(command
);
124 aicon
->wm_class
= wstrdup(wm_class
);
127 aicon
->wm_instance
= wstrdup(wm_instance
);
129 if (wPreferences
.flags
.clip_merged_in_dock
&& wm_class
!= NULL
&& strcmp(wm_class
, "WMDock") == 0)
131 aicon
->icon
= icon_create_for_dock(scr
, command
, wm_instance
, wm_class
, tile
);
134 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
137 /* will be overriden by dock */
138 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
139 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
140 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
141 aicon
->icon
->core
->descriptor
.parent
= aicon
;
142 AddToStackList(aicon
->icon
->core
);
147 void create_appicon_for_application(WApplication
*wapp
, WWindow
*wwin
)
149 /* Try to create an icon from the dock or clip */
150 create_appicon_from_dock(wwin
, wapp
, wapp
->main_window
);
152 /* If app_icon was not found, create it */
153 if (!wapp
->app_icon
) {
154 /* Create the icon */
155 wapp
->app_icon
= wAppIconCreate(wapp
->main_window_desc
);
156 wIconUpdate(wapp
->app_icon
->icon
);
158 /* Now, paint the icon */
159 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
))
160 paint_app_icon(wapp
);
163 /* At this point the application is fully set up and all icon and
164 * window data are known - so try to save the icon file for docked
165 * applications to ensure that the file exists when window maker
166 * starts up next time. */
167 if (wapp
->app_icon
->docked
&& !WFLAGP(wapp
->main_window_desc
, no_appicon
))
168 save_appicon(wapp
->app_icon
);
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
)
205 WDock
*attracting_dock
;
207 Bool update_icon
= False
;
209 if (!wapp
|| !wapp
->app_icon
|| !wapp
->main_window_desc
)
212 icon
= wapp
->app_icon
->icon
;
213 scr
= wapp
->main_window_desc
->screen_ptr
;
214 wapp
->app_icon
->main_window
= wapp
->main_window
;
216 /* If the icon is docked, don't continue */
217 if (wapp
->app_icon
->docked
)
220 attracting_dock
= scr
->attracting_drawer
!= NULL
?
221 scr
->attracting_drawer
:
222 scr
->workspaces
[scr
->current_workspace
]->clip
;
223 if (attracting_dock
&& attracting_dock
->attract_icons
&&
224 wDockFindFreeSlot(attracting_dock
, &x
, &y
)) {
225 wapp
->app_icon
->attracted
= 1;
226 if (!icon
->shadowed
) {
230 wDockAttachIcon(attracting_dock
, wapp
->app_icon
, x
, y
, update_icon
);
232 /* We must know if the icon is painted in the screen,
233 * because if painted, then PlaceIcon will return the next
234 * space on the screen, and the icon will move */
235 if (wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
) {
236 PlaceIcon(scr
, &x
, &y
, wGetHeadForWindow(wapp
->main_window_desc
));
237 wAppIconMove(wapp
->app_icon
, x
, y
);
238 wLowerFrame(icon
->core
);
242 /* If we want appicon (no_appicon is not set) and the icon is not
243 * in the appicon_list, we must add it. Else, we want to avoid
244 * having it on the list */
245 if (!WFLAGP(wapp
->main_window_desc
, no_appicon
) &&
246 wapp
->app_icon
->next
== NULL
&& wapp
->app_icon
->prev
== NULL
)
247 add_to_appicon_list(scr
, wapp
->app_icon
);
249 if (!attracting_dock
|| !wapp
->app_icon
->attracted
|| !attracting_dock
->collapsed
)
250 XMapWindow(dpy
, icon
->core
->window
);
252 if (wPreferences
.auto_arrange_icons
&& !wapp
->app_icon
->attracted
)
253 wArrangeIcons(scr
, True
);
256 void removeAppIconFor(WApplication
*wapp
)
261 if (wPreferences
.highlight_active_app
)
262 wIconSetHighlited(wapp
->app_icon
->icon
, False
);
263 if (wapp
->app_icon
->docked
&& !wapp
->app_icon
->attracted
) {
264 wapp
->app_icon
->running
= 0;
265 /* since we keep it, we don't care if it was attracted or not */
266 wapp
->app_icon
->attracted
= 0;
267 wapp
->app_icon
->icon
->shadowed
= 0;
268 wapp
->app_icon
->main_window
= None
;
269 wapp
->app_icon
->pid
= 0;
270 wapp
->app_icon
->icon
->owner
= NULL
;
271 wapp
->app_icon
->icon
->icon_win
= None
;
273 /* Set the icon image */
274 set_icon_image_from_database(wapp
->app_icon
->icon
, wapp
->app_icon
->wm_instance
,
275 wapp
->app_icon
->wm_class
, wapp
->app_icon
->command
);
277 /* Update the icon, because wapp->app_icon->icon could be NULL */
278 wIconUpdate(wapp
->app_icon
->icon
);
281 wAppIconPaint(wapp
->app_icon
);
282 } else if (wapp
->app_icon
->docked
) {
283 wapp
->app_icon
->running
= 0;
284 if (wapp
->app_icon
->dock
->type
== WM_DRAWER
) {
285 wDrawerFillTheGap(wapp
->app_icon
->dock
, wapp
->app_icon
, True
);
287 wDockDetach(wapp
->app_icon
->dock
, wapp
->app_icon
);
289 wAppIconDestroy(wapp
->app_icon
);
292 wapp
->app_icon
= NULL
;
294 if (wPreferences
.auto_arrange_icons
)
295 wArrangeIcons(wapp
->main_window_desc
->screen_ptr
, True
);
298 static WAppIcon
*wAppIconCreate(WWindow
*leader_win
)
302 aicon
= wmalloc(sizeof(WAppIcon
));
309 if (leader_win
->wm_class
)
310 aicon
->wm_class
= wstrdup(leader_win
->wm_class
);
312 if (leader_win
->wm_instance
)
313 aicon
->wm_instance
= wstrdup(leader_win
->wm_instance
);
315 aicon
->icon
= icon_create_for_wwindow(leader_win
);
317 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
320 /* will be overriden if docked */
321 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
322 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
323 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
324 aicon
->icon
->core
->descriptor
.parent
= aicon
;
325 AddToStackList(aicon
->icon
->core
);
326 aicon
->icon
->show_title
= 0;
331 void wAppIconDestroy(WAppIcon
* aicon
)
333 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
335 RemoveFromStackList(aicon
->icon
->core
);
336 wIconDestroy(aicon
->icon
);
338 wfree(aicon
->command
);
340 if (aicon
->dnd_command
)
341 wfree(aicon
->dnd_command
);
343 if (aicon
->wm_instance
)
344 wfree(aicon
->wm_instance
);
347 wfree(aicon
->wm_class
);
349 remove_from_appicon_list(scr
, aicon
);
351 aicon
->destroyed
= 1;
355 static void drawCorner(WIcon
* icon
)
357 WScreen
*scr
= icon
->core
->screen_ptr
;
366 XFillPolygon(dpy
, icon
->core
->window
, scr
->icon_title_texture
->normal_gc
,
367 points
, 3, Convex
, CoordModeOrigin
);
368 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 0, 12);
369 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 12, 0);
372 void wAppIconMove(WAppIcon
* aicon
, int x
, int y
)
374 XMoveWindow(dpy
, aicon
->icon
->core
->window
, x
, y
);
380 static void updateDockNumbers(WScreen
*scr
)
384 WAppIcon
*dicon
= scr
->dock
->icon_array
[0];
386 snprintf(ws_numbers
, sizeof(ws_numbers
), "%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
);
397 #endif /* WS_INDICATOR */
399 void wAppIconPaint(WAppIcon
*aicon
)
402 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
404 if (aicon
->icon
->owner
)
405 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
409 wIconPaint(aicon
->icon
);
412 if (aicon
->docked
&& scr
->dock
&& scr
->dock
== aicon
->dock
&& aicon
->yindex
== 0)
413 updateDockNumbers(scr
);
415 if (aicon
->docked
&& !aicon
->running
&& aicon
->command
!= NULL
) {
416 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
417 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
418 XCopyArea(dpy
, scr
->dock_dots
->image
, aicon
->icon
->core
->window
,
419 scr
->copy_gc
, 0, 0, scr
->dock_dots
->width
, scr
->dock_dots
->height
, 0, 0);
422 if (wapp
&& wapp
->flags
.hidden
) {
423 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
424 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
425 XCopyArea(dpy
, scr
->dock_dots
->image
,
426 aicon
->icon
->core
->window
, scr
->copy_gc
, 0, 0, 7, scr
->dock_dots
->height
, 0, 0);
428 #endif /* HIDDENDOT */
430 if (aicon
->omnipresent
)
431 drawCorner(aicon
->icon
);
433 XSetClipMask(dpy
, scr
->copy_gc
, None
);
434 if (aicon
->launching
)
435 XFillRectangle(dpy
, aicon
->icon
->core
->window
, scr
->stipple_gc
,
436 0, 0, wPreferences
.icon_size
, wPreferences
.icon_size
);
439 /* Save the application icon, if it's a dockapp then use it with dock = True */
440 void save_appicon(WAppIcon
*aicon
)
447 if (!aicon
->docked
|| aicon
->attracted
)
450 path
= wIconStore(aicon
->icon
);
454 wApplicationSaveIconPathFor(path
, aicon
->wm_instance
, aicon
->wm_class
);
458 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
460 /* main_window may not have the full command line; try to find one which does */
461 static void relaunchApplication(WApplication
*wapp
)
464 WWindow
*wlist
, *next
;
466 scr
= wapp
->main_window_desc
->screen_ptr
;
467 wlist
= scr
->focused_window
;
477 if (wlist
->main_window
== wapp
->main_window
) {
478 if (RelaunchWindow(wlist
))
486 static void relaunchCallback(WMenu
* menu
, WMenuEntry
* entry
)
488 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
490 /* Parameter not used, but tell the compiler that it is ok */
493 relaunchApplication(wapp
);
496 static void hideCallback(WMenu
* menu
, WMenuEntry
* entry
)
498 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
500 if (wapp
->flags
.hidden
) {
501 wWorkspaceChange(menu
->menu
->screen_ptr
, wapp
->last_workspace
);
502 wUnhideApplication(wapp
, False
, False
);
504 wHideApplication(wapp
);
508 static void unhideHereCallback(WMenu
* menu
, WMenuEntry
* entry
)
510 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
512 /* Parameter not used, but tell the compiler that it is ok */
515 wUnhideApplication(wapp
, False
, True
);
518 static void setIconCallback(WMenu
*menu
, WMenuEntry
*entry
)
520 WAppIcon
*icon
= ((WApplication
*) entry
->clientdata
)->app_icon
;
525 /* Parameter not used, but tell the compiler that it is ok */
528 assert(icon
!= NULL
);
534 scr
= icon
->icon
->core
->screen_ptr
;
538 result
= wIconChooserDialog(scr
, &file
, icon
->wm_instance
, icon
->wm_class
);
541 if (!icon
->destroyed
) {
542 if (!wIconChangeImageFile(icon
->icon
, file
)) {
543 wMessageDialog(scr
, _("Error"),
544 _("Could not open specified icon file"),
545 _("OK"), NULL
, NULL
);
547 wDefaultChangeIcon(icon
->wm_instance
, icon
->wm_class
, file
);
558 static void killCallback(WMenu
* menu
, WMenuEntry
* entry
)
560 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
561 WFakeGroupLeader
*fPtr
;
565 if (!WCHECK_STATE(WSTATE_NORMAL
))
568 WCHANGE_STATE(WSTATE_MODAL
);
570 assert(entry
->clientdata
!= NULL
);
572 shortname
= basename(wapp
->app_icon
->wm_instance
);
574 buffer
= wstrconcat(wapp
->app_icon
? shortname
: NULL
,
575 _(" will be forcibly closed.\n"
576 "Any unsaved changes will be lost.\n" "Please confirm."));
578 fPtr
= wapp
->main_window_desc
->fake_group
;
580 wretain(wapp
->main_window_desc
);
581 if (wPreferences
.dont_confirm_kill
582 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
583 buffer
, _("Yes"), _("No"), NULL
) == WAPRDefault
) {
585 WWindow
*wwin
, *twin
;
587 wwin
= wapp
->main_window_desc
->screen_ptr
->focused_window
;
590 if (wwin
->fake_group
== fPtr
)
594 } else if (!wapp
->main_window_desc
->flags
.destroyed
) {
595 wClientKill(wapp
->main_window_desc
);
598 wrelease(wapp
->main_window_desc
);
600 WCHANGE_STATE(WSTATE_NORMAL
);
603 static WMenu
*createApplicationMenu(WScreen
*scr
)
607 menu
= wMenuCreate(scr
, NULL
, False
);
608 wMenuAddCallback(menu
, _("Unhide Here"), unhideHereCallback
, NULL
);
609 wMenuAddCallback(menu
, _("Hide"), hideCallback
, NULL
);
610 wMenuAddCallback(menu
, _("Launch"), relaunchCallback
, NULL
);
611 wMenuAddCallback(menu
, _("Set Icon..."), setIconCallback
, NULL
);
612 wMenuAddCallback(menu
, _("Kill"), killCallback
, NULL
);
617 static void openApplicationMenu(WApplication
* wapp
, int x
, int y
)
620 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
623 if (!scr
->icon_menu
) {
624 scr
->icon_menu
= createApplicationMenu(scr
);
625 wfree(scr
->icon_menu
->entries
[1]->text
);
628 menu
= scr
->icon_menu
;
630 if (wapp
->flags
.hidden
)
631 menu
->entries
[1]->text
= _("Unhide");
633 menu
->entries
[1]->text
= _("Hide");
635 menu
->flags
.realized
= 0;
638 x
-= menu
->frame
->core
->width
/ 2;
639 if (x
+ menu
->frame
->core
->width
> scr
->scr_width
)
640 x
= scr
->scr_width
- menu
->frame
->core
->width
;
645 /* set client data */
646 for (i
= 0; i
< menu
->entry_no
; i
++)
647 menu
->entries
[i
]->clientdata
= wapp
;
649 wMenuMapAt(menu
, x
, y
, False
);
652 /******************************************************************/
654 static void iconExpose(WObjDescriptor
*desc
, XEvent
*event
)
656 /* Parameter not used, but tell the compiler that it is ok */
659 wAppIconPaint(desc
->parent
);
662 static void iconDblClick(WObjDescriptor
*desc
, XEvent
*event
)
664 WAppIcon
*aicon
= desc
->parent
;
666 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
669 assert(aicon
->icon
->owner
!= NULL
);
671 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
673 if (event
->xbutton
.state
& ControlMask
) {
674 relaunchApplication(wapp
);
678 unhideHere
= (event
->xbutton
.state
& ShiftMask
);
679 /* go to the last workspace that the user worked on the app */
680 if (!unhideHere
&& wapp
->last_workspace
!= scr
->current_workspace
)
681 wWorkspaceChange(scr
, wapp
->last_workspace
);
683 wUnhideApplication(wapp
, event
->xbutton
.button
== Button2
, unhideHere
);
685 if (event
->xbutton
.state
& MOD_MASK
)
686 wHideOtherApplications(aicon
->icon
->owner
);
689 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
)
691 WAppIcon
*aicon
= desc
->parent
;
692 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
695 if (aicon
->editing
|| WCHECK_STATE(WSTATE_MODAL
))
698 if (IsDoubleClick(scr
, event
)) {
699 /* Middle or right mouse actions were handled on first click */
700 if (event
->xbutton
.button
== Button1
)
701 iconDblClick(desc
, event
);
705 if (event
->xbutton
.button
== Button2
) {
706 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
709 relaunchApplication(wapp
);
714 if (event
->xbutton
.button
== Button3
) {
715 WObjDescriptor
*desc
;
716 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
721 if (event
->xbutton
.send_event
&&
722 XGrabPointer(dpy
, aicon
->icon
->core
->window
, True
, ButtonMotionMask
723 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
724 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
725 wwarning("pointer grab failed for appicon menu");
729 openApplicationMenu(wapp
, event
->xbutton
.x_root
, event
->xbutton
.y_root
);
731 /* allow drag select of menu */
732 desc
= &scr
->icon_menu
->menu
->descriptor
;
733 event
->xbutton
.send_event
= True
;
734 (*desc
->handle_mousedown
) (desc
, event
);
738 hasMoved
= wHandleAppIconMove(aicon
, event
);
739 if (wPreferences
.single_click
&& !hasMoved
&& aicon
->dock
!= NULL
)
741 iconDblClick(desc
, event
);
745 Bool
wHandleAppIconMove(WAppIcon
*aicon
, XEvent
*event
)
747 WIcon
*icon
= aicon
->icon
;
748 WScreen
*scr
= icon
->core
->screen_ptr
;
749 WDock
*originalDock
= aicon
->dock
; /* can be NULL */
750 WDock
*lastDock
= originalDock
;
751 WDock
*allDocks
[scr
->drawer_count
+ 2]; /* clip, dock and drawers (order determined at runtime) */
753 Bool dockable
, ondock
;
754 Bool grabbed
= False
;
755 Bool collapsed
= False
; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
756 int superfluous
= wPreferences
.superfluous
; /* we cache it to avoid problems */
757 int omnipresent
= aicon
->omnipresent
; /* this must be cached */
758 Bool showed_all_clips
= False
;
760 int clickButton
= event
->xbutton
.button
;
762 Window wins
[2]; /* Managing shadow window */
765 int x
= aicon
->x_pos
, y
= aicon
->y_pos
;
766 int ofs_x
= event
->xbutton
.x
, ofs_y
= event
->xbutton
.y
;
767 int shad_x
= x
, shad_y
= y
;
768 int ix
= aicon
->xindex
, iy
= aicon
->yindex
;
772 Bool hasMoved
= False
;
774 if (wPreferences
.flags
.noupdates
&& originalDock
!= NULL
)
777 if (!(event
->xbutton
.state
& MOD_MASK
))
778 wRaiseFrame(icon
->core
);
780 /* If Mod is pressed for an docked appicon, assume it is to undock it,
781 * so don't lower it */
782 if (originalDock
== NULL
)
783 wLowerFrame(icon
->core
);
786 if (XGrabPointer(dpy
, icon
->core
->window
, True
, ButtonMotionMask
787 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
788 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
789 wwarning("Pointer grab failed in wHandleAppIconMove");
792 if (originalDock
!= NULL
) {
798 if (wPreferences
.flags
.nodock
&& wPreferences
.flags
.noclip
&& wPreferences
.flags
.nodrawer
)
801 dockable
= canBeDocked(icon
->owner
);
804 /* We try the various docks in that order:
805 * - First, the dock the appicon comes from, if any
806 * - Then, the drawers
807 * - Then, the "dock" (WM_DOCK)
808 * - Finally, the clip
811 if (originalDock
!= NULL
)
812 allDocks
[ i
++ ] = originalDock
;
813 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
814 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
815 if (dc
->adrawer
!= originalDock
)
816 allDocks
[ i
++ ] = dc
->adrawer
;
818 if (!wPreferences
.flags
.nodock
&& scr
->dock
!= originalDock
)
819 allDocks
[ i
++ ] = scr
->dock
;
821 if (!wPreferences
.flags
.noclip
&&
822 originalDock
!= scr
->workspaces
[scr
->current_workspace
]->clip
)
823 allDocks
[ i
++ ] = scr
->workspaces
[scr
->current_workspace
]->clip
;
825 for ( ; i
< scr
->drawer_count
+ 2; i
++) /* In case the clip, the dock, or both, are disabled */
826 allDocks
[ i
] = NULL
;
828 wins
[0] = icon
->core
->window
;
829 wins
[1] = scr
->dock_shadow
;
830 XRestackWindows(dpy
, wins
, 2);
831 XMoveResizeWindow(dpy
, scr
->dock_shadow
, aicon
->x_pos
, aicon
->y_pos
, ICON_SIZE
, ICON_SIZE
);
833 if (icon
->pixmap
!= None
)
834 ghost
= MakeGhostIcon(scr
, icon
->pixmap
);
836 ghost
= MakeGhostIcon(scr
, icon
->core
->window
);
837 XSetWindowBackgroundPixmap(dpy
, scr
->dock_shadow
, ghost
);
838 XClearWindow(dpy
, scr
->dock_shadow
);
841 XMapWindow(dpy
, scr
->dock_shadow
);
844 WMMaskEvent(dpy
, PointerMotionMask
| ButtonReleaseMask
| ButtonPressMask
845 | ButtonMotionMask
| ExposureMask
| EnterWindowMask
, &ev
);
852 /* It means the cursor moved so fast that it entered
853 * something else (if moving slowly, it would have
854 * stayed in the appIcon that is being moved. Ignore
855 * such "spurious" EnterNotifiy's */
861 if (abs(ofs_x
- ev
.xmotion
.x
) >= MOVE_THRESHOLD
862 || abs(ofs_y
- ev
.xmotion
.y
) >= MOVE_THRESHOLD
) {
863 XChangeActivePointerGrab(dpy
, ButtonMotionMask
864 | ButtonReleaseMask
| ButtonPressMask
,
865 wPreferences
.cursor
[WCUR_MOVE
], CurrentTime
);
872 if (omnipresent
&& !showed_all_clips
) {
874 for (i
= 0; i
< scr
->workspace_count
; i
++) {
875 if (i
== scr
->current_workspace
)
878 wDockShowIcons(scr
->workspaces
[i
]->clip
);
879 /* Note: if dock is collapsed (for instance, because it
880 auto-collapses), its icons still won't show up */
882 showed_all_clips
= True
; /* To prevent flickering */
885 x
= ev
.xmotion
.x_root
- ofs_x
;
886 y
= ev
.xmotion
.y_root
- ofs_y
;
887 wAppIconMove(aicon
, x
, y
);
889 WDock
*theNewDock
= NULL
;
890 if (!(ev
.xmotion
.state
& MOD_MASK
) || aicon
->launching
|| aicon
->lock
|| originalDock
== NULL
) {
891 for (i
= 0; dockable
&& i
< scr
->drawer_count
+ 2; i
++) {
892 WDock
*theDock
= allDocks
[i
];
895 if (wDockSnapIcon(theDock
, aicon
, x
, y
, &ix
, &iy
, (theDock
== originalDock
))) {
896 theNewDock
= theDock
;
900 if (originalDock
!= NULL
&& theNewDock
== NULL
&&
901 (aicon
->launching
|| aicon
->lock
|| aicon
->running
)) {
902 /* In those cases, stay in lastDock if no dock really wants us */
903 theNewDock
= lastDock
;
906 if (lastDock
!= NULL
&& lastDock
!= theNewDock
) {
907 /* Leave lastDock in the state we found it */
908 if (lastDock
->type
== WM_DRAWER
) {
909 wDrawerFillTheGap(lastDock
, aicon
, (lastDock
== originalDock
));
912 lastDock
->collapsed
= 1;
913 wDockHideIcons(lastDock
);
916 if (lastDock
->auto_raise_lower
) {
917 wDockLower(lastDock
);
920 if (theNewDock
!= NULL
) {
921 if (lastDock
!= theNewDock
) {
922 collapsed
= theNewDock
->collapsed
;
924 theNewDock
->collapsed
= 0;
925 wDockShowIcons(theNewDock
);
927 if (theNewDock
->auto_raise_lower
) {
928 wDockRaise(theNewDock
);
929 /* And raise the moving tile above it */
930 wRaiseFrame(aicon
->icon
->core
);
932 lastDock
= theNewDock
;
935 shad_x
= lastDock
->x_pos
+ ix
*wPreferences
.icon_size
;
936 shad_y
= lastDock
->y_pos
+ iy
*wPreferences
.icon_size
;
938 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
941 XMapWindow(dpy
, scr
->dock_shadow
);
945 lastDock
= theNewDock
; // i.e., NULL
947 XUnmapWindow(dpy
, scr
->dock_shadow
);
949 * Leaving that weird comment for now.
950 * But if we see no gap, there is no need to fill one!
951 * We could test ondock first and the lastDock to NULL afterwards
952 if (lastDock_before_it_was_null->type == WM_DRAWER) {
953 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
965 if (ev
.xbutton
.button
!= clickButton
)
967 XUngrabPointer(dpy
, CurrentTime
);
971 slide_window(icon
->core
->window
, x
, y
, shad_x
, shad_y
);
972 XUnmapWindow(dpy
, scr
->dock_shadow
);
973 if (originalDock
== NULL
) { // docking an undocked appicon
974 docked
= wDockAttachIcon(lastDock
, aicon
, ix
, iy
, False
);
976 /* AppIcon got rejected (happens only when we can't get the
977 command for that appicon, and the user cancels the
978 wInputDialog asking for one). Make the rejection obvious by
979 sliding the icon to its old position */
980 if (lastDock
->type
== WM_DRAWER
) {
981 // Also fill the gap left in the drawer
982 wDrawerFillTheGap(lastDock
, aicon
, False
);
984 slide_window(icon
->core
->window
, x
, y
, oldX
, oldY
);
987 else { // moving a docked appicon to a dock
988 if (originalDock
== lastDock
) {
990 wDockReattachIcon(originalDock
, aicon
, ix
, iy
);
993 docked
= wDockMoveIconBetweenDocks(originalDock
, lastDock
, aicon
, ix
, iy
);
995 /* Possible scenario: user moved an auto-attracted appicon
996 from the clip to the dock, and cancelled the wInputDialog
997 asking for a command */
998 if (lastDock
->type
== WM_DRAWER
) {
999 wDrawerFillTheGap(lastDock
, aicon
, False
);
1001 /* If aicon comes from a drawer, make some room to reattach it */
1002 if (originalDock
->type
== WM_DRAWER
) {
1003 WAppIcon
*aiconsToShift
[ originalDock
->icon_count
];
1006 for (i
= 0; i
< originalDock
->max_icons
; i
++) {
1007 WAppIcon
*ai
= originalDock
->icon_array
[ i
];
1008 if (ai
&& ai
!= aicon
&&
1009 abs(ai
->xindex
) >= abs(aicon
->xindex
))
1010 aiconsToShift
[j
++] = ai
;
1012 if (j
!= originalDock
->icon_count
- abs(aicon
->xindex
) - 1)
1013 // Trust this never happens?
1014 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1015 j
, originalDock
->icon_count
- abs(aicon
->xindex
) - 1, aicon
->xindex
);
1016 wSlideAppicons(aiconsToShift
, j
, originalDock
->on_right_side
);
1017 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1020 slide_window(icon
->core
->window
, x
, y
, oldX
, oldY
);
1021 wDockReattachIcon(originalDock
, aicon
, aicon
->xindex
, aicon
->yindex
);
1024 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1025 originalDock
->collapsed
= 1;
1026 wDockHideIcons(originalDock
);
1028 if (originalDock
->auto_raise_lower
)
1029 wDockLower(originalDock
);
1033 // No matter what happened above, check to lower lastDock
1034 // Don't see why I commented out the following 2 lines
1035 /* if (lastDock->auto_raise_lower)
1036 wDockLower(lastDock); */
1037 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1038 * collapsed, so that wHandleAppIconMove doesn't collapse it
1039 * right away (the timer will take care of it) */
1040 if (lastDock
->auto_collapse
)
1044 if (originalDock
!= NULL
) { /* Detaching a docked appicon */
1046 if (!aicon
->running
&& !wPreferences
.no_animations
) {
1047 /* We need to deselect it, even if is deselected in
1048 * wDockDetach(), because else DoKaboom() will fail.
1050 if (aicon
->icon
->selected
)
1051 wIconSelect(aicon
->icon
);
1052 DoKaboom(scr
, aicon
->icon
->core
->window
, x
, y
);
1055 wDockDetach(originalDock
, aicon
);
1056 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1057 originalDock
->collapsed
= 1;
1058 wDockHideIcons(originalDock
);
1060 if (originalDock
->auto_raise_lower
)
1061 wDockLower(originalDock
);
1064 // Can't remember why the icon hiding is better done above than below (commented out)
1065 // Also, lastDock is quite different from originalDock
1068 lastDock->collapsed = 1;
1069 wDockHideIcons(lastDock);
1075 XFreePixmap(dpy
, ghost
);
1076 XSetWindowBackground(dpy
, scr
->dock_shadow
, scr
->white_pixel
);
1078 if (showed_all_clips
) {
1080 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1081 if (i
== scr
->current_workspace
)
1084 wDockHideIcons(scr
->workspaces
[i
]->clip
);
1087 if (wPreferences
.auto_arrange_icons
&& !(originalDock
!= NULL
&& docked
))
1088 /* Need to rearrange unless moving from dock to dock */
1089 wArrangeIcons(scr
, True
);
1095 /* This function save the application icon and store the path in the Dictionary */
1096 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
)
1098 WMPropList
*dict
= w_global
.domain
.window_attr
->dictionary
;
1099 WMPropList
*adict
, *key
, *iconk
;
1103 tmp
= get_name_for_instance_class(wm_instance
, wm_class
);
1104 key
= WMCreatePLString(tmp
);
1107 adict
= WMGetFromPLDictionary(dict
, key
);
1108 iconk
= WMCreatePLString("Icon");
1111 val
= WMGetFromPLDictionary(adict
, iconk
);
1113 /* no dictionary for app, so create one */
1114 adict
= WMCreatePLDictionary(NULL
, NULL
);
1115 WMPutInPLDictionary(dict
, key
, adict
);
1116 WMReleasePropList(adict
);
1121 val
= WMCreatePLString(iconPath
);
1122 WMPutInPLDictionary(adict
, iconk
, val
);
1123 WMReleasePropList(val
);
1128 WMReleasePropList(key
);
1129 WMReleasePropList(iconk
);
1131 if (val
&& !wPreferences
.flags
.noupdates
)
1132 UpdateDomainFile(w_global
.domain
.window_attr
);
1135 static WAppIcon
*findDockIconFor(WDock
*dock
, Window main_window
)
1137 WAppIcon
*aicon
= NULL
;
1139 aicon
= wDockFindIconForWindow(dock
, main_window
);
1141 wDockTrackWindowLaunch(dock
, main_window
);
1142 aicon
= wDockFindIconForWindow(dock
, main_window
);
1147 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
)
1149 WScreen
*scr
= wwin
->screen_ptr
;
1150 wapp
->app_icon
= NULL
;
1153 wapp
->app_icon
= findDockIconFor(scr
->last_dock
, main_window
);
1155 /* check main dock if we did not find it in last dock */
1156 if (!wapp
->app_icon
&& scr
->dock
)
1157 wapp
->app_icon
= findDockIconFor(scr
->dock
, main_window
);
1160 if (!wapp
->app_icon
) {
1162 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1163 WDock
*dock
= scr
->workspaces
[i
]->clip
;
1166 wapp
->app_icon
= findDockIconFor(dock
, main_window
);
1173 /* Finally check drawers */
1174 if (!wapp
->app_icon
) {
1176 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
1177 wapp
->app_icon
= findDockIconFor(dc
->adrawer
, main_window
);
1183 /* If created, then set some flags */
1184 if (wapp
->app_icon
&& !WFLAGP(wapp
->main_window_desc
, no_appicon
)) {
1185 WWindow
*mainw
= wapp
->main_window_desc
;
1187 wapp
->app_icon
->running
= 1;
1188 wapp
->app_icon
->icon
->owner
= mainw
;
1189 if (mainw
->wm_hints
&& (mainw
->wm_hints
->flags
& IconWindowHint
))
1190 wapp
->app_icon
->icon
->icon_win
= mainw
->wm_hints
->icon_window
;
1192 /* Update the icon images */
1193 wIconUpdate(wapp
->app_icon
->icon
);
1196 wAppIconPaint(wapp
->app_icon
);
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
;