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 ws_numbers
= wmalloc(20);
387 snprintf(ws_numbers
, 20, "%i [ %i ]", scr
->current_workspace
+ 1, ((scr
->current_workspace
/ 10) + 1));
388 length
= strlen(ws_numbers
);
390 XClearArea(dpy
, dicon
->icon
->core
->window
, 2, 2, 50, WMFontHeight(scr
->icon_title_font
) + 1, False
);
392 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->black
,
393 scr
->icon_title_font
, 4, 3, ws_numbers
, length
);
395 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->white
,
396 scr
->icon_title_font
, 3, 2, ws_numbers
, length
);
400 #endif /* WS_INDICATOR */
402 void wAppIconPaint(WAppIcon
*aicon
)
405 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
407 if (aicon
->icon
->owner
)
408 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
412 wIconPaint(aicon
->icon
);
415 if (aicon
->docked
&& scr
->dock
&& scr
->dock
== aicon
->dock
&& aicon
->yindex
== 0)
416 updateDockNumbers(scr
);
418 if (aicon
->docked
&& !aicon
->running
&& aicon
->command
!= NULL
) {
419 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
420 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
421 XCopyArea(dpy
, scr
->dock_dots
->image
, aicon
->icon
->core
->window
,
422 scr
->copy_gc
, 0, 0, scr
->dock_dots
->width
, scr
->dock_dots
->height
, 0, 0);
425 if (wapp
&& wapp
->flags
.hidden
) {
426 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
427 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
428 XCopyArea(dpy
, scr
->dock_dots
->image
,
429 aicon
->icon
->core
->window
, scr
->copy_gc
, 0, 0, 7, scr
->dock_dots
->height
, 0, 0);
431 #endif /* HIDDENDOT */
433 if (aicon
->omnipresent
)
434 drawCorner(aicon
->icon
);
436 XSetClipMask(dpy
, scr
->copy_gc
, None
);
437 if (aicon
->launching
)
438 XFillRectangle(dpy
, aicon
->icon
->core
->window
, scr
->stipple_gc
,
439 0, 0, wPreferences
.icon_size
, wPreferences
.icon_size
);
442 /* Save the application icon, if it's a dockapp then use it with dock = True */
443 void save_appicon(WAppIcon
*aicon
)
450 if (!aicon
->docked
|| aicon
->attracted
)
453 path
= wIconStore(aicon
->icon
);
457 wApplicationSaveIconPathFor(path
, aicon
->wm_instance
, aicon
->wm_class
);
461 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
463 /* main_window may not have the full command line; try to find one which does */
464 static void relaunchApplication(WApplication
*wapp
)
467 WWindow
*wlist
, *next
;
469 scr
= wapp
->main_window_desc
->screen_ptr
;
470 wlist
= scr
->focused_window
;
480 if (wlist
->main_window
== wapp
->main_window
) {
481 if (RelaunchWindow(wlist
))
489 static void relaunchCallback(WMenu
* menu
, WMenuEntry
* entry
)
491 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
493 /* Parameter not used, but tell the compiler that it is ok */
496 relaunchApplication(wapp
);
499 static void hideCallback(WMenu
* menu
, WMenuEntry
* entry
)
501 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
503 if (wapp
->flags
.hidden
) {
504 wWorkspaceChange(menu
->menu
->screen_ptr
, wapp
->last_workspace
);
505 wUnhideApplication(wapp
, False
, False
);
507 wHideApplication(wapp
);
511 static void unhideHereCallback(WMenu
* menu
, WMenuEntry
* entry
)
513 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
515 /* Parameter not used, but tell the compiler that it is ok */
518 wUnhideApplication(wapp
, False
, True
);
521 static void setIconCallback(WMenu
*menu
, WMenuEntry
*entry
)
523 WAppIcon
*icon
= ((WApplication
*) entry
->clientdata
)->app_icon
;
528 /* Parameter not used, but tell the compiler that it is ok */
531 assert(icon
!= NULL
);
537 scr
= icon
->icon
->core
->screen_ptr
;
541 result
= wIconChooserDialog(scr
, &file
, icon
->wm_instance
, icon
->wm_class
);
544 if (!icon
->destroyed
) {
545 if (!wIconChangeImageFile(icon
->icon
, file
)) {
546 wMessageDialog(scr
, _("Error"),
547 _("Could not open specified icon file"),
548 _("OK"), NULL
, NULL
);
550 wDefaultChangeIcon(icon
->wm_instance
, icon
->wm_class
, file
);
561 static void killCallback(WMenu
* menu
, WMenuEntry
* entry
)
563 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
564 WFakeGroupLeader
*fPtr
;
568 if (!WCHECK_STATE(WSTATE_NORMAL
))
571 WCHANGE_STATE(WSTATE_MODAL
);
573 assert(entry
->clientdata
!= NULL
);
575 shortname
= basename(wapp
->app_icon
->wm_instance
);
577 buffer
= wstrconcat(wapp
->app_icon
? shortname
: NULL
,
578 _(" will be forcibly closed.\n"
579 "Any unsaved changes will be lost.\n" "Please confirm."));
581 fPtr
= wapp
->main_window_desc
->fake_group
;
583 wretain(wapp
->main_window_desc
);
584 if (wPreferences
.dont_confirm_kill
585 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
586 buffer
, _("Yes"), _("No"), NULL
) == WAPRDefault
) {
588 WWindow
*wwin
, *twin
;
590 wwin
= wapp
->main_window_desc
->screen_ptr
->focused_window
;
593 if (wwin
->fake_group
== fPtr
)
597 } else if (!wapp
->main_window_desc
->flags
.destroyed
) {
598 wClientKill(wapp
->main_window_desc
);
601 wrelease(wapp
->main_window_desc
);
603 WCHANGE_STATE(WSTATE_NORMAL
);
606 static WMenu
*createApplicationMenu(WScreen
*scr
)
610 menu
= wMenuCreate(scr
, NULL
, False
);
611 wMenuAddCallback(menu
, _("Unhide Here"), unhideHereCallback
, NULL
);
612 wMenuAddCallback(menu
, _("Hide"), hideCallback
, NULL
);
613 wMenuAddCallback(menu
, _("Launch"), relaunchCallback
, NULL
);
614 wMenuAddCallback(menu
, _("Set Icon..."), setIconCallback
, NULL
);
615 wMenuAddCallback(menu
, _("Kill"), killCallback
, NULL
);
620 static void openApplicationMenu(WApplication
* wapp
, int x
, int y
)
623 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
626 if (!scr
->icon_menu
) {
627 scr
->icon_menu
= createApplicationMenu(scr
);
628 wfree(scr
->icon_menu
->entries
[1]->text
);
631 menu
= scr
->icon_menu
;
633 if (wapp
->flags
.hidden
)
634 menu
->entries
[1]->text
= _("Unhide");
636 menu
->entries
[1]->text
= _("Hide");
638 menu
->flags
.realized
= 0;
641 x
-= menu
->frame
->core
->width
/ 2;
642 if (x
+ menu
->frame
->core
->width
> scr
->scr_width
)
643 x
= scr
->scr_width
- menu
->frame
->core
->width
;
648 /* set client data */
649 for (i
= 0; i
< menu
->entry_no
; i
++)
650 menu
->entries
[i
]->clientdata
= wapp
;
652 wMenuMapAt(menu
, x
, y
, False
);
655 /******************************************************************/
657 static void iconExpose(WObjDescriptor
*desc
, XEvent
*event
)
659 /* Parameter not used, but tell the compiler that it is ok */
662 wAppIconPaint(desc
->parent
);
665 static void iconDblClick(WObjDescriptor
*desc
, XEvent
*event
)
667 WAppIcon
*aicon
= desc
->parent
;
669 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
672 assert(aicon
->icon
->owner
!= NULL
);
674 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
676 if (event
->xbutton
.state
& ControlMask
) {
677 relaunchApplication(wapp
);
681 unhideHere
= (event
->xbutton
.state
& ShiftMask
);
682 /* go to the last workspace that the user worked on the app */
683 if (!unhideHere
&& wapp
->last_workspace
!= scr
->current_workspace
)
684 wWorkspaceChange(scr
, wapp
->last_workspace
);
686 wUnhideApplication(wapp
, event
->xbutton
.button
== Button2
, unhideHere
);
688 if (event
->xbutton
.state
& MOD_MASK
)
689 wHideOtherApplications(aicon
->icon
->owner
);
692 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
)
694 WAppIcon
*aicon
= desc
->parent
;
695 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
698 if (aicon
->editing
|| WCHECK_STATE(WSTATE_MODAL
))
701 if (IsDoubleClick(scr
, event
)) {
702 /* Middle or right mouse actions were handled on first click */
703 if (event
->xbutton
.button
== Button1
)
704 iconDblClick(desc
, event
);
708 if (event
->xbutton
.button
== Button2
) {
709 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
712 relaunchApplication(wapp
);
717 if (event
->xbutton
.button
== Button3
) {
718 WObjDescriptor
*desc
;
719 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
724 if (event
->xbutton
.send_event
&&
725 XGrabPointer(dpy
, aicon
->icon
->core
->window
, True
, ButtonMotionMask
726 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
727 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
728 wwarning("pointer grab failed for appicon menu");
732 openApplicationMenu(wapp
, event
->xbutton
.x_root
, event
->xbutton
.y_root
);
734 /* allow drag select of menu */
735 desc
= &scr
->icon_menu
->menu
->descriptor
;
736 event
->xbutton
.send_event
= True
;
737 (*desc
->handle_mousedown
) (desc
, event
);
741 hasMoved
= wHandleAppIconMove(aicon
, event
);
742 if (wPreferences
.single_click
&& !hasMoved
&& aicon
->dock
!= NULL
)
744 iconDblClick(desc
, event
);
748 Bool
wHandleAppIconMove(WAppIcon
*aicon
, XEvent
*event
)
750 WIcon
*icon
= aicon
->icon
;
751 WScreen
*scr
= icon
->core
->screen_ptr
;
752 WDock
*originalDock
= aicon
->dock
; /* can be NULL */
753 WDock
*lastDock
= originalDock
;
754 WDock
*allDocks
[scr
->drawer_count
+ 2]; /* clip, dock and drawers (order determined at runtime) */
756 Bool dockable
, ondock
;
757 Bool grabbed
= False
;
758 Bool collapsed
= False
; /* Stores the collapsed state of lastDock, before the moving appicon entered it */
759 int superfluous
= wPreferences
.superfluous
; /* we cache it to avoid problems */
760 int omnipresent
= aicon
->omnipresent
; /* this must be cached */
761 Bool showed_all_clips
= False
;
763 int clickButton
= event
->xbutton
.button
;
765 Window wins
[2]; /* Managing shadow window */
768 int x
= aicon
->x_pos
, y
= aicon
->y_pos
;
769 int ofs_x
= event
->xbutton
.x
, ofs_y
= event
->xbutton
.y
;
770 int shad_x
= x
, shad_y
= y
;
771 int ix
= aicon
->xindex
, iy
= aicon
->yindex
;
775 Bool hasMoved
= False
;
777 if (wPreferences
.flags
.noupdates
&& originalDock
!= NULL
)
780 if (!(event
->xbutton
.state
& MOD_MASK
))
781 wRaiseFrame(icon
->core
);
783 /* If Mod is pressed for an docked appicon, assume it is to undock it,
784 * so don't lower it */
785 if (originalDock
== NULL
)
786 wLowerFrame(icon
->core
);
789 if (XGrabPointer(dpy
, icon
->core
->window
, True
, ButtonMotionMask
790 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
791 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
792 wwarning("Pointer grab failed in wHandleAppIconMove");
795 if (originalDock
!= NULL
) {
801 if (wPreferences
.flags
.nodock
&& wPreferences
.flags
.noclip
&& wPreferences
.flags
.nodrawer
)
804 dockable
= canBeDocked(icon
->owner
);
807 /* We try the various docks in that order:
808 * - First, the dock the appicon comes from, if any
809 * - Then, the drawers
810 * - Then, the "dock" (WM_DOCK)
811 * - Finally, the clip
814 if (originalDock
!= NULL
)
815 allDocks
[ i
++ ] = originalDock
;
816 /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */
817 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
818 if (dc
->adrawer
!= originalDock
)
819 allDocks
[ i
++ ] = dc
->adrawer
;
821 if (!wPreferences
.flags
.nodock
&& scr
->dock
!= originalDock
)
822 allDocks
[ i
++ ] = scr
->dock
;
824 if (!wPreferences
.flags
.noclip
&&
825 originalDock
!= scr
->workspaces
[scr
->current_workspace
]->clip
)
826 allDocks
[ i
++ ] = scr
->workspaces
[scr
->current_workspace
]->clip
;
828 for ( ; i
< scr
->drawer_count
+ 2; i
++) /* In case the clip, the dock, or both, are disabled */
829 allDocks
[ i
] = NULL
;
831 wins
[0] = icon
->core
->window
;
832 wins
[1] = scr
->dock_shadow
;
833 XRestackWindows(dpy
, wins
, 2);
834 XMoveResizeWindow(dpy
, scr
->dock_shadow
, aicon
->x_pos
, aicon
->y_pos
, ICON_SIZE
, ICON_SIZE
);
836 if (icon
->pixmap
!= None
)
837 ghost
= MakeGhostIcon(scr
, icon
->pixmap
);
839 ghost
= MakeGhostIcon(scr
, icon
->core
->window
);
840 XSetWindowBackgroundPixmap(dpy
, scr
->dock_shadow
, ghost
);
841 XClearWindow(dpy
, scr
->dock_shadow
);
844 XMapWindow(dpy
, scr
->dock_shadow
);
847 WMMaskEvent(dpy
, PointerMotionMask
| ButtonReleaseMask
| ButtonPressMask
848 | ButtonMotionMask
| ExposureMask
| EnterWindowMask
, &ev
);
855 /* It means the cursor moved so fast that it entered
856 * something else (if moving slowly, it would have
857 * stayed in the appIcon that is being moved. Ignore
858 * such "spurious" EnterNotifiy's */
864 if (abs(ofs_x
- ev
.xmotion
.x
) >= MOVE_THRESHOLD
865 || abs(ofs_y
- ev
.xmotion
.y
) >= MOVE_THRESHOLD
) {
866 XChangeActivePointerGrab(dpy
, ButtonMotionMask
867 | ButtonReleaseMask
| ButtonPressMask
,
868 wPreferences
.cursor
[WCUR_MOVE
], CurrentTime
);
875 if (omnipresent
&& !showed_all_clips
) {
877 for (i
= 0; i
< scr
->workspace_count
; i
++) {
878 if (i
== scr
->current_workspace
)
881 wDockShowIcons(scr
->workspaces
[i
]->clip
);
882 /* Note: if dock is collapsed (for instance, because it
883 auto-collapses), its icons still won't show up */
885 showed_all_clips
= True
; /* To prevent flickering */
888 x
= ev
.xmotion
.x_root
- ofs_x
;
889 y
= ev
.xmotion
.y_root
- ofs_y
;
890 wAppIconMove(aicon
, x
, y
);
892 WDock
*theNewDock
= NULL
;
893 if (!(ev
.xmotion
.state
& MOD_MASK
) || aicon
->launching
|| aicon
->lock
|| originalDock
== NULL
) {
894 for (i
= 0; dockable
&& i
< scr
->drawer_count
+ 2; i
++) {
895 WDock
*theDock
= allDocks
[i
];
898 if (wDockSnapIcon(theDock
, aicon
, x
, y
, &ix
, &iy
, (theDock
== originalDock
))) {
899 theNewDock
= theDock
;
903 if (originalDock
!= NULL
&& theNewDock
== NULL
&&
904 (aicon
->launching
|| aicon
->lock
|| aicon
->running
)) {
905 /* In those cases, stay in lastDock if no dock really wants us */
906 theNewDock
= lastDock
;
909 if (lastDock
!= NULL
&& lastDock
!= theNewDock
) {
910 /* Leave lastDock in the state we found it */
911 if (lastDock
->type
== WM_DRAWER
) {
912 wDrawerFillTheGap(lastDock
, aicon
, (lastDock
== originalDock
));
915 lastDock
->collapsed
= 1;
916 wDockHideIcons(lastDock
);
919 if (lastDock
->auto_raise_lower
) {
920 wDockLower(lastDock
);
923 if (theNewDock
!= NULL
) {
924 if (lastDock
!= theNewDock
) {
925 collapsed
= theNewDock
->collapsed
;
927 theNewDock
->collapsed
= 0;
928 wDockShowIcons(theNewDock
);
930 if (theNewDock
->auto_raise_lower
) {
931 wDockRaise(theNewDock
);
932 /* And raise the moving tile above it */
933 wRaiseFrame(aicon
->icon
->core
);
935 lastDock
= theNewDock
;
938 shad_x
= lastDock
->x_pos
+ ix
*wPreferences
.icon_size
;
939 shad_y
= lastDock
->y_pos
+ iy
*wPreferences
.icon_size
;
941 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
944 XMapWindow(dpy
, scr
->dock_shadow
);
948 lastDock
= theNewDock
; // i.e., NULL
950 XUnmapWindow(dpy
, scr
->dock_shadow
);
952 * Leaving that weird comment for now.
953 * But if we see no gap, there is no need to fill one!
954 * We could test ondock first and the lastDock to NULL afterwards
955 if (lastDock_before_it_was_null->type == WM_DRAWER) {
956 wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock));
968 if (ev
.xbutton
.button
!= clickButton
)
970 XUngrabPointer(dpy
, CurrentTime
);
974 slide_window(icon
->core
->window
, x
, y
, shad_x
, shad_y
);
975 XUnmapWindow(dpy
, scr
->dock_shadow
);
976 if (originalDock
== NULL
) { // docking an undocked appicon
977 docked
= wDockAttachIcon(lastDock
, aicon
, ix
, iy
, False
);
979 /* AppIcon got rejected (happens only when we can't get the
980 command for that appicon, and the user cancels the
981 wInputDialog asking for one). Make the rejection obvious by
982 sliding the icon to its old position */
983 if (lastDock
->type
== WM_DRAWER
) {
984 // Also fill the gap left in the drawer
985 wDrawerFillTheGap(lastDock
, aicon
, False
);
987 slide_window(icon
->core
->window
, x
, y
, oldX
, oldY
);
990 else { // moving a docked appicon to a dock
991 if (originalDock
== lastDock
) {
993 wDockReattachIcon(originalDock
, aicon
, ix
, iy
);
996 docked
= wDockMoveIconBetweenDocks(originalDock
, lastDock
, aicon
, ix
, iy
);
998 /* Possible scenario: user moved an auto-attracted appicon
999 from the clip to the dock, and cancelled the wInputDialog
1000 asking for a command */
1001 if (lastDock
->type
== WM_DRAWER
) {
1002 wDrawerFillTheGap(lastDock
, aicon
, False
);
1004 /* If aicon comes from a drawer, make some room to reattach it */
1005 if (originalDock
->type
== WM_DRAWER
) {
1006 WAppIcon
*aiconsToShift
[ originalDock
->icon_count
];
1009 for (i
= 0; i
< originalDock
->max_icons
; i
++) {
1010 WAppIcon
*ai
= originalDock
->icon_array
[ i
];
1011 if (ai
&& ai
!= aicon
&&
1012 abs(ai
->xindex
) >= abs(aicon
->xindex
))
1013 aiconsToShift
[j
++] = ai
;
1015 if (j
!= originalDock
->icon_count
- abs(aicon
->xindex
) - 1)
1016 // Trust this never happens?
1017 wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.",
1018 j
, originalDock
->icon_count
- abs(aicon
->xindex
) - 1, aicon
->xindex
);
1019 wSlideAppicons(aiconsToShift
, j
, originalDock
->on_right_side
);
1020 // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location?
1023 slide_window(icon
->core
->window
, x
, y
, oldX
, oldY
);
1024 wDockReattachIcon(originalDock
, aicon
, aicon
->xindex
, aicon
->yindex
);
1027 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1028 originalDock
->collapsed
= 1;
1029 wDockHideIcons(originalDock
);
1031 if (originalDock
->auto_raise_lower
)
1032 wDockLower(originalDock
);
1036 // No matter what happened above, check to lower lastDock
1037 // Don't see why I commented out the following 2 lines
1038 /* if (lastDock->auto_raise_lower)
1039 wDockLower(lastDock); */
1040 /* If docked (or tried to dock) to a auto_collapsing dock, unset
1041 * collapsed, so that wHandleAppIconMove doesn't collapse it
1042 * right away (the timer will take care of it) */
1043 if (lastDock
->auto_collapse
)
1047 if (originalDock
!= NULL
) { /* Detaching a docked appicon */
1049 if (!aicon
->running
&& !wPreferences
.no_animations
) {
1050 /* We need to deselect it, even if is deselected in
1051 * wDockDetach(), because else DoKaboom() will fail.
1053 if (aicon
->icon
->selected
)
1054 wIconSelect(aicon
->icon
);
1055 DoKaboom(scr
, aicon
->icon
->core
->window
, x
, y
);
1058 wDockDetach(originalDock
, aicon
);
1059 if (originalDock
->auto_collapse
&& !originalDock
->collapsed
) {
1060 originalDock
->collapsed
= 1;
1061 wDockHideIcons(originalDock
);
1063 if (originalDock
->auto_raise_lower
)
1064 wDockLower(originalDock
);
1067 // Can't remember why the icon hiding is better done above than below (commented out)
1068 // Also, lastDock is quite different from originalDock
1071 lastDock->collapsed = 1;
1072 wDockHideIcons(lastDock);
1078 XFreePixmap(dpy
, ghost
);
1079 XSetWindowBackground(dpy
, scr
->dock_shadow
, scr
->white_pixel
);
1081 if (showed_all_clips
) {
1083 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1084 if (i
== scr
->current_workspace
)
1087 wDockHideIcons(scr
->workspaces
[i
]->clip
);
1090 if (wPreferences
.auto_arrange_icons
&& !(originalDock
!= NULL
&& docked
))
1091 /* Need to rearrange unless moving from dock to dock */
1092 wArrangeIcons(scr
, True
);
1098 /* This function save the application icon and store the path in the Dictionary */
1099 static void wApplicationSaveIconPathFor(const char *iconPath
, const char *wm_instance
, const char *wm_class
)
1101 WMPropList
*dict
= w_global
.domain
.window_attr
->dictionary
;
1102 WMPropList
*adict
, *key
, *iconk
;
1106 tmp
= get_name_for_instance_class(wm_instance
, wm_class
);
1107 key
= WMCreatePLString(tmp
);
1110 adict
= WMGetFromPLDictionary(dict
, key
);
1111 iconk
= WMCreatePLString("Icon");
1114 val
= WMGetFromPLDictionary(adict
, iconk
);
1116 /* no dictionary for app, so create one */
1117 adict
= WMCreatePLDictionary(NULL
, NULL
);
1118 WMPutInPLDictionary(dict
, key
, adict
);
1119 WMReleasePropList(adict
);
1124 val
= WMCreatePLString(iconPath
);
1125 WMPutInPLDictionary(adict
, iconk
, val
);
1126 WMReleasePropList(val
);
1131 WMReleasePropList(key
);
1132 WMReleasePropList(iconk
);
1134 if (val
&& !wPreferences
.flags
.noupdates
)
1135 UpdateDomainFile(w_global
.domain
.window_attr
);
1138 static WAppIcon
*findDockIconFor(WDock
*dock
, Window main_window
)
1140 WAppIcon
*aicon
= NULL
;
1142 aicon
= wDockFindIconForWindow(dock
, main_window
);
1144 wDockTrackWindowLaunch(dock
, main_window
);
1145 aicon
= wDockFindIconForWindow(dock
, main_window
);
1150 static void create_appicon_from_dock(WWindow
*wwin
, WApplication
*wapp
, Window main_window
)
1152 WScreen
*scr
= wwin
->screen_ptr
;
1153 wapp
->app_icon
= NULL
;
1156 wapp
->app_icon
= findDockIconFor(scr
->last_dock
, main_window
);
1158 /* check main dock if we did not find it in last dock */
1159 if (!wapp
->app_icon
&& scr
->dock
)
1160 wapp
->app_icon
= findDockIconFor(scr
->dock
, main_window
);
1163 if (!wapp
->app_icon
) {
1165 for (i
= 0; i
< scr
->workspace_count
; i
++) {
1166 WDock
*dock
= scr
->workspaces
[i
]->clip
;
1169 wapp
->app_icon
= findDockIconFor(dock
, main_window
);
1176 /* Finally check drawers */
1177 if (!wapp
->app_icon
) {
1179 for (dc
= scr
->drawers
; dc
!= NULL
; dc
= dc
->next
) {
1180 wapp
->app_icon
= findDockIconFor(dc
->adrawer
, main_window
);
1186 /* If created, then set some flags */
1187 if (wapp
->app_icon
&& !WFLAGP(wapp
->main_window_desc
, no_appicon
)) {
1188 WWindow
*mainw
= wapp
->main_window_desc
;
1190 wapp
->app_icon
->running
= 1;
1191 wapp
->app_icon
->icon
->owner
= mainw
;
1192 if (mainw
->wm_hints
&& (mainw
->wm_hints
->flags
& IconWindowHint
))
1193 wapp
->app_icon
->icon
->icon_win
= mainw
->wm_hints
->icon_window
;
1195 /* Update the icon images */
1196 wIconUpdate(wapp
->app_icon
->icon
);
1199 wAppIconPaint(wapp
->app_icon
);
1203 /* Add the appicon to the appiconlist */
1204 static void add_to_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1206 appicon
->prev
= NULL
;
1207 appicon
->next
= scr
->app_icon_list
;
1208 if (scr
->app_icon_list
)
1209 scr
->app_icon_list
->prev
= appicon
;
1211 scr
->app_icon_list
= appicon
;
1214 /* Remove the appicon from the appiconlist */
1215 static void remove_from_appicon_list(WScreen
*scr
, WAppIcon
*appicon
)
1217 if (appicon
== scr
->app_icon_list
) {
1219 appicon
->next
->prev
= NULL
;
1220 scr
->app_icon_list
= appicon
->next
;
1223 appicon
->next
->prev
= appicon
->prev
;
1225 appicon
->prev
->next
= appicon
->next
;
1228 appicon
->prev
= NULL
;
1229 appicon
->next
= NULL
;
1232 /* Return the AppIcon associated with a given (Xlib) Window. */
1233 WAppIcon
*wAppIconFor(Window window
)
1235 WObjDescriptor
*desc
;
1240 if (XFindContext(dpy
, window
, w_global
.context
.client_win
, (XPointer
*) & desc
) == XCNOENT
)
1243 if (desc
->parent_type
== WCLASS_APPICON
|| desc
->parent_type
== WCLASS_DOCK_ICON
)
1244 return desc
->parent
;