Removed "Keep Attracted Icons" from Clip's menu.
[wmaker-crm.git] / src / dock.c
blob676e7e6e6523ea8740e5dec3bb3ca6c831aff581
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 #if 0
656 static void
657 toggleKeepCallback(WMenu *menu, WMenuEntry *entry)
659 WDock *dock = (WDock*)entry->clientdata;
660 WAppIcon *btn;
661 int i;
663 assert(entry->clientdata!=NULL);
665 dock->keep_attracted = !dock->keep_attracted;
667 if (dock->keep_attracted) {
668 for (i=0; i< dock->max_icons; i++) {
669 btn = dock->icon_array[i];
670 if (btn && btn->attracted && btn->command) {
671 btn->attracted = 0;
672 if (btn->icon->shadowed) {
673 btn->icon->shadowed = 0;
674 btn->icon->force_paint = 1;
675 wAppIconPaint(btn);
681 entry->flags.indicator_on = dock->keep_attracted;
683 wMenuPaint(menu);
685 #endif
688 static void
689 selectCallback(WMenu *menu, WMenuEntry *entry)
691 WAppIcon *icon = (WAppIcon*)entry->clientdata;
693 assert(icon!=NULL);
695 wIconSelect(icon->icon);
697 wMenuPaint(menu);
701 static void
702 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
704 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
705 WDock *clip;
706 WAppIcon *aicon;
707 int x, y, x_pos, y_pos;
709 assert(entry->clientdata!=NULL);
710 clip = clickedIcon->dock;
712 aicon = clip->screen_ptr->app_icon_list;
714 while (aicon) {
715 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
716 x_pos = clip->x_pos + x*ICON_SIZE;
717 y_pos = clip->y_pos + y*ICON_SIZE;
718 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
719 #ifdef ANIMATIONS
720 if (wPreferences.no_animations) {
721 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
722 } else {
723 SlideWindow(aicon->icon->core->window,
724 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
726 #else
727 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
728 #endif /* ANIMATIONS */
730 aicon->attracted = 1;
731 if (!aicon->icon->shadowed) {
732 aicon->icon->shadowed = 1;
733 aicon->icon->force_paint = 1;
734 /* We don't do an wAppIconPaint() here because it's in
735 * wDockAttachIcon(). -Dan
738 wDockAttachIcon(clip, aicon, x, y);
739 if (clip->collapsed || !clip->mapped)
740 XUnmapWindow(dpy, aicon->icon->core->window);
742 aicon = aicon->next;
747 static void
748 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
750 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
751 WDock *dock;
752 WMBag *selectedIcons;
753 WAppIcon *btn;
754 int i;
756 assert(clickedIcon!=NULL);
757 dock = clickedIcon->dock;
759 selectedIcons = getSelected(dock);
761 if (!WMGetBagItemCount(selectedIcons)) {
762 for (i=1; i<dock->max_icons; i++) {
763 btn = dock->icon_array[i];
764 if (btn && !btn->icon->selected) {
765 wIconSelect(btn->icon);
768 } else {
769 for (i = 0; i < WMGetBagItemCount(selectedIcons); i++) {
770 btn = WMGetFromBag(selectedIcons, i);
771 wIconSelect(btn->icon);
774 WMFreeBag(selectedIcons);
776 wMenuPaint(menu);
780 static void
781 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
783 assert(entry->clientdata!=NULL);
785 toggleCollapsed(entry->clientdata);
787 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
789 wMenuPaint(menu);
793 static void
794 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
796 WDock *dock;
797 assert(entry->clientdata!=NULL);
799 dock = (WDock*) entry->clientdata;
801 dock->auto_collapse = !dock->auto_collapse;
803 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
805 wMenuPaint(menu);
809 static void
810 toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
812 WDock *dock;
813 assert(entry->clientdata!=NULL);
815 dock = (WDock*) entry->clientdata;
817 dock->auto_raise_lower = !dock->auto_raise_lower;
819 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_raise_lower;
821 wMenuPaint(menu);
825 static void
826 launchCallback(WMenu *menu, WMenuEntry *entry)
828 WAppIcon *btn = (WAppIcon*)entry->clientdata;
830 launchDockedApplication(btn);
834 static void
835 settingsCallback(WMenu *menu, WMenuEntry *entry)
837 WAppIcon *btn = (WAppIcon*)entry->clientdata;
839 if (btn->editing)
840 return;
841 ShowDockAppSettingsPanel(btn);
845 static void
846 hideCallback(WMenu *menu, WMenuEntry *entry)
848 WApplication *wapp;
849 WAppIcon *btn = (WAppIcon*)entry->clientdata;
851 wapp = wApplicationOf(btn->icon->owner->main_window);
853 if (wapp->flags.hidden) {
854 wWorkspaceChange(btn->icon->core->screen_ptr,wapp->last_workspace);
855 wUnhideApplication(wapp, False, False);
856 } else {
857 wHideApplication(wapp);
862 static void
863 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
865 WApplication *wapp;
866 WAppIcon *btn = (WAppIcon*)entry->clientdata;
868 wapp = wApplicationOf(btn->icon->owner->main_window);
870 wUnhideApplication(wapp, False, True);
874 WAppIcon*
875 mainIconCreate(WScreen *scr, int type)
877 WAppIcon *btn;
878 int x_pos;
880 if (type == WM_CLIP) {
881 if (scr->clip_icon)
882 return scr->clip_icon;
883 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
884 btn->icon->core->descriptor.handle_expose = clipIconExpose;
885 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
886 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
887 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
888 x_pos = 0;
889 } else {
890 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
891 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
894 btn->xindex = 0;
895 btn->yindex = 0;
897 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
898 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
899 btn->icon->core->descriptor.parent = btn;
900 /*ChangeStackingLevel(btn->icon->core, WMDockLevel);*/
901 XMapWindow(dpy, btn->icon->core->window);
902 btn->x_pos = x_pos;
903 btn->y_pos = 0;
904 btn->docked = 1;
905 if (type == WM_CLIP)
906 scr->clip_icon = btn;
908 return btn;
912 static void
913 switchWSCommand(WMenu *menu, WMenuEntry *entry)
915 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
916 WScreen *scr = icon->icon->core->screen_ptr;
917 WDock *src, *dest;
918 WMBag *selectedIcons;
919 int x, y;
921 if (entry->order == scr->current_workspace)
922 return;
923 src = icon->dock;
924 dest = scr->workspaces[entry->order]->clip;
926 selectedIcons = getSelected(src);
928 if (WMGetBagItemCount(selectedIcons)) {
929 int i;
930 for (i = 0; i < WMGetBagItemCount(selectedIcons); i++) {
931 btn = WMGetFromBag(selectedIcons, i);
932 if (wDockFindFreeSlot(dest, &x, &y)) {
933 moveIconBetweenDocks(src, dest, btn, x, y);
934 XUnmapWindow(dpy, btn->icon->core->window);
937 } else if (icon != scr->clip_icon) {
938 if (wDockFindFreeSlot(dest, &x, &y)) {
939 moveIconBetweenDocks(src, dest, icon, x, y);
940 XUnmapWindow(dpy, icon->icon->core->window);
943 WMFreeBag(selectedIcons);
948 static void
949 launchDockedApplication(WAppIcon *btn)
951 WScreen *scr = btn->icon->core->screen_ptr;
953 if (!btn->launching && btn->command!=NULL) {
954 if (!btn->forced_dock) {
955 btn->relaunching = btn->running;
956 btn->running = 1;
958 if (btn->wm_instance || btn->wm_class) {
959 WWindowAttributes attr;
960 memset(&attr, 0, sizeof(WWindowAttributes));
961 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class,
962 &attr, NULL, True);
964 if (!attr.no_appicon && !btn->buggy_app)
965 btn->launching = 1;
966 else
967 btn->running = 0;
969 btn->drop_launch = 0;
970 scr->last_dock = btn->dock;
971 btn->pid = execCommand(btn, btn->command, NULL);
972 if (btn->pid>0) {
973 if (btn->buggy_app) {
974 /* give feedback that the app was launched */
975 btn->launching = 1;
976 dockIconPaint(btn);
977 btn->launching = 0;
978 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
979 } else {
980 dockIconPaint(btn);
982 } else {
983 wwarning(_("could not launch application %s\n"), btn->command);
984 btn->launching = 0;
985 if (!btn->relaunching)
986 btn->running = 0;
993 static void
994 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
996 WScreen *scr = menu->frame->screen_ptr;
997 char title[MAX_WORKSPACENAME_WIDTH+1];
998 int i;
1000 if (!menu || !icon)
1001 return;
1003 for (i=0; i<scr->workspace_count; i++) {
1004 if (i < menu->entry_no) {
1005 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
1006 free(menu->entries[i]->text);
1007 strcpy(title, scr->workspaces[i]->name);
1008 menu->entries[i]->text = wstrdup(title);
1009 menu->flags.realized = 0;
1011 menu->entries[i]->clientdata = (void*)icon;
1012 } else {
1013 strcpy(title, scr->workspaces[i]->name);
1015 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
1017 menu->flags.realized = 0;
1019 if (i == scr->current_workspace) {
1020 wMenuSetEnabled(menu, i, False);
1021 } else {
1022 wMenuSetEnabled(menu, i, True);
1026 if (!menu->flags.realized)
1027 wMenuRealize(menu);
1031 static WMenu*
1032 makeWorkspaceMenu(WScreen *scr)
1034 WMenu *menu;
1036 menu = wMenuCreate(scr, NULL, False);
1037 if (!menu)
1038 wwarning(_("could not create workspace submenu for Clip menu"));
1040 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
1042 menu->flags.realized = 0;
1043 wMenuRealize(menu);
1045 return menu;
1049 static void
1050 updateClipOptionsMenu(WMenu *menu, WDock *dock)
1052 WMenuEntry *entry;
1053 int index = 0;
1055 if (!menu || !dock)
1056 return;
1058 /* keep on top */
1059 entry = menu->entries[index];
1060 entry->flags.indicator_on = !dock->lowered;
1061 entry->clientdata = dock;
1063 /* collapsed */
1064 entry = menu->entries[++index];
1065 entry->flags.indicator_on = dock->collapsed;
1066 entry->clientdata = dock;
1068 /* auto-collapse */
1069 entry = menu->entries[++index];
1070 entry->flags.indicator_on = dock->auto_collapse;
1071 entry->clientdata = dock;
1073 /* auto-raise/lower */
1074 entry = menu->entries[++index];
1075 entry->flags.indicator_on = dock->auto_raise_lower;
1076 entry->clientdata = dock;
1078 /* attract icons */
1079 entry = menu->entries[++index];
1080 entry->flags.indicator_on = dock->attract_icons;
1081 entry->clientdata = dock;
1083 menu->flags.realized = 0;
1084 wMenuRealize(menu);
1088 static WMenu*
1089 makeClipOptionsMenu(WScreen *scr)
1091 WMenu *menu;
1092 WMenuEntry *entry;
1094 menu = wMenuCreate(scr, NULL, False);
1095 if (!menu) {
1096 wwarning(_("could not create options submenu for Clip menu"));
1097 return NULL;
1100 entry = wMenuAddCallback(menu, _("Keep on Top"),
1101 toggleLoweredCallback, NULL);
1102 entry->flags.indicator = 1;
1103 entry->flags.indicator_on = 1;
1104 entry->flags.indicator_type = MI_CHECK;
1106 entry = wMenuAddCallback(menu, _("Collapsed"),
1107 toggleCollapsedCallback, NULL);
1108 entry->flags.indicator = 1;
1109 entry->flags.indicator_on = 1;
1110 entry->flags.indicator_type = MI_CHECK;
1112 entry = wMenuAddCallback(menu, _("AutoCollapse"),
1113 toggleAutoCollapseCallback, NULL);
1114 entry->flags.indicator = 1;
1115 entry->flags.indicator_on = 1;
1116 entry->flags.indicator_type = MI_CHECK;
1118 entry = wMenuAddCallback(menu, _("AutoRaiseLower"),
1119 toggleAutoRaiseLowerCallback, NULL);
1120 entry->flags.indicator = 1;
1121 entry->flags.indicator_on = 1;
1122 entry->flags.indicator_type = MI_CHECK;
1124 entry = wMenuAddCallback(menu, _("AutoAttract Icons"),
1125 toggleAutoAttractCallback, NULL);
1126 entry->flags.indicator = 1;
1127 entry->flags.indicator_on = 1;
1128 entry->flags.indicator_type = MI_CHECK;
1130 menu->flags.realized = 0;
1131 wMenuRealize(menu);
1133 return menu;
1137 static WMenu*
1138 dockMenuCreate(WScreen *scr, int type)
1140 WMenu *menu;
1141 WMenuEntry *entry;
1143 if (type == WM_CLIP && scr->clip_menu)
1144 return scr->clip_menu;
1146 menu = wMenuCreate(scr, NULL, False);
1147 if (type != WM_CLIP) {
1148 entry = wMenuAddCallback(menu, _("Keep on top"),
1149 toggleLoweredCallback, NULL);
1150 entry->flags.indicator = 1;
1151 entry->flags.indicator_on = 1;
1152 entry->flags.indicator_type = MI_CHECK;
1153 } else {
1154 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1155 scr->clip_options = makeClipOptionsMenu(scr);
1156 if (scr->clip_options)
1157 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1159 entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback,
1160 NULL);
1161 free(entry->text);
1162 entry->text = _("Rename Workspace");
1164 entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
1165 entry->flags.indicator = 1;
1166 entry->flags.indicator_on = 1;
1167 entry->flags.indicator_type = MI_CHECK;
1169 entry = wMenuAddCallback(menu, _("Select All Icons"),
1170 selectIconsCallback, NULL);
1171 free(entry->text);
1172 entry->text = _("Select All Icons");
1174 entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
1175 free(entry->text);
1176 entry->text = _("Keep Icon");
1178 entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
1179 free(entry->text);
1180 entry->text = _("Move Icon To");
1181 scr->clip_submenu = makeWorkspaceMenu(scr);
1182 if (scr->clip_submenu)
1183 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1185 entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback,
1186 NULL);
1187 free(entry->text);
1188 entry->text = _("Remove Icon");
1190 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1193 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1195 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1197 entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1198 free(entry->text);
1199 entry->text = _("Hide");
1201 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1203 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1205 if (type == WM_CLIP)
1206 scr->clip_menu = menu;
1208 return menu;
1212 WDock*
1213 wDockCreate(WScreen *scr, int type)
1215 WDock *dock;
1216 WAppIcon *btn;
1217 int icon_count;
1219 make_keys();
1221 dock = wmalloc(sizeof(WDock));
1222 memset(dock, 0, sizeof(WDock));
1224 if (type == WM_CLIP)
1225 icon_count = CLIP_MAX_ICONS;
1226 else
1227 icon_count = scr->scr_height/wPreferences.icon_size;
1229 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1230 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1232 dock->max_icons = icon_count;
1234 btn = mainIconCreate(scr, type);
1236 btn->dock = dock;
1238 dock->x_pos = btn->x_pos;
1239 dock->y_pos = btn->y_pos;
1240 dock->screen_ptr = scr;
1241 dock->type = type;
1242 dock->icon_count = 1;
1243 dock->on_right_side = 1;
1244 dock->collapsed = 0;
1245 dock->auto_collapse = 0;
1246 dock->auto_collapse_magic = NULL;
1247 dock->auto_raise_lower = 0;
1248 dock->auto_lower_magic = NULL;
1249 dock->auto_raise_magic = NULL;
1250 dock->attract_icons = 0;
1251 dock->lowered = 1;
1252 dock->icon_array[0] = btn;
1253 wRaiseFrame(btn->icon->core);
1254 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1256 /* create dock menu */
1257 dock->menu = dockMenuCreate(scr, type);
1259 return dock;
1263 void
1264 wDockDestroy(WDock *dock)
1266 int i;
1267 WAppIcon *aicon;
1269 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1270 aicon = dock->icon_array[i];
1271 if (aicon) {
1272 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1273 wDockDetach(dock, aicon);
1274 if (keepit) {
1275 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
1276 XMoveWindow(dpy, aicon->icon->core->window,
1277 aicon->x_pos, aicon->y_pos);
1278 if (!dock->mapped || dock->collapsed)
1279 XMapWindow(dpy, aicon->icon->core->window);
1283 if (wPreferences.auto_arrange_icons)
1284 wArrangeIcons(dock->screen_ptr, True);
1285 free(dock->icon_array);
1286 if (dock->menu && dock->type!=WM_CLIP)
1287 wMenuDestroy(dock->menu, True);
1288 free(dock);
1292 void
1293 wClipIconPaint(WAppIcon *aicon)
1295 WScreen *scr = aicon->icon->core->screen_ptr;
1296 WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1297 GC gc;
1298 Window win = aicon->icon->core->window;
1299 int length, nlength;
1300 char *ws_name, ws_number[10];
1301 int ty, tx;
1303 wIconPaint(aicon->icon);
1305 length = strlen(workspace->name);
1306 ws_name = malloc(length + 1);
1307 sprintf(ws_name, "%s", workspace->name);
1308 sprintf(ws_number, "%i", scr->current_workspace + 1);
1309 nlength = strlen(ws_number);
1311 gc = scr->clip_title_gc;
1313 if (!workspace->clip->collapsed)
1314 XSetForeground(dpy, gc, scr->clip_title_pixel[CLIP_NORMAL]);
1315 else
1316 XSetForeground(dpy, gc, scr->clip_title_pixel[CLIP_COLLAPSED]);
1318 ty = ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
1320 tx = CLIP_BUTTON_SIZE*ICON_SIZE/64;
1322 WMDrawString(scr->wmscreen, win, gc, scr->clip_title_font, tx,
1323 ty, ws_name, length);
1325 tx = (ICON_SIZE/2 - WMWidthOfString(scr->clip_title_font, ws_number,
1326 nlength))/2;
1328 WMDrawString(scr->wmscreen, win, gc, scr->clip_title_font, tx,
1329 2, ws_number, nlength);
1331 free(ws_name);
1333 if (aicon->launching) {
1334 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1335 0, 0, wPreferences.icon_size, wPreferences.icon_size);
1337 paintClipButtons(aicon, aicon->dock->lclip_button_pushed,
1338 aicon->dock->rclip_button_pushed);
1342 static void
1343 clipIconExpose(WObjDescriptor *desc, XEvent *event)
1345 wClipIconPaint(desc->parent);
1349 static void
1350 dockIconPaint(WAppIcon *btn)
1352 if (btn == btn->icon->core->screen_ptr->clip_icon)
1353 wClipIconPaint(btn);
1354 else
1355 wAppIconPaint(btn);
1359 static proplist_t
1360 make_icon_state(WAppIcon *btn)
1362 proplist_t node = NULL;
1363 proplist_t command, autolaunch, name, forced, host, position, buggy;
1364 proplist_t omnipresent;
1365 char *tmp;
1366 char buffer[64];
1368 if (btn) {
1369 if (!btn->command)
1370 command = PLMakeString("-");
1371 else
1372 command = PLMakeString(btn->command);
1374 autolaunch = btn->auto_launch ? dYes : dNo;
1376 tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
1378 name = PLMakeString(tmp);
1380 free(tmp);
1382 forced = btn->forced_dock ? dYes : dNo;
1384 buggy = btn->buggy_app ? dYes : dNo;
1386 if (btn == btn->icon->core->screen_ptr->clip_icon)
1387 sprintf(buffer, "%i,%i", btn->x_pos, btn->y_pos);
1388 else
1389 sprintf(buffer, "%hi,%hi", btn->xindex, btn->yindex);
1390 position = PLMakeString(buffer);
1392 node = PLMakeDictionaryFromEntries(dCommand, command,
1393 dName, name,
1394 dAutoLaunch, autolaunch,
1395 dForced, forced,
1396 dBuggyApplication, buggy,
1397 dPosition, position,
1398 NULL);
1399 PLRelease(command);
1400 PLRelease(name);
1401 PLRelease(position);
1403 omnipresent = btn->omnipresent ? dYes : dNo;
1404 if (btn->dock != btn->icon->core->screen_ptr->dock &&
1405 (btn->xindex != 0 || btn->yindex != 0))
1406 PLInsertDictionaryEntry(node, dOmnipresent, omnipresent);
1408 #ifdef OFFIX_DND
1409 if (btn->dnd_command) {
1410 command = PLMakeString(btn->dnd_command);
1411 PLInsertDictionaryEntry(node, dDropCommand, command);
1412 PLRelease(command);
1414 #endif /* OFFIX_DND */
1416 if (btn->client_machine && btn->remote_start) {
1417 host = PLMakeString(btn->client_machine);
1418 PLInsertDictionaryEntry(node, dHost, host);
1419 PLRelease(host);
1423 return node;
1427 static proplist_t
1428 dockSaveState(WDock *dock)
1430 int i;
1431 proplist_t icon_info;
1432 proplist_t list=NULL, dock_state=NULL;
1433 proplist_t value, key;
1434 char buffer[256];
1436 list = PLMakeArrayFromElements(NULL);
1438 for (i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
1439 WAppIcon *btn = dock->icon_array[i];
1441 if (!btn || btn->attracted)
1442 continue;
1444 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1445 list = PLAppendArrayElement(list, icon_info);
1446 PLRelease(icon_info);
1450 dock_state = PLMakeDictionaryFromEntries(dApplications, list,
1451 NULL);
1453 if (dock->type == WM_DOCK) {
1454 sprintf(buffer, "Applications%i", dock->screen_ptr->scr_height);
1455 key = PLMakeString(buffer);
1456 PLInsertDictionaryEntry(dock_state, key, list);
1457 PLRelease(key);
1460 sprintf(buffer, "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0),
1461 dock->y_pos);
1462 value = PLMakeString(buffer);
1463 PLInsertDictionaryEntry(dock_state, dPosition, value);
1464 PLRelease(value);
1466 PLRelease(list);
1469 value = (dock->lowered ? dYes : dNo);
1470 PLInsertDictionaryEntry(dock_state, dLowered, value);
1472 if (dock->type == WM_CLIP) {
1473 value = (dock->collapsed ? dYes : dNo);
1474 PLInsertDictionaryEntry(dock_state, dCollapsed, value);
1476 value = (dock->auto_collapse ? dYes : dNo);
1477 PLInsertDictionaryEntry(dock_state, dAutoCollapse, value);
1479 value = (dock->auto_raise_lower ? dYes : dNo);
1480 PLInsertDictionaryEntry(dock_state, dAutoRaiseLower, value);
1482 value = (dock->attract_icons ? dYes : dNo);
1483 PLInsertDictionaryEntry(dock_state, dAutoAttractIcons, value);
1486 return dock_state;
1490 void
1491 wDockSaveState(WScreen *scr, proplist_t old_state)
1493 proplist_t dock_state;
1494 proplist_t keys;
1496 dock_state = dockSaveState(scr->dock);
1499 * Copy saved states of docks with different sizes.
1501 if (old_state) {
1502 int i;
1503 proplist_t tmp;
1505 keys = PLGetAllDictionaryKeys(old_state);
1506 for (i = 0; i < PLGetNumberOfElements(keys); i++) {
1507 tmp = PLGetArrayElement(keys, i);
1509 if (strncasecmp(PLGetString(tmp), "applications", 12) == 0
1510 && !PLGetDictionaryEntry(dock_state, tmp)) {
1512 PLInsertDictionaryEntry(dock_state,
1513 tmp,
1514 PLGetDictionaryEntry(old_state, tmp));
1517 PLRelease(keys);
1521 PLInsertDictionaryEntry(scr->session_state, dDock, dock_state);
1523 PLRelease(dock_state);
1527 void
1528 wClipSaveState(WScreen *scr)
1530 proplist_t clip_state;
1532 clip_state = make_icon_state(scr->clip_icon);
1534 PLInsertDictionaryEntry(scr->session_state, dClip, clip_state);
1536 PLRelease(clip_state);
1540 proplist_t
1541 wClipSaveWorkspaceState(WScreen *scr, int workspace)
1543 return dockSaveState(scr->workspaces[workspace]->clip);
1547 static WAppIcon*
1548 restore_icon_state(WScreen *scr, proplist_t info, int type, int index)
1550 WAppIcon *aicon;
1551 char *wclass, *winstance;
1552 proplist_t cmd, value;
1553 char *command;
1556 cmd = PLGetDictionaryEntry(info, dCommand);
1557 if (!cmd || !PLIsString(cmd)) {
1558 return NULL;
1561 /* parse window name */
1562 value = PLGetDictionaryEntry(info, dName);
1563 if (!value)
1564 return NULL;
1566 ParseWindowName(value, &winstance, &wclass, "dock");
1568 if (!winstance && !wclass) {
1569 return NULL;
1572 /* get commands */
1574 if (cmd)
1575 command = wstrdup(PLGetString(cmd));
1576 else
1577 command = NULL;
1579 if (!command || strcmp(command, "-")==0) {
1580 if (command)
1581 free(command);
1582 if (wclass)
1583 free(wclass);
1584 if (winstance)
1585 free(winstance);
1587 return NULL;
1590 aicon = wAppIconCreateForDock(scr, command, winstance, wclass,
1591 TILE_NORMAL);
1592 if (wclass)
1593 free(wclass);
1594 if (winstance)
1595 free(winstance);
1597 aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1598 if (type == WM_CLIP) {
1599 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1600 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1602 aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1603 aicon->icon->core->descriptor.parent = aicon;
1606 #ifdef OFFIX_DND
1607 cmd = PLGetDictionaryEntry(info, dDropCommand);
1608 if (cmd)
1609 aicon->dnd_command = wstrdup(PLGetString(cmd));
1610 #endif
1612 /* check auto launch */
1613 value = PLGetDictionaryEntry(info, dAutoLaunch);
1615 aicon->auto_launch = 0;
1616 if (value) {
1617 if (PLIsString(value)) {
1618 if (strcasecmp(PLGetString(value), "YES")==0)
1619 aicon->auto_launch = 1;
1620 } else {
1621 wwarning(_("bad value in docked icon state info %s"),
1622 PLGetString(dAutoLaunch));
1626 /* check if it wasn't normally docked */
1627 value = PLGetDictionaryEntry(info, dForced);
1629 aicon->forced_dock = 0;
1630 if (value) {
1631 if (PLIsString(value)) {
1632 if (strcasecmp(PLGetString(value), "YES")==0)
1633 aicon->forced_dock = 1;
1634 } else {
1635 wwarning(_("bad value in docked icon state info %s"),
1636 PLGetString(dForced));
1640 /* check if we can rely on the stuff in the app */
1641 value = PLGetDictionaryEntry(info, dBuggyApplication);
1643 aicon->buggy_app = 0;
1644 if (value) {
1645 if (PLIsString(value)) {
1646 if (strcasecmp(PLGetString(value), "YES")==0)
1647 aicon->buggy_app = 1;
1648 } else {
1649 wwarning(_("bad value in docked icon state info %s"),
1650 PLGetString(dBuggyApplication));
1654 /* get position in the dock */
1655 value = PLGetDictionaryEntry(info, dPosition);
1656 if (value && PLIsString(value)) {
1657 if (sscanf(PLGetString(value), "%hi,%hi", &aicon->xindex,
1658 &aicon->yindex)!=2)
1659 wwarning(_("bad value in docked icon state info %s"),
1660 PLGetString(dPosition));
1662 /* check position sanity */
1663 /* incomplete section! */
1664 if (type == WM_DOCK) {
1665 aicon->xindex = 0;
1666 if (aicon->yindex < 0)
1667 wwarning(_("bad value in docked icon position %i,%i"),
1668 aicon->xindex, aicon->yindex);
1670 } else {
1671 aicon->yindex = index;
1672 aicon->xindex = 0;
1675 /* check if icon is omnipresent */
1676 value = PLGetDictionaryEntry(info, dOmnipresent);
1678 aicon->omnipresent = 0;
1679 if (value) {
1680 if (PLIsString(value)) {
1681 if (strcasecmp(PLGetString(value), "YES")==0)
1682 aicon->omnipresent = 1;
1683 } else {
1684 wwarning(_("bad value in docked icon state info %s"),
1685 PLGetString(dOmnipresent));
1689 aicon->running = 0;
1690 aicon->docked = 1;
1692 return aicon;
1696 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1699 WAppIcon*
1700 wClipRestoreState(WScreen *scr, proplist_t clip_state)
1702 WAppIcon *icon;
1703 proplist_t value;
1706 icon = mainIconCreate(scr, WM_CLIP);
1708 if (!clip_state)
1709 return icon;
1710 else
1711 PLRetain(clip_state);
1713 /* restore position */
1715 value = PLGetDictionaryEntry(clip_state, dPosition);
1717 if (value) {
1718 if (!PLIsString(value))
1719 COMPLAIN("Position");
1720 else {
1721 if (sscanf(PLGetString(value), "%i,%i", &icon->x_pos,
1722 &icon->y_pos)!=2)
1723 COMPLAIN("Position");
1725 /* check position sanity */
1726 if (icon->y_pos < 0)
1727 icon->y_pos = 0;
1728 else if (icon->y_pos > scr->scr_height-ICON_SIZE)
1729 icon->y_pos = scr->scr_height-ICON_SIZE;
1731 if (icon->x_pos < 0)
1732 icon->x_pos = 0;
1733 else if (icon->x_pos > scr->scr_width-ICON_SIZE)
1734 icon->x_pos = scr->scr_width-ICON_SIZE;
1738 #ifdef OFFIX_DND
1739 value = PLGetDictionaryEntry(clip_state, dDropCommand);
1740 if (value && PLIsString(value))
1741 icon->dnd_command = wstrdup(PLGetString(value));
1742 #endif
1744 PLRelease(clip_state);
1746 return icon;
1750 WDock*
1751 wDockRestoreState(WScreen *scr, proplist_t dock_state, int type)
1753 WDock *dock;
1754 proplist_t apps;
1755 proplist_t value;
1756 WAppIcon *aicon, *old_top;
1757 int count, i;
1760 dock = wDockCreate(scr, type);
1762 if (!dock_state)
1763 return dock;
1765 if (dock_state)
1766 PLRetain(dock_state);
1769 /* restore position */
1771 value = PLGetDictionaryEntry(dock_state, dPosition);
1773 if (value) {
1774 if (!PLIsString(value))
1775 COMPLAIN("Position");
1776 else {
1777 if (sscanf(PLGetString(value), "%i,%i", &dock->x_pos,
1778 &dock->y_pos)!=2)
1779 COMPLAIN("Position");
1781 /* check position sanity */
1782 if (dock->y_pos < 0)
1783 dock->y_pos = 0;
1784 else if (dock->y_pos > scr->scr_height-ICON_SIZE)
1785 dock->y_pos = scr->scr_height - ICON_SIZE;
1787 /* This is no more needed. ??? */
1788 if (type == WM_CLIP) {
1789 if (dock->x_pos < 0)
1790 dock->x_pos = 0;
1791 else if (dock->x_pos > scr->scr_width-ICON_SIZE)
1792 dock->x_pos = scr->scr_width-ICON_SIZE;
1793 } else {
1794 if (dock->x_pos >= 0) {
1795 dock->x_pos = DOCK_EXTRA_SPACE;
1796 dock->on_right_side = 0;
1797 } else {
1798 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1799 dock->on_right_side = 1;
1805 /* restore lowered/raised state */
1807 dock->lowered = 0;
1809 value = PLGetDictionaryEntry(dock_state, dLowered);
1811 if (value) {
1812 if (!PLIsString(value))
1813 COMPLAIN("Lowered");
1814 else {
1815 if (strcasecmp(PLGetString(value), "YES")==0)
1816 dock->lowered = 1;
1821 /* restore collapsed state */
1823 dock->collapsed = 0;
1825 value = PLGetDictionaryEntry(dock_state, dCollapsed);
1827 if (value) {
1828 if (!PLIsString(value))
1829 COMPLAIN("Collapsed");
1830 else {
1831 if (strcasecmp(PLGetString(value), "YES")==0)
1832 dock->collapsed = 1;
1837 /* restore auto-collapsed state */
1839 value = PLGetDictionaryEntry(dock_state, dAutoCollapse);
1841 if (value) {
1842 if (!PLIsString(value))
1843 COMPLAIN("AutoCollapse");
1844 else {
1845 if (strcasecmp(PLGetString(value), "YES")==0) {
1846 dock->auto_collapse = 1;
1847 dock->collapsed = 1;
1853 /* restore auto-raise/lower state */
1855 value = PLGetDictionaryEntry(dock_state, dAutoRaiseLower);
1857 if (value) {
1858 if (!PLIsString(value))
1859 COMPLAIN("AutoRaiseLower");
1860 else {
1861 if (strcasecmp(PLGetString(value), "YES")==0) {
1862 dock->auto_raise_lower = 1;
1867 /* restore attract icons state */
1869 dock->attract_icons = 0;
1871 value = PLGetDictionaryEntry(dock_state, dAutoAttractIcons);
1873 if (value) {
1874 if (!PLIsString(value))
1875 COMPLAIN("AutoAttractIcons");
1876 else {
1877 if (strcasecmp(PLGetString(value), "YES")==0)
1878 dock->attract_icons = 1;
1883 /* application list */
1886 proplist_t tmp;
1887 char buffer[64];
1890 * When saving, it saves the dock state in
1891 * Applications and Applicationsnnn
1893 * When loading, it will first try Applicationsnnn.
1894 * If it does not exist, use Applications as default.
1897 sprintf(buffer, "Applications%i", scr->scr_height);
1899 tmp = PLMakeString(buffer);
1900 apps = PLGetDictionaryEntry(dock_state, tmp);
1901 PLRelease(tmp);
1903 if (!apps) {
1904 apps = PLGetDictionaryEntry(dock_state, dApplications);
1908 if (!apps) {
1909 goto finish;
1912 count = PLGetNumberOfElements(apps);
1914 if (count==0)
1915 goto finish;
1917 old_top = dock->icon_array[0];
1919 /* dock->icon_count is set to 1 when dock is created.
1920 * Since Clip is already restored, we want to keep it so for clip,
1921 * but for dock we may change the default top tile, so we set it to 0.
1923 if (type == WM_DOCK)
1924 dock->icon_count = 0;
1926 for (i=0; i<count; i++) {
1927 if (dock->icon_count >= dock->max_icons) {
1928 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1929 break;
1932 value = PLGetArrayElement(apps, i);
1933 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1935 dock->icon_array[dock->icon_count] = aicon;
1937 if (aicon) {
1938 aicon->dock = dock;
1939 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1940 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1942 if (dock->lowered)
1943 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1944 else
1945 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1947 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1948 0, 0);
1950 if (!dock->collapsed)
1951 XMapWindow(dpy, aicon->icon->core->window);
1952 wRaiseFrame(aicon->icon->core);
1954 dock->icon_count++;
1955 } else if (dock->icon_count==0 && type==WM_DOCK)
1956 dock->icon_count++;
1959 /* if the first icon is not defined, use the default */
1960 if (dock->icon_array[0]==NULL) {
1961 /* update default icon */
1962 old_top->x_pos = dock->x_pos;
1963 old_top->y_pos = dock->y_pos;
1964 if (dock->lowered)
1965 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1966 else
1967 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1968 dock->icon_array[0] = old_top;
1969 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1970 /* we don't need to increment dock->icon_count here because it was
1971 * incremented in the loop above.
1973 } else if (old_top!=dock->icon_array[0]) {
1974 if (old_top == scr->clip_icon)
1975 scr->clip_icon = dock->icon_array[0];
1976 wAppIconDestroy(old_top);
1979 finish:
1980 if (dock_state)
1981 PLRelease(dock_state);
1983 return dock;
1988 void
1989 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
1991 if (btn && btn->command && !btn->running && !btn->launching) {
1993 btn->drop_launch = 0;
1995 btn->pid = execCommand(btn, btn->command, state);
1997 if (btn->pid>0) {
1998 if (!btn->forced_dock && !btn->buggy_app) {
1999 btn->launching = 1;
2000 dockIconPaint(btn);
2003 } else {
2004 free(state);
2009 void
2010 wDockDoAutoLaunch(WDock *dock, int workspace)
2012 WAppIcon *btn;
2013 WSavedState *state;
2014 int i;
2016 for (i = 0; i < dock->max_icons; i++) {
2017 btn = dock->icon_array[i];
2018 if (!btn || !btn->auto_launch)
2019 continue;
2021 state = wmalloc(sizeof(WSavedState));
2022 memset(state, 0, sizeof(WSavedState));
2023 state->workspace = workspace;
2024 /* TODO: this is klugy and is very difficult to understand
2025 * what's going on. Try to clean up */
2026 wDockLaunchWithState(dock, btn, state);
2030 #ifdef REDUCE_APPICONS
2031 void
2032 wDockSimulateLaunch(WDock *dock, WAppIcon *btn)
2034 if ((btn == NULL) || (dock == NULL))
2035 return;
2037 if (!btn->running) {
2038 if ((btn->icon->owner == NULL) && (btn->applist))
2039 btn->icon->owner = btn->applist->wapp->main_window_desc;
2040 if (!btn->forced_dock)
2041 btn->launching = 1;
2042 dockIconPaint(btn);
2043 wusleep(5000);
2046 #endif
2048 #ifdef OFFIX_DND
2049 static WDock*
2050 findDock(WScreen *scr, XEvent *event, int *icon_pos)
2052 WDock *dock;
2053 int i;
2055 *icon_pos = -1;
2056 if ((dock = scr->dock)!=NULL) {
2057 for (i=0; i<dock->max_icons; i++) {
2058 if (dock->icon_array[i]
2059 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2060 *icon_pos = i;
2061 break;
2065 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
2066 for (i=0; i<dock->max_icons; i++) {
2067 if (dock->icon_array[i]
2068 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2069 *icon_pos = i;
2070 break;
2074 if(*icon_pos>=0)
2075 return dock;
2076 return NULL;
2081 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
2083 WDock *dock;
2084 WAppIcon *btn;
2085 int icon_pos;
2087 dock = findDock(scr, event, &icon_pos);
2088 if (!dock)
2089 return False;
2092 * Return True if the drop was on an application icon window.
2093 * In this case, let the ClientMessage handler redirect the
2094 * message to the app.
2096 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
2097 return True;
2099 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
2100 scr->flags.dnd_data_convertion_status = 0;
2102 btn = dock->icon_array[icon_pos];
2104 if (!btn->forced_dock) {
2105 btn->relaunching = btn->running;
2106 btn->running = 1;
2108 if (btn->wm_instance || btn->wm_class) {
2109 WWindowAttributes attr;
2110 memset(&attr, 0, sizeof(WWindowAttributes));
2111 wDefaultFillAttributes(btn->icon->core->screen_ptr,
2112 btn->wm_instance,
2113 btn->wm_class, &attr, NULL, True);
2115 if (!attr.no_appicon)
2116 btn->launching = 1;
2117 else
2118 btn->running = 0;
2121 btn->drop_launch = 1;
2122 scr->last_dock = dock;
2123 btn->pid = execCommand(btn, btn->dnd_command, NULL);
2124 if (btn->pid>0) {
2125 dockIconPaint(btn);
2126 } else {
2127 btn->launching = 0;
2128 if (!btn->relaunching) {
2129 btn->running = 0;
2133 return False;
2135 #endif /* OFFIX_DND */
2139 Bool
2140 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2142 WWindow *wwin;
2143 char **argv;
2144 int argc;
2145 int index;
2147 wwin = icon->icon->owner;
2148 if (icon->command==NULL) {
2149 icon->editing = 0;
2150 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2152 icon->command = FlattenStringList(argv, argc);
2153 XFreeStringList(argv);
2154 } else {
2155 char *command=NULL;
2157 /* icon->forced_dock = 1;*/
2158 if (dock->type!=WM_CLIP || !icon->attracted) {
2159 icon->editing = 1;
2160 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
2161 _("Type the command used to launch the application"),
2162 &command)) {
2163 if (command && (command[0]==0 ||
2164 (command[0]=='-' && command[1]==0))) {
2165 free(command);
2166 command = NULL;
2168 icon->command = command;
2169 icon->editing = 0;
2170 } else {
2171 icon->editing = 0;
2172 if (command)
2173 free(command);
2174 /* If the target is the dock, reject the icon. If
2175 * the target is the clip, make it an attracted icon
2177 if (dock->type==WM_CLIP) {
2178 icon->attracted = 1;
2179 if (!icon->icon->shadowed) {
2180 icon->icon->shadowed = 1;
2181 icon->icon->force_paint = 1;
2183 } else {
2184 return False;
2189 } else {
2190 icon->editing = 0;
2193 for (index=1; index<dock->max_icons; index++)
2194 if (dock->icon_array[index] == NULL)
2195 break;
2196 /* if (index == dock->max_icons)
2197 return; */
2199 assert(index < dock->max_icons);
2201 dock->icon_array[index] = icon;
2202 icon->yindex = y;
2203 icon->xindex = x;
2205 icon->omnipresent = 0;
2207 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2208 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2210 dock->icon_count++;
2212 icon->running = 1;
2213 icon->launching = 0;
2214 icon->docked = 1;
2215 icon->dock = dock;
2216 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2217 if (dock->type == WM_CLIP) {
2218 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2219 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2221 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2222 icon->icon->core->descriptor.parent = icon;
2224 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2225 icon->icon->core);
2226 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2227 wAppIconPaint(icon);
2229 if (wPreferences.auto_arrange_icons)
2230 wArrangeIcons(dock->screen_ptr, True);
2232 #ifdef OFFIX_DND
2233 if (icon->command && !icon->dnd_command) {
2234 icon->dnd_command = wmalloc(strlen(icon->command)+8);
2235 sprintf(icon->dnd_command, "%s %%d", icon->command);
2237 #endif
2239 return True;
2243 void
2244 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2246 int index;
2248 for(index=1; index<dock->max_icons; index++) {
2249 if(dock->icon_array[index] == icon)
2250 break;
2252 assert(index < dock->max_icons);
2254 icon->yindex = y;
2255 icon->xindex = x;
2257 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2258 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2262 Bool
2263 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2265 WWindow *wwin;
2266 char **argv;
2267 int argc;
2268 int index;
2270 if (src == dest)
2271 return True; /* No move needed, we're already there */
2273 if (dest == NULL)
2274 return False;
2276 wwin = icon->icon->owner;
2279 * For the moment we can't do this if we move icons in Clip from one
2280 * workspace to other, because if we move two or more icons without
2281 * command, the dialog box will not be able to tell us to which of the
2282 * moved icons it applies. -Dan
2284 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2285 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2287 icon->command = FlattenStringList(argv, argc);
2288 XFreeStringList(argv);
2289 } else {
2290 char *command=NULL;
2292 icon->editing = 1;
2293 /* icon->forced_dock = 1;*/
2294 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2295 _("Type the command used to launch the application"),
2296 &command)) {
2297 if (command && (command[0]==0 ||
2298 (command[0]=='-' && command[1]==0))) {
2299 free(command);
2300 command = NULL;
2302 icon->command = command;
2303 } else {
2304 icon->editing = 0;
2305 if (command)
2306 free(command);
2307 return False;
2309 icon->editing = 0;
2313 if (dest->type == WM_DOCK)
2314 wClipMakeIconOmnipresent(icon, False);
2316 for(index=1; index<src->max_icons; index++) {
2317 if(src->icon_array[index] == icon)
2318 break;
2320 assert(index < src->max_icons);
2322 src->icon_array[index] = NULL;
2323 src->icon_count--;
2325 for(index=1; index<dest->max_icons; index++) {
2326 if(dest->icon_array[index] == NULL)
2327 break;
2329 /* if (index == dest->max_icons)
2330 return; */
2332 assert(index < dest->max_icons);
2334 dest->icon_array[index] = icon;
2335 icon->dock = dest;
2337 /* deselect the icon */
2338 if (icon->icon->selected)
2339 wIconSelect(icon->icon);
2341 if (dest->type == WM_DOCK) {
2342 icon->icon->core->descriptor.handle_enternotify = NULL;
2343 icon->icon->core->descriptor.handle_leavenotify = NULL;
2344 } else {
2345 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2346 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2349 /* set it to be kept when moving to dock.
2350 * Unless the icon does not have a command set
2352 if (icon->command && dest->type==WM_DOCK) {
2353 icon->attracted = 0;
2354 if (icon->icon->shadowed) {
2355 icon->icon->shadowed = 0;
2356 icon->icon->force_paint = 1;
2360 if (src->auto_collapse || src->auto_raise_lower)
2361 clipLeave(src);
2363 icon->yindex = y;
2364 icon->xindex = x;
2366 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2367 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2369 dest->icon_count++;
2371 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2372 icon->icon->core);
2373 wAppIconPaint(icon);
2375 return True;
2379 void
2380 wDockDetach(WDock *dock, WAppIcon *icon)
2382 int index;
2384 /* make the settings panel be closed */
2385 if (icon->panel) {
2386 DestroyDockAppSettingsPanel(icon->panel);
2389 /* This must be called before icon->dock is set to NULL.
2390 * Don't move it. -Dan
2392 wClipMakeIconOmnipresent(icon, False);
2394 icon->docked = 0;
2395 icon->dock = NULL;
2396 icon->attracted = 0;
2397 if (icon->icon->shadowed) {
2398 icon->icon->shadowed = 0;
2399 icon->icon->force_paint = 1;
2402 /* deselect the icon */
2403 if (icon->icon->selected)
2404 wIconSelect(icon->icon);
2406 if (icon->command) {
2407 free(icon->command);
2408 icon->command = NULL;
2410 #ifdef OFFIX_DND
2411 if (icon->dnd_command) {
2412 free(icon->dnd_command);
2413 icon->dnd_command = NULL;
2415 #endif
2417 for (index=1; index<dock->max_icons; index++)
2418 if (dock->icon_array[index] == icon)
2419 break;
2420 assert(index < dock->max_icons);
2421 dock->icon_array[index] = NULL;
2422 icon->yindex = -1;
2423 icon->xindex = -1;
2425 dock->icon_count--;
2427 /* if the dock is not attached to an application or
2428 * the the application did not set the approriate hints yet,
2429 * destroy the icon */
2430 #ifdef REDUCE_APPICONS
2431 if ((icon->num_apps == 0) && (!icon->running || !wApplicationOf(icon->main_window)) )
2432 #else
2433 if (!icon->running || !wApplicationOf(icon->main_window))
2434 #endif
2435 wAppIconDestroy(icon);
2436 else {
2437 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2438 icon->icon->core->descriptor.handle_enternotify = NULL;
2439 icon->icon->core->descriptor.handle_leavenotify = NULL;
2440 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2441 icon->icon->core->descriptor.parent = icon;
2443 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2445 wAppIconPaint(icon);
2446 if (wPreferences.auto_arrange_icons) {
2447 wArrangeIcons(dock->screen_ptr, True);
2450 if (dock->auto_collapse || dock->auto_raise_lower)
2451 clipLeave(dock);
2456 * returns the closest Dock slot index for the passed
2457 * coordinates.
2459 * Returns False if icon can't be docked.
2461 * Note: this function should NEVER alter ret_x or ret_y, unless it will
2462 * return True. -Dan
2464 Bool
2465 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2466 int *ret_x, int *ret_y, int redocking)
2468 WScreen *scr = dock->screen_ptr;
2469 int dx, dy;
2470 int ex_x, ex_y;
2471 int i, offset = ICON_SIZE/2;
2472 WAppIcon *aicon = NULL;
2473 WAppIcon *nicon = NULL;
2474 int max_y_icons, max_x_icons;
2476 max_x_icons = scr->scr_width/ICON_SIZE;
2477 max_y_icons = scr->scr_height/ICON_SIZE-1;
2479 if (wPreferences.flags.noupdates)
2480 return False;
2482 dx = dock->x_pos;
2483 dy = dock->y_pos;
2485 /* if the dock is full */
2486 if (!redocking &&
2487 (dock->icon_count >= dock->max_icons)) {
2488 return False;
2491 /* exact position */
2492 if (req_y < dy)
2493 ex_y = (req_y - offset - dy)/ICON_SIZE;
2494 else
2495 ex_y = (req_y + offset - dy)/ICON_SIZE;
2497 if (req_x < dx)
2498 ex_x = (req_x - offset - dx)/ICON_SIZE;
2499 else
2500 ex_x = (req_x + offset - dx)/ICON_SIZE;
2502 /* check if the icon is outside the screen boundaries */
2503 if (dx + ex_x*ICON_SIZE < -ICON_SIZE+2 ||
2504 dx + ex_x*ICON_SIZE >= scr->scr_width-1 ||
2505 dy + ex_y*ICON_SIZE < -ICON_SIZE+2 ||
2506 dy + ex_y*ICON_SIZE >= scr->scr_height-1)
2507 return False;
2509 if (dock->type == WM_DOCK) {
2510 if (icon->dock != dock && ex_x != 0)
2511 return False;
2513 aicon = NULL;
2514 for (i=0; i<dock->max_icons; i++) {
2515 nicon = dock->icon_array[i];
2516 if (nicon && nicon->yindex == ex_y) {
2517 aicon = nicon;
2518 break;
2522 if (redocking) {
2523 int sig, done, closest;
2525 /* Possible cases when redocking:
2527 * icon dragged out of range of any slot -> false
2528 * icon dragged to range of free slot
2529 * icon dragged to range of same slot
2530 * icon dragged to range of different icon
2532 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2533 return False;
2535 if (ex_y>=0 && ex_y<=max_y_icons && (aicon==icon || !aicon)) {
2536 *ret_x = 0;
2537 *ret_y = ex_y;
2538 return True;
2541 /* start looking at the upper slot or lower? */
2542 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2543 sig = 1;
2544 else
2545 sig = -1;
2547 closest = -1;
2548 done = 0;
2549 /* look for closest free slot */
2550 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2551 int j;
2553 done = 1;
2554 closest = sig*(i/2) + ex_y;
2555 /* check if this slot is used */
2556 if (closest >= 0) {
2557 for (j = 0; j<dock->max_icons; j++) {
2558 if (dock->icon_array[j]
2559 && dock->icon_array[j]->yindex==closest) {
2560 /* slot is used by someone else */
2561 if (dock->icon_array[j]!=icon)
2562 done = 0;
2563 break;
2567 sig = -sig;
2569 if (done && closest >= 0 && closest <= max_y_icons &&
2570 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2572 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2573 *ret_x = 0;
2574 *ret_y = closest;
2575 return True;
2577 } else { /* !redocking */
2579 /* if slot is free and the icon is close enough, return it */
2580 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2581 *ret_x = 0;
2582 *ret_y = ex_y;
2583 return True;
2586 } else { /* CLIP */
2587 int neighbours = 0;
2588 int start, stop, k;
2590 start = icon->omnipresent ? 0 : scr->current_workspace;
2591 stop = icon->omnipresent ? scr->workspace_count : start+1;
2593 aicon = NULL;
2594 for (k=start; k<stop; k++) {
2595 WDock *tmp = scr->workspaces[k]->clip;
2596 if (!tmp)
2597 continue;
2598 for (i=0; i<tmp->max_icons; i++) {
2599 nicon = tmp->icon_array[i];
2600 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2601 aicon = nicon;
2602 break;
2605 if (aicon)
2606 break;
2608 for (k=start; k<stop; k++) {
2609 WDock *tmp = scr->workspaces[k]->clip;
2610 if (!tmp)
2611 continue;
2612 for (i=0; i<tmp->max_icons; i++) {
2613 nicon = tmp->icon_array[i];
2614 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2615 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2616 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2617 neighbours = 1;
2618 break;
2621 if (neighbours)
2622 break;
2625 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2626 *ret_x = ex_x;
2627 *ret_y = ex_y;
2628 return True;
2631 return False;
2634 #define MIN(x, y) ((x) > (y) ? (y) : (x))
2635 #define MAX(x, y) ((x) < (y) ? (y) : (x))
2637 #define ON_SCREEN(x, y, sx, ex, sy, ey) \
2638 ((((x)+ICON_SIZE/2) >= (sx)) && (((y)+ICON_SIZE/2) >= (sy)) && \
2639 (((x) + (ICON_SIZE/2)) <= (ex)) && (((y) + (ICON_SIZE/2)) <= ey))
2643 * returns true if it can find a free slot in the dock,
2644 * in which case it changes x_pos and y_pos accordingly.
2645 * Else returns false.
2647 Bool
2648 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2650 WScreen *scr = dock->screen_ptr;
2651 WAppIcon *btn;
2652 WAppIconChain *chain;
2653 unsigned char *slot_map;
2654 int mwidth;
2655 int r;
2656 int x, y;
2657 int i, done = False;
2658 int corner;
2659 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2660 int extra_count=0;
2662 if (dock->type == WM_CLIP &&
2663 dock != scr->workspaces[scr->current_workspace]->clip)
2664 extra_count = scr->global_icon_count;
2666 /* if the dock is full */
2667 if (dock->icon_count+extra_count >= dock->max_icons) {
2668 return False;
2671 if (!wPreferences.flags.nodock && scr->dock) {
2672 if (scr->dock->on_right_side)
2673 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2674 else
2675 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2678 if (ex < dock->x_pos)
2679 ex = dock->x_pos;
2680 if (sx > dock->x_pos+ICON_SIZE)
2681 sx = dock->x_pos+ICON_SIZE;
2682 #define C_NONE 0
2683 #define C_NW 1
2684 #define C_NE 2
2685 #define C_SW 3
2686 #define C_SE 4
2688 /* check if clip is in a corner */
2689 if (dock->type==WM_CLIP) {
2690 if (dock->x_pos < 1 && dock->y_pos < 1)
2691 corner = C_NE;
2692 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2693 corner = C_SE;
2694 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2695 corner = C_SW;
2696 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2697 corner = C_NW;
2698 else
2699 corner = C_NONE;
2700 } else
2701 corner = C_NONE;
2703 /* If the clip is in the corner, use only slots that are in the border
2704 * of the screen */
2705 if (corner!=C_NONE) {
2706 char *hmap, *vmap;
2707 int hcount, vcount;
2709 hcount = MIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2710 vcount = MIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2711 hmap = wmalloc(hcount+1);
2712 memset(hmap, 0, hcount+1);
2713 vmap = wmalloc(vcount+1);
2714 memset(vmap, 0, vcount+1);
2716 /* mark used positions */
2717 switch (corner) {
2718 case C_NE:
2719 for (i=0; i<dock->max_icons; i++) {
2720 btn = dock->icon_array[i];
2721 if (!btn)
2722 continue;
2724 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2725 vmap[btn->yindex] = 1;
2726 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2727 hmap[btn->xindex] = 1;
2729 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2730 btn = chain->aicon;
2731 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2732 vmap[btn->yindex] = 1;
2733 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2734 hmap[btn->xindex] = 1;
2736 break;
2737 case C_NW:
2738 for (i=0; i<dock->max_icons; i++) {
2739 btn = dock->icon_array[i];
2740 if (!btn)
2741 continue;
2743 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2744 vmap[btn->yindex] = 1;
2745 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2746 hmap[-btn->xindex] = 1;
2748 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2749 btn = chain->aicon;
2750 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2751 vmap[btn->yindex] = 1;
2752 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2753 hmap[-btn->xindex] = 1;
2755 break;
2756 case C_SE:
2757 for (i=0; i<dock->max_icons; i++) {
2758 btn = dock->icon_array[i];
2759 if (!btn)
2760 continue;
2762 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2763 vmap[-btn->yindex] = 1;
2764 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2765 hmap[btn->xindex] = 1;
2767 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2768 btn = chain->aicon;
2769 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2770 vmap[-btn->yindex] = 1;
2771 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2772 hmap[btn->xindex] = 1;
2774 break;
2775 case C_SW:
2776 default:
2777 for (i=0; i<dock->max_icons; i++) {
2778 btn = dock->icon_array[i];
2779 if (!btn)
2780 continue;
2782 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2783 vmap[-btn->yindex] = 1;
2784 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2785 hmap[-btn->xindex] = 1;
2787 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2788 btn = chain->aicon;
2789 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2790 vmap[-btn->yindex] = 1;
2791 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2792 hmap[-btn->xindex] = 1;
2795 x=0; y=0;
2796 done = 0;
2797 /* search a vacant slot */
2798 for (i=1; i<MAX(vcount, hcount); i++) {
2799 if (i < vcount && vmap[i]==0) {
2800 /* found a slot */
2801 x = 0;
2802 y = i;
2803 done = 1;
2804 break;
2805 } else if (i < hcount && hmap[i]==0) {
2806 /* found a slot */
2807 x = i;
2808 y = 0;
2809 done = 1;
2810 break;
2813 free(vmap);
2814 free(hmap);
2815 /* If found a slot, translate and return */
2816 if (done) {
2817 if (corner==C_NW || corner==C_NE) {
2818 *y_pos = y;
2819 } else {
2820 *y_pos = -y;
2822 if (corner==C_NE || corner==C_SE) {
2823 *x_pos = x;
2824 } else {
2825 *x_pos = -x;
2827 return True;
2829 /* else, try to find a slot somewhere else */
2832 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2833 * placed outside of screen */
2834 mwidth = (int)ceil(sqrt(dock->max_icons));
2836 /* In the worst case (the clip is in the corner of the screen),
2837 * the amount of icons that fit in the clip is smaller.
2838 * Double the map to get a safe value.
2840 mwidth += mwidth;
2842 r = (mwidth-1)/2;
2844 slot_map = wmalloc(mwidth*mwidth);
2845 memset(slot_map, 0, mwidth*mwidth);
2847 #define XY2OFS(x,y) (MAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2849 /* mark used slots in the map. If the slot falls outside the map
2850 * (for example, when all icons are placed in line), ignore them. */
2851 for (i=0; i<dock->max_icons; i++) {
2852 btn = dock->icon_array[i];
2853 if (btn)
2854 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2856 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2857 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2859 /* Find closest slot from the center that is free by scanning the
2860 * map from the center to outward in circular passes.
2861 * This will not result in a neat layout, but will be optimal
2862 * in the sense that there will not be holes left.
2864 done = 0;
2865 for (i = 1; i <= r && !done; i++) {
2866 int tx, ty;
2868 /* top and bottom parts of the ring */
2869 for (x = -i; x <= i && !done; x++) {
2870 tx = dock->x_pos + x*ICON_SIZE;
2871 y = -i;
2872 ty = dock->y_pos + y*ICON_SIZE;
2873 if (slot_map[XY2OFS(x,y)]==0
2874 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2875 *x_pos = x;
2876 *y_pos = y;
2877 done = 1;
2878 break;
2880 y = i;
2881 ty = dock->y_pos + y*ICON_SIZE;
2882 if (slot_map[XY2OFS(x,y)]==0
2883 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2884 *x_pos = x;
2885 *y_pos = y;
2886 done = 1;
2887 break;
2890 /* left and right parts of the ring */
2891 for (y = -i+1; y <= i-1; y++) {
2892 ty = dock->y_pos + y*ICON_SIZE;
2893 x = -i;
2894 tx = dock->x_pos + x*ICON_SIZE;
2895 if (slot_map[XY2OFS(x,y)]==0
2896 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2897 *x_pos = x;
2898 *y_pos = y;
2899 done = 1;
2900 break;
2902 x = i;
2903 tx = dock->x_pos + x*ICON_SIZE;
2904 if (slot_map[XY2OFS(x,y)]==0
2905 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2906 *x_pos = x;
2907 *y_pos = y;
2908 done = 1;
2909 break;
2913 free(slot_map);
2914 #undef XY2OFS
2915 return done;
2919 static void
2920 moveDock(WDock *dock, int new_x, int new_y)
2922 WAppIcon *btn;
2923 int i;
2925 dock->x_pos = new_x;
2926 dock->y_pos = new_y;
2927 for (i=0; i<dock->max_icons; i++) {
2928 btn = dock->icon_array[i];
2929 if (btn) {
2930 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2931 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2932 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2938 static void
2939 swapDock(WDock *dock)
2941 WScreen *scr = dock->screen_ptr;
2942 WAppIcon *btn;
2943 int x, i;
2946 if (dock->on_right_side) {
2947 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2948 } else {
2949 x = dock->x_pos = DOCK_EXTRA_SPACE;
2952 for (i=0; i<dock->max_icons; i++) {
2953 btn = dock->icon_array[i];
2954 if (btn) {
2955 btn->x_pos = x;
2956 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2960 wScreenUpdateUsableArea(scr);
2964 static pid_t
2965 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2967 WScreen *scr = btn->icon->core->screen_ptr;
2968 pid_t pid;
2969 char **argv;
2970 int argc;
2971 char *cmdline;
2973 cmdline = ExpandOptions(scr, command);
2975 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2976 if (cmdline)
2977 free(cmdline);
2978 if (state)
2979 free(state);
2980 return 0;
2983 ParseCommand(cmdline, &argv, &argc);
2985 if (argv==NULL) {
2986 if (cmdline)
2987 free(cmdline);
2988 if (state)
2989 free(state);
2990 return 0;
2993 if ((pid=fork())==0) {
2994 char **args;
2995 int i;
2997 SetupEnvironment(scr);
2999 #ifdef HAVE_SETPGID
3000 setpgid(0, 0);
3001 #endif
3003 args = malloc(sizeof(char*)*(argc+1));
3004 if (!args)
3005 exit(111);
3006 for (i=0; i<argc; i++) {
3007 args[i] = argv[i];
3009 args[argc] = NULL;
3010 execvp(argv[0], args);
3011 exit(111);
3013 while (argc > 0)
3014 free(argv[--argc]);
3015 free(argv);
3017 if (pid > 0) {
3018 if (!state) {
3019 state = wmalloc(sizeof(WSavedState));
3020 memset(state, 0, sizeof(WSavedState));
3021 state->hidden = -1;
3022 state->miniaturized = -1;
3023 state->shaded = -1;
3024 if (btn->dock==scr->dock || btn->omnipresent)
3025 state->workspace = -1;
3026 else
3027 state->workspace = scr->current_workspace;
3029 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
3030 state);
3031 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
3032 btn->dock);
3033 } else if (state) {
3034 free(state);
3036 free(cmdline);
3037 return pid;
3041 void
3042 wDockHideIcons(WDock *dock)
3044 int i;
3046 if (dock==NULL)
3047 return;
3049 for (i=1; i<dock->max_icons; i++) {
3050 if (dock->icon_array[i])
3051 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
3053 dock->mapped = 0;
3055 dockIconPaint(dock->icon_array[0]);
3059 void
3060 wDockShowIcons(WDock *dock)
3062 int i, newlevel;
3063 WAppIcon *btn;
3065 if (dock==NULL)
3066 return;
3068 btn = dock->icon_array[0];
3069 moveDock(dock, btn->x_pos, btn->y_pos);
3071 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
3072 ChangeStackingLevel(btn->icon->core, newlevel);
3074 for (i=1; i<dock->max_icons; i++) {
3075 if (dock->icon_array[i]) {
3076 MoveInStackListAbove(dock->icon_array[i]->icon->core,
3077 btn->icon->core);
3078 break;
3082 if (!dock->collapsed) {
3083 for (i=1; i<dock->max_icons; i++) {
3084 if (dock->icon_array[i]) {
3085 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
3089 dock->mapped = 1;
3091 dockIconPaint(btn);
3095 void
3096 wDockLower(WDock *dock)
3098 int i;
3100 for (i=0; i<dock->max_icons; i++) {
3101 if (dock->icon_array[i])
3102 wLowerFrame(dock->icon_array[i]->icon->core);
3107 void
3108 wDockRaise(WDock *dock)
3110 int i;
3112 for (i=dock->max_icons-1; i>=0; i--) {
3113 if (dock->icon_array[i])
3114 wRaiseFrame(dock->icon_array[i]->icon->core);
3119 void
3120 wDockRaiseLower(WDock *dock)
3122 if (!dock->icon_array[0]->icon->core->stacking->above
3123 ||(dock->icon_array[0]->icon->core->stacking->window_level
3124 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
3125 wDockLower(dock);
3126 else
3127 wDockRaise(dock);
3131 void
3132 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
3134 icon->launching = 0;
3135 icon->relaunching = 0;
3136 dockIconPaint(icon);
3140 WAppIcon*
3141 wDockFindIconFor(WDock *dock, Window window)
3143 WAppIcon *icon;
3144 int i;
3146 for (i=0; i<dock->max_icons; i++) {
3147 icon = dock->icon_array[i];
3148 if (icon && icon->main_window == window)
3149 return icon;
3151 return NULL;
3155 void
3156 wDockTrackWindowLaunch(WDock *dock, Window window)
3158 WAppIcon *icon;
3159 #ifdef REDUCE_APPICONS
3160 WAppIconAppList *tapplist;
3161 #endif
3162 char *wm_class, *wm_instance;
3163 int i;
3164 Bool firstPass = True;
3165 Bool found = False;
3166 char *command = NULL;
3169 int argc;
3170 char **argv;
3172 if (XGetCommand(dpy, window, &argv, &argc)) {
3173 if (argc > 0 && argv != NULL)
3174 command = FlattenStringList(argv,argc);
3175 if (argv) {
3176 XFreeStringList(argv);
3181 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
3182 (!wm_class && !wm_instance))
3183 return;
3185 retry:
3186 for (i=0; i<dock->max_icons; i++) {
3187 icon = dock->icon_array[i];
3188 if (!icon)
3189 continue;
3191 /* app is already attached to icon */
3192 if (icon->main_window == window) {
3193 found = True;
3194 break;
3197 if ((icon->wm_instance || icon->wm_class)
3198 && (icon->launching
3199 || (dock->screen_ptr->flags.startup && !icon->running))) {
3201 if (icon->wm_instance && wm_instance &&
3202 strcmp(icon->wm_instance, wm_instance)!=0) {
3203 continue;
3205 if (icon->wm_class && wm_class &&
3206 strcmp(icon->wm_class, wm_class)!=0) {
3207 continue;
3209 if (firstPass && command && strcmp(icon->command, command)!=0) {
3210 continue;
3213 if (!icon->relaunching) {
3214 WApplication *wapp;
3216 /* Possibly an application that was docked with dockit,
3217 * but the user did not update WMState to indicate that
3218 * it was docked by force */
3219 wapp = wApplicationOf(window);
3220 if (!wapp) {
3221 icon->forced_dock = 1;
3222 icon->running = 0;
3224 if (!icon->forced_dock)
3225 icon->main_window = window;
3227 #ifdef REDUCE_APPICONS
3228 tapplist = wmalloc(sizeof(WAppIconAppList));
3229 memset(tapplist, 0, sizeof(WAppIconAppList));
3230 tapplist->next = icon->applist;
3231 if (icon->applist)
3232 icon->applist->prev = tapplist;
3233 icon->applist = tapplist;
3234 tapplist->wapp = wApplicationOf(window);
3235 icon->num_apps++;
3236 #endif
3238 found = True;
3239 wDockFinishLaunch(dock, icon);
3240 break;
3244 if (firstPass && !found) {
3245 firstPass = False;
3246 goto retry;
3249 if (command)
3250 free(command);
3252 if (wm_class)
3253 XFree(wm_class);
3254 if (wm_instance)
3255 XFree(wm_instance);
3260 void
3261 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3263 if (!wPreferences.flags.noclip) {
3264 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3265 if (scr->current_workspace != workspace) {
3266 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3267 WAppIconChain *chain = scr->global_icons;
3269 while (chain) {
3270 moveIconBetweenDocks(chain->aicon->dock,
3271 scr->workspaces[workspace]->clip,
3272 chain->aicon, chain->aicon->xindex,
3273 chain->aicon->yindex);
3274 if (scr->workspaces[workspace]->clip->collapsed)
3275 XUnmapWindow(dpy, chain->aicon->icon->core->window);
3276 chain = chain->next;
3279 wDockHideIcons(old_clip);
3280 if (old_clip->auto_raise_lower) {
3281 if (old_clip->auto_raise_magic) {
3282 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3283 old_clip->auto_raise_magic = NULL;
3285 wDockLower(old_clip);
3287 if (old_clip->auto_collapse) {
3288 if (old_clip->auto_expand_magic) {
3289 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3290 old_clip->auto_expand_magic = NULL;
3292 old_clip->collapsed = 1;
3294 wDockShowIcons(scr->workspaces[workspace]->clip);
3296 if (scr->flags.clip_balloon_mapped)
3297 showClipBalloon(scr->clip_icon->dock, workspace);
3303 static void
3304 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3306 WAppIcon *icon;
3307 int i;
3309 for (i=0; i<dock->max_icons; i++) {
3310 icon = dock->icon_array[i];
3311 if (!icon)
3312 continue;
3314 if (icon->launching && icon->pid == pid) {
3315 if (!icon->relaunching) {
3316 icon->running = 0;
3317 icon->main_window = None;
3319 wDockFinishLaunch(dock, icon);
3320 icon->pid = 0;
3321 if (status==111) {
3322 char msg[PATH_MAX];
3323 #ifdef OFFIX_DND
3324 sprintf(msg, _("Could not execute command \"%s\""),
3325 icon->drop_launch && icon->dnd_command
3326 ? icon->dnd_command : icon->command);
3327 #else
3328 sprintf(msg, _("Could not execute command \"%s\""),
3329 icon->command);
3330 #endif
3331 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3332 _("OK"), NULL, NULL);
3334 break;
3340 static void
3341 toggleLowered(WDock *dock)
3343 WAppIcon *tmp;
3344 int newlevel, i;
3346 /* lower/raise Dock */
3347 if (!dock->lowered) {
3348 newlevel = WMNormalLevel;
3349 dock->lowered = 1;
3350 } else {
3351 newlevel = WMDockLevel;
3352 dock->lowered = 0;
3355 for (i=0; i<dock->max_icons; i++) {
3356 tmp = dock->icon_array[i];
3357 if (!tmp)
3358 continue;
3360 ChangeStackingLevel(tmp->icon->core, newlevel);
3361 if (dock->lowered)
3362 wLowerFrame(tmp->icon->core);
3365 if (dock->type == WM_DOCK)
3366 wScreenUpdateUsableArea(dock->screen_ptr);
3370 static void
3371 toggleCollapsed(WDock *dock)
3373 if (dock->collapsed) {
3374 dock->collapsed = 0;
3375 wDockShowIcons(dock);
3377 else {
3378 dock->collapsed = 1;
3379 wDockHideIcons(dock);
3384 static void
3385 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3387 WScreen *scr = dock->screen_ptr;
3388 WObjDescriptor *desc;
3389 WMenuEntry *entry;
3390 WApplication *wapp = NULL;
3391 int index = 0;
3392 int x_pos;
3393 int n_selected;
3394 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3396 if (dock->type == WM_DOCK) {
3397 /* keep on top */
3398 entry = dock->menu->entries[index];
3399 entry->flags.indicator_on = !dock->lowered;
3400 entry->clientdata = dock;
3401 } else {
3402 /* clip options */
3403 if (scr->clip_options)
3404 updateClipOptionsMenu(scr->clip_options, dock);
3406 n_selected = numberOfSelectedIcons(dock);
3408 /* Rename Workspace */
3409 entry = dock->menu->entries[++index];
3410 if (aicon == scr->clip_icon) {
3411 entry->callback = renameCallback;
3412 entry->clientdata = dock;
3413 entry->flags.indicator = 0;
3414 entry->text = _("Rename Workspace");
3415 } else {
3416 entry->callback = omnipresentCallback;
3417 entry->clientdata = aicon;
3418 if (n_selected > 0) {
3419 entry->flags.indicator = 0;
3420 entry->text = _("Toggle Omnipresent");
3421 } else {
3422 entry->flags.indicator = 1;
3423 entry->flags.indicator_on = aicon->omnipresent;
3424 entry->flags.indicator_type = MI_CHECK;
3425 entry->text = _("Omnipresent");
3429 /* select/unselect icon */
3430 entry = dock->menu->entries[++index];
3431 entry->clientdata = aicon;
3432 entry->flags.indicator_on = aicon->icon->selected;
3433 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3435 /* select/unselect all icons */
3436 entry = dock->menu->entries[++index];
3437 entry->clientdata = aicon;
3438 if (n_selected > 0)
3439 entry->text = _("Unselect All Icons");
3440 else
3441 entry->text = _("Select All Icons");
3442 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3444 /* keep icon(s) */
3445 entry = dock->menu->entries[++index];
3446 entry->clientdata = aicon;
3447 if (n_selected > 1)
3448 entry->text = _("Keep Icons");
3449 else
3450 entry->text = _("Keep Icon");
3451 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3453 /* this is the workspace submenu part */
3454 entry = dock->menu->entries[++index];
3455 if (n_selected > 1)
3456 entry->text = _("Move Icons To");
3457 else
3458 entry->text = _("Move Icon To");
3459 if (scr->clip_submenu)
3460 updateWorkspaceMenu(scr->clip_submenu, aicon);
3461 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3463 /* remove icon(s) */
3464 entry = dock->menu->entries[++index];
3465 entry->clientdata = aicon;
3466 if (n_selected > 1)
3467 entry->text = _("Remove Icons");
3468 else
3469 entry->text = _("Remove Icon");
3470 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3472 /* attract icon(s) */
3473 entry = dock->menu->entries[++index];
3474 entry->clientdata = aicon;
3476 dock->menu->flags.realized = 0;
3477 wMenuRealize(dock->menu);
3480 /* launch */
3481 entry = dock->menu->entries[++index];
3482 entry->clientdata = aicon;
3483 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3485 /* unhide here */
3486 entry = dock->menu->entries[++index];
3487 entry->clientdata = aicon;
3488 wMenuSetEnabled(dock->menu, index, appIsRunning);
3490 /* hide */
3491 entry = dock->menu->entries[++index];
3492 entry->clientdata = aicon;
3493 if (aicon->icon->owner) {
3494 wapp = wApplicationOf(aicon->icon->owner->main_window);
3495 if (wapp && wapp->flags.hidden)
3496 entry->text = _("Unhide");
3497 else
3498 entry->text = _("Hide");
3499 } else {
3500 entry->text = _("Hide");
3502 wMenuSetEnabled(dock->menu, index, appIsRunning);
3504 /* settings */
3505 entry = dock->menu->entries[++index];
3506 entry->clientdata = aicon;
3507 wMenuSetEnabled(dock->menu, index, !aicon->editing
3508 && !wPreferences.flags.noupdates);
3510 /* kill */
3511 entry = dock->menu->entries[++index];
3512 entry->clientdata = aicon;
3513 wMenuSetEnabled(dock->menu, index, appIsRunning);
3515 if (!dock->menu->flags.realized)
3516 wMenuRealize(dock->menu);
3518 if (dock->type == WM_CLIP) {
3519 x_pos = event->xbutton.x_root+2;
3520 } else {
3521 x_pos = dock->on_right_side ?
3522 scr->scr_width - dock->menu->frame->core->width - 2 : 0;
3525 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3527 /* allow drag select */
3528 event->xany.send_event = True;
3529 desc = &dock->menu->menu->descriptor;
3530 (*desc->handle_mousedown)(desc, event);
3534 static void
3535 openClipWorkspaceMenu(WScreen *scr, int x, int y)
3537 if (!scr->clip_ws_menu) {
3538 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
3540 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
3541 wMenuMapAt(scr->clip_ws_menu, x, y, False);
3545 /******************************************************************/
3546 static void
3547 iconDblClick(WObjDescriptor *desc, XEvent *event)
3549 WAppIcon *btn = desc->parent;
3550 WDock *dock = btn->dock;
3551 WApplication *wapp = NULL;
3552 int unhideHere = 0;
3554 #ifdef REDUCE_APPICONS
3555 if ((btn->icon->owner && !(event->xbutton.state & ControlMask)) ||
3556 ((btn->icon->owner == NULL) && (btn->applist != NULL))) {
3557 if (btn->icon->owner == NULL)
3558 btn->icon->owner = btn->applist->wapp->main_window_desc;
3559 #else
3560 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3561 #endif
3562 wapp = wApplicationOf(btn->icon->owner->main_window);
3564 assert(wapp!=NULL);
3566 unhideHere = (event->xbutton.state & ShiftMask);
3568 /* go to the last workspace that the user worked on the app */
3569 if (wapp->last_workspace != dock->screen_ptr->current_workspace
3570 && !unhideHere) {
3571 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3574 wUnhideApplication(wapp, event->xbutton.button==Button2,
3575 unhideHere);
3577 if (event->xbutton.state & MOD_MASK) {
3578 wHideOtherApplications(btn->icon->owner);
3580 } else {
3581 if (event->xbutton.button==Button1) {
3583 if (event->xbutton.state & MOD_MASK) {
3584 /* raise/lower dock */
3585 toggleLowered(dock);
3586 } else if (btn == dock->screen_ptr->clip_icon) {
3587 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3588 toggleCollapsed(dock);
3589 else
3590 handleClipChangeWorkspace(dock->screen_ptr, event);
3591 } else if (btn->command) {
3592 if (!btn->launching &&
3593 (!btn->running || (event->xbutton.state & ControlMask))) {
3594 launchDockedApplication(btn);
3596 } else if (btn->xindex == 0 && btn->yindex == 0
3597 && btn->dock->type == WM_DOCK) {
3599 wShowGNUstepPanel(dock->screen_ptr);
3606 static void
3607 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3609 WScreen *scr = dock->screen_ptr;
3610 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3611 int x, y;
3612 XEvent ev;
3613 int grabbed = 0, swapped = 0, done;
3614 Pixmap ghost = None;
3615 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3617 #ifdef DEBUG
3618 puts("moving dock");
3619 #endif
3620 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3621 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3622 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3623 wwarning("pointer grab failed for dock move");
3625 y = 0;
3626 for (x=0; x<dock->max_icons; x++) {
3627 if (dock->icon_array[x]!=NULL &&
3628 dock->icon_array[x]->yindex > y)
3629 y = dock->icon_array[x]->yindex;
3631 y++;
3632 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3634 done = 0;
3635 while (!done) {
3636 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3637 |ButtonMotionMask|ExposureMask, &ev);
3638 switch (ev.type) {
3639 case Expose:
3640 WMHandleEvent(&ev);
3641 break;
3643 case MotionNotify:
3644 if (!grabbed) {
3645 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3646 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3647 XChangeActivePointerGrab(dpy, ButtonMotionMask
3648 |ButtonReleaseMask|ButtonPressMask,
3649 wCursor[WCUR_MOVE], CurrentTime);
3650 grabbed=1;
3652 break;
3654 if (dock->type == WM_CLIP) {
3655 if (ev.xmotion.x_root - ofs_x < 0) {
3656 x = 0;
3657 } else if (ev.xmotion.x_root - ofs_x + ICON_SIZE >
3658 scr->scr_width) {
3659 x = scr->scr_width - ICON_SIZE;
3660 } else {
3661 x = ev.xmotion.x_root - ofs_x;
3663 if (ev.xmotion.y_root - ofs_y < 0) {
3664 y = 0;
3665 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3666 scr->scr_height) {
3667 y = scr->scr_height - ICON_SIZE;
3668 } else {
3669 y = ev.xmotion.y_root - ofs_y;
3671 moveDock(dock, x, y);
3672 } else {
3673 /* move vertically if pointer is inside the dock*/
3674 if ((dock->on_right_side &&
3675 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3676 || (!dock->on_right_side &&
3677 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3679 if (ev.xmotion.y_root - ofs_y < 0) {
3680 y = 0;
3681 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3682 scr->scr_height) {
3683 y = scr->scr_height - ICON_SIZE;
3684 } else {
3685 y = ev.xmotion.y_root - ofs_y;
3687 moveDock(dock, dock->x_pos, y);
3689 /* move horizontally to change sides */
3690 x = ev.xmotion.x_root - ofs_x;
3691 if (!dock->on_right_side) {
3693 /* is on left */
3695 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3696 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3697 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3698 if (superfluous && ghost==None) {
3699 ghost = MakeGhostDock(dock, dock->x_pos,
3700 scr->scr_width-ICON_SIZE
3701 -DOCK_EXTRA_SPACE-1,
3702 dock->y_pos);
3703 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3704 ghost);
3705 XClearWindow(dpy, scr->dock_shadow);
3707 XMapRaised(dpy, scr->dock_shadow);
3708 swapped = 1;
3709 } else {
3710 if (superfluous && ghost!=None) {
3711 XFreePixmap(dpy, ghost);
3712 ghost = None;
3714 XUnmapWindow(dpy, scr->dock_shadow);
3715 swapped = 0;
3717 } else {
3718 /* is on right */
3719 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3720 XMoveWindow(dpy, scr->dock_shadow,
3721 DOCK_EXTRA_SPACE, dock->y_pos);
3722 if (superfluous && ghost==None) {
3723 ghost = MakeGhostDock(dock, dock->x_pos,
3724 DOCK_EXTRA_SPACE, dock->y_pos);
3725 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3726 ghost);
3727 XClearWindow(dpy, scr->dock_shadow);
3729 XMapRaised(dpy, scr->dock_shadow);
3730 swapped = -1;
3731 } else {
3732 XUnmapWindow(dpy, scr->dock_shadow);
3733 swapped = 0;
3734 if (superfluous && ghost!=None) {
3735 XFreePixmap(dpy, ghost);
3736 ghost = None;
3741 break;
3743 case ButtonPress:
3744 break;
3746 case ButtonRelease:
3747 if (ev.xbutton.button != event->xbutton.button)
3748 break;
3749 XUngrabPointer(dpy, CurrentTime);
3750 XUnmapWindow(dpy, scr->dock_shadow);
3751 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3752 if (dock->type == WM_DOCK) {
3753 if (swapped!=0) {
3754 if (swapped>0)
3755 dock->on_right_side = 1;
3756 else
3757 dock->on_right_side = 0;
3758 swapDock(dock);
3759 wArrangeIcons(scr, False);
3762 done = 1;
3763 break;
3766 if (superfluous) {
3767 if (ghost!=None)
3768 XFreePixmap(dpy, ghost);
3769 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3771 #ifdef DEBUG
3772 puts("End dock move");
3773 #endif
3778 static void
3779 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3781 WScreen *scr = dock->screen_ptr;
3782 Window wins[2];
3783 WIcon *icon = aicon->icon;
3784 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3785 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3786 XEvent ev;
3787 int x = aicon->x_pos, y = aicon->y_pos;
3788 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3789 int shad_x = x, shad_y = y;
3790 int ix = aicon->xindex, iy = aicon->yindex;
3791 int tmp;
3792 Pixmap ghost = None;
3793 Bool docked;
3794 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3795 int omnipresent = aicon->omnipresent; /* this must be cached!!! */
3798 if (wPreferences.flags.noupdates)
3799 return;
3801 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3802 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3803 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3804 #ifdef DEBUG0
3805 wwarning("pointer grab failed for icon move");
3806 #endif
3809 if (!(event->xbutton.state & MOD_MASK))
3810 wRaiseFrame(icon->core);
3812 if (!wPreferences.flags.noclip)
3813 clip = scr->workspaces[scr->current_workspace]->clip;
3815 if (dock == scr->dock && !wPreferences.flags.noclip)
3816 dock2 = clip;
3817 else if (dock != scr->dock && !wPreferences.flags.nodock)
3818 dock2 = scr->dock;
3820 wins[0] = icon->core->window;
3821 wins[1] = scr->dock_shadow;
3822 XRestackWindows(dpy, wins, 2);
3823 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3824 ICON_SIZE, ICON_SIZE);
3825 if (superfluous) {
3826 if (icon->pixmap!=None)
3827 ghost = MakeGhostIcon(scr, icon->pixmap);
3828 else
3829 ghost = MakeGhostIcon(scr, icon->core->window);
3831 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3832 XClearWindow(dpy, scr->dock_shadow);
3834 XMapWindow(dpy, scr->dock_shadow);
3836 ondock = 1;
3839 while(1) {
3840 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3841 |ButtonMotionMask|ExposureMask, &ev);
3842 switch (ev.type) {
3843 case Expose:
3844 WMHandleEvent(&ev);
3845 break;
3847 case MotionNotify:
3848 if (!grabbed) {
3849 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3850 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3851 XChangeActivePointerGrab(dpy, ButtonMotionMask
3852 |ButtonReleaseMask|ButtonPressMask,
3853 wCursor[WCUR_MOVE], CurrentTime);
3854 grabbed=1;
3855 } else {
3856 break;
3860 if (omnipresent) {
3861 int i;
3862 for (i=0; i<scr->workspace_count; i++) {
3863 if (i == scr->current_workspace)
3864 continue;
3865 wDockShowIcons(scr->workspaces[i]->clip);
3869 x = ev.xmotion.x_root - ofs_x;
3870 y = ev.xmotion.y_root - ofs_y;
3871 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3872 if (tmp && dock2) {
3873 change_dock = 0;
3874 if (last_dock != dock && collapsed) {
3875 last_dock->collapsed = 1;
3876 wDockHideIcons(last_dock);
3877 collapsed = 0;
3879 if (!collapsed && (collapsed = dock->collapsed)) {
3880 dock->collapsed = 0;
3881 wDockShowIcons(dock);
3883 if (dock->auto_raise_lower)
3884 wDockRaise(dock);
3885 last_dock = dock;
3886 } else if (dock2) {
3887 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3888 if (tmp) {
3889 change_dock = 1;
3890 if (last_dock != dock2 && collapsed) {
3891 last_dock->collapsed = 1;
3892 wDockHideIcons(last_dock);
3893 collapsed = 0;
3895 if (!collapsed && (collapsed = dock2->collapsed)) {
3896 dock2->collapsed = 0;
3897 wDockShowIcons(dock2);
3899 if (dock2->auto_raise_lower)
3900 wDockRaise(dock2);
3901 last_dock = dock2;
3904 if (aicon->launching
3905 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3906 || (!aicon->running && tmp)) {
3907 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3908 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3910 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3912 if (!ondock) {
3913 XMapWindow(dpy, scr->dock_shadow);
3915 ondock = 1;
3916 } else {
3917 if (ondock) {
3918 XUnmapWindow(dpy, scr->dock_shadow);
3920 ondock = 0;
3922 XMoveWindow(dpy, icon->core->window, x, y);
3923 break;
3925 case ButtonPress:
3926 break;
3928 case ButtonRelease:
3929 if (ev.xbutton.button != event->xbutton.button)
3930 break;
3931 XUngrabPointer(dpy, CurrentTime);
3932 if (ondock) {
3933 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3934 XUnmapWindow(dpy, scr->dock_shadow);
3935 if (!change_dock) {
3936 reattachIcon(dock, aicon, ix, iy);
3937 if (clip && dock!=clip && clip->auto_raise_lower)
3938 wDockLower(clip);
3939 } else {
3940 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3941 if (!docked) {
3942 /* Slide it back if dock rejected it */
3943 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3944 aicon->y_pos);
3945 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3947 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3948 collapsed = 0;
3951 } else {
3952 aicon->x_pos = x;
3953 aicon->y_pos = y;
3954 if (superfluous) {
3955 if (!aicon->running && !wPreferences.no_animations) {
3956 /* We need to deselect it, even if is deselected in
3957 * wDockDetach(), because else DoKaboom() will fail.
3959 if (aicon->icon->selected)
3960 wIconSelect(aicon->icon);
3961 DoKaboom(scr,aicon->icon->core->window, x, y);
3964 if (clip && clip->auto_raise_lower)
3965 wDockLower(clip);
3966 wDockDetach(dock, aicon);
3968 if (collapsed) {
3969 last_dock->collapsed = 1;
3970 wDockHideIcons(last_dock);
3971 collapsed = 0;
3973 if (superfluous) {
3974 if (ghost!=None)
3975 XFreePixmap(dpy, ghost);
3976 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3978 if (omnipresent) {
3979 int i;
3980 for (i=0; i<scr->workspace_count; i++) {
3981 if (i == scr->current_workspace)
3982 continue;
3983 wDockHideIcons(scr->workspaces[i]->clip);
3987 #ifdef DEBUG
3988 puts("End icon move");
3989 #endif
3990 return;
3996 static int
3997 getClipButton(int px, int py)
3999 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
4001 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
4002 return CLIP_IDLE;
4004 if (py <= pt-((int)ICON_SIZE-1-px))
4005 return CLIP_FORWARD;
4006 else if (px <= pt-((int)ICON_SIZE-1-py))
4007 return CLIP_REWIND;
4009 return CLIP_IDLE;
4013 static void
4014 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
4016 XEvent ev;
4017 int done, direction, new_ws;
4018 int new_dir;
4019 WDock *clip = scr->clip_icon->dock;
4021 direction = getClipButton(event->xbutton.x, event->xbutton.y);
4023 clip->lclip_button_pushed = direction==CLIP_REWIND;
4024 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4026 wClipIconPaint(scr->clip_icon);
4027 done = 0;
4028 while(!done) {
4029 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
4030 |ButtonPressMask, &ev);
4031 switch (ev.type) {
4032 case Expose:
4033 WMHandleEvent(&ev);
4034 break;
4036 case MotionNotify:
4037 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
4038 if (new_dir != direction) {
4039 direction = new_dir;
4040 clip->lclip_button_pushed = direction==CLIP_REWIND;
4041 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4042 wClipIconPaint(scr->clip_icon);
4044 break;
4046 case ButtonPress:
4047 break;
4049 case ButtonRelease:
4050 if (ev.xbutton.button == event->xbutton.button)
4051 done = 1;
4055 clip->lclip_button_pushed = 0;
4056 clip->rclip_button_pushed = 0;
4058 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
4060 if (direction == CLIP_FORWARD) {
4061 if (scr->current_workspace < scr->workspace_count-1)
4062 wWorkspaceChange(scr, scr->current_workspace+1);
4063 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
4064 wWorkspaceChange(scr, scr->current_workspace+1);
4065 else if (wPreferences.ws_cycle)
4066 wWorkspaceChange(scr, 0);
4068 else if (direction == CLIP_REWIND) {
4069 if (scr->current_workspace > 0)
4070 wWorkspaceChange(scr, scr->current_workspace-1);
4071 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
4072 wWorkspaceChange(scr, scr->workspace_count-1);
4075 wClipIconPaint(scr->clip_icon);
4079 static void
4080 iconMouseDown(WObjDescriptor *desc, XEvent *event)
4082 WAppIcon *aicon = desc->parent;
4083 WDock *dock = aicon->dock;
4084 WScreen *scr = aicon->icon->core->screen_ptr;
4086 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
4087 return;
4089 scr->last_dock = dock;
4091 if (dock->menu->flags.mapped)
4092 wMenuUnmap(dock->menu);
4094 if (IsDoubleClick(scr, event)) {
4095 /* double-click was not in the main clip icon */
4096 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
4097 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
4098 iconDblClick(desc, event);
4099 return;
4103 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
4104 XUnmapWindow(dpy, scr->clip_balloon);
4105 scr->flags.clip_balloon_mapped = 0;
4108 #ifdef DEBUG
4109 puts("handling dock");
4110 #endif
4111 if (event->xbutton.button == Button1) {
4112 if (event->xbutton.state & MOD_MASK)
4113 wDockLower(dock);
4114 else
4115 wDockRaise(dock);
4117 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
4118 dock->type!=WM_DOCK) {
4119 wIconSelect(aicon->icon);
4120 return;
4123 if (aicon->yindex==0 && aicon->xindex==0) {
4124 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
4125 && dock->type==WM_CLIP)
4126 handleClipChangeWorkspace(scr, event);
4127 else
4128 handleDockMove(dock, aicon, event);
4129 } else
4130 handleIconMove(dock, aicon, event);
4132 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4133 aicon==scr->clip_icon) {
4134 openClipWorkspaceMenu(scr, event->xbutton.x_root+2,
4135 event->xbutton.y_root+2);
4136 if (scr->clip_ws_menu) {
4137 WMenu *menu;
4138 menu = scr->clip_ws_menu;
4139 desc = &menu->menu->descriptor;
4141 event->xany.send_event = True;
4142 (*desc->handle_mousedown)(desc, event);
4144 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4145 (event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon) {
4146 wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
4147 } else if (event->xbutton.button == Button3) {
4148 openDockMenu(dock, aicon, event);
4153 static void
4154 showClipBalloon(WDock *dock, int workspace)
4156 int w, h;
4157 int x, y;
4158 WScreen *scr = dock->screen_ptr;
4159 char *text;
4160 Window stack[2];
4162 scr->flags.clip_balloon_mapped = 1;
4163 XMapWindow(dpy, scr->clip_balloon);
4165 text = scr->workspaces[workspace]->name;
4167 w = WMWidthOfString(scr->clip_title_font, text, strlen(text));
4169 h = WMFontHeight(scr->clip_title_font);
4170 XResizeWindow(dpy, scr->clip_balloon, w, h);
4172 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
4173 y = dock->y_pos + ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
4175 if (x+w > scr->scr_width) {
4176 x = scr->scr_width - w;
4177 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
4178 y = dock->y_pos - h - 1;
4179 else
4180 y = dock->y_pos + ICON_SIZE;
4181 XRaiseWindow(dpy, scr->clip_balloon);
4182 } else {
4183 stack[0] = scr->clip_icon->icon->core->window;
4184 stack[1] = scr->clip_balloon;
4185 XRestackWindows(dpy, stack, 2);
4187 XMoveWindow(dpy, scr->clip_balloon, x, y);
4188 XSetForeground(dpy, scr->clip_title_gc,
4189 scr->clip_title_pixel[CLIP_NORMAL]);
4190 XClearWindow(dpy, scr->clip_balloon);
4191 WMDrawString(scr->wmscreen, scr->clip_balloon, scr->clip_title_gc,
4192 scr->clip_title_font, 0, 0, text, strlen(text));
4196 static void
4197 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
4199 WAppIcon *btn = (WAppIcon*)desc->parent;
4200 WDock *dock;
4201 WScreen *scr;
4203 assert(event->type==EnterNotify);
4205 if(desc->parent_type!=WCLASS_DOCK_ICON)
4206 return;
4208 scr = btn->icon->core->screen_ptr;
4209 if (!btn->omnipresent)
4210 dock = btn->dock;
4211 else
4212 dock = scr->workspaces[scr->current_workspace]->clip;
4214 if (!dock || dock->type!=WM_CLIP)
4215 return;
4217 /* The auto raise/lower code */
4218 if (dock->auto_lower_magic) {
4219 WMDeleteTimerHandler(dock->auto_lower_magic);
4220 dock->auto_lower_magic = NULL;
4222 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
4223 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
4224 clipAutoRaise,
4225 (void *)dock);
4228 /* The auto expand/collapse code */
4229 if (dock->auto_collapse_magic) {
4230 WMDeleteTimerHandler(dock->auto_collapse_magic);
4231 dock->auto_collapse_magic = NULL;
4233 if (dock->auto_collapse && !dock->auto_expand_magic) {
4234 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
4235 clipAutoExpand,
4236 (void *)dock);
4239 if (btn->xindex == 0 && btn->yindex == 0)
4240 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4241 else {
4242 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4243 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
4244 dock->screen_ptr->flags.clip_balloon_mapped = 0;
4250 static void
4251 clipLeave(WDock *dock)
4253 XEvent event;
4254 WObjDescriptor *desc = NULL;
4256 if (!dock || dock->type!=WM_CLIP)
4257 return;
4259 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
4260 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
4261 (XPointer *)&desc)!=XCNOENT
4262 && desc && desc->parent_type==WCLASS_DOCK_ICON
4263 && ((WAppIcon*)desc->parent)->dock
4264 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
4265 /* We didn't left the Clip yet */
4266 XPutBackEvent(dpy, &event);
4267 return;
4270 XPutBackEvent(dpy, &event);
4271 } else {
4272 /* We entered a withdrawn window, so we're still in Clip */
4273 return;
4276 if (dock->auto_raise_magic) {
4277 WMDeleteTimerHandler(dock->auto_raise_magic);
4278 dock->auto_raise_magic = NULL;
4280 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
4281 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
4282 clipAutoLower,
4283 (void *)dock);
4286 if (dock->auto_expand_magic) {
4287 WMDeleteTimerHandler(dock->auto_expand_magic);
4288 dock->auto_expand_magic = NULL;
4290 if (dock->auto_collapse && !dock->auto_collapse_magic) {
4291 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
4292 clipAutoCollapse,
4293 (void *)dock);
4298 static void
4299 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
4301 WAppIcon *btn = (WAppIcon*)desc->parent;
4303 assert(event->type==LeaveNotify);
4305 if(desc->parent_type!=WCLASS_DOCK_ICON)
4306 return;
4308 clipLeave(btn->dock);
4312 static void
4313 clipAutoCollapse(void *cdata)
4315 WDock *dock = (WDock *)cdata;
4317 if (dock->type!=WM_CLIP)
4318 return;
4320 if (dock->auto_collapse) {
4321 dock->collapsed = 1;
4322 wDockHideIcons(dock);
4324 dock->auto_collapse_magic = NULL;
4328 static void
4329 clipAutoExpand(void *cdata)
4331 WDock *dock = (WDock *)cdata;
4333 if (dock->type!=WM_CLIP)
4334 return;
4336 if (dock->auto_collapse) {
4337 dock->collapsed = 0;
4338 wDockShowIcons(dock);
4340 dock->auto_expand_magic = NULL;
4344 static void
4345 clipAutoLower(void *cdata)
4347 WDock *dock = (WDock *)cdata;
4349 if (dock->type!=WM_CLIP)
4350 return;
4352 if (dock->auto_raise_lower)
4353 wDockLower(dock);
4355 dock->auto_lower_magic = NULL;
4359 static void
4360 clipAutoRaise(void *cdata)
4362 WDock *dock = (WDock *)cdata;
4364 if (dock->type!=WM_CLIP)
4365 return;
4367 if (dock->auto_raise_lower)
4368 wDockRaise(dock);
4370 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4371 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4374 dock->auto_raise_magic = NULL;
4378 static Bool
4379 iconCanBeOmnipresent(WAppIcon *aicon)
4381 WScreen *scr = aicon->icon->core->screen_ptr;
4382 WDock *clip;
4383 WAppIcon *btn;
4384 int i, j;
4386 for (i=0; i<scr->workspace_count; i++) {
4387 clip = scr->workspaces[i]->clip;
4389 if (clip == aicon->dock)
4390 continue;
4392 if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
4393 return False; /* Clip is full in some workspace */
4395 for (j=0; j<clip->max_icons; j++) {
4396 btn = clip->icon_array[j];
4397 if(btn && btn->xindex==aicon->xindex && btn->yindex==aicon->yindex)
4398 return False;
4402 return True;
4407 wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
4409 WScreen *scr = aicon->icon->core->screen_ptr;
4410 WAppIconChain *new_entry, *tmp, *tmp1;
4411 int status = WO_SUCCESS;
4413 if ((scr->dock && aicon->dock==scr->dock) || aicon==scr->clip_icon) {
4414 return WO_NOT_APPLICABLE;
4417 if (aicon->omnipresent == omnipresent)
4418 return WO_SUCCESS;
4420 if (omnipresent) {
4421 if (iconCanBeOmnipresent(aicon)) {
4422 aicon->omnipresent = 1;
4423 new_entry = wmalloc(sizeof(WAppIconChain));
4424 new_entry->aicon = aicon;
4425 new_entry->next = scr->global_icons;
4426 scr->global_icons = new_entry;
4427 scr->global_icon_count++;
4428 } else {
4429 aicon->omnipresent = 0;
4430 status = WO_FAILED;
4432 } else {
4433 aicon->omnipresent = 0;
4434 if (aicon == scr->global_icons->aicon) {
4435 tmp = scr->global_icons->next;
4436 free(scr->global_icons);
4437 scr->global_icons = tmp;
4438 scr->global_icon_count--;
4439 } else {
4440 tmp = scr->global_icons;
4441 while (tmp->next) {
4442 if (tmp->next->aicon == aicon) {
4443 tmp1 = tmp->next->next;
4444 free(tmp->next);
4445 tmp->next = tmp1;
4446 scr->global_icon_count--;
4447 break;
4449 tmp = tmp->next;
4454 wAppIconPaint(aicon);
4456 return status;