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
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
27 #include <X11/Xutil.h>
31 #include "WindowMaker.h"
41 #include "workspace.h"
42 #include "superfluous.h"
53 * icon_file for the dock is got from the preferences file by
54 * using the classname/instancename
57 /**** Global variables ****/
58 extern Cursor wCursor
[WCUR_LAST
];
59 extern WPreferences wPreferences
;
61 #define MOD_MASK wPreferences.modifier_mask
63 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
);
64 static void iconDblClick(WObjDescriptor
* desc
, XEvent
* event
);
65 static void iconExpose(WObjDescriptor
* desc
, XEvent
* event
);
67 WAppIcon
*wAppIconCreateForDock(WScreen
* scr
, char *command
, char *wm_instance
, char *wm_class
, int tile
)
72 dicon
= wmalloc(sizeof(WAppIcon
));
74 memset(dicon
, 0, sizeof(WAppIcon
));
79 dicon
->next
= scr
->app_icon_list
;
80 if (scr
->app_icon_list
) {
81 scr
->app_icon_list
->prev
= dicon
;
83 scr
->app_icon_list
= dicon
;
86 dicon
->command
= wstrdup(command
);
89 dicon
->wm_class
= wstrdup(wm_class
);
91 dicon
->wm_instance
= wstrdup(wm_instance
);
93 path
= wDefaultGetIconFile(scr
, wm_instance
, wm_class
, True
);
94 if (!path
&& command
) {
95 wApplicationExtractDirPackIcon(scr
, command
, wm_instance
, wm_class
);
97 path
= wDefaultGetIconFile(scr
, wm_instance
, wm_class
, False
);
101 path
= FindImage(wPreferences
.icon_path
, path
);
103 dicon
->icon
= wIconCreateWithIconFile(scr
, path
, tile
);
107 wXDNDMakeAwareness(dicon
->icon
->core
->window
);
110 #ifdef DEMATERIALIZE_ICON
112 XSetWindowAttributes attribs
;
113 attribs
.save_under
= True
;
114 XChangeWindowAttributes(dpy
, dicon
->icon
->core
->window
, CWSaveUnder
, &attribs
);
118 /* will be overriden by dock */
119 dicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
120 dicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
121 dicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
122 dicon
->icon
->core
->descriptor
.parent
= dicon
;
123 AddToStackList(dicon
->icon
->core
);
128 WAppIcon
*wAppIconCreate(WWindow
* leader_win
)
131 WScreen
*scr
= leader_win
->screen_ptr
;
133 aicon
= wmalloc(sizeof(WAppIcon
));
135 memset(aicon
, 0, sizeof(WAppIcon
));
141 aicon
->next
= scr
->app_icon_list
;
142 if (scr
->app_icon_list
) {
143 scr
->app_icon_list
->prev
= aicon
;
145 scr
->app_icon_list
= aicon
;
147 if (leader_win
->wm_class
)
148 aicon
->wm_class
= wstrdup(leader_win
->wm_class
);
149 if (leader_win
->wm_instance
)
150 aicon
->wm_instance
= wstrdup(leader_win
->wm_instance
);
152 aicon
->icon
= wIconCreate(leader_win
);
153 #ifdef DEMATERIALIZE_ICON
155 XSetWindowAttributes attribs
;
156 attribs
.save_under
= True
;
157 XChangeWindowAttributes(dpy
, aicon
->icon
->core
->window
, CWSaveUnder
, &attribs
);
161 wXDNDMakeAwareness(aicon
->icon
->core
->window
);
164 /* will be overriden if docked */
165 aicon
->icon
->core
->descriptor
.handle_mousedown
= appIconMouseDown
;
166 aicon
->icon
->core
->descriptor
.handle_expose
= iconExpose
;
167 aicon
->icon
->core
->descriptor
.parent_type
= WCLASS_APPICON
;
168 aicon
->icon
->core
->descriptor
.parent
= aicon
;
169 AddToStackList(aicon
->icon
->core
);
170 aicon
->icon
->show_title
= 0;
171 wIconUpdate(aicon
->icon
);
176 void wAppIconDestroy(WAppIcon
* aicon
)
178 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
180 RemoveFromStackList(aicon
->icon
->core
);
181 wIconDestroy(aicon
->icon
);
183 wfree(aicon
->command
);
185 if (aicon
->dnd_command
)
186 wfree(aicon
->dnd_command
);
188 if (aicon
->wm_instance
)
189 wfree(aicon
->wm_instance
);
191 wfree(aicon
->wm_class
);
193 if (aicon
== scr
->app_icon_list
) {
195 aicon
->next
->prev
= NULL
;
196 scr
->app_icon_list
= aicon
->next
;
199 aicon
->next
->prev
= aicon
->prev
;
201 aicon
->prev
->next
= aicon
->next
;
204 aicon
->destroyed
= 1;
209 static void drawCorner(WIcon
* icon
, WWindow
* wwin
, int active
)
211 WScreen
*scr
= wwin
->screen_ptr
;
222 gc
= scr
->focused_texture
->any
.gc
;
224 gc
= scr
->unfocused_texture
->any
.gc
;
226 XFillPolygon(dpy
, icon
->core
->window
, gc
, points
, 3, Convex
, CoordModeOrigin
);
228 #endif /* NEWAPPICON */
230 static void drawCorner(WIcon
* icon
)
232 WScreen
*scr
= icon
->core
->screen_ptr
;
241 XFillPolygon(dpy
, icon
->core
->window
, scr
->icon_title_texture
->normal_gc
,
242 points
, 3, Convex
, CoordModeOrigin
);
243 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 0, 12);
244 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 0, 0, 12, 0);
245 /* drawing the second line gives a weird concave look. -Dan */
247 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 1, 1, 1, 11);
248 XDrawLine(dpy
, icon
->core
->window
, scr
->icon_title_texture
->light_gc
, 1, 1, 11, 1);
252 void wAppIconMove(WAppIcon
* aicon
, int x
, int y
)
254 XMoveWindow(dpy
, aicon
->icon
->core
->window
, x
, y
);
260 static void updateDockNumbers(WScreen
* scr
)
264 WAppIcon
*dicon
= scr
->dock
->icon_array
[0];
266 ws_numbers
= wmalloc(20);
267 snprintf(ws_numbers
, 20, "%i [ %i ]", scr
->current_workspace
+ 1, ((scr
->current_workspace
/ 10) + 1));
268 length
= strlen(ws_numbers
);
270 XClearArea(dpy
, dicon
->icon
->core
->window
, 2, 2, 50, WMFontHeight(scr
->icon_title_font
) + 1, False
);
272 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->black
,
273 scr
->icon_title_font
, 4, 3, ws_numbers
, length
);
275 WMDrawString(scr
->wmscreen
, dicon
->icon
->core
->window
, scr
->white
,
276 scr
->icon_title_font
, 3, 2, ws_numbers
, length
);
280 #endif /* WS_INDICATOR */
282 void wAppIconPaint(WAppIcon
* aicon
)
285 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
287 if (aicon
->icon
->owner
)
288 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
292 wIconPaint(aicon
->icon
);
295 if (aicon
->docked
&& scr
->dock
&& scr
->dock
== aicon
->dock
&& aicon
->yindex
== 0)
296 updateDockNumbers(scr
);
298 if (scr
->dock_dots
&& aicon
->docked
&& !aicon
->running
&& aicon
->command
!= NULL
) {
299 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
300 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
301 XCopyArea(dpy
, scr
->dock_dots
->image
, aicon
->icon
->core
->window
,
302 scr
->copy_gc
, 0, 0, scr
->dock_dots
->width
, scr
->dock_dots
->height
, 0, 0);
305 if (wapp
&& wapp
->flags
.hidden
) {
306 XSetClipMask(dpy
, scr
->copy_gc
, scr
->dock_dots
->mask
);
307 XSetClipOrigin(dpy
, scr
->copy_gc
, 0, 0);
308 XCopyArea(dpy
, scr
->dock_dots
->image
,
309 aicon
->icon
->core
->window
, scr
->copy_gc
, 0, 0, 7, scr
->dock_dots
->height
, 0, 0);
311 #endif /* HIDDENDOT */
313 if (aicon
->omnipresent
)
314 drawCorner(aicon
->icon
);
316 XSetClipMask(dpy
, scr
->copy_gc
, None
);
317 if (aicon
->launching
) {
318 XFillRectangle(dpy
, aicon
->icon
->core
->window
, scr
->stipple_gc
,
319 0, 0, wPreferences
.icon_size
, wPreferences
.icon_size
);
323 #define canBeDocked(wwin) ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
325 static void hideCallback(WMenu
* menu
, WMenuEntry
* entry
)
327 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
329 if (wapp
->flags
.hidden
) {
330 wWorkspaceChange(menu
->menu
->screen_ptr
, wapp
->last_workspace
);
331 wUnhideApplication(wapp
, False
, False
);
333 wHideApplication(wapp
);
337 static void unhideHereCallback(WMenu
* menu
, WMenuEntry
* entry
)
339 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
341 wUnhideApplication(wapp
, False
, True
);
344 static void setIconCallback(WMenu
* menu
, WMenuEntry
* entry
)
346 WAppIcon
*icon
= ((WApplication
*) entry
->clientdata
)->app_icon
;
351 assert(icon
!= NULL
);
356 scr
= icon
->icon
->core
->screen_ptr
;
360 result
= wIconChooserDialog(scr
, &file
, icon
->wm_instance
, icon
->wm_class
);
362 if (result
&& !icon
->destroyed
) {
363 if (file
&& *file
== 0) {
367 if (!wIconChangeImageFile(icon
->icon
, file
)) {
368 wMessageDialog(scr
, _("Error"),
369 _("Could not open specified icon file"), _("OK"), NULL
, NULL
);
371 wDefaultChangeIcon(scr
, icon
->wm_instance
, icon
->wm_class
, file
);
381 static void killCallback(WMenu
* menu
, WMenuEntry
* entry
)
383 WApplication
*wapp
= (WApplication
*) entry
->clientdata
;
384 WFakeGroupLeader
*fPtr
;
387 char *basename(const char *shortname
);
389 if (!WCHECK_STATE(WSTATE_NORMAL
))
392 WCHANGE_STATE(WSTATE_MODAL
);
394 assert(entry
->clientdata
!= NULL
);
396 shortname
= basename(wapp
->app_icon
->wm_instance
);
398 buffer
= wstrconcat(wapp
->app_icon
? shortname
: NULL
,
399 _(" will be forcibly closed.\n"
400 "Any unsaved changes will be lost.\n" "Please confirm."));
402 fPtr
= wapp
->main_window_desc
->fake_group
;
404 wretain(wapp
->main_window_desc
);
405 if (wPreferences
.dont_confirm_kill
406 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
407 buffer
, _("Yes"), _("No"), NULL
) == WAPRDefault
) {
409 WWindow
*wwin
, *twin
;
411 wwin
= wapp
->main_window_desc
->screen_ptr
->focused_window
;
414 if (wwin
->fake_group
== fPtr
) {
419 } else if (!wapp
->main_window_desc
->flags
.destroyed
) {
420 wClientKill(wapp
->main_window_desc
);
423 wrelease(wapp
->main_window_desc
);
427 WCHANGE_STATE(WSTATE_NORMAL
);
430 static WMenu
*createApplicationMenu(WScreen
* scr
)
434 menu
= wMenuCreate(scr
, NULL
, False
);
435 wMenuAddCallback(menu
, _("Unhide Here"), unhideHereCallback
, NULL
);
436 wMenuAddCallback(menu
, _("Hide"), hideCallback
, NULL
);
437 wMenuAddCallback(menu
, _("Set Icon..."), setIconCallback
, NULL
);
438 wMenuAddCallback(menu
, _("Kill"), killCallback
, NULL
);
443 static void openApplicationMenu(WApplication
* wapp
, int x
, int y
)
446 WScreen
*scr
= wapp
->main_window_desc
->screen_ptr
;
449 if (!scr
->icon_menu
) {
450 scr
->icon_menu
= createApplicationMenu(scr
);
451 wfree(scr
->icon_menu
->entries
[1]->text
);
454 menu
= scr
->icon_menu
;
456 if (wapp
->flags
.hidden
) {
457 menu
->entries
[1]->text
= _("Unhide");
459 menu
->entries
[1]->text
= _("Hide");
462 menu
->flags
.realized
= 0;
465 x
-= menu
->frame
->core
->width
/ 2;
466 if (x
+ menu
->frame
->core
->width
> scr
->scr_width
)
467 x
= scr
->scr_width
- menu
->frame
->core
->width
;
471 /* set client data */
472 for (i
= 0; i
< menu
->entry_no
; i
++) {
473 menu
->entries
[i
]->clientdata
= wapp
;
475 wMenuMapAt(menu
, x
, y
, False
);
478 /******************************************************************/
480 static void iconExpose(WObjDescriptor
* desc
, XEvent
* event
)
482 wAppIconPaint(desc
->parent
);
485 static void iconDblClick(WObjDescriptor
* desc
, XEvent
* event
)
487 WAppIcon
*aicon
= desc
->parent
;
489 WScreen
*scr
= aicon
->icon
->core
->screen_ptr
;
492 assert(aicon
->icon
->owner
!= NULL
);
494 wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
497 wwarning("could not find application descriptor for app icon!!");
502 unhideHere
= (event
->xbutton
.state
& ShiftMask
);
504 /* go to the last workspace that the user worked on the app */
505 if (!unhideHere
&& wapp
->last_workspace
!= scr
->current_workspace
)
506 wWorkspaceChange(scr
, wapp
->last_workspace
);
508 wUnhideApplication(wapp
, event
->xbutton
.button
== Button2
, unhideHere
);
510 if (event
->xbutton
.state
& MOD_MASK
) {
511 wHideOtherApplications(aicon
->icon
->owner
);
515 void appIconMouseDown(WObjDescriptor
* desc
, XEvent
* event
)
517 WAppIcon
*aicon
= desc
->parent
;
518 WIcon
*icon
= aicon
->icon
;
520 int x
= aicon
->x_pos
, y
= aicon
->y_pos
;
521 int dx
= event
->xbutton
.x
, dy
= event
->xbutton
.y
;
524 int superfluous
= wPreferences
.superfluous
; /* we catch it to avoid problems */
525 WScreen
*scr
= icon
->core
->screen_ptr
;
526 WWorkspace
*workspace
= scr
->workspaces
[scr
->current_workspace
];
527 int shad_x
= 0, shad_y
= 0, docking
= 0, dockable
, collapsed
= 0;
529 int clickButton
= event
->xbutton
.button
;
532 Bool movingSingle
= False
;
535 Bool hasMoved
= False
;
537 if (aicon
->editing
|| WCHECK_STATE(WSTATE_MODAL
))
540 if (IsDoubleClick(scr
, event
)) {
541 iconDblClick(desc
, event
);
545 if (event
->xbutton
.button
== Button3
) {
546 WObjDescriptor
*desc
;
547 WApplication
*wapp
= wApplicationOf(aicon
->icon
->owner
->main_window
);
552 if (event
->xbutton
.send_event
&&
553 XGrabPointer(dpy
, aicon
->icon
->core
->window
, True
, ButtonMotionMask
554 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
555 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
556 wwarning("pointer grab failed for appicon menu");
560 openApplicationMenu(wapp
, event
->xbutton
.x_root
, event
->xbutton
.y_root
);
562 /* allow drag select of menu */
563 desc
= &scr
->icon_menu
->menu
->descriptor
;
564 event
->xbutton
.send_event
= True
;
565 (*desc
->handle_mousedown
) (desc
, event
);
571 if (event
->xbutton
.state
& MOD_MASK
)
572 wLowerFrame(icon
->core
);
574 wRaiseFrame(icon
->core
);
576 if (XGrabPointer(dpy
, icon
->core
->window
, True
, ButtonMotionMask
577 | ButtonReleaseMask
| ButtonPressMask
, GrabModeAsync
,
578 GrabModeAsync
, None
, None
, CurrentTime
) != GrabSuccess
) {
579 wwarning("pointer grab failed for appicon move");
582 if (wPreferences
.flags
.nodock
&& wPreferences
.flags
.noclip
)
585 dockable
= canBeDocked(icon
->owner
);
587 wins
[0] = icon
->core
->window
;
588 wins
[1] = scr
->dock_shadow
;
589 XRestackWindows(dpy
, wins
, 2);
591 if (icon
->pixmap
!= None
)
592 ghost
= MakeGhostIcon(scr
, icon
->pixmap
);
594 ghost
= MakeGhostIcon(scr
, icon
->core
->window
);
595 XSetWindowBackgroundPixmap(dpy
, scr
->dock_shadow
, ghost
);
596 XClearWindow(dpy
, scr
->dock_shadow
);
600 WMMaskEvent(dpy
, PointerMotionMask
| ButtonReleaseMask
| ButtonPressMask
601 | ButtonMotionMask
| ExposureMask
| EnterWindowMask
, &ev
);
608 /* It means the cursor moved so fast that it entered
609 * something else (if moving slowly, it would have
610 * stayed in the appIcon that is being moved. Ignore
611 * such "spurious" EnterNotifiy's */
617 if (abs(dx
- ev
.xmotion
.x
) >= MOVE_THRESHOLD
618 || abs(dy
- ev
.xmotion
.y
) >= MOVE_THRESHOLD
) {
619 XChangeActivePointerGrab(dpy
, ButtonMotionMask
620 | ButtonReleaseMask
| ButtonPressMask
,
621 wCursor
[WCUR_MOVE
], CurrentTime
);
627 x
= ev
.xmotion
.x_root
- dx
;
628 y
= ev
.xmotion
.y_root
- dy
;
631 XMoveWindow(dpy
, icon
->core
->window
, x
, y
);
633 wAppIconMove(aicon
, x
, y
);
637 if (scr
->dock
&& wDockSnapIcon(scr
->dock
, aicon
, x
, y
, &ix
, &iy
, False
)) {
638 shad_x
= scr
->dock
->x_pos
+ ix
* wPreferences
.icon_size
;
639 shad_y
= scr
->dock
->y_pos
+ iy
* wPreferences
.icon_size
;
641 if (scr
->last_dock
!= scr
->dock
&& collapsed
) {
642 scr
->last_dock
->collapsed
= 1;
643 wDockHideIcons(scr
->last_dock
);
646 if (!collapsed
&& (collapsed
= scr
->dock
->collapsed
)) {
647 scr
->dock
->collapsed
= 0;
648 wDockShowIcons(scr
->dock
);
651 if (scr
->dock
->auto_raise_lower
)
652 wDockRaise(scr
->dock
);
654 scr
->last_dock
= scr
->dock
;
656 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
658 XMapWindow(dpy
, scr
->dock_shadow
);
661 } else if (workspace
->clip
&&
662 wDockSnapIcon(workspace
->clip
, aicon
, x
, y
, &ix
, &iy
, False
)) {
663 shad_x
= workspace
->clip
->x_pos
+ ix
* wPreferences
.icon_size
;
664 shad_y
= workspace
->clip
->y_pos
+ iy
* wPreferences
.icon_size
;
666 if (scr
->last_dock
!= workspace
->clip
&& collapsed
) {
667 scr
->last_dock
->collapsed
= 1;
668 wDockHideIcons(scr
->last_dock
);
671 if (!collapsed
&& (collapsed
= workspace
->clip
->collapsed
)) {
672 workspace
->clip
->collapsed
= 0;
673 wDockShowIcons(workspace
->clip
);
676 if (workspace
->clip
->auto_raise_lower
)
677 wDockRaise(workspace
->clip
);
679 scr
->last_dock
= workspace
->clip
;
681 XMoveWindow(dpy
, scr
->dock_shadow
, shad_x
, shad_y
);
683 XMapWindow(dpy
, scr
->dock_shadow
);
686 } else if (docking
) {
687 XUnmapWindow(dpy
, scr
->dock_shadow
);
698 if (ev
.xbutton
.button
!= clickButton
)
700 XUngrabPointer(dpy
, CurrentTime
);
705 /* icon is trying to be docked */
706 SlideWindow(icon
->core
->window
, x
, y
, shad_x
, shad_y
);
707 XUnmapWindow(dpy
, scr
->dock_shadow
);
708 docked
= wDockAttachIcon(scr
->last_dock
, aicon
, ix
, iy
);
709 if (scr
->last_dock
->auto_collapse
) {
712 if (workspace
->clip
&&
713 workspace
->clip
!= scr
->last_dock
&& workspace
->clip
->auto_raise_lower
)
714 wDockLower(workspace
->clip
);
717 /* If icon could not be docked, slide it back to the old
719 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
722 wSoundPlay(WSOUND_DOCK
);
725 /* move back to its place */
726 SlideWindow(icon
->core
->window
, x
, y
, oldX
, oldY
);
727 wAppIconMove(aicon
, oldX
, oldY
);
729 XMoveWindow(dpy
, icon
->core
->window
, x
, y
);
733 if (workspace
->clip
&& workspace
->clip
->auto_raise_lower
)
734 wDockLower(workspace
->clip
);
737 scr
->last_dock
->collapsed
= 1;
738 wDockHideIcons(scr
->last_dock
);
743 XFreePixmap(dpy
, ghost
);
744 XSetWindowBackground(dpy
, scr
->dock_shadow
, scr
->white_pixel
);
747 if (wPreferences
.auto_arrange_icons
)
748 wArrangeIcons(scr
, True
);
750 if (wPreferences
.single_click
&& !hasMoved
)
751 iconDblClick(desc
, event
);
758 puts("End icon move");