Removed some #if 0 ... #endif surrounded code, related to KeepAttractedIcons
[wmaker-crm.git] / src / dock.c
blobb269e1f9a6c6c6a574683c6b2bdbfcf31f3b8ef4
1 /* dock.c- built-in Dock module for WindowMaker
3 * Window Maker window manager
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
6 * Copyright (c) 1998, 1999 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,
21 * USA.
25 #include "wconfig.h"
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <math.h>
33 #include <limits.h>
35 #ifndef PATH_MAX
36 #define PATH_MAX DEFAULT_PATH_MAX
37 #endif
39 #include "WindowMaker.h"
40 #include "wcore.h"
41 #include "window.h"
42 #include "icon.h"
43 #include "appicon.h"
44 #include "actions.h"
45 #include "stacking.h"
46 #include "dock.h"
47 #include "dialog.h"
48 #include "funcs.h"
49 #include "properties.h"
50 #include "menu.h"
51 #include "client.h"
52 #include "defaults.h"
53 #include "workspace.h"
54 #include "framewin.h"
55 #include "superfluous.h"
58 #include <proplist.h>
63 /**** Local variables ****/
64 #define CLIP_REWIND 1
65 #define CLIP_IDLE 0
66 #define CLIP_FORWARD 2
69 /**** Global variables ****/
71 /* in dockedapp.c */
72 extern void DestroyDockAppSettingsPanel();
74 extern void ShowDockAppSettingsPanel(WAppIcon *aicon);
77 extern XContext wWinContext;
79 extern Cursor wCursor[WCUR_LAST];
81 extern WPreferences wPreferences;
83 extern XContext wWinContext;
85 #ifdef OFFIX_DND
86 extern Atom _XA_DND_PROTOCOL;
87 #endif
90 #define MOD_MASK wPreferences.modifier_mask
92 extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);
94 #define ICON_SIZE wPreferences.icon_size
97 /***** Local variables ****/
99 static proplist_t dCommand=NULL;
100 #ifdef OFFIX_DND
101 static proplist_t dDropCommand=NULL;
102 #endif
103 static proplist_t dAutoLaunch, dName, dForced, dBuggyApplication, dYes, dNo;
104 static proplist_t dHost, dDock, dClip;
105 static proplist_t dAutoAttractIcons;
107 static proplist_t dPosition, dApplications, dLowered, dCollapsed, dAutoCollapse;
109 static proplist_t dAutoRaiseLower, dOmnipresent;
111 static void dockIconPaint(WAppIcon *btn);
113 static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
115 static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state);
117 static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);
119 static int getClipButton(int px, int py);
121 static void toggleLowered(WDock *dock);
123 static void toggleCollapsed(WDock *dock);
125 static void clipIconExpose(WObjDescriptor *desc, XEvent *event);
127 static void clipLeave(WDock *dock);
129 static void handleClipChangeWorkspace(WScreen *scr, XEvent *event);
131 Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y);
133 static void clipEnterNotify(WObjDescriptor *desc, XEvent *event);
134 static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event);
135 static void clipAutoCollapse(void *cdata);
136 static void clipAutoExpand(void *cdata);
137 static void launchDockedApplication(WAppIcon *btn);
139 static void clipAutoLower(void *cdata);
140 static void clipAutoRaise(void *cdata);
142 static void showClipBalloon(WDock *dock, int workspace);
144 #ifdef OFFIX_DND
146 #define DndNotDnd -1
147 #define DndUnknown 0
148 #define DndRawData 1
149 #define DndFile 2
150 #define DndFiles 3
151 #define DndText 4
152 #define DndDir 5
153 #define DndLink 6
154 #define DndExe 7
156 #define DndEND 8
158 #endif /* OFFIX_DND */
162 static void
163 make_keys()
165 if (dCommand!=NULL)
166 return;
168 dCommand = PLRetain(PLMakeString("Command"));
169 #ifdef OFFIX_DND
170 dDropCommand = PLRetain(PLMakeString("DropCommand"));
171 #endif
172 dAutoLaunch = PLRetain(PLMakeString("AutoLaunch"));
173 dName = PLRetain(PLMakeString("Name"));
174 dForced = PLRetain(PLMakeString("Forced"));
175 dBuggyApplication = PLRetain(PLMakeString("BuggyApplication"));
176 dYes = PLRetain(PLMakeString("Yes"));
177 dNo = PLRetain(PLMakeString("No"));
178 dHost = PLRetain(PLMakeString("Host"));
180 dPosition = PLMakeString("Position");
181 dApplications = PLMakeString("Applications");
182 dLowered = PLMakeString("Lowered");
183 dCollapsed = PLMakeString("Collapsed");
184 dAutoCollapse = PLMakeString("AutoCollapse");
185 dAutoRaiseLower = PLMakeString("AutoRaiseLower");
186 dAutoAttractIcons = PLMakeString("AutoAttractIcons");
188 dOmnipresent = PLMakeString("Omnipresent");
190 dDock = PLMakeString("Dock");
191 dClip = PLMakeString("Clip");
196 static void
197 renameCallback(WMenu *menu, WMenuEntry *entry)
199 WDock *dock = entry->clientdata;
200 char buffer[128];
201 int wspace;
202 char *name;
204 assert(entry->clientdata!=NULL);
206 wspace = dock->screen_ptr->current_workspace;
208 name = wstrdup(dock->screen_ptr->workspaces[wspace]->name);
210 sprintf(buffer, _("Type the name for workspace %i:"), wspace+1);
211 if (wInputDialog(dock->screen_ptr, _("Rename Workspace"), buffer,
212 &name)) {
213 wWorkspaceRename(dock->screen_ptr, wspace, name);
215 if (name) {
216 free(name);
221 static void
222 toggleLoweredCallback(WMenu *menu, WMenuEntry *entry)
224 assert(entry->clientdata!=NULL);
226 toggleLowered(entry->clientdata);
228 entry->flags.indicator_on = !((WDock*)entry->clientdata)->lowered;
230 wMenuPaint(menu);
235 static void
236 killCallback(WMenu *menu, WMenuEntry *entry)
238 WAppIcon *icon;
239 #ifdef REDUCE_APPICONS
240 WAppIconAppList *tapplist;
242 extern Atom _XA_WM_DELETE_WINDOW;
243 #else
244 char *buffer;
245 #endif
247 if (!WCHECK_STATE(WSTATE_NORMAL))
248 return;
250 assert(entry->clientdata!=NULL);
252 icon = (WAppIcon*)entry->clientdata;
254 icon->editing = 1;
256 WCHANGE_STATE(WSTATE_MODAL);
258 #ifdef REDUCE_APPICONS
259 /* Send a delete message to the main window of each application
260 * bound to this docked appicon. - cls
262 tapplist = icon->applist;
263 while (tapplist != NULL) {
264 if (tapplist->wapp->main_window_desc != NULL) {
265 if (tapplist->wapp->main_window_desc->protocols.DELETE_WINDOW) {
266 wClientSendProtocol(tapplist->wapp->main_window_desc,
267 _XA_WM_DELETE_WINDOW, CurrentTime);
268 } else {
269 wClientKill(tapplist->wapp->main_window_desc);
272 tapplist = tapplist->next;
274 #else
275 buffer = wstrappend(icon->wm_class,
276 _(" will be forcibly closed.\n"
277 "Any unsaved changes will be lost.\n"
278 "Please confirm."));
280 if (wPreferences.dont_confirm_kill
281 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
282 buffer, _("Yes"), _("No"), NULL)==WAPRDefault) {
283 if (icon->icon && icon->icon->owner) {
284 wClientKill(icon->icon->owner);
287 #endif /* !REDUCE_APPICONS */
289 icon->editing = 0;
291 WCHANGE_STATE(WSTATE_NORMAL);
295 /* TODO: replace this function with a member of the dock struct */
296 static int
297 numberOfSelectedIcons(WDock *dock)
299 WAppIcon *aicon;
300 int i, n;
302 n = 0;
303 for (i=1; i<dock->max_icons; i++) {
304 aicon = dock->icon_array[i];
305 if (aicon && aicon->icon->selected) {
306 n++;
310 return n;
314 static WMBag*
315 getSelected(WDock *dock)
317 WMBag *ret = WMCreateBag(8);
318 WAppIcon *btn;
319 int i;
321 for (i=1; i<dock->max_icons; i++) {
322 btn = dock->icon_array[i];
323 if (btn && btn->icon->selected) {
324 WMPutInBag(ret, btn);
328 return ret;
332 static void
333 paintClipButtons(WAppIcon *clipIcon, Bool lpushed, Bool rpushed)
335 Window win = clipIcon->icon->core->window;
336 WScreen *scr = clipIcon->icon->core->screen_ptr;
337 XPoint p[4];
338 int pt = CLIP_BUTTON_SIZE*ICON_SIZE/64;
339 int tp = ICON_SIZE - pt;
340 int as = pt - 15; /* 15 = 5+5+5 */
341 GC gc = scr->clip_title_gc;
342 #ifdef GRADIENT_CLIP_ARROW
343 Bool collapsed = clipIcon->dock->collapsed;
344 #endif
346 if (rpushed) {
347 p[0].x = tp+1;
348 p[0].y = 1;
349 p[1].x = ICON_SIZE-2;
350 p[1].y = 1;
351 p[2].x = ICON_SIZE-2;
352 p[2].y = pt-1;
353 } else if (lpushed) {
354 p[0].x = 1;
355 p[0].y = tp;
356 p[1].x = pt;
357 p[1].y = ICON_SIZE-2;
358 p[2].x = 1;
359 p[2].y = ICON_SIZE-2;
361 if (lpushed || rpushed) {
362 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
363 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
364 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
366 #ifdef GRADIENT_CLIP_ARROW
367 if (!collapsed) {
368 XSetFillStyle(dpy, scr->copy_gc, FillTiled);
369 XSetTile(dpy, scr->copy_gc, scr->clip_arrow_gradient);
370 XSetClipMask(dpy, scr->copy_gc, None);
371 gc = scr->copy_gc;
373 #endif /* GRADIENT_CLIP_ARROW */
375 /* top right arrow */
376 p[0].x = p[3].x = ICON_SIZE-5-as;
377 p[0].y = p[3].y = 5;
378 p[1].x = ICON_SIZE-6;
379 p[1].y = 5;
380 p[2].x = ICON_SIZE-6;
381 p[2].y = 4+as;
382 if (rpushed) {
383 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
384 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
385 } else {
386 #ifdef GRADIENT_CLIP_ARROW
387 if (!collapsed)
388 XSetTSOrigin(dpy, gc, ICON_SIZE-6-as, 5);
389 #endif
390 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
391 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
394 /* bottom left arrow */
395 p[0].x = p[3].x = 5;
396 p[0].y = p[3].y = ICON_SIZE-5-as;
397 p[1].x = 5;
398 p[1].y = ICON_SIZE-6;
399 p[2].x = 4+as;
400 p[2].y = ICON_SIZE-6;
401 if (lpushed) {
402 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
403 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
404 } else {
405 #ifdef GRADIENT_CLIP_ARROW
406 if (!collapsed)
407 XSetTSOrigin(dpy, gc, 5, ICON_SIZE-6-as);
408 #endif
409 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
410 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
412 #ifdef GRADIENT_CLIP_ARROW
413 if (!collapsed)
414 XSetFillStyle(dpy, scr->copy_gc, FillSolid);
415 #endif
419 RImage*
420 wClipMakeTile(WScreen *scr, RImage *normalTile)
422 RImage *tile = RCloneImage(normalTile);
423 RColor black;
424 RColor dark;
425 RColor light;
426 int pt, tp;
427 int as;
429 pt = CLIP_BUTTON_SIZE*wPreferences.icon_size/64;
430 tp = wPreferences.icon_size-1 - pt;
431 as = pt - 15;
433 black.alpha = 255;
434 black.red = black.green = black.blue = 0;
436 dark.alpha = 0;
437 dark.red = dark.green = dark.blue = 60;
439 light.alpha = 0;
440 light.red = light.green = light.blue = 80;
443 /* top right */
444 ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size-2,
445 pt-1, &dark);
446 RDrawLine(tile, tp-1, 0, wPreferences.icon_size-1, pt+1, &black);
447 ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size-3,
448 pt, &light);
450 /* arrow bevel */
451 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 7 - as, 4,
452 ICON_SIZE - 5, 4, &dark);
453 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 6 - as, 5,
454 ICON_SIZE - 5, 6 + as, &dark);
455 ROperateLine(tile, RAddOperation, ICON_SIZE - 5, 4, ICON_SIZE - 5, 6 + as,
456 &light);
458 /* bottom left */
459 ROperateLine(tile, RAddOperation, 2, tp+2, pt-2,
460 wPreferences.icon_size-3, &dark);
461 RDrawLine(tile, 0, tp-1, pt+1, wPreferences.icon_size-1, &black);
462 ROperateLine(tile, RSubtractOperation, 0, tp-2, pt+1,
463 wPreferences.icon_size-2, &light);
465 /* arrow bevel */
466 ROperateLine(tile, RSubtractOperation, 4, ICON_SIZE - 7 - as, 4,
467 ICON_SIZE - 5, &dark);
468 ROperateLine(tile, RSubtractOperation, 5, ICON_SIZE - 6 - as,
469 6 + as, ICON_SIZE - 5, &dark);
470 ROperateLine(tile, RAddOperation, 4, ICON_SIZE - 5, 6 + as, ICON_SIZE - 5,
471 &light);
473 return tile;
477 static void
478 omnipresentCallback(WMenu *menu, WMenuEntry *entry)
480 WAppIcon *clickedIcon = entry->clientdata;
481 WAppIcon *aicon;
482 WDock *dock;
483 WMBag *selectedIcons;
484 int failed;
485 int i;
487 assert(entry->clientdata!=NULL);
489 dock = clickedIcon->dock;
491 selectedIcons = getSelected(dock);
493 if (!WMGetBagItemCount(selectedIcons))
494 WMPutInBag(selectedIcons, clickedIcon);
496 failed = 0;
497 for (i = 0; i < WMGetBagItemCount(selectedIcons); i++) {
498 aicon = WMGetFromBag(selectedIcons, i);
500 if (wClipMakeIconOmnipresent(aicon, !aicon->omnipresent) == WO_FAILED)
501 failed++;
502 else if (aicon->icon->selected)
503 wIconSelect(aicon->icon);
505 WMFreeBag(selectedIcons);
507 if (failed > 1) {
508 wMessageDialog(dock->screen_ptr, _("Warning"),
509 _("Some icons cannot be made omnipresent. "
510 "Please make sure that no other icon is "
511 "docked in the same positions on the other "
512 "workspaces and the Clip is not full in "
513 "some workspace."),
514 _("OK"), NULL, NULL);
515 } else if (failed == 1) {
516 wMessageDialog(dock->screen_ptr, _("Warning"),
517 _("Icon cannot be made omnipresent. "
518 "Please make sure that no other icon is "
519 "docked in the same position on the other "
520 "workspaces and the Clip is not full in "
521 "some workspace."),
522 _("OK"), NULL, NULL);
527 static void
528 removeIconsCallback(WMenu *menu, WMenuEntry *entry)
530 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
531 WDock *dock;
532 WAppIcon *aicon;
533 WMBag *selectedIcons;
534 int keepit;
535 int i;
537 assert(clickedIcon!=NULL);
539 dock = clickedIcon->dock;
541 selectedIcons = getSelected(dock);
543 if (WMGetBagItemCount(selectedIcons)) {
544 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
545 _("All selected icons will be removed!"),
546 _("OK"), _("Cancel"), NULL)!=WAPRDefault) {
547 WMFreeBag(selectedIcons);
548 return;
550 } else {
551 if (clickedIcon->xindex==0 && clickedIcon->yindex==0) {
552 WMFreeBag(selectedIcons);
553 return;
555 WMPutInBag(selectedIcons, clickedIcon);
558 for (i = 0; i < WMGetBagItemCount(selectedIcons); i++) {
559 aicon = WMGetFromBag(selectedIcons, i);
560 keepit = aicon->running && wApplicationOf(aicon->main_window);
561 wDockDetach(dock, aicon);
562 if (keepit) {
563 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
564 XMoveWindow(dpy, aicon->icon->core->window,
565 aicon->x_pos, aicon->y_pos);
566 if (!dock->mapped || dock->collapsed)
567 XMapWindow(dpy, aicon->icon->core->window);
570 WMFreeBag(selectedIcons);
572 if (wPreferences.auto_arrange_icons)
573 wArrangeIcons(dock->screen_ptr, True);
577 static void
578 keepIconsCallback(WMenu *menu, WMenuEntry *entry)
580 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
581 WDock *dock;
582 WAppIcon *aicon;
583 WMBag *selectedIcons;
584 int i;
586 assert(clickedIcon!=NULL);
587 dock = clickedIcon->dock;
589 selectedIcons = getSelected(dock);
591 if (!WMGetBagItemCount(selectedIcons)
592 && clickedIcon!=dock->screen_ptr->clip_icon) {
593 char *command = NULL;
595 if (!clickedIcon->command && !clickedIcon->editing) {
596 clickedIcon->editing = 1;
597 if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
598 _("Type the command used to launch the application"),
599 &command)) {
600 if (command && (command[0]==0 ||
601 (command[0]=='-' && command[1]==0))) {
602 free(command);
603 command = NULL;
605 clickedIcon->command = command;
606 clickedIcon->editing = 0;
607 } else {
608 clickedIcon->editing = 0;
609 if (command)
610 free(command);
611 WMFreeBag(selectedIcons);
612 return;
616 WMPutInBag(selectedIcons, clickedIcon);
619 for (i = 0; i < WMGetBagItemCount(selectedIcons); i++) {
620 aicon = WMGetFromBag(selectedIcons, i);
621 if (aicon->icon->selected)
622 wIconSelect(aicon->icon);
623 if (aicon && aicon->attracted && aicon->command) {
624 aicon->attracted = 0;
625 if (aicon->icon->shadowed) {
626 aicon->icon->shadowed = 0;
627 aicon->icon->force_paint = 1;
628 wAppIconPaint(aicon);
632 WMFreeBag(selectedIcons);
638 static void
639 toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
641 WDock *dock = (WDock*)entry->clientdata;
643 assert(entry->clientdata!=NULL);
645 dock->attract_icons = !dock->attract_icons;
646 /*if (!dock->attract_icons)
647 dock->keep_attracted = 0;*/
649 entry->flags.indicator_on = dock->attract_icons;
651 wMenuPaint(menu);
655 static void
656 selectCallback(WMenu *menu, WMenuEntry *entry)
658 WAppIcon *icon = (WAppIcon*)entry->clientdata;
660 assert(icon!=NULL);
662 wIconSelect(icon->icon);
664 wMenuPaint(menu);
668 static void
669 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
671 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
672 WDock *clip;
673 WAppIcon *aicon;
674 int x, y, x_pos, y_pos;
676 assert(entry->clientdata!=NULL);
677 clip = clickedIcon->dock;
679 aicon = clip->screen_ptr->app_icon_list;
681 while (aicon) {
682 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
683 x_pos = clip->x_pos + x*ICON_SIZE;
684 y_pos = clip->y_pos + y*ICON_SIZE;
685 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
686 #ifdef ANIMATIONS
687 if (wPreferences.no_animations) {
688 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
689 } else {
690 SlideWindow(aicon->icon->core->window,
691 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
693 #else
694 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
695 #endif /* ANIMATIONS */
697 aicon->attracted = 1;
698 if (!aicon->icon->shadowed) {
699 aicon->icon->shadowed = 1;
700 aicon->icon->force_paint = 1;
701 /* We don't do an wAppIconPaint() here because it's in
702 * wDockAttachIcon(). -Dan
705 wDockAttachIcon(clip, aicon, x, y);
706 if (clip->collapsed || !clip->mapped)
707 XUnmapWindow(dpy, aicon->icon->core->window);
709 aicon = aicon->next;
714 static void
715 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
717 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
718 WDock *dock;
719 WMBag *selectedIcons;
720 WAppIcon *btn;
721 int i;
723 assert(clickedIcon!=NULL);
724 dock = clickedIcon->dock;
726 selectedIcons = getSelected(dock);
728 if (!WMGetBagItemCount(selectedIcons)) {
729 for (i=1; i<dock->max_icons; i++) {
730 btn = dock->icon_array[i];
731 if (btn && !btn->icon->selected) {
732 wIconSelect(btn->icon);
735 } else {
736 for (i = 0; i < WMGetBagItemCount(selectedIcons); i++) {
737 btn = WMGetFromBag(selectedIcons, i);
738 wIconSelect(btn->icon);
741 WMFreeBag(selectedIcons);
743 wMenuPaint(menu);
747 static void
748 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
750 assert(entry->clientdata!=NULL);
752 toggleCollapsed(entry->clientdata);
754 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
756 wMenuPaint(menu);
760 static void
761 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
763 WDock *dock;
764 assert(entry->clientdata!=NULL);
766 dock = (WDock*) entry->clientdata;
768 dock->auto_collapse = !dock->auto_collapse;
770 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
772 wMenuPaint(menu);
776 static void
777 toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
779 WDock *dock;
780 assert(entry->clientdata!=NULL);
782 dock = (WDock*) entry->clientdata;
784 dock->auto_raise_lower = !dock->auto_raise_lower;
786 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_raise_lower;
788 wMenuPaint(menu);
792 static void
793 launchCallback(WMenu *menu, WMenuEntry *entry)
795 WAppIcon *btn = (WAppIcon*)entry->clientdata;
797 launchDockedApplication(btn);
801 static void
802 settingsCallback(WMenu *menu, WMenuEntry *entry)
804 WAppIcon *btn = (WAppIcon*)entry->clientdata;
806 if (btn->editing)
807 return;
808 ShowDockAppSettingsPanel(btn);
812 static void
813 hideCallback(WMenu *menu, WMenuEntry *entry)
815 WApplication *wapp;
816 WAppIcon *btn = (WAppIcon*)entry->clientdata;
818 wapp = wApplicationOf(btn->icon->owner->main_window);
820 if (wapp->flags.hidden) {
821 wWorkspaceChange(btn->icon->core->screen_ptr,wapp->last_workspace);
822 wUnhideApplication(wapp, False, False);
823 } else {
824 wHideApplication(wapp);
829 static void
830 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
832 WApplication *wapp;
833 WAppIcon *btn = (WAppIcon*)entry->clientdata;
835 wapp = wApplicationOf(btn->icon->owner->main_window);
837 wUnhideApplication(wapp, False, True);
841 WAppIcon*
842 mainIconCreate(WScreen *scr, int type)
844 WAppIcon *btn;
845 int x_pos;
847 if (type == WM_CLIP) {
848 if (scr->clip_icon)
849 return scr->clip_icon;
850 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
851 btn->icon->core->descriptor.handle_expose = clipIconExpose;
852 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
853 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
854 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
855 x_pos = 0;
856 } else {
857 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
858 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
861 btn->xindex = 0;
862 btn->yindex = 0;
864 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
865 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
866 btn->icon->core->descriptor.parent = btn;
867 /*ChangeStackingLevel(btn->icon->core, WMDockLevel);*/
868 XMapWindow(dpy, btn->icon->core->window);
869 btn->x_pos = x_pos;
870 btn->y_pos = 0;
871 btn->docked = 1;
872 if (type == WM_CLIP)
873 scr->clip_icon = btn;
875 return btn;
879 static void
880 switchWSCommand(WMenu *menu, WMenuEntry *entry)
882 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
883 WScreen *scr = icon->icon->core->screen_ptr;
884 WDock *src, *dest;
885 WMBag *selectedIcons;
886 int x, y;
888 if (entry->order == scr->current_workspace)
889 return;
890 src = icon->dock;
891 dest = scr->workspaces[entry->order]->clip;
893 selectedIcons = getSelected(src);
895 if (WMGetBagItemCount(selectedIcons)) {
896 int i;
897 for (i = 0; i < WMGetBagItemCount(selectedIcons); i++) {
898 btn = WMGetFromBag(selectedIcons, i);
899 if (wDockFindFreeSlot(dest, &x, &y)) {
900 moveIconBetweenDocks(src, dest, btn, x, y);
901 XUnmapWindow(dpy, btn->icon->core->window);
904 } else if (icon != scr->clip_icon) {
905 if (wDockFindFreeSlot(dest, &x, &y)) {
906 moveIconBetweenDocks(src, dest, icon, x, y);
907 XUnmapWindow(dpy, icon->icon->core->window);
910 WMFreeBag(selectedIcons);
915 static void
916 launchDockedApplication(WAppIcon *btn)
918 WScreen *scr = btn->icon->core->screen_ptr;
920 if (!btn->launching && btn->command!=NULL) {
921 if (!btn->forced_dock) {
922 btn->relaunching = btn->running;
923 btn->running = 1;
925 if (btn->wm_instance || btn->wm_class) {
926 WWindowAttributes attr;
927 memset(&attr, 0, sizeof(WWindowAttributes));
928 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class,
929 &attr, NULL, True);
931 if (!attr.no_appicon && !btn->buggy_app)
932 btn->launching = 1;
933 else
934 btn->running = 0;
936 btn->drop_launch = 0;
937 scr->last_dock = btn->dock;
938 btn->pid = execCommand(btn, btn->command, NULL);
939 if (btn->pid>0) {
940 if (btn->buggy_app) {
941 /* give feedback that the app was launched */
942 btn->launching = 1;
943 dockIconPaint(btn);
944 btn->launching = 0;
945 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
946 } else {
947 dockIconPaint(btn);
949 } else {
950 wwarning(_("could not launch application %s\n"), btn->command);
951 btn->launching = 0;
952 if (!btn->relaunching)
953 btn->running = 0;
960 static void
961 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
963 WScreen *scr = menu->frame->screen_ptr;
964 char title[MAX_WORKSPACENAME_WIDTH+1];
965 int i;
967 if (!menu || !icon)
968 return;
970 for (i=0; i<scr->workspace_count; i++) {
971 if (i < menu->entry_no) {
972 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
973 free(menu->entries[i]->text);
974 strcpy(title, scr->workspaces[i]->name);
975 menu->entries[i]->text = wstrdup(title);
976 menu->flags.realized = 0;
978 menu->entries[i]->clientdata = (void*)icon;
979 } else {
980 strcpy(title, scr->workspaces[i]->name);
982 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
984 menu->flags.realized = 0;
986 if (i == scr->current_workspace) {
987 wMenuSetEnabled(menu, i, False);
988 } else {
989 wMenuSetEnabled(menu, i, True);
993 if (!menu->flags.realized)
994 wMenuRealize(menu);
998 static WMenu*
999 makeWorkspaceMenu(WScreen *scr)
1001 WMenu *menu;
1003 menu = wMenuCreate(scr, NULL, False);
1004 if (!menu)
1005 wwarning(_("could not create workspace submenu for Clip menu"));
1007 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
1009 menu->flags.realized = 0;
1010 wMenuRealize(menu);
1012 return menu;
1016 static void
1017 updateClipOptionsMenu(WMenu *menu, WDock *dock)
1019 WMenuEntry *entry;
1020 int index = 0;
1022 if (!menu || !dock)
1023 return;
1025 /* keep on top */
1026 entry = menu->entries[index];
1027 entry->flags.indicator_on = !dock->lowered;
1028 entry->clientdata = dock;
1030 /* collapsed */
1031 entry = menu->entries[++index];
1032 entry->flags.indicator_on = dock->collapsed;
1033 entry->clientdata = dock;
1035 /* auto-collapse */
1036 entry = menu->entries[++index];
1037 entry->flags.indicator_on = dock->auto_collapse;
1038 entry->clientdata = dock;
1040 /* auto-raise/lower */
1041 entry = menu->entries[++index];
1042 entry->flags.indicator_on = dock->auto_raise_lower;
1043 entry->clientdata = dock;
1045 /* attract icons */
1046 entry = menu->entries[++index];
1047 entry->flags.indicator_on = dock->attract_icons;
1048 entry->clientdata = dock;
1050 menu->flags.realized = 0;
1051 wMenuRealize(menu);
1055 static WMenu*
1056 makeClipOptionsMenu(WScreen *scr)
1058 WMenu *menu;
1059 WMenuEntry *entry;
1061 menu = wMenuCreate(scr, NULL, False);
1062 if (!menu) {
1063 wwarning(_("could not create options submenu for Clip menu"));
1064 return NULL;
1067 entry = wMenuAddCallback(menu, _("Keep on Top"),
1068 toggleLoweredCallback, NULL);
1069 entry->flags.indicator = 1;
1070 entry->flags.indicator_on = 1;
1071 entry->flags.indicator_type = MI_CHECK;
1073 entry = wMenuAddCallback(menu, _("Collapsed"),
1074 toggleCollapsedCallback, NULL);
1075 entry->flags.indicator = 1;
1076 entry->flags.indicator_on = 1;
1077 entry->flags.indicator_type = MI_CHECK;
1079 entry = wMenuAddCallback(menu, _("AutoCollapse"),
1080 toggleAutoCollapseCallback, NULL);
1081 entry->flags.indicator = 1;
1082 entry->flags.indicator_on = 1;
1083 entry->flags.indicator_type = MI_CHECK;
1085 entry = wMenuAddCallback(menu, _("AutoRaiseLower"),
1086 toggleAutoRaiseLowerCallback, NULL);
1087 entry->flags.indicator = 1;
1088 entry->flags.indicator_on = 1;
1089 entry->flags.indicator_type = MI_CHECK;
1091 entry = wMenuAddCallback(menu, _("AutoAttract Icons"),
1092 toggleAutoAttractCallback, NULL);
1093 entry->flags.indicator = 1;
1094 entry->flags.indicator_on = 1;
1095 entry->flags.indicator_type = MI_CHECK;
1097 menu->flags.realized = 0;
1098 wMenuRealize(menu);
1100 return menu;
1104 static WMenu*
1105 dockMenuCreate(WScreen *scr, int type)
1107 WMenu *menu;
1108 WMenuEntry *entry;
1110 if (type == WM_CLIP && scr->clip_menu)
1111 return scr->clip_menu;
1113 menu = wMenuCreate(scr, NULL, False);
1114 if (type != WM_CLIP) {
1115 entry = wMenuAddCallback(menu, _("Keep on top"),
1116 toggleLoweredCallback, NULL);
1117 entry->flags.indicator = 1;
1118 entry->flags.indicator_on = 1;
1119 entry->flags.indicator_type = MI_CHECK;
1120 } else {
1121 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1122 scr->clip_options = makeClipOptionsMenu(scr);
1123 if (scr->clip_options)
1124 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1126 entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback,
1127 NULL);
1128 free(entry->text);
1129 entry->text = _("Rename Workspace");
1131 entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
1132 entry->flags.indicator = 1;
1133 entry->flags.indicator_on = 1;
1134 entry->flags.indicator_type = MI_CHECK;
1136 entry = wMenuAddCallback(menu, _("Select All Icons"),
1137 selectIconsCallback, NULL);
1138 free(entry->text);
1139 entry->text = _("Select All Icons");
1141 entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
1142 free(entry->text);
1143 entry->text = _("Keep Icon");
1145 entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
1146 free(entry->text);
1147 entry->text = _("Move Icon To");
1148 scr->clip_submenu = makeWorkspaceMenu(scr);
1149 if (scr->clip_submenu)
1150 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1152 entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback,
1153 NULL);
1154 free(entry->text);
1155 entry->text = _("Remove Icon");
1157 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1160 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1162 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1164 entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1165 free(entry->text);
1166 entry->text = _("Hide");
1168 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1170 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1172 if (type == WM_CLIP)
1173 scr->clip_menu = menu;
1175 return menu;
1179 WDock*
1180 wDockCreate(WScreen *scr, int type)
1182 WDock *dock;
1183 WAppIcon *btn;
1184 int icon_count;
1186 make_keys();
1188 dock = wmalloc(sizeof(WDock));
1189 memset(dock, 0, sizeof(WDock));
1191 if (type == WM_CLIP)
1192 icon_count = CLIP_MAX_ICONS;
1193 else
1194 icon_count = scr->scr_height/wPreferences.icon_size;
1196 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1197 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1199 dock->max_icons = icon_count;
1201 btn = mainIconCreate(scr, type);
1203 btn->dock = dock;
1205 dock->x_pos = btn->x_pos;
1206 dock->y_pos = btn->y_pos;
1207 dock->screen_ptr = scr;
1208 dock->type = type;
1209 dock->icon_count = 1;
1210 dock->on_right_side = 1;
1211 dock->collapsed = 0;
1212 dock->auto_collapse = 0;
1213 dock->auto_collapse_magic = NULL;
1214 dock->auto_raise_lower = 0;
1215 dock->auto_lower_magic = NULL;
1216 dock->auto_raise_magic = NULL;
1217 dock->attract_icons = 0;
1218 dock->lowered = 1;
1219 dock->icon_array[0] = btn;
1220 wRaiseFrame(btn->icon->core);
1221 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1223 /* create dock menu */
1224 dock->menu = dockMenuCreate(scr, type);
1226 return dock;
1230 void
1231 wDockDestroy(WDock *dock)
1233 int i;
1234 WAppIcon *aicon;
1236 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1237 aicon = dock->icon_array[i];
1238 if (aicon) {
1239 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1240 wDockDetach(dock, aicon);
1241 if (keepit) {
1242 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
1243 XMoveWindow(dpy, aicon->icon->core->window,
1244 aicon->x_pos, aicon->y_pos);
1245 if (!dock->mapped || dock->collapsed)
1246 XMapWindow(dpy, aicon->icon->core->window);
1250 if (wPreferences.auto_arrange_icons)
1251 wArrangeIcons(dock->screen_ptr, True);
1252 free(dock->icon_array);
1253 if (dock->menu && dock->type!=WM_CLIP)
1254 wMenuDestroy(dock->menu, True);
1255 free(dock);
1259 void
1260 wClipIconPaint(WAppIcon *aicon)
1262 WScreen *scr = aicon->icon->core->screen_ptr;
1263 WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1264 GC gc;
1265 Window win = aicon->icon->core->window;
1266 int length, nlength;
1267 char *ws_name, ws_number[10];
1268 int ty, tx;
1270 wIconPaint(aicon->icon);
1272 length = strlen(workspace->name);
1273 ws_name = malloc(length + 1);
1274 sprintf(ws_name, "%s", workspace->name);
1275 sprintf(ws_number, "%i", scr->current_workspace + 1);
1276 nlength = strlen(ws_number);
1278 gc = scr->clip_title_gc;
1280 if (!workspace->clip->collapsed)
1281 XSetForeground(dpy, gc, scr->clip_title_pixel[CLIP_NORMAL]);
1282 else
1283 XSetForeground(dpy, gc, scr->clip_title_pixel[CLIP_COLLAPSED]);
1285 ty = ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
1287 tx = CLIP_BUTTON_SIZE*ICON_SIZE/64;
1289 WMDrawString(scr->wmscreen, win, gc, scr->clip_title_font, tx,
1290 ty, ws_name, length);
1292 tx = (ICON_SIZE/2 - WMWidthOfString(scr->clip_title_font, ws_number,
1293 nlength))/2;
1295 WMDrawString(scr->wmscreen, win, gc, scr->clip_title_font, tx,
1296 2, ws_number, nlength);
1298 free(ws_name);
1300 if (aicon->launching) {
1301 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1302 0, 0, wPreferences.icon_size, wPreferences.icon_size);
1304 paintClipButtons(aicon, aicon->dock->lclip_button_pushed,
1305 aicon->dock->rclip_button_pushed);
1309 static void
1310 clipIconExpose(WObjDescriptor *desc, XEvent *event)
1312 wClipIconPaint(desc->parent);
1316 static void
1317 dockIconPaint(WAppIcon *btn)
1319 if (btn == btn->icon->core->screen_ptr->clip_icon)
1320 wClipIconPaint(btn);
1321 else
1322 wAppIconPaint(btn);
1326 static proplist_t
1327 make_icon_state(WAppIcon *btn)
1329 proplist_t node = NULL;
1330 proplist_t command, autolaunch, name, forced, host, position, buggy;
1331 proplist_t omnipresent;
1332 char *tmp;
1333 char buffer[64];
1335 if (btn) {
1336 if (!btn->command)
1337 command = PLMakeString("-");
1338 else
1339 command = PLMakeString(btn->command);
1341 autolaunch = btn->auto_launch ? dYes : dNo;
1343 tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
1345 name = PLMakeString(tmp);
1347 free(tmp);
1349 forced = btn->forced_dock ? dYes : dNo;
1351 buggy = btn->buggy_app ? dYes : dNo;
1353 if (btn == btn->icon->core->screen_ptr->clip_icon)
1354 sprintf(buffer, "%i,%i", btn->x_pos, btn->y_pos);
1355 else
1356 sprintf(buffer, "%hi,%hi", btn->xindex, btn->yindex);
1357 position = PLMakeString(buffer);
1359 node = PLMakeDictionaryFromEntries(dCommand, command,
1360 dName, name,
1361 dAutoLaunch, autolaunch,
1362 dForced, forced,
1363 dBuggyApplication, buggy,
1364 dPosition, position,
1365 NULL);
1366 PLRelease(command);
1367 PLRelease(name);
1368 PLRelease(position);
1370 omnipresent = btn->omnipresent ? dYes : dNo;
1371 if (btn->dock != btn->icon->core->screen_ptr->dock &&
1372 (btn->xindex != 0 || btn->yindex != 0))
1373 PLInsertDictionaryEntry(node, dOmnipresent, omnipresent);
1375 #ifdef OFFIX_DND
1376 if (btn->dnd_command) {
1377 command = PLMakeString(btn->dnd_command);
1378 PLInsertDictionaryEntry(node, dDropCommand, command);
1379 PLRelease(command);
1381 #endif /* OFFIX_DND */
1383 if (btn->client_machine && btn->remote_start) {
1384 host = PLMakeString(btn->client_machine);
1385 PLInsertDictionaryEntry(node, dHost, host);
1386 PLRelease(host);
1390 return node;
1394 static proplist_t
1395 dockSaveState(WDock *dock)
1397 int i;
1398 proplist_t icon_info;
1399 proplist_t list=NULL, dock_state=NULL;
1400 proplist_t value, key;
1401 char buffer[256];
1403 list = PLMakeArrayFromElements(NULL);
1405 for (i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
1406 WAppIcon *btn = dock->icon_array[i];
1408 if (!btn || btn->attracted)
1409 continue;
1411 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1412 list = PLAppendArrayElement(list, icon_info);
1413 PLRelease(icon_info);
1417 dock_state = PLMakeDictionaryFromEntries(dApplications, list,
1418 NULL);
1420 if (dock->type == WM_DOCK) {
1421 sprintf(buffer, "Applications%i", dock->screen_ptr->scr_height);
1422 key = PLMakeString(buffer);
1423 PLInsertDictionaryEntry(dock_state, key, list);
1424 PLRelease(key);
1427 sprintf(buffer, "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0),
1428 dock->y_pos);
1429 value = PLMakeString(buffer);
1430 PLInsertDictionaryEntry(dock_state, dPosition, value);
1431 PLRelease(value);
1433 PLRelease(list);
1436 value = (dock->lowered ? dYes : dNo);
1437 PLInsertDictionaryEntry(dock_state, dLowered, value);
1439 if (dock->type == WM_CLIP) {
1440 value = (dock->collapsed ? dYes : dNo);
1441 PLInsertDictionaryEntry(dock_state, dCollapsed, value);
1443 value = (dock->auto_collapse ? dYes : dNo);
1444 PLInsertDictionaryEntry(dock_state, dAutoCollapse, value);
1446 value = (dock->auto_raise_lower ? dYes : dNo);
1447 PLInsertDictionaryEntry(dock_state, dAutoRaiseLower, value);
1449 value = (dock->attract_icons ? dYes : dNo);
1450 PLInsertDictionaryEntry(dock_state, dAutoAttractIcons, value);
1453 return dock_state;
1457 void
1458 wDockSaveState(WScreen *scr, proplist_t old_state)
1460 proplist_t dock_state;
1461 proplist_t keys;
1463 dock_state = dockSaveState(scr->dock);
1466 * Copy saved states of docks with different sizes.
1468 if (old_state) {
1469 int i;
1470 proplist_t tmp;
1472 keys = PLGetAllDictionaryKeys(old_state);
1473 for (i = 0; i < PLGetNumberOfElements(keys); i++) {
1474 tmp = PLGetArrayElement(keys, i);
1476 if (strncasecmp(PLGetString(tmp), "applications", 12) == 0
1477 && !PLGetDictionaryEntry(dock_state, tmp)) {
1479 PLInsertDictionaryEntry(dock_state,
1480 tmp,
1481 PLGetDictionaryEntry(old_state, tmp));
1484 PLRelease(keys);
1488 PLInsertDictionaryEntry(scr->session_state, dDock, dock_state);
1490 PLRelease(dock_state);
1494 void
1495 wClipSaveState(WScreen *scr)
1497 proplist_t clip_state;
1499 clip_state = make_icon_state(scr->clip_icon);
1501 PLInsertDictionaryEntry(scr->session_state, dClip, clip_state);
1503 PLRelease(clip_state);
1507 proplist_t
1508 wClipSaveWorkspaceState(WScreen *scr, int workspace)
1510 return dockSaveState(scr->workspaces[workspace]->clip);
1514 static WAppIcon*
1515 restore_icon_state(WScreen *scr, proplist_t info, int type, int index)
1517 WAppIcon *aicon;
1518 char *wclass, *winstance;
1519 proplist_t cmd, value;
1520 char *command;
1523 cmd = PLGetDictionaryEntry(info, dCommand);
1524 if (!cmd || !PLIsString(cmd)) {
1525 return NULL;
1528 /* parse window name */
1529 value = PLGetDictionaryEntry(info, dName);
1530 if (!value)
1531 return NULL;
1533 ParseWindowName(value, &winstance, &wclass, "dock");
1535 if (!winstance && !wclass) {
1536 return NULL;
1539 /* get commands */
1541 if (cmd)
1542 command = wstrdup(PLGetString(cmd));
1543 else
1544 command = NULL;
1546 if (!command || strcmp(command, "-")==0) {
1547 if (command)
1548 free(command);
1549 if (wclass)
1550 free(wclass);
1551 if (winstance)
1552 free(winstance);
1554 return NULL;
1557 aicon = wAppIconCreateForDock(scr, command, winstance, wclass,
1558 TILE_NORMAL);
1559 if (wclass)
1560 free(wclass);
1561 if (winstance)
1562 free(winstance);
1564 aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1565 if (type == WM_CLIP) {
1566 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1567 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1569 aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1570 aicon->icon->core->descriptor.parent = aicon;
1573 #ifdef OFFIX_DND
1574 cmd = PLGetDictionaryEntry(info, dDropCommand);
1575 if (cmd)
1576 aicon->dnd_command = wstrdup(PLGetString(cmd));
1577 #endif
1579 /* check auto launch */
1580 value = PLGetDictionaryEntry(info, dAutoLaunch);
1582 aicon->auto_launch = 0;
1583 if (value) {
1584 if (PLIsString(value)) {
1585 if (strcasecmp(PLGetString(value), "YES")==0)
1586 aicon->auto_launch = 1;
1587 } else {
1588 wwarning(_("bad value in docked icon state info %s"),
1589 PLGetString(dAutoLaunch));
1593 /* check if it wasn't normally docked */
1594 value = PLGetDictionaryEntry(info, dForced);
1596 aicon->forced_dock = 0;
1597 if (value) {
1598 if (PLIsString(value)) {
1599 if (strcasecmp(PLGetString(value), "YES")==0)
1600 aicon->forced_dock = 1;
1601 } else {
1602 wwarning(_("bad value in docked icon state info %s"),
1603 PLGetString(dForced));
1607 /* check if we can rely on the stuff in the app */
1608 value = PLGetDictionaryEntry(info, dBuggyApplication);
1610 aicon->buggy_app = 0;
1611 if (value) {
1612 if (PLIsString(value)) {
1613 if (strcasecmp(PLGetString(value), "YES")==0)
1614 aicon->buggy_app = 1;
1615 } else {
1616 wwarning(_("bad value in docked icon state info %s"),
1617 PLGetString(dBuggyApplication));
1621 /* get position in the dock */
1622 value = PLGetDictionaryEntry(info, dPosition);
1623 if (value && PLIsString(value)) {
1624 if (sscanf(PLGetString(value), "%hi,%hi", &aicon->xindex,
1625 &aicon->yindex)!=2)
1626 wwarning(_("bad value in docked icon state info %s"),
1627 PLGetString(dPosition));
1629 /* check position sanity */
1630 /* incomplete section! */
1631 if (type == WM_DOCK) {
1632 aicon->xindex = 0;
1633 if (aicon->yindex < 0)
1634 wwarning(_("bad value in docked icon position %i,%i"),
1635 aicon->xindex, aicon->yindex);
1637 } else {
1638 aicon->yindex = index;
1639 aicon->xindex = 0;
1642 /* check if icon is omnipresent */
1643 value = PLGetDictionaryEntry(info, dOmnipresent);
1645 aicon->omnipresent = 0;
1646 if (value) {
1647 if (PLIsString(value)) {
1648 if (strcasecmp(PLGetString(value), "YES")==0)
1649 aicon->omnipresent = 1;
1650 } else {
1651 wwarning(_("bad value in docked icon state info %s"),
1652 PLGetString(dOmnipresent));
1656 aicon->running = 0;
1657 aicon->docked = 1;
1659 return aicon;
1663 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1666 WAppIcon*
1667 wClipRestoreState(WScreen *scr, proplist_t clip_state)
1669 WAppIcon *icon;
1670 proplist_t value;
1673 icon = mainIconCreate(scr, WM_CLIP);
1675 if (!clip_state)
1676 return icon;
1677 else
1678 PLRetain(clip_state);
1680 /* restore position */
1682 value = PLGetDictionaryEntry(clip_state, dPosition);
1684 if (value) {
1685 if (!PLIsString(value))
1686 COMPLAIN("Position");
1687 else {
1688 if (sscanf(PLGetString(value), "%i,%i", &icon->x_pos,
1689 &icon->y_pos)!=2)
1690 COMPLAIN("Position");
1692 /* check position sanity */
1693 if (icon->y_pos < 0)
1694 icon->y_pos = 0;
1695 else if (icon->y_pos > scr->scr_height-ICON_SIZE)
1696 icon->y_pos = scr->scr_height-ICON_SIZE;
1698 if (icon->x_pos < 0)
1699 icon->x_pos = 0;
1700 else if (icon->x_pos > scr->scr_width-ICON_SIZE)
1701 icon->x_pos = scr->scr_width-ICON_SIZE;
1705 #ifdef OFFIX_DND
1706 value = PLGetDictionaryEntry(clip_state, dDropCommand);
1707 if (value && PLIsString(value))
1708 icon->dnd_command = wstrdup(PLGetString(value));
1709 #endif
1711 PLRelease(clip_state);
1713 return icon;
1717 WDock*
1718 wDockRestoreState(WScreen *scr, proplist_t dock_state, int type)
1720 WDock *dock;
1721 proplist_t apps;
1722 proplist_t value;
1723 WAppIcon *aicon, *old_top;
1724 int count, i;
1727 dock = wDockCreate(scr, type);
1729 if (!dock_state)
1730 return dock;
1732 if (dock_state)
1733 PLRetain(dock_state);
1736 /* restore position */
1738 value = PLGetDictionaryEntry(dock_state, dPosition);
1740 if (value) {
1741 if (!PLIsString(value))
1742 COMPLAIN("Position");
1743 else {
1744 if (sscanf(PLGetString(value), "%i,%i", &dock->x_pos,
1745 &dock->y_pos)!=2)
1746 COMPLAIN("Position");
1748 /* check position sanity */
1749 if (dock->y_pos < 0)
1750 dock->y_pos = 0;
1751 else if (dock->y_pos > scr->scr_height-ICON_SIZE)
1752 dock->y_pos = scr->scr_height - ICON_SIZE;
1754 /* This is no more needed. ??? */
1755 if (type == WM_CLIP) {
1756 if (dock->x_pos < 0)
1757 dock->x_pos = 0;
1758 else if (dock->x_pos > scr->scr_width-ICON_SIZE)
1759 dock->x_pos = scr->scr_width-ICON_SIZE;
1760 } else {
1761 if (dock->x_pos >= 0) {
1762 dock->x_pos = DOCK_EXTRA_SPACE;
1763 dock->on_right_side = 0;
1764 } else {
1765 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1766 dock->on_right_side = 1;
1772 /* restore lowered/raised state */
1774 dock->lowered = 0;
1776 value = PLGetDictionaryEntry(dock_state, dLowered);
1778 if (value) {
1779 if (!PLIsString(value))
1780 COMPLAIN("Lowered");
1781 else {
1782 if (strcasecmp(PLGetString(value), "YES")==0)
1783 dock->lowered = 1;
1788 /* restore collapsed state */
1790 dock->collapsed = 0;
1792 value = PLGetDictionaryEntry(dock_state, dCollapsed);
1794 if (value) {
1795 if (!PLIsString(value))
1796 COMPLAIN("Collapsed");
1797 else {
1798 if (strcasecmp(PLGetString(value), "YES")==0)
1799 dock->collapsed = 1;
1804 /* restore auto-collapsed state */
1806 value = PLGetDictionaryEntry(dock_state, dAutoCollapse);
1808 if (value) {
1809 if (!PLIsString(value))
1810 COMPLAIN("AutoCollapse");
1811 else {
1812 if (strcasecmp(PLGetString(value), "YES")==0) {
1813 dock->auto_collapse = 1;
1814 dock->collapsed = 1;
1820 /* restore auto-raise/lower state */
1822 value = PLGetDictionaryEntry(dock_state, dAutoRaiseLower);
1824 if (value) {
1825 if (!PLIsString(value))
1826 COMPLAIN("AutoRaiseLower");
1827 else {
1828 if (strcasecmp(PLGetString(value), "YES")==0) {
1829 dock->auto_raise_lower = 1;
1834 /* restore attract icons state */
1836 dock->attract_icons = 0;
1838 value = PLGetDictionaryEntry(dock_state, dAutoAttractIcons);
1840 if (value) {
1841 if (!PLIsString(value))
1842 COMPLAIN("AutoAttractIcons");
1843 else {
1844 if (strcasecmp(PLGetString(value), "YES")==0)
1845 dock->attract_icons = 1;
1850 /* application list */
1853 proplist_t tmp;
1854 char buffer[64];
1857 * When saving, it saves the dock state in
1858 * Applications and Applicationsnnn
1860 * When loading, it will first try Applicationsnnn.
1861 * If it does not exist, use Applications as default.
1864 sprintf(buffer, "Applications%i", scr->scr_height);
1866 tmp = PLMakeString(buffer);
1867 apps = PLGetDictionaryEntry(dock_state, tmp);
1868 PLRelease(tmp);
1870 if (!apps) {
1871 apps = PLGetDictionaryEntry(dock_state, dApplications);
1875 if (!apps) {
1876 goto finish;
1879 count = PLGetNumberOfElements(apps);
1881 if (count==0)
1882 goto finish;
1884 old_top = dock->icon_array[0];
1886 /* dock->icon_count is set to 1 when dock is created.
1887 * Since Clip is already restored, we want to keep it so for clip,
1888 * but for dock we may change the default top tile, so we set it to 0.
1890 if (type == WM_DOCK)
1891 dock->icon_count = 0;
1893 for (i=0; i<count; i++) {
1894 if (dock->icon_count >= dock->max_icons) {
1895 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1896 break;
1899 value = PLGetArrayElement(apps, i);
1900 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1902 dock->icon_array[dock->icon_count] = aicon;
1904 if (aicon) {
1905 aicon->dock = dock;
1906 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1907 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1909 if (dock->lowered)
1910 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1911 else
1912 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1914 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1915 0, 0);
1917 if (!dock->collapsed)
1918 XMapWindow(dpy, aicon->icon->core->window);
1919 wRaiseFrame(aicon->icon->core);
1921 dock->icon_count++;
1922 } else if (dock->icon_count==0 && type==WM_DOCK)
1923 dock->icon_count++;
1926 /* if the first icon is not defined, use the default */
1927 if (dock->icon_array[0]==NULL) {
1928 /* update default icon */
1929 old_top->x_pos = dock->x_pos;
1930 old_top->y_pos = dock->y_pos;
1931 if (dock->lowered)
1932 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1933 else
1934 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1935 dock->icon_array[0] = old_top;
1936 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1937 /* we don't need to increment dock->icon_count here because it was
1938 * incremented in the loop above.
1940 } else if (old_top!=dock->icon_array[0]) {
1941 if (old_top == scr->clip_icon)
1942 scr->clip_icon = dock->icon_array[0];
1943 wAppIconDestroy(old_top);
1946 finish:
1947 if (dock_state)
1948 PLRelease(dock_state);
1950 return dock;
1955 void
1956 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
1958 if (btn && btn->command && !btn->running && !btn->launching) {
1960 btn->drop_launch = 0;
1962 btn->pid = execCommand(btn, btn->command, state);
1964 if (btn->pid>0) {
1965 if (!btn->forced_dock && !btn->buggy_app) {
1966 btn->launching = 1;
1967 dockIconPaint(btn);
1970 } else {
1971 free(state);
1976 void
1977 wDockDoAutoLaunch(WDock *dock, int workspace)
1979 WAppIcon *btn;
1980 WSavedState *state;
1981 int i;
1983 for (i = 0; i < dock->max_icons; i++) {
1984 btn = dock->icon_array[i];
1985 if (!btn || !btn->auto_launch)
1986 continue;
1988 state = wmalloc(sizeof(WSavedState));
1989 memset(state, 0, sizeof(WSavedState));
1990 state->workspace = workspace;
1991 /* TODO: this is klugy and is very difficult to understand
1992 * what's going on. Try to clean up */
1993 wDockLaunchWithState(dock, btn, state);
1997 #ifdef REDUCE_APPICONS
1998 void
1999 wDockSimulateLaunch(WDock *dock, WAppIcon *btn)
2001 if ((btn == NULL) || (dock == NULL))
2002 return;
2004 if (!btn->running) {
2005 if ((btn->icon->owner == NULL) && (btn->applist))
2006 btn->icon->owner = btn->applist->wapp->main_window_desc;
2007 if (!btn->forced_dock)
2008 btn->launching = 1;
2009 dockIconPaint(btn);
2010 wusleep(5000);
2013 #endif
2015 #ifdef OFFIX_DND
2016 static WDock*
2017 findDock(WScreen *scr, XEvent *event, int *icon_pos)
2019 WDock *dock;
2020 int i;
2022 *icon_pos = -1;
2023 if ((dock = scr->dock)!=NULL) {
2024 for (i=0; i<dock->max_icons; i++) {
2025 if (dock->icon_array[i]
2026 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2027 *icon_pos = i;
2028 break;
2032 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
2033 for (i=0; i<dock->max_icons; i++) {
2034 if (dock->icon_array[i]
2035 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2036 *icon_pos = i;
2037 break;
2041 if(*icon_pos>=0)
2042 return dock;
2043 return NULL;
2048 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
2050 WDock *dock;
2051 WAppIcon *btn;
2052 int icon_pos;
2054 dock = findDock(scr, event, &icon_pos);
2055 if (!dock)
2056 return False;
2059 * Return True if the drop was on an application icon window.
2060 * In this case, let the ClientMessage handler redirect the
2061 * message to the app.
2063 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
2064 return True;
2066 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
2067 scr->flags.dnd_data_convertion_status = 0;
2069 btn = dock->icon_array[icon_pos];
2071 if (!btn->forced_dock) {
2072 btn->relaunching = btn->running;
2073 btn->running = 1;
2075 if (btn->wm_instance || btn->wm_class) {
2076 WWindowAttributes attr;
2077 memset(&attr, 0, sizeof(WWindowAttributes));
2078 wDefaultFillAttributes(btn->icon->core->screen_ptr,
2079 btn->wm_instance,
2080 btn->wm_class, &attr, NULL, True);
2082 if (!attr.no_appicon)
2083 btn->launching = 1;
2084 else
2085 btn->running = 0;
2088 btn->drop_launch = 1;
2089 scr->last_dock = dock;
2090 btn->pid = execCommand(btn, btn->dnd_command, NULL);
2091 if (btn->pid>0) {
2092 dockIconPaint(btn);
2093 } else {
2094 btn->launching = 0;
2095 if (!btn->relaunching) {
2096 btn->running = 0;
2100 return False;
2102 #endif /* OFFIX_DND */
2106 Bool
2107 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2109 WWindow *wwin;
2110 char **argv;
2111 int argc;
2112 int index;
2114 wwin = icon->icon->owner;
2115 if (icon->command==NULL) {
2116 icon->editing = 0;
2117 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2119 icon->command = FlattenStringList(argv, argc);
2120 XFreeStringList(argv);
2121 } else {
2122 char *command=NULL;
2124 /* icon->forced_dock = 1;*/
2125 if (dock->type!=WM_CLIP || !icon->attracted) {
2126 icon->editing = 1;
2127 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
2128 _("Type the command used to launch the application"),
2129 &command)) {
2130 if (command && (command[0]==0 ||
2131 (command[0]=='-' && command[1]==0))) {
2132 free(command);
2133 command = NULL;
2135 icon->command = command;
2136 icon->editing = 0;
2137 } else {
2138 icon->editing = 0;
2139 if (command)
2140 free(command);
2141 /* If the target is the dock, reject the icon. If
2142 * the target is the clip, make it an attracted icon
2144 if (dock->type==WM_CLIP) {
2145 icon->attracted = 1;
2146 if (!icon->icon->shadowed) {
2147 icon->icon->shadowed = 1;
2148 icon->icon->force_paint = 1;
2150 } else {
2151 return False;
2156 } else {
2157 icon->editing = 0;
2160 for (index=1; index<dock->max_icons; index++)
2161 if (dock->icon_array[index] == NULL)
2162 break;
2163 /* if (index == dock->max_icons)
2164 return; */
2166 assert(index < dock->max_icons);
2168 dock->icon_array[index] = icon;
2169 icon->yindex = y;
2170 icon->xindex = x;
2172 icon->omnipresent = 0;
2174 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2175 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2177 dock->icon_count++;
2179 icon->running = 1;
2180 icon->launching = 0;
2181 icon->docked = 1;
2182 icon->dock = dock;
2183 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2184 if (dock->type == WM_CLIP) {
2185 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2186 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2188 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2189 icon->icon->core->descriptor.parent = icon;
2191 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2192 icon->icon->core);
2193 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2194 wAppIconPaint(icon);
2196 if (wPreferences.auto_arrange_icons)
2197 wArrangeIcons(dock->screen_ptr, True);
2199 #ifdef OFFIX_DND
2200 if (icon->command && !icon->dnd_command) {
2201 icon->dnd_command = wmalloc(strlen(icon->command)+8);
2202 sprintf(icon->dnd_command, "%s %%d", icon->command);
2204 #endif
2206 return True;
2210 void
2211 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2213 int index;
2215 for(index=1; index<dock->max_icons; index++) {
2216 if(dock->icon_array[index] == icon)
2217 break;
2219 assert(index < dock->max_icons);
2221 icon->yindex = y;
2222 icon->xindex = x;
2224 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2225 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2229 Bool
2230 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2232 WWindow *wwin;
2233 char **argv;
2234 int argc;
2235 int index;
2237 if (src == dest)
2238 return True; /* No move needed, we're already there */
2240 if (dest == NULL)
2241 return False;
2243 wwin = icon->icon->owner;
2246 * For the moment we can't do this if we move icons in Clip from one
2247 * workspace to other, because if we move two or more icons without
2248 * command, the dialog box will not be able to tell us to which of the
2249 * moved icons it applies. -Dan
2251 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2252 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2254 icon->command = FlattenStringList(argv, argc);
2255 XFreeStringList(argv);
2256 } else {
2257 char *command=NULL;
2259 icon->editing = 1;
2260 /* icon->forced_dock = 1;*/
2261 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2262 _("Type the command used to launch the application"),
2263 &command)) {
2264 if (command && (command[0]==0 ||
2265 (command[0]=='-' && command[1]==0))) {
2266 free(command);
2267 command = NULL;
2269 icon->command = command;
2270 } else {
2271 icon->editing = 0;
2272 if (command)
2273 free(command);
2274 return False;
2276 icon->editing = 0;
2280 if (dest->type == WM_DOCK)
2281 wClipMakeIconOmnipresent(icon, False);
2283 for(index=1; index<src->max_icons; index++) {
2284 if(src->icon_array[index] == icon)
2285 break;
2287 assert(index < src->max_icons);
2289 src->icon_array[index] = NULL;
2290 src->icon_count--;
2292 for(index=1; index<dest->max_icons; index++) {
2293 if(dest->icon_array[index] == NULL)
2294 break;
2296 /* if (index == dest->max_icons)
2297 return; */
2299 assert(index < dest->max_icons);
2301 dest->icon_array[index] = icon;
2302 icon->dock = dest;
2304 /* deselect the icon */
2305 if (icon->icon->selected)
2306 wIconSelect(icon->icon);
2308 if (dest->type == WM_DOCK) {
2309 icon->icon->core->descriptor.handle_enternotify = NULL;
2310 icon->icon->core->descriptor.handle_leavenotify = NULL;
2311 } else {
2312 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2313 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2316 /* set it to be kept when moving to dock.
2317 * Unless the icon does not have a command set
2319 if (icon->command && dest->type==WM_DOCK) {
2320 icon->attracted = 0;
2321 if (icon->icon->shadowed) {
2322 icon->icon->shadowed = 0;
2323 icon->icon->force_paint = 1;
2327 if (src->auto_collapse || src->auto_raise_lower)
2328 clipLeave(src);
2330 icon->yindex = y;
2331 icon->xindex = x;
2333 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2334 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2336 dest->icon_count++;
2338 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2339 icon->icon->core);
2340 wAppIconPaint(icon);
2342 return True;
2346 void
2347 wDockDetach(WDock *dock, WAppIcon *icon)
2349 int index;
2351 /* make the settings panel be closed */
2352 if (icon->panel) {
2353 DestroyDockAppSettingsPanel(icon->panel);
2356 /* This must be called before icon->dock is set to NULL.
2357 * Don't move it. -Dan
2359 wClipMakeIconOmnipresent(icon, False);
2361 icon->docked = 0;
2362 icon->dock = NULL;
2363 icon->attracted = 0;
2364 if (icon->icon->shadowed) {
2365 icon->icon->shadowed = 0;
2366 icon->icon->force_paint = 1;
2369 /* deselect the icon */
2370 if (icon->icon->selected)
2371 wIconSelect(icon->icon);
2373 if (icon->command) {
2374 free(icon->command);
2375 icon->command = NULL;
2377 #ifdef OFFIX_DND
2378 if (icon->dnd_command) {
2379 free(icon->dnd_command);
2380 icon->dnd_command = NULL;
2382 #endif
2384 for (index=1; index<dock->max_icons; index++)
2385 if (dock->icon_array[index] == icon)
2386 break;
2387 assert(index < dock->max_icons);
2388 dock->icon_array[index] = NULL;
2389 icon->yindex = -1;
2390 icon->xindex = -1;
2392 dock->icon_count--;
2394 /* if the dock is not attached to an application or
2395 * the the application did not set the approriate hints yet,
2396 * destroy the icon */
2397 #ifdef REDUCE_APPICONS
2398 if ((icon->num_apps == 0) && (!icon->running || !wApplicationOf(icon->main_window)) )
2399 #else
2400 if (!icon->running || !wApplicationOf(icon->main_window))
2401 #endif
2402 wAppIconDestroy(icon);
2403 else {
2404 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2405 icon->icon->core->descriptor.handle_enternotify = NULL;
2406 icon->icon->core->descriptor.handle_leavenotify = NULL;
2407 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2408 icon->icon->core->descriptor.parent = icon;
2410 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2412 wAppIconPaint(icon);
2413 if (wPreferences.auto_arrange_icons) {
2414 wArrangeIcons(dock->screen_ptr, True);
2417 if (dock->auto_collapse || dock->auto_raise_lower)
2418 clipLeave(dock);
2423 * returns the closest Dock slot index for the passed
2424 * coordinates.
2426 * Returns False if icon can't be docked.
2428 * Note: this function should NEVER alter ret_x or ret_y, unless it will
2429 * return True. -Dan
2431 Bool
2432 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2433 int *ret_x, int *ret_y, int redocking)
2435 WScreen *scr = dock->screen_ptr;
2436 int dx, dy;
2437 int ex_x, ex_y;
2438 int i, offset = ICON_SIZE/2;
2439 WAppIcon *aicon = NULL;
2440 WAppIcon *nicon = NULL;
2441 int max_y_icons, max_x_icons;
2443 max_x_icons = scr->scr_width/ICON_SIZE;
2444 max_y_icons = scr->scr_height/ICON_SIZE-1;
2446 if (wPreferences.flags.noupdates)
2447 return False;
2449 dx = dock->x_pos;
2450 dy = dock->y_pos;
2452 /* if the dock is full */
2453 if (!redocking &&
2454 (dock->icon_count >= dock->max_icons)) {
2455 return False;
2458 /* exact position */
2459 if (req_y < dy)
2460 ex_y = (req_y - offset - dy)/ICON_SIZE;
2461 else
2462 ex_y = (req_y + offset - dy)/ICON_SIZE;
2464 if (req_x < dx)
2465 ex_x = (req_x - offset - dx)/ICON_SIZE;
2466 else
2467 ex_x = (req_x + offset - dx)/ICON_SIZE;
2469 /* check if the icon is outside the screen boundaries */
2470 if (dx + ex_x*ICON_SIZE < -ICON_SIZE+2 ||
2471 dx + ex_x*ICON_SIZE >= scr->scr_width-1 ||
2472 dy + ex_y*ICON_SIZE < -ICON_SIZE+2 ||
2473 dy + ex_y*ICON_SIZE >= scr->scr_height-1)
2474 return False;
2476 if (dock->type == WM_DOCK) {
2477 if (icon->dock != dock && ex_x != 0)
2478 return False;
2480 aicon = NULL;
2481 for (i=0; i<dock->max_icons; i++) {
2482 nicon = dock->icon_array[i];
2483 if (nicon && nicon->yindex == ex_y) {
2484 aicon = nicon;
2485 break;
2489 if (redocking) {
2490 int sig, done, closest;
2492 /* Possible cases when redocking:
2494 * icon dragged out of range of any slot -> false
2495 * icon dragged to range of free slot
2496 * icon dragged to range of same slot
2497 * icon dragged to range of different icon
2499 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2500 return False;
2502 if (ex_y>=0 && ex_y<=max_y_icons && (aicon==icon || !aicon)) {
2503 *ret_x = 0;
2504 *ret_y = ex_y;
2505 return True;
2508 /* start looking at the upper slot or lower? */
2509 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2510 sig = 1;
2511 else
2512 sig = -1;
2514 closest = -1;
2515 done = 0;
2516 /* look for closest free slot */
2517 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2518 int j;
2520 done = 1;
2521 closest = sig*(i/2) + ex_y;
2522 /* check if this slot is used */
2523 if (closest >= 0) {
2524 for (j = 0; j<dock->max_icons; j++) {
2525 if (dock->icon_array[j]
2526 && dock->icon_array[j]->yindex==closest) {
2527 /* slot is used by someone else */
2528 if (dock->icon_array[j]!=icon)
2529 done = 0;
2530 break;
2534 sig = -sig;
2536 if (done && closest >= 0 && closest <= max_y_icons &&
2537 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2539 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2540 *ret_x = 0;
2541 *ret_y = closest;
2542 return True;
2544 } else { /* !redocking */
2546 /* if slot is free and the icon is close enough, return it */
2547 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2548 *ret_x = 0;
2549 *ret_y = ex_y;
2550 return True;
2553 } else { /* CLIP */
2554 int neighbours = 0;
2555 int start, stop, k;
2557 start = icon->omnipresent ? 0 : scr->current_workspace;
2558 stop = icon->omnipresent ? scr->workspace_count : start+1;
2560 aicon = NULL;
2561 for (k=start; k<stop; k++) {
2562 WDock *tmp = scr->workspaces[k]->clip;
2563 if (!tmp)
2564 continue;
2565 for (i=0; i<tmp->max_icons; i++) {
2566 nicon = tmp->icon_array[i];
2567 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2568 aicon = nicon;
2569 break;
2572 if (aicon)
2573 break;
2575 for (k=start; k<stop; k++) {
2576 WDock *tmp = scr->workspaces[k]->clip;
2577 if (!tmp)
2578 continue;
2579 for (i=0; i<tmp->max_icons; i++) {
2580 nicon = tmp->icon_array[i];
2581 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2582 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2583 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2584 neighbours = 1;
2585 break;
2588 if (neighbours)
2589 break;
2592 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2593 *ret_x = ex_x;
2594 *ret_y = ex_y;
2595 return True;
2598 return False;
2601 #define MIN(x, y) ((x) > (y) ? (y) : (x))
2602 #define MAX(x, y) ((x) < (y) ? (y) : (x))
2604 #define ON_SCREEN(x, y, sx, ex, sy, ey) \
2605 ((((x)+ICON_SIZE/2) >= (sx)) && (((y)+ICON_SIZE/2) >= (sy)) && \
2606 (((x) + (ICON_SIZE/2)) <= (ex)) && (((y) + (ICON_SIZE/2)) <= ey))
2610 * returns true if it can find a free slot in the dock,
2611 * in which case it changes x_pos and y_pos accordingly.
2612 * Else returns false.
2614 Bool
2615 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2617 WScreen *scr = dock->screen_ptr;
2618 WAppIcon *btn;
2619 WAppIconChain *chain;
2620 unsigned char *slot_map;
2621 int mwidth;
2622 int r;
2623 int x, y;
2624 int i, done = False;
2625 int corner;
2626 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2627 int extra_count=0;
2629 if (dock->type == WM_CLIP &&
2630 dock != scr->workspaces[scr->current_workspace]->clip)
2631 extra_count = scr->global_icon_count;
2633 /* if the dock is full */
2634 if (dock->icon_count+extra_count >= dock->max_icons) {
2635 return False;
2638 if (!wPreferences.flags.nodock && scr->dock) {
2639 if (scr->dock->on_right_side)
2640 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2641 else
2642 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2645 if (ex < dock->x_pos)
2646 ex = dock->x_pos;
2647 if (sx > dock->x_pos+ICON_SIZE)
2648 sx = dock->x_pos+ICON_SIZE;
2649 #define C_NONE 0
2650 #define C_NW 1
2651 #define C_NE 2
2652 #define C_SW 3
2653 #define C_SE 4
2655 /* check if clip is in a corner */
2656 if (dock->type==WM_CLIP) {
2657 if (dock->x_pos < 1 && dock->y_pos < 1)
2658 corner = C_NE;
2659 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2660 corner = C_SE;
2661 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2662 corner = C_SW;
2663 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2664 corner = C_NW;
2665 else
2666 corner = C_NONE;
2667 } else
2668 corner = C_NONE;
2670 /* If the clip is in the corner, use only slots that are in the border
2671 * of the screen */
2672 if (corner!=C_NONE) {
2673 char *hmap, *vmap;
2674 int hcount, vcount;
2676 hcount = MIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2677 vcount = MIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2678 hmap = wmalloc(hcount+1);
2679 memset(hmap, 0, hcount+1);
2680 vmap = wmalloc(vcount+1);
2681 memset(vmap, 0, vcount+1);
2683 /* mark used positions */
2684 switch (corner) {
2685 case C_NE:
2686 for (i=0; i<dock->max_icons; i++) {
2687 btn = dock->icon_array[i];
2688 if (!btn)
2689 continue;
2691 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2692 vmap[btn->yindex] = 1;
2693 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2694 hmap[btn->xindex] = 1;
2696 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2697 btn = chain->aicon;
2698 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2699 vmap[btn->yindex] = 1;
2700 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2701 hmap[btn->xindex] = 1;
2703 break;
2704 case C_NW:
2705 for (i=0; i<dock->max_icons; i++) {
2706 btn = dock->icon_array[i];
2707 if (!btn)
2708 continue;
2710 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2711 vmap[btn->yindex] = 1;
2712 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2713 hmap[-btn->xindex] = 1;
2715 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2716 btn = chain->aicon;
2717 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2718 vmap[btn->yindex] = 1;
2719 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2720 hmap[-btn->xindex] = 1;
2722 break;
2723 case C_SE:
2724 for (i=0; i<dock->max_icons; i++) {
2725 btn = dock->icon_array[i];
2726 if (!btn)
2727 continue;
2729 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2730 vmap[-btn->yindex] = 1;
2731 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2732 hmap[btn->xindex] = 1;
2734 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2735 btn = chain->aicon;
2736 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2737 vmap[-btn->yindex] = 1;
2738 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2739 hmap[btn->xindex] = 1;
2741 break;
2742 case C_SW:
2743 default:
2744 for (i=0; i<dock->max_icons; i++) {
2745 btn = dock->icon_array[i];
2746 if (!btn)
2747 continue;
2749 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2750 vmap[-btn->yindex] = 1;
2751 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2752 hmap[-btn->xindex] = 1;
2754 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2755 btn = chain->aicon;
2756 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2757 vmap[-btn->yindex] = 1;
2758 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2759 hmap[-btn->xindex] = 1;
2762 x=0; y=0;
2763 done = 0;
2764 /* search a vacant slot */
2765 for (i=1; i<MAX(vcount, hcount); i++) {
2766 if (i < vcount && vmap[i]==0) {
2767 /* found a slot */
2768 x = 0;
2769 y = i;
2770 done = 1;
2771 break;
2772 } else if (i < hcount && hmap[i]==0) {
2773 /* found a slot */
2774 x = i;
2775 y = 0;
2776 done = 1;
2777 break;
2780 free(vmap);
2781 free(hmap);
2782 /* If found a slot, translate and return */
2783 if (done) {
2784 if (corner==C_NW || corner==C_NE) {
2785 *y_pos = y;
2786 } else {
2787 *y_pos = -y;
2789 if (corner==C_NE || corner==C_SE) {
2790 *x_pos = x;
2791 } else {
2792 *x_pos = -x;
2794 return True;
2796 /* else, try to find a slot somewhere else */
2799 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2800 * placed outside of screen */
2801 mwidth = (int)ceil(sqrt(dock->max_icons));
2803 /* In the worst case (the clip is in the corner of the screen),
2804 * the amount of icons that fit in the clip is smaller.
2805 * Double the map to get a safe value.
2807 mwidth += mwidth;
2809 r = (mwidth-1)/2;
2811 slot_map = wmalloc(mwidth*mwidth);
2812 memset(slot_map, 0, mwidth*mwidth);
2814 #define XY2OFS(x,y) (MAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2816 /* mark used slots in the map. If the slot falls outside the map
2817 * (for example, when all icons are placed in line), ignore them. */
2818 for (i=0; i<dock->max_icons; i++) {
2819 btn = dock->icon_array[i];
2820 if (btn)
2821 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2823 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2824 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2826 /* Find closest slot from the center that is free by scanning the
2827 * map from the center to outward in circular passes.
2828 * This will not result in a neat layout, but will be optimal
2829 * in the sense that there will not be holes left.
2831 done = 0;
2832 for (i = 1; i <= r && !done; i++) {
2833 int tx, ty;
2835 /* top and bottom parts of the ring */
2836 for (x = -i; x <= i && !done; x++) {
2837 tx = dock->x_pos + x*ICON_SIZE;
2838 y = -i;
2839 ty = dock->y_pos + y*ICON_SIZE;
2840 if (slot_map[XY2OFS(x,y)]==0
2841 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2842 *x_pos = x;
2843 *y_pos = y;
2844 done = 1;
2845 break;
2847 y = i;
2848 ty = dock->y_pos + y*ICON_SIZE;
2849 if (slot_map[XY2OFS(x,y)]==0
2850 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2851 *x_pos = x;
2852 *y_pos = y;
2853 done = 1;
2854 break;
2857 /* left and right parts of the ring */
2858 for (y = -i+1; y <= i-1; y++) {
2859 ty = dock->y_pos + y*ICON_SIZE;
2860 x = -i;
2861 tx = dock->x_pos + x*ICON_SIZE;
2862 if (slot_map[XY2OFS(x,y)]==0
2863 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2864 *x_pos = x;
2865 *y_pos = y;
2866 done = 1;
2867 break;
2869 x = i;
2870 tx = dock->x_pos + x*ICON_SIZE;
2871 if (slot_map[XY2OFS(x,y)]==0
2872 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2873 *x_pos = x;
2874 *y_pos = y;
2875 done = 1;
2876 break;
2880 free(slot_map);
2881 #undef XY2OFS
2882 return done;
2886 static void
2887 moveDock(WDock *dock, int new_x, int new_y)
2889 WAppIcon *btn;
2890 int i;
2892 dock->x_pos = new_x;
2893 dock->y_pos = new_y;
2894 for (i=0; i<dock->max_icons; i++) {
2895 btn = dock->icon_array[i];
2896 if (btn) {
2897 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2898 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2899 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2905 static void
2906 swapDock(WDock *dock)
2908 WScreen *scr = dock->screen_ptr;
2909 WAppIcon *btn;
2910 int x, i;
2913 if (dock->on_right_side) {
2914 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2915 } else {
2916 x = dock->x_pos = DOCK_EXTRA_SPACE;
2919 for (i=0; i<dock->max_icons; i++) {
2920 btn = dock->icon_array[i];
2921 if (btn) {
2922 btn->x_pos = x;
2923 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2927 wScreenUpdateUsableArea(scr);
2931 static pid_t
2932 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2934 WScreen *scr = btn->icon->core->screen_ptr;
2935 pid_t pid;
2936 char **argv;
2937 int argc;
2938 char *cmdline;
2940 cmdline = ExpandOptions(scr, command);
2942 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2943 if (cmdline)
2944 free(cmdline);
2945 if (state)
2946 free(state);
2947 return 0;
2950 ParseCommand(cmdline, &argv, &argc);
2952 if (argv==NULL) {
2953 if (cmdline)
2954 free(cmdline);
2955 if (state)
2956 free(state);
2957 return 0;
2960 if ((pid=fork())==0) {
2961 char **args;
2962 int i;
2964 SetupEnvironment(scr);
2966 #ifdef HAVE_SETPGID
2967 setpgid(0, 0);
2968 #endif
2970 args = malloc(sizeof(char*)*(argc+1));
2971 if (!args)
2972 exit(111);
2973 for (i=0; i<argc; i++) {
2974 args[i] = argv[i];
2976 args[argc] = NULL;
2977 execvp(argv[0], args);
2978 exit(111);
2980 while (argc > 0)
2981 free(argv[--argc]);
2982 free(argv);
2984 if (pid > 0) {
2985 if (!state) {
2986 state = wmalloc(sizeof(WSavedState));
2987 memset(state, 0, sizeof(WSavedState));
2988 state->hidden = -1;
2989 state->miniaturized = -1;
2990 state->shaded = -1;
2991 if (btn->dock==scr->dock || btn->omnipresent)
2992 state->workspace = -1;
2993 else
2994 state->workspace = scr->current_workspace;
2996 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
2997 state);
2998 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
2999 btn->dock);
3000 } else if (state) {
3001 free(state);
3003 free(cmdline);
3004 return pid;
3008 void
3009 wDockHideIcons(WDock *dock)
3011 int i;
3013 if (dock==NULL)
3014 return;
3016 for (i=1; i<dock->max_icons; i++) {
3017 if (dock->icon_array[i])
3018 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
3020 dock->mapped = 0;
3022 dockIconPaint(dock->icon_array[0]);
3026 void
3027 wDockShowIcons(WDock *dock)
3029 int i, newlevel;
3030 WAppIcon *btn;
3032 if (dock==NULL)
3033 return;
3035 btn = dock->icon_array[0];
3036 moveDock(dock, btn->x_pos, btn->y_pos);
3038 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
3039 ChangeStackingLevel(btn->icon->core, newlevel);
3041 for (i=1; i<dock->max_icons; i++) {
3042 if (dock->icon_array[i]) {
3043 MoveInStackListAbove(dock->icon_array[i]->icon->core,
3044 btn->icon->core);
3045 break;
3049 if (!dock->collapsed) {
3050 for (i=1; i<dock->max_icons; i++) {
3051 if (dock->icon_array[i]) {
3052 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
3056 dock->mapped = 1;
3058 dockIconPaint(btn);
3062 void
3063 wDockLower(WDock *dock)
3065 int i;
3067 for (i=0; i<dock->max_icons; i++) {
3068 if (dock->icon_array[i])
3069 wLowerFrame(dock->icon_array[i]->icon->core);
3074 void
3075 wDockRaise(WDock *dock)
3077 int i;
3079 for (i=dock->max_icons-1; i>=0; i--) {
3080 if (dock->icon_array[i])
3081 wRaiseFrame(dock->icon_array[i]->icon->core);
3086 void
3087 wDockRaiseLower(WDock *dock)
3089 if (!dock->icon_array[0]->icon->core->stacking->above
3090 ||(dock->icon_array[0]->icon->core->stacking->window_level
3091 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
3092 wDockLower(dock);
3093 else
3094 wDockRaise(dock);
3098 void
3099 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
3101 icon->launching = 0;
3102 icon->relaunching = 0;
3103 dockIconPaint(icon);
3107 WAppIcon*
3108 wDockFindIconFor(WDock *dock, Window window)
3110 WAppIcon *icon;
3111 int i;
3113 for (i=0; i<dock->max_icons; i++) {
3114 icon = dock->icon_array[i];
3115 if (icon && icon->main_window == window)
3116 return icon;
3118 return NULL;
3122 void
3123 wDockTrackWindowLaunch(WDock *dock, Window window)
3125 WAppIcon *icon;
3126 #ifdef REDUCE_APPICONS
3127 WAppIconAppList *tapplist;
3128 #endif
3129 char *wm_class, *wm_instance;
3130 int i;
3131 Bool firstPass = True;
3132 Bool found = False;
3133 char *command = NULL;
3136 int argc;
3137 char **argv;
3139 if (XGetCommand(dpy, window, &argv, &argc)) {
3140 if (argc > 0 && argv != NULL)
3141 command = FlattenStringList(argv,argc);
3142 if (argv) {
3143 XFreeStringList(argv);
3148 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
3149 (!wm_class && !wm_instance))
3150 return;
3152 retry:
3153 for (i=0; i<dock->max_icons; i++) {
3154 icon = dock->icon_array[i];
3155 if (!icon)
3156 continue;
3158 /* app is already attached to icon */
3159 if (icon->main_window == window) {
3160 found = True;
3161 break;
3164 if ((icon->wm_instance || icon->wm_class)
3165 && (icon->launching
3166 || (dock->screen_ptr->flags.startup && !icon->running))) {
3168 if (icon->wm_instance && wm_instance &&
3169 strcmp(icon->wm_instance, wm_instance)!=0) {
3170 continue;
3172 if (icon->wm_class && wm_class &&
3173 strcmp(icon->wm_class, wm_class)!=0) {
3174 continue;
3176 if (firstPass && command && strcmp(icon->command, command)!=0) {
3177 continue;
3180 if (!icon->relaunching) {
3181 WApplication *wapp;
3183 /* Possibly an application that was docked with dockit,
3184 * but the user did not update WMState to indicate that
3185 * it was docked by force */
3186 wapp = wApplicationOf(window);
3187 if (!wapp) {
3188 icon->forced_dock = 1;
3189 icon->running = 0;
3191 if (!icon->forced_dock)
3192 icon->main_window = window;
3194 #ifdef REDUCE_APPICONS
3195 tapplist = wmalloc(sizeof(WAppIconAppList));
3196 memset(tapplist, 0, sizeof(WAppIconAppList));
3197 tapplist->next = icon->applist;
3198 if (icon->applist)
3199 icon->applist->prev = tapplist;
3200 icon->applist = tapplist;
3201 tapplist->wapp = wApplicationOf(window);
3202 icon->num_apps++;
3203 #endif
3205 found = True;
3206 wDockFinishLaunch(dock, icon);
3207 break;
3211 if (firstPass && !found) {
3212 firstPass = False;
3213 goto retry;
3216 if (command)
3217 free(command);
3219 if (wm_class)
3220 XFree(wm_class);
3221 if (wm_instance)
3222 XFree(wm_instance);
3227 void
3228 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3230 if (!wPreferences.flags.noclip) {
3231 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3232 if (scr->current_workspace != workspace) {
3233 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3234 WAppIconChain *chain = scr->global_icons;
3236 while (chain) {
3237 moveIconBetweenDocks(chain->aicon->dock,
3238 scr->workspaces[workspace]->clip,
3239 chain->aicon, chain->aicon->xindex,
3240 chain->aicon->yindex);
3241 if (scr->workspaces[workspace]->clip->collapsed)
3242 XUnmapWindow(dpy, chain->aicon->icon->core->window);
3243 chain = chain->next;
3246 wDockHideIcons(old_clip);
3247 if (old_clip->auto_raise_lower) {
3248 if (old_clip->auto_raise_magic) {
3249 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3250 old_clip->auto_raise_magic = NULL;
3252 wDockLower(old_clip);
3254 if (old_clip->auto_collapse) {
3255 if (old_clip->auto_expand_magic) {
3256 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3257 old_clip->auto_expand_magic = NULL;
3259 old_clip->collapsed = 1;
3261 wDockShowIcons(scr->workspaces[workspace]->clip);
3263 if (scr->flags.clip_balloon_mapped)
3264 showClipBalloon(scr->clip_icon->dock, workspace);
3270 static void
3271 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3273 WAppIcon *icon;
3274 int i;
3276 for (i=0; i<dock->max_icons; i++) {
3277 icon = dock->icon_array[i];
3278 if (!icon)
3279 continue;
3281 if (icon->launching && icon->pid == pid) {
3282 if (!icon->relaunching) {
3283 icon->running = 0;
3284 icon->main_window = None;
3286 wDockFinishLaunch(dock, icon);
3287 icon->pid = 0;
3288 if (status==111) {
3289 char msg[PATH_MAX];
3290 #ifdef OFFIX_DND
3291 sprintf(msg, _("Could not execute command \"%s\""),
3292 icon->drop_launch && icon->dnd_command
3293 ? icon->dnd_command : icon->command);
3294 #else
3295 sprintf(msg, _("Could not execute command \"%s\""),
3296 icon->command);
3297 #endif
3298 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3299 _("OK"), NULL, NULL);
3301 break;
3307 static void
3308 toggleLowered(WDock *dock)
3310 WAppIcon *tmp;
3311 int newlevel, i;
3313 /* lower/raise Dock */
3314 if (!dock->lowered) {
3315 newlevel = WMNormalLevel;
3316 dock->lowered = 1;
3317 } else {
3318 newlevel = WMDockLevel;
3319 dock->lowered = 0;
3322 for (i=0; i<dock->max_icons; i++) {
3323 tmp = dock->icon_array[i];
3324 if (!tmp)
3325 continue;
3327 ChangeStackingLevel(tmp->icon->core, newlevel);
3328 if (dock->lowered)
3329 wLowerFrame(tmp->icon->core);
3332 if (dock->type == WM_DOCK)
3333 wScreenUpdateUsableArea(dock->screen_ptr);
3337 static void
3338 toggleCollapsed(WDock *dock)
3340 if (dock->collapsed) {
3341 dock->collapsed = 0;
3342 wDockShowIcons(dock);
3344 else {
3345 dock->collapsed = 1;
3346 wDockHideIcons(dock);
3351 static void
3352 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3354 WScreen *scr = dock->screen_ptr;
3355 WObjDescriptor *desc;
3356 WMenuEntry *entry;
3357 WApplication *wapp = NULL;
3358 int index = 0;
3359 int x_pos;
3360 int n_selected;
3361 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3363 if (dock->type == WM_DOCK) {
3364 /* keep on top */
3365 entry = dock->menu->entries[index];
3366 entry->flags.indicator_on = !dock->lowered;
3367 entry->clientdata = dock;
3368 } else {
3369 /* clip options */
3370 if (scr->clip_options)
3371 updateClipOptionsMenu(scr->clip_options, dock);
3373 n_selected = numberOfSelectedIcons(dock);
3375 /* Rename Workspace */
3376 entry = dock->menu->entries[++index];
3377 if (aicon == scr->clip_icon) {
3378 entry->callback = renameCallback;
3379 entry->clientdata = dock;
3380 entry->flags.indicator = 0;
3381 entry->text = _("Rename Workspace");
3382 } else {
3383 entry->callback = omnipresentCallback;
3384 entry->clientdata = aicon;
3385 if (n_selected > 0) {
3386 entry->flags.indicator = 0;
3387 entry->text = _("Toggle Omnipresent");
3388 } else {
3389 entry->flags.indicator = 1;
3390 entry->flags.indicator_on = aicon->omnipresent;
3391 entry->flags.indicator_type = MI_CHECK;
3392 entry->text = _("Omnipresent");
3396 /* select/unselect icon */
3397 entry = dock->menu->entries[++index];
3398 entry->clientdata = aicon;
3399 entry->flags.indicator_on = aicon->icon->selected;
3400 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3402 /* select/unselect all icons */
3403 entry = dock->menu->entries[++index];
3404 entry->clientdata = aicon;
3405 if (n_selected > 0)
3406 entry->text = _("Unselect All Icons");
3407 else
3408 entry->text = _("Select All Icons");
3409 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3411 /* keep icon(s) */
3412 entry = dock->menu->entries[++index];
3413 entry->clientdata = aicon;
3414 if (n_selected > 1)
3415 entry->text = _("Keep Icons");
3416 else
3417 entry->text = _("Keep Icon");
3418 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3420 /* this is the workspace submenu part */
3421 entry = dock->menu->entries[++index];
3422 if (n_selected > 1)
3423 entry->text = _("Move Icons To");
3424 else
3425 entry->text = _("Move Icon To");
3426 if (scr->clip_submenu)
3427 updateWorkspaceMenu(scr->clip_submenu, aicon);
3428 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3430 /* remove icon(s) */
3431 entry = dock->menu->entries[++index];
3432 entry->clientdata = aicon;
3433 if (n_selected > 1)
3434 entry->text = _("Remove Icons");
3435 else
3436 entry->text = _("Remove Icon");
3437 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3439 /* attract icon(s) */
3440 entry = dock->menu->entries[++index];
3441 entry->clientdata = aicon;
3443 dock->menu->flags.realized = 0;
3444 wMenuRealize(dock->menu);
3447 /* launch */
3448 entry = dock->menu->entries[++index];
3449 entry->clientdata = aicon;
3450 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3452 /* unhide here */
3453 entry = dock->menu->entries[++index];
3454 entry->clientdata = aicon;
3455 wMenuSetEnabled(dock->menu, index, appIsRunning);
3457 /* hide */
3458 entry = dock->menu->entries[++index];
3459 entry->clientdata = aicon;
3460 if (aicon->icon->owner) {
3461 wapp = wApplicationOf(aicon->icon->owner->main_window);
3462 if (wapp && wapp->flags.hidden)
3463 entry->text = _("Unhide");
3464 else
3465 entry->text = _("Hide");
3466 } else {
3467 entry->text = _("Hide");
3469 wMenuSetEnabled(dock->menu, index, appIsRunning);
3471 /* settings */
3472 entry = dock->menu->entries[++index];
3473 entry->clientdata = aicon;
3474 wMenuSetEnabled(dock->menu, index, !aicon->editing
3475 && !wPreferences.flags.noupdates);
3477 /* kill */
3478 entry = dock->menu->entries[++index];
3479 entry->clientdata = aicon;
3480 wMenuSetEnabled(dock->menu, index, appIsRunning);
3482 if (!dock->menu->flags.realized)
3483 wMenuRealize(dock->menu);
3485 if (dock->type == WM_CLIP) {
3486 x_pos = event->xbutton.x_root+2;
3487 } else {
3488 x_pos = dock->on_right_side ?
3489 scr->scr_width - dock->menu->frame->core->width - 2 : 0;
3492 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3494 /* allow drag select */
3495 event->xany.send_event = True;
3496 desc = &dock->menu->menu->descriptor;
3497 (*desc->handle_mousedown)(desc, event);
3501 static void
3502 openClipWorkspaceMenu(WScreen *scr, int x, int y)
3504 if (!scr->clip_ws_menu) {
3505 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
3507 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
3508 wMenuMapAt(scr->clip_ws_menu, x, y, False);
3512 /******************************************************************/
3513 static void
3514 iconDblClick(WObjDescriptor *desc, XEvent *event)
3516 WAppIcon *btn = desc->parent;
3517 WDock *dock = btn->dock;
3518 WApplication *wapp = NULL;
3519 int unhideHere = 0;
3521 #ifdef REDUCE_APPICONS
3522 if ((btn->icon->owner && !(event->xbutton.state & ControlMask)) ||
3523 ((btn->icon->owner == NULL) && (btn->applist != NULL))) {
3524 if (btn->icon->owner == NULL)
3525 btn->icon->owner = btn->applist->wapp->main_window_desc;
3526 #else
3527 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3528 #endif
3529 wapp = wApplicationOf(btn->icon->owner->main_window);
3531 assert(wapp!=NULL);
3533 unhideHere = (event->xbutton.state & ShiftMask);
3535 /* go to the last workspace that the user worked on the app */
3536 if (wapp->last_workspace != dock->screen_ptr->current_workspace
3537 && !unhideHere) {
3538 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3541 wUnhideApplication(wapp, event->xbutton.button==Button2,
3542 unhideHere);
3544 if (event->xbutton.state & MOD_MASK) {
3545 wHideOtherApplications(btn->icon->owner);
3547 } else {
3548 if (event->xbutton.button==Button1) {
3550 if (event->xbutton.state & MOD_MASK) {
3551 /* raise/lower dock */
3552 toggleLowered(dock);
3553 } else if (btn == dock->screen_ptr->clip_icon) {
3554 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3555 toggleCollapsed(dock);
3556 else
3557 handleClipChangeWorkspace(dock->screen_ptr, event);
3558 } else if (btn->command) {
3559 if (!btn->launching &&
3560 (!btn->running || (event->xbutton.state & ControlMask))) {
3561 launchDockedApplication(btn);
3563 } else if (btn->xindex == 0 && btn->yindex == 0
3564 && btn->dock->type == WM_DOCK) {
3566 wShowGNUstepPanel(dock->screen_ptr);
3573 static void
3574 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3576 WScreen *scr = dock->screen_ptr;
3577 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3578 int x, y;
3579 XEvent ev;
3580 int grabbed = 0, swapped = 0, done;
3581 Pixmap ghost = None;
3582 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3584 #ifdef DEBUG
3585 puts("moving dock");
3586 #endif
3587 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3588 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3589 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3590 wwarning("pointer grab failed for dock move");
3592 y = 0;
3593 for (x=0; x<dock->max_icons; x++) {
3594 if (dock->icon_array[x]!=NULL &&
3595 dock->icon_array[x]->yindex > y)
3596 y = dock->icon_array[x]->yindex;
3598 y++;
3599 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3601 done = 0;
3602 while (!done) {
3603 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3604 |ButtonMotionMask|ExposureMask, &ev);
3605 switch (ev.type) {
3606 case Expose:
3607 WMHandleEvent(&ev);
3608 break;
3610 case MotionNotify:
3611 if (!grabbed) {
3612 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3613 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3614 XChangeActivePointerGrab(dpy, ButtonMotionMask
3615 |ButtonReleaseMask|ButtonPressMask,
3616 wCursor[WCUR_MOVE], CurrentTime);
3617 grabbed=1;
3619 break;
3621 if (dock->type == WM_CLIP) {
3622 if (ev.xmotion.x_root - ofs_x < 0) {
3623 x = 0;
3624 } else if (ev.xmotion.x_root - ofs_x + ICON_SIZE >
3625 scr->scr_width) {
3626 x = scr->scr_width - ICON_SIZE;
3627 } else {
3628 x = ev.xmotion.x_root - ofs_x;
3630 if (ev.xmotion.y_root - ofs_y < 0) {
3631 y = 0;
3632 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3633 scr->scr_height) {
3634 y = scr->scr_height - ICON_SIZE;
3635 } else {
3636 y = ev.xmotion.y_root - ofs_y;
3638 moveDock(dock, x, y);
3639 } else {
3640 /* move vertically if pointer is inside the dock*/
3641 if ((dock->on_right_side &&
3642 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3643 || (!dock->on_right_side &&
3644 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3646 if (ev.xmotion.y_root - ofs_y < 0) {
3647 y = 0;
3648 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3649 scr->scr_height) {
3650 y = scr->scr_height - ICON_SIZE;
3651 } else {
3652 y = ev.xmotion.y_root - ofs_y;
3654 moveDock(dock, dock->x_pos, y);
3656 /* move horizontally to change sides */
3657 x = ev.xmotion.x_root - ofs_x;
3658 if (!dock->on_right_side) {
3660 /* is on left */
3662 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3663 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3664 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3665 if (superfluous && ghost==None) {
3666 ghost = MakeGhostDock(dock, dock->x_pos,
3667 scr->scr_width-ICON_SIZE
3668 -DOCK_EXTRA_SPACE-1,
3669 dock->y_pos);
3670 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3671 ghost);
3672 XClearWindow(dpy, scr->dock_shadow);
3674 XMapRaised(dpy, scr->dock_shadow);
3675 swapped = 1;
3676 } else {
3677 if (superfluous && ghost!=None) {
3678 XFreePixmap(dpy, ghost);
3679 ghost = None;
3681 XUnmapWindow(dpy, scr->dock_shadow);
3682 swapped = 0;
3684 } else {
3685 /* is on right */
3686 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3687 XMoveWindow(dpy, scr->dock_shadow,
3688 DOCK_EXTRA_SPACE, dock->y_pos);
3689 if (superfluous && ghost==None) {
3690 ghost = MakeGhostDock(dock, dock->x_pos,
3691 DOCK_EXTRA_SPACE, dock->y_pos);
3692 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3693 ghost);
3694 XClearWindow(dpy, scr->dock_shadow);
3696 XMapRaised(dpy, scr->dock_shadow);
3697 swapped = -1;
3698 } else {
3699 XUnmapWindow(dpy, scr->dock_shadow);
3700 swapped = 0;
3701 if (superfluous && ghost!=None) {
3702 XFreePixmap(dpy, ghost);
3703 ghost = None;
3708 break;
3710 case ButtonPress:
3711 break;
3713 case ButtonRelease:
3714 if (ev.xbutton.button != event->xbutton.button)
3715 break;
3716 XUngrabPointer(dpy, CurrentTime);
3717 XUnmapWindow(dpy, scr->dock_shadow);
3718 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3719 if (dock->type == WM_DOCK) {
3720 if (swapped!=0) {
3721 if (swapped>0)
3722 dock->on_right_side = 1;
3723 else
3724 dock->on_right_side = 0;
3725 swapDock(dock);
3726 wArrangeIcons(scr, False);
3729 done = 1;
3730 break;
3733 if (superfluous) {
3734 if (ghost!=None)
3735 XFreePixmap(dpy, ghost);
3736 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3738 #ifdef DEBUG
3739 puts("End dock move");
3740 #endif
3745 static void
3746 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3748 WScreen *scr = dock->screen_ptr;
3749 Window wins[2];
3750 WIcon *icon = aicon->icon;
3751 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3752 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3753 XEvent ev;
3754 int x = aicon->x_pos, y = aicon->y_pos;
3755 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3756 int shad_x = x, shad_y = y;
3757 int ix = aicon->xindex, iy = aicon->yindex;
3758 int tmp;
3759 Pixmap ghost = None;
3760 Bool docked;
3761 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3762 int omnipresent = aicon->omnipresent; /* this must be cached!!! */
3765 if (wPreferences.flags.noupdates)
3766 return;
3768 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3769 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3770 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3771 #ifdef DEBUG0
3772 wwarning("pointer grab failed for icon move");
3773 #endif
3776 if (!(event->xbutton.state & MOD_MASK))
3777 wRaiseFrame(icon->core);
3779 if (!wPreferences.flags.noclip)
3780 clip = scr->workspaces[scr->current_workspace]->clip;
3782 if (dock == scr->dock && !wPreferences.flags.noclip)
3783 dock2 = clip;
3784 else if (dock != scr->dock && !wPreferences.flags.nodock)
3785 dock2 = scr->dock;
3787 wins[0] = icon->core->window;
3788 wins[1] = scr->dock_shadow;
3789 XRestackWindows(dpy, wins, 2);
3790 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3791 ICON_SIZE, ICON_SIZE);
3792 if (superfluous) {
3793 if (icon->pixmap!=None)
3794 ghost = MakeGhostIcon(scr, icon->pixmap);
3795 else
3796 ghost = MakeGhostIcon(scr, icon->core->window);
3798 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3799 XClearWindow(dpy, scr->dock_shadow);
3801 XMapWindow(dpy, scr->dock_shadow);
3803 ondock = 1;
3806 while(1) {
3807 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3808 |ButtonMotionMask|ExposureMask, &ev);
3809 switch (ev.type) {
3810 case Expose:
3811 WMHandleEvent(&ev);
3812 break;
3814 case MotionNotify:
3815 if (!grabbed) {
3816 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3817 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3818 XChangeActivePointerGrab(dpy, ButtonMotionMask
3819 |ButtonReleaseMask|ButtonPressMask,
3820 wCursor[WCUR_MOVE], CurrentTime);
3821 grabbed=1;
3822 } else {
3823 break;
3827 if (omnipresent) {
3828 int i;
3829 for (i=0; i<scr->workspace_count; i++) {
3830 if (i == scr->current_workspace)
3831 continue;
3832 wDockShowIcons(scr->workspaces[i]->clip);
3836 x = ev.xmotion.x_root - ofs_x;
3837 y = ev.xmotion.y_root - ofs_y;
3838 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3839 if (tmp && dock2) {
3840 change_dock = 0;
3841 if (last_dock != dock && collapsed) {
3842 last_dock->collapsed = 1;
3843 wDockHideIcons(last_dock);
3844 collapsed = 0;
3846 if (!collapsed && (collapsed = dock->collapsed)) {
3847 dock->collapsed = 0;
3848 wDockShowIcons(dock);
3850 if (dock->auto_raise_lower)
3851 wDockRaise(dock);
3852 last_dock = dock;
3853 } else if (dock2) {
3854 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3855 if (tmp) {
3856 change_dock = 1;
3857 if (last_dock != dock2 && collapsed) {
3858 last_dock->collapsed = 1;
3859 wDockHideIcons(last_dock);
3860 collapsed = 0;
3862 if (!collapsed && (collapsed = dock2->collapsed)) {
3863 dock2->collapsed = 0;
3864 wDockShowIcons(dock2);
3866 if (dock2->auto_raise_lower)
3867 wDockRaise(dock2);
3868 last_dock = dock2;
3871 if (aicon->launching
3872 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3873 || (!aicon->running && tmp)) {
3874 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3875 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3877 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3879 if (!ondock) {
3880 XMapWindow(dpy, scr->dock_shadow);
3882 ondock = 1;
3883 } else {
3884 if (ondock) {
3885 XUnmapWindow(dpy, scr->dock_shadow);
3887 ondock = 0;
3889 XMoveWindow(dpy, icon->core->window, x, y);
3890 break;
3892 case ButtonPress:
3893 break;
3895 case ButtonRelease:
3896 if (ev.xbutton.button != event->xbutton.button)
3897 break;
3898 XUngrabPointer(dpy, CurrentTime);
3899 if (ondock) {
3900 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3901 XUnmapWindow(dpy, scr->dock_shadow);
3902 if (!change_dock) {
3903 reattachIcon(dock, aicon, ix, iy);
3904 if (clip && dock!=clip && clip->auto_raise_lower)
3905 wDockLower(clip);
3906 } else {
3907 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3908 if (!docked) {
3909 /* Slide it back if dock rejected it */
3910 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3911 aicon->y_pos);
3912 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3914 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3915 collapsed = 0;
3918 } else {
3919 aicon->x_pos = x;
3920 aicon->y_pos = y;
3921 if (superfluous) {
3922 if (!aicon->running && !wPreferences.no_animations) {
3923 /* We need to deselect it, even if is deselected in
3924 * wDockDetach(), because else DoKaboom() will fail.
3926 if (aicon->icon->selected)
3927 wIconSelect(aicon->icon);
3928 DoKaboom(scr,aicon->icon->core->window, x, y);
3931 if (clip && clip->auto_raise_lower)
3932 wDockLower(clip);
3933 wDockDetach(dock, aicon);
3935 if (collapsed) {
3936 last_dock->collapsed = 1;
3937 wDockHideIcons(last_dock);
3938 collapsed = 0;
3940 if (superfluous) {
3941 if (ghost!=None)
3942 XFreePixmap(dpy, ghost);
3943 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3945 if (omnipresent) {
3946 int i;
3947 for (i=0; i<scr->workspace_count; i++) {
3948 if (i == scr->current_workspace)
3949 continue;
3950 wDockHideIcons(scr->workspaces[i]->clip);
3954 #ifdef DEBUG
3955 puts("End icon move");
3956 #endif
3957 return;
3963 static int
3964 getClipButton(int px, int py)
3966 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
3968 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
3969 return CLIP_IDLE;
3971 if (py <= pt-((int)ICON_SIZE-1-px))
3972 return CLIP_FORWARD;
3973 else if (px <= pt-((int)ICON_SIZE-1-py))
3974 return CLIP_REWIND;
3976 return CLIP_IDLE;
3980 static void
3981 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
3983 XEvent ev;
3984 int done, direction, new_ws;
3985 int new_dir;
3986 WDock *clip = scr->clip_icon->dock;
3988 direction = getClipButton(event->xbutton.x, event->xbutton.y);
3990 clip->lclip_button_pushed = direction==CLIP_REWIND;
3991 clip->rclip_button_pushed = direction==CLIP_FORWARD;
3993 wClipIconPaint(scr->clip_icon);
3994 done = 0;
3995 while(!done) {
3996 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
3997 |ButtonPressMask, &ev);
3998 switch (ev.type) {
3999 case Expose:
4000 WMHandleEvent(&ev);
4001 break;
4003 case MotionNotify:
4004 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
4005 if (new_dir != direction) {
4006 direction = new_dir;
4007 clip->lclip_button_pushed = direction==CLIP_REWIND;
4008 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4009 wClipIconPaint(scr->clip_icon);
4011 break;
4013 case ButtonPress:
4014 break;
4016 case ButtonRelease:
4017 if (ev.xbutton.button == event->xbutton.button)
4018 done = 1;
4022 clip->lclip_button_pushed = 0;
4023 clip->rclip_button_pushed = 0;
4025 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
4027 if (direction == CLIP_FORWARD) {
4028 if (scr->current_workspace < scr->workspace_count-1)
4029 wWorkspaceChange(scr, scr->current_workspace+1);
4030 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
4031 wWorkspaceChange(scr, scr->current_workspace+1);
4032 else if (wPreferences.ws_cycle)
4033 wWorkspaceChange(scr, 0);
4035 else if (direction == CLIP_REWIND) {
4036 if (scr->current_workspace > 0)
4037 wWorkspaceChange(scr, scr->current_workspace-1);
4038 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
4039 wWorkspaceChange(scr, scr->workspace_count-1);
4042 wClipIconPaint(scr->clip_icon);
4046 static void
4047 iconMouseDown(WObjDescriptor *desc, XEvent *event)
4049 WAppIcon *aicon = desc->parent;
4050 WDock *dock = aicon->dock;
4051 WScreen *scr = aicon->icon->core->screen_ptr;
4053 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
4054 return;
4056 scr->last_dock = dock;
4058 if (dock->menu->flags.mapped)
4059 wMenuUnmap(dock->menu);
4061 if (IsDoubleClick(scr, event)) {
4062 /* double-click was not in the main clip icon */
4063 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
4064 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
4065 iconDblClick(desc, event);
4066 return;
4070 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
4071 XUnmapWindow(dpy, scr->clip_balloon);
4072 scr->flags.clip_balloon_mapped = 0;
4075 #ifdef DEBUG
4076 puts("handling dock");
4077 #endif
4078 if (event->xbutton.button == Button1) {
4079 if (event->xbutton.state & MOD_MASK)
4080 wDockLower(dock);
4081 else
4082 wDockRaise(dock);
4084 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
4085 dock->type!=WM_DOCK) {
4086 wIconSelect(aicon->icon);
4087 return;
4090 if (aicon->yindex==0 && aicon->xindex==0) {
4091 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
4092 && dock->type==WM_CLIP)
4093 handleClipChangeWorkspace(scr, event);
4094 else
4095 handleDockMove(dock, aicon, event);
4096 } else
4097 handleIconMove(dock, aicon, event);
4099 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4100 aicon==scr->clip_icon) {
4101 openClipWorkspaceMenu(scr, event->xbutton.x_root+2,
4102 event->xbutton.y_root+2);
4103 if (scr->clip_ws_menu) {
4104 WMenu *menu;
4105 menu = scr->clip_ws_menu;
4106 desc = &menu->menu->descriptor;
4108 event->xany.send_event = True;
4109 (*desc->handle_mousedown)(desc, event);
4111 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4112 (event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon) {
4113 wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
4114 } else if (event->xbutton.button == Button3) {
4115 openDockMenu(dock, aicon, event);
4120 static void
4121 showClipBalloon(WDock *dock, int workspace)
4123 int w, h;
4124 int x, y;
4125 WScreen *scr = dock->screen_ptr;
4126 char *text;
4127 Window stack[2];
4129 scr->flags.clip_balloon_mapped = 1;
4130 XMapWindow(dpy, scr->clip_balloon);
4132 text = scr->workspaces[workspace]->name;
4134 w = WMWidthOfString(scr->clip_title_font, text, strlen(text));
4136 h = WMFontHeight(scr->clip_title_font);
4137 XResizeWindow(dpy, scr->clip_balloon, w, h);
4139 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
4140 y = dock->y_pos + ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
4142 if (x+w > scr->scr_width) {
4143 x = scr->scr_width - w;
4144 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
4145 y = dock->y_pos - h - 1;
4146 else
4147 y = dock->y_pos + ICON_SIZE;
4148 XRaiseWindow(dpy, scr->clip_balloon);
4149 } else {
4150 stack[0] = scr->clip_icon->icon->core->window;
4151 stack[1] = scr->clip_balloon;
4152 XRestackWindows(dpy, stack, 2);
4154 XMoveWindow(dpy, scr->clip_balloon, x, y);
4155 XSetForeground(dpy, scr->clip_title_gc,
4156 scr->clip_title_pixel[CLIP_NORMAL]);
4157 XClearWindow(dpy, scr->clip_balloon);
4158 WMDrawString(scr->wmscreen, scr->clip_balloon, scr->clip_title_gc,
4159 scr->clip_title_font, 0, 0, text, strlen(text));
4163 static void
4164 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
4166 WAppIcon *btn = (WAppIcon*)desc->parent;
4167 WDock *dock;
4168 WScreen *scr;
4170 assert(event->type==EnterNotify);
4172 if(desc->parent_type!=WCLASS_DOCK_ICON)
4173 return;
4175 scr = btn->icon->core->screen_ptr;
4176 if (!btn->omnipresent)
4177 dock = btn->dock;
4178 else
4179 dock = scr->workspaces[scr->current_workspace]->clip;
4181 if (!dock || dock->type!=WM_CLIP)
4182 return;
4184 /* The auto raise/lower code */
4185 if (dock->auto_lower_magic) {
4186 WMDeleteTimerHandler(dock->auto_lower_magic);
4187 dock->auto_lower_magic = NULL;
4189 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
4190 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
4191 clipAutoRaise,
4192 (void *)dock);
4195 /* The auto expand/collapse code */
4196 if (dock->auto_collapse_magic) {
4197 WMDeleteTimerHandler(dock->auto_collapse_magic);
4198 dock->auto_collapse_magic = NULL;
4200 if (dock->auto_collapse && !dock->auto_expand_magic) {
4201 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
4202 clipAutoExpand,
4203 (void *)dock);
4206 if (btn->xindex == 0 && btn->yindex == 0)
4207 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4208 else {
4209 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4210 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
4211 dock->screen_ptr->flags.clip_balloon_mapped = 0;
4217 static void
4218 clipLeave(WDock *dock)
4220 XEvent event;
4221 WObjDescriptor *desc = NULL;
4223 if (!dock || dock->type!=WM_CLIP)
4224 return;
4226 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
4227 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
4228 (XPointer *)&desc)!=XCNOENT
4229 && desc && desc->parent_type==WCLASS_DOCK_ICON
4230 && ((WAppIcon*)desc->parent)->dock
4231 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
4232 /* We didn't left the Clip yet */
4233 XPutBackEvent(dpy, &event);
4234 return;
4237 XPutBackEvent(dpy, &event);
4238 } else {
4239 /* We entered a withdrawn window, so we're still in Clip */
4240 return;
4243 if (dock->auto_raise_magic) {
4244 WMDeleteTimerHandler(dock->auto_raise_magic);
4245 dock->auto_raise_magic = NULL;
4247 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
4248 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
4249 clipAutoLower,
4250 (void *)dock);
4253 if (dock->auto_expand_magic) {
4254 WMDeleteTimerHandler(dock->auto_expand_magic);
4255 dock->auto_expand_magic = NULL;
4257 if (dock->auto_collapse && !dock->auto_collapse_magic) {
4258 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
4259 clipAutoCollapse,
4260 (void *)dock);
4265 static void
4266 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
4268 WAppIcon *btn = (WAppIcon*)desc->parent;
4270 assert(event->type==LeaveNotify);
4272 if(desc->parent_type!=WCLASS_DOCK_ICON)
4273 return;
4275 clipLeave(btn->dock);
4279 static void
4280 clipAutoCollapse(void *cdata)
4282 WDock *dock = (WDock *)cdata;
4284 if (dock->type!=WM_CLIP)
4285 return;
4287 if (dock->auto_collapse) {
4288 dock->collapsed = 1;
4289 wDockHideIcons(dock);
4291 dock->auto_collapse_magic = NULL;
4295 static void
4296 clipAutoExpand(void *cdata)
4298 WDock *dock = (WDock *)cdata;
4300 if (dock->type!=WM_CLIP)
4301 return;
4303 if (dock->auto_collapse) {
4304 dock->collapsed = 0;
4305 wDockShowIcons(dock);
4307 dock->auto_expand_magic = NULL;
4311 static void
4312 clipAutoLower(void *cdata)
4314 WDock *dock = (WDock *)cdata;
4316 if (dock->type!=WM_CLIP)
4317 return;
4319 if (dock->auto_raise_lower)
4320 wDockLower(dock);
4322 dock->auto_lower_magic = NULL;
4326 static void
4327 clipAutoRaise(void *cdata)
4329 WDock *dock = (WDock *)cdata;
4331 if (dock->type!=WM_CLIP)
4332 return;
4334 if (dock->auto_raise_lower)
4335 wDockRaise(dock);
4337 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4338 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4341 dock->auto_raise_magic = NULL;
4345 static Bool
4346 iconCanBeOmnipresent(WAppIcon *aicon)
4348 WScreen *scr = aicon->icon->core->screen_ptr;
4349 WDock *clip;
4350 WAppIcon *btn;
4351 int i, j;
4353 for (i=0; i<scr->workspace_count; i++) {
4354 clip = scr->workspaces[i]->clip;
4356 if (clip == aicon->dock)
4357 continue;
4359 if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
4360 return False; /* Clip is full in some workspace */
4362 for (j=0; j<clip->max_icons; j++) {
4363 btn = clip->icon_array[j];
4364 if(btn && btn->xindex==aicon->xindex && btn->yindex==aicon->yindex)
4365 return False;
4369 return True;
4374 wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
4376 WScreen *scr = aicon->icon->core->screen_ptr;
4377 WAppIconChain *new_entry, *tmp, *tmp1;
4378 int status = WO_SUCCESS;
4380 if ((scr->dock && aicon->dock==scr->dock) || aicon==scr->clip_icon) {
4381 return WO_NOT_APPLICABLE;
4384 if (aicon->omnipresent == omnipresent)
4385 return WO_SUCCESS;
4387 if (omnipresent) {
4388 if (iconCanBeOmnipresent(aicon)) {
4389 aicon->omnipresent = 1;
4390 new_entry = wmalloc(sizeof(WAppIconChain));
4391 new_entry->aicon = aicon;
4392 new_entry->next = scr->global_icons;
4393 scr->global_icons = new_entry;
4394 scr->global_icon_count++;
4395 } else {
4396 aicon->omnipresent = 0;
4397 status = WO_FAILED;
4399 } else {
4400 aicon->omnipresent = 0;
4401 if (aicon == scr->global_icons->aicon) {
4402 tmp = scr->global_icons->next;
4403 free(scr->global_icons);
4404 scr->global_icons = tmp;
4405 scr->global_icon_count--;
4406 } else {
4407 tmp = scr->global_icons;
4408 while (tmp->next) {
4409 if (tmp->next->aicon == aicon) {
4410 tmp1 = tmp->next->next;
4411 free(tmp->next);
4412 tmp->next = tmp1;
4413 scr->global_icon_count--;
4414 break;
4416 tmp = tmp->next;
4421 wAppIconPaint(aicon);
4423 return status;