Check for argc==0 when calling wtokensplit()
[wmaker-crm.git] / src / dock.c
blobdfda3022e7ef2d142d1cd482d6df80228aea2ee8
1 /* dock.c- built-in Dock module for WindowMaker
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
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"
56 #include "wsound.h"
57 #include "xinerama.h"
62 /**** Local variables ****/
63 #define CLIP_REWIND 1
64 #define CLIP_IDLE 0
65 #define CLIP_FORWARD 2
68 /**** Global variables ****/
70 /* in dockedapp.c */
71 extern void DestroyDockAppSettingsPanel();
73 extern void ShowDockAppSettingsPanel(WAppIcon *aicon);
76 extern XContext wWinContext;
78 extern Cursor wCursor[WCUR_LAST];
80 extern WPreferences wPreferences;
82 extern XContext wWinContext;
85 #define MOD_MASK wPreferences.modifier_mask
87 extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);
89 #define ICON_SIZE wPreferences.icon_size
92 /***** Local variables ****/
94 static WMPropList *dCommand=NULL;
95 static WMPropList *dPasteCommand=NULL;
96 #ifdef XDND /* XXX was OFFIX */
97 static WMPropList *dDropCommand=NULL;
98 #endif
99 static WMPropList *dAutoLaunch, *dLock;
100 static WMPropList *dName, *dForced, *dBuggyApplication, *dYes, *dNo;
101 static WMPropList *dHost, *dDock, *dClip;
102 static WMPropList *dAutoAttractIcons;
104 static WMPropList *dPosition, *dApplications, *dLowered, *dCollapsed;
106 static WMPropList *dAutoCollapse, *dAutoRaiseLower, *dOmnipresent;
108 static void dockIconPaint(WAppIcon *btn);
110 static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
112 static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state);
114 static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);
116 static int getClipButton(int px, int py);
118 static void toggleLowered(WDock *dock);
120 static void toggleCollapsed(WDock *dock);
122 static void clipIconExpose(WObjDescriptor *desc, XEvent *event);
124 static void clipLeave(WDock *dock);
126 static void handleClipChangeWorkspace(WScreen *scr, XEvent *event);
128 Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y);
130 static void clipEnterNotify(WObjDescriptor *desc, XEvent *event);
131 static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event);
132 static void clipAutoCollapse(void *cdata);
133 static void clipAutoExpand(void *cdata);
134 static void launchDockedApplication(WAppIcon *btn, Bool withSelection);
136 static void clipAutoLower(void *cdata);
137 static void clipAutoRaise(void *cdata);
139 static void showClipBalloon(WDock *dock, int workspace);
143 static void
144 make_keys()
146 if (dCommand!=NULL)
147 return;
149 dCommand = WMRetainPropList(WMCreatePLString("Command"));
150 dPasteCommand = WMRetainPropList(WMCreatePLString("PasteCommand"));
151 #ifdef XDND
152 dDropCommand = WMRetainPropList(WMCreatePLString("DropCommand"));
153 #endif
154 dLock = WMRetainPropList(WMCreatePLString("Lock"));
155 dAutoLaunch = WMRetainPropList(WMCreatePLString("AutoLaunch"));
156 dName = WMRetainPropList(WMCreatePLString("Name"));
157 dForced = WMRetainPropList(WMCreatePLString("Forced"));
158 dBuggyApplication = WMRetainPropList(WMCreatePLString("BuggyApplication"));
159 dYes = WMRetainPropList(WMCreatePLString("Yes"));
160 dNo = WMRetainPropList(WMCreatePLString("No"));
161 dHost = WMRetainPropList(WMCreatePLString("Host"));
163 dPosition = WMCreatePLString("Position");
164 dApplications = WMCreatePLString("Applications");
165 dLowered = WMCreatePLString("Lowered");
166 dCollapsed = WMCreatePLString("Collapsed");
167 dAutoCollapse = WMCreatePLString("AutoCollapse");
168 dAutoRaiseLower = WMCreatePLString("AutoRaiseLower");
169 dAutoAttractIcons = WMCreatePLString("AutoAttractIcons");
171 dOmnipresent = WMCreatePLString("Omnipresent");
173 dDock = WMCreatePLString("Dock");
174 dClip = WMCreatePLString("Clip");
179 static void
180 renameCallback(WMenu *menu, WMenuEntry *entry)
182 WDock *dock = entry->clientdata;
183 char buffer[128];
184 int wspace;
185 char *name;
187 assert(entry->clientdata!=NULL);
189 wspace = dock->screen_ptr->current_workspace;
191 name = wstrdup(dock->screen_ptr->workspaces[wspace]->name);
193 snprintf(buffer, sizeof(buffer), _("Type the name for workspace %i:"), wspace+1);
194 if (wInputDialog(dock->screen_ptr, _("Rename Workspace"), buffer,
195 &name)) {
196 wWorkspaceRename(dock->screen_ptr, wspace, name);
198 if (name) {
199 wfree(name);
204 static void
205 toggleLoweredCallback(WMenu *menu, WMenuEntry *entry)
207 assert(entry->clientdata!=NULL);
209 toggleLowered(entry->clientdata);
211 entry->flags.indicator_on = !(((WDock*)entry->clientdata)->lowered);
213 wMenuPaint(menu);
217 static int
218 matchWindow(void *item, void *cdata)
220 return (((WFakeGroupLeader*)item)->leader == (Window)cdata);
224 static void
225 killCallback(WMenu *menu, WMenuEntry *entry)
227 WScreen *scr = menu->menu->screen_ptr;
228 WAppIcon *icon;
229 WFakeGroupLeader *fPtr;
230 char *buffer;
232 if (!WCHECK_STATE(WSTATE_NORMAL))
233 return;
235 assert(entry->clientdata!=NULL);
237 icon = (WAppIcon*)entry->clientdata;
239 icon->editing = 1;
241 WCHANGE_STATE(WSTATE_MODAL);
243 buffer = wstrconcat(icon->wm_class,
244 _(" will be forcibly closed.\n"
245 "Any unsaved changes will be lost.\n"
246 "Please confirm."));
248 if (icon->icon && icon->icon->owner) {
249 fPtr = icon->icon->owner->fake_group;
250 } else {
251 /* is this really necessary? can we kill a non-running dock icon? */
252 Window win = icon->main_window;
253 int index;
255 index = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void*)win);
256 if (index != WANotFound)
257 fPtr = WMGetFromArray(scr->fakeGroupLeaders, index);
258 else
259 fPtr = NULL;
262 if (wPreferences.dont_confirm_kill
263 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
264 buffer, _("Yes"), _("No"), NULL)==WAPRDefault) {
265 if (fPtr!=NULL) {
266 WWindow *wwin, *twin;
268 wwin = scr->focused_window;
269 while (wwin) {
270 twin = wwin->prev;
271 if (wwin->fake_group == fPtr) {
272 wClientKill(wwin);
274 wwin = twin;
276 } else if (icon->icon && icon->icon->owner) {
277 wClientKill(icon->icon->owner);
281 wfree(buffer);
283 icon->editing = 0;
285 WCHANGE_STATE(WSTATE_NORMAL);
289 /* TODO: replace this function with a member of the dock struct */
290 static int
291 numberOfSelectedIcons(WDock *dock)
293 WAppIcon *aicon;
294 int i, n;
296 n = 0;
297 for (i=1; i<dock->max_icons; i++) {
298 aicon = dock->icon_array[i];
299 if (aicon && aicon->icon->selected) {
300 n++;
304 return n;
308 static WMArray*
309 getSelected(WDock *dock)
311 WMArray *ret = WMCreateArray(8);
312 WAppIcon *btn;
313 int i;
315 for (i=1; i<dock->max_icons; i++) {
316 btn = dock->icon_array[i];
317 if (btn && btn->icon->selected) {
318 WMAddToArray(ret, btn);
322 return ret;
326 static void
327 paintClipButtons(WAppIcon *clipIcon, Bool lpushed, Bool rpushed)
329 Window win = clipIcon->icon->core->window;
330 WScreen *scr = clipIcon->icon->core->screen_ptr;
331 XPoint p[4];
332 int pt = CLIP_BUTTON_SIZE*ICON_SIZE/64;
333 int tp = ICON_SIZE - pt;
334 int as = pt - 15; /* 15 = 5+5+5 */
335 GC gc = scr->draw_gc; /* maybe use WMColorGC() instead here? */
336 WMColor *color;
337 #ifdef GRADIENT_CLIP_ARROW
338 Bool collapsed = clipIcon->dock->collapsed;
339 #endif
341 /*if (!clipIcon->dock->collapsed)
342 color = scr->clip_title_color[CLIP_NORMAL];
343 else
344 color = scr->clip_title_color[CLIP_COLLAPSED];*/
345 color = scr->clip_title_color[CLIP_NORMAL];
347 XSetForeground(dpy, gc, WMColorPixel(color));
349 if (rpushed) {
350 p[0].x = tp+1;
351 p[0].y = 1;
352 p[1].x = ICON_SIZE-2;
353 p[1].y = 1;
354 p[2].x = ICON_SIZE-2;
355 p[2].y = pt-1;
356 } else if (lpushed) {
357 p[0].x = 1;
358 p[0].y = tp;
359 p[1].x = pt;
360 p[1].y = ICON_SIZE-2;
361 p[2].x = 1;
362 p[2].y = ICON_SIZE-2;
364 if (lpushed || rpushed) {
365 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
366 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
367 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
369 #ifdef GRADIENT_CLIP_ARROW
370 if (!collapsed) {
371 XSetFillStyle(dpy, scr->copy_gc, FillTiled);
372 XSetTile(dpy, scr->copy_gc, scr->clip_arrow_gradient);
373 XSetClipMask(dpy, scr->copy_gc, None);
374 gc = scr->copy_gc;
376 #endif /* GRADIENT_CLIP_ARROW */
378 /* top right arrow */
379 p[0].x = p[3].x = ICON_SIZE-5-as;
380 p[0].y = p[3].y = 5;
381 p[1].x = ICON_SIZE-6;
382 p[1].y = 5;
383 p[2].x = ICON_SIZE-6;
384 p[2].y = 4+as;
385 if (rpushed) {
386 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
387 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
388 } else {
389 #ifdef GRADIENT_CLIP_ARROW
390 if (!collapsed)
391 XSetTSOrigin(dpy, gc, ICON_SIZE-6-as, 5);
392 #endif
393 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
394 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
397 /* bottom left arrow */
398 p[0].x = p[3].x = 5;
399 p[0].y = p[3].y = ICON_SIZE-5-as;
400 p[1].x = 5;
401 p[1].y = ICON_SIZE-6;
402 p[2].x = 4+as;
403 p[2].y = ICON_SIZE-6;
404 if (lpushed) {
405 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
406 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
407 } else {
408 #ifdef GRADIENT_CLIP_ARROW
409 if (!collapsed)
410 XSetTSOrigin(dpy, gc, 5, ICON_SIZE-6-as);
411 #endif
412 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
413 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
415 #ifdef GRADIENT_CLIP_ARROW
416 if (!collapsed)
417 XSetFillStyle(dpy, scr->copy_gc, FillSolid);
418 #endif
422 RImage*
423 wClipMakeTile(WScreen *scr, RImage *normalTile)
425 RImage *tile = RCloneImage(normalTile);
426 RColor black;
427 RColor dark;
428 RColor light;
429 int pt, tp;
430 int as;
432 pt = CLIP_BUTTON_SIZE*wPreferences.icon_size/64;
433 tp = wPreferences.icon_size-1 - pt;
434 as = pt - 15;
436 black.alpha = 255;
437 black.red = black.green = black.blue = 0;
439 dark.alpha = 0;
440 dark.red = dark.green = dark.blue = 60;
442 light.alpha = 0;
443 light.red = light.green = light.blue = 80;
446 /* top right */
447 ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size-2,
448 pt-1, &dark);
449 RDrawLine(tile, tp-1, 0, wPreferences.icon_size-1, pt+1, &black);
450 ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size-3,
451 pt, &light);
453 /* arrow bevel */
454 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 7 - as, 4,
455 ICON_SIZE - 5, 4, &dark);
456 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 6 - as, 5,
457 ICON_SIZE - 5, 6 + as, &dark);
458 ROperateLine(tile, RAddOperation, ICON_SIZE - 5, 4, ICON_SIZE - 5, 6 + as,
459 &light);
461 /* bottom left */
462 ROperateLine(tile, RAddOperation, 2, tp+2, pt-2,
463 wPreferences.icon_size-3, &dark);
464 RDrawLine(tile, 0, tp-1, pt+1, wPreferences.icon_size-1, &black);
465 ROperateLine(tile, RSubtractOperation, 0, tp-2, pt+1,
466 wPreferences.icon_size-2, &light);
468 /* arrow bevel */
469 ROperateLine(tile, RSubtractOperation, 4, ICON_SIZE - 7 - as, 4,
470 ICON_SIZE - 5, &dark);
471 ROperateLine(tile, RSubtractOperation, 5, ICON_SIZE - 6 - as,
472 6 + as, ICON_SIZE - 5, &dark);
473 ROperateLine(tile, RAddOperation, 4, ICON_SIZE - 5, 6 + as, ICON_SIZE - 5,
474 &light);
476 return tile;
480 static void
481 omnipresentCallback(WMenu *menu, WMenuEntry *entry)
483 WAppIcon *clickedIcon = entry->clientdata;
484 WAppIcon *aicon;
485 WDock *dock;
486 WMArray *selectedIcons;
487 WMArrayIterator iter;
488 int failed;
490 assert(entry->clientdata!=NULL);
492 dock = clickedIcon->dock;
494 selectedIcons = getSelected(dock);
496 if (!WMGetArrayItemCount(selectedIcons))
497 WMAddToArray(selectedIcons, clickedIcon);
499 failed = 0;
500 WM_ITERATE_ARRAY(selectedIcons, aicon, iter) {
501 if (wClipMakeIconOmnipresent(aicon, !aicon->omnipresent) == WO_FAILED)
502 failed++;
503 else if (aicon->icon->selected)
504 wIconSelect(aicon->icon);
506 WMFreeArray(selectedIcons);
508 if (failed > 1) {
509 wMessageDialog(dock->screen_ptr, _("Warning"),
510 _("Some icons cannot be made omnipresent. "
511 "Please make sure that no other icon is "
512 "docked in the same positions on the other "
513 "workspaces and the Clip is not full in "
514 "some workspace."),
515 _("OK"), NULL, NULL);
516 } else if (failed == 1) {
517 wMessageDialog(dock->screen_ptr, _("Warning"),
518 _("Icon cannot be made omnipresent. "
519 "Please make sure that no other icon is "
520 "docked in the same position on the other "
521 "workspaces and the Clip is not full in "
522 "some workspace."),
523 _("OK"), NULL, NULL);
528 static void
529 removeIconsCallback(WMenu *menu, WMenuEntry *entry)
531 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
532 WDock *dock;
533 WAppIcon *aicon;
534 WMArray *selectedIcons;
535 int keepit;
536 WMArrayIterator it;
538 assert(clickedIcon!=NULL);
540 dock = clickedIcon->dock;
542 selectedIcons = getSelected(dock);
544 if (WMGetArrayItemCount(selectedIcons)) {
545 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
546 _("All selected icons will be removed!"),
547 _("OK"), _("Cancel"), NULL)!=WAPRDefault) {
548 WMFreeArray(selectedIcons);
549 return;
551 } else {
552 if (clickedIcon->xindex==0 && clickedIcon->yindex==0) {
553 WMFreeArray(selectedIcons);
554 return;
556 WMAddToArray(selectedIcons, clickedIcon);
559 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
560 keepit = aicon->running && wApplicationOf(aicon->main_window);
561 wDockDetach(dock, aicon);
562 if (keepit) {
563 /* XXX: can: aicon->icon == NULL ? */
564 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, wGetHeadForWindow(aicon->icon->owner));
565 XMoveWindow(dpy, aicon->icon->core->window,
566 aicon->x_pos, aicon->y_pos);
567 if (!dock->mapped || dock->collapsed)
568 XMapWindow(dpy, aicon->icon->core->window);
571 WMFreeArray(selectedIcons);
573 if (wPreferences.auto_arrange_icons)
574 wArrangeIcons(dock->screen_ptr, True);
578 static void
579 keepIconsCallback(WMenu *menu, WMenuEntry *entry)
581 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
582 WDock *dock;
583 WAppIcon *aicon;
584 WMArray *selectedIcons;
585 WMArrayIterator it;
587 assert(clickedIcon!=NULL);
588 dock = clickedIcon->dock;
590 selectedIcons = getSelected(dock);
592 if (!WMGetArrayItemCount(selectedIcons)
593 && clickedIcon!=dock->screen_ptr->clip_icon) {
594 char *command = NULL;
596 if (!clickedIcon->command && !clickedIcon->editing) {
597 clickedIcon->editing = 1;
598 if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
599 _("Type the command used to launch the application"),
600 &command)) {
601 if (command && (command[0]==0 ||
602 (command[0]=='-' && command[1]==0))) {
603 wfree(command);
604 command = NULL;
606 clickedIcon->command = command;
607 clickedIcon->editing = 0;
608 } else {
609 clickedIcon->editing = 0;
610 if (command)
611 wfree(command);
612 WMFreeArray(selectedIcons);
613 return;
617 WMAddToArray(selectedIcons, clickedIcon);
620 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
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 WMFreeArray(selectedIcons);
638 static void
639 toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
641 WDock *dock = (WDock*)entry->clientdata;
643 assert(entry->clientdata!=NULL);
645 dock->attract_icons = !dock->attract_icons;
646 /*if (!dock->attract_icons)
647 dock->keep_attracted = 0;*/
649 entry->flags.indicator_on = dock->attract_icons;
651 wMenuPaint(menu);
655 static void
656 selectCallback(WMenu *menu, WMenuEntry *entry)
658 WAppIcon *icon = (WAppIcon*)entry->clientdata;
660 assert(icon!=NULL);
662 wIconSelect(icon->icon);
664 wMenuPaint(menu);
668 static void
669 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
671 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
672 WDock *clip;
673 WAppIcon *aicon;
674 int x, y, x_pos, y_pos;
676 assert(entry->clientdata!=NULL);
677 clip = clickedIcon->dock;
679 aicon = clip->screen_ptr->app_icon_list;
681 while (aicon) {
682 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
683 x_pos = clip->x_pos + x*ICON_SIZE;
684 y_pos = clip->y_pos + y*ICON_SIZE;
685 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
686 #ifdef ANIMATIONS
687 if (wPreferences.no_animations) {
688 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
689 } else {
690 SlideWindow(aicon->icon->core->window,
691 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
693 #else
694 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
695 #endif /* ANIMATIONS */
697 aicon->attracted = 1;
698 if (!aicon->icon->shadowed) {
699 aicon->icon->shadowed = 1;
700 aicon->icon->force_paint = 1;
701 /* We don't do an wAppIconPaint() here because it's in
702 * wDockAttachIcon(). -Dan
705 wDockAttachIcon(clip, aicon, x, y);
706 if (clip->collapsed || !clip->mapped)
707 XUnmapWindow(dpy, aicon->icon->core->window);
709 aicon = aicon->next;
714 static void
715 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
717 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
718 WDock *dock;
719 WMArray *selectedIcons;
720 WMArrayIterator iter;
721 WAppIcon *btn;
722 int i;
724 assert(clickedIcon!=NULL);
725 dock = clickedIcon->dock;
727 selectedIcons = getSelected(dock);
729 if (!WMGetArrayItemCount(selectedIcons)) {
730 for (i=1; i<dock->max_icons; i++) {
731 btn = dock->icon_array[i];
732 if (btn && !btn->icon->selected) {
733 wIconSelect(btn->icon);
736 } else {
737 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
738 wIconSelect(btn->icon);
741 WMFreeArray(selectedIcons);
743 wMenuPaint(menu);
747 static void
748 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
750 assert(entry->clientdata!=NULL);
752 toggleCollapsed(entry->clientdata);
754 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
756 wMenuPaint(menu);
760 static void
761 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
763 WDock *dock;
764 assert(entry->clientdata!=NULL);
766 dock = (WDock*) entry->clientdata;
768 dock->auto_collapse = !dock->auto_collapse;
770 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
772 wMenuPaint(menu);
776 static void
777 toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
779 WDock *dock;
780 assert(entry->clientdata!=NULL);
782 dock = (WDock*) entry->clientdata;
784 dock->auto_raise_lower = !dock->auto_raise_lower;
786 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_raise_lower;
788 wMenuPaint(menu);
792 static void
793 launchCallback(WMenu *menu, WMenuEntry *entry)
795 WAppIcon *btn = (WAppIcon*)entry->clientdata;
797 launchDockedApplication(btn, False);
801 static void
802 settingsCallback(WMenu *menu, WMenuEntry *entry)
804 WAppIcon *btn = (WAppIcon*)entry->clientdata;
806 if (btn->editing)
807 return;
808 ShowDockAppSettingsPanel(btn);
812 static void
813 hideCallback(WMenu *menu, WMenuEntry *entry)
815 WApplication *wapp;
816 WAppIcon *btn = (WAppIcon*)entry->clientdata;
818 wapp = wApplicationOf(btn->icon->owner->main_window);
820 if (wapp->flags.hidden) {
821 wWorkspaceChange(btn->icon->core->screen_ptr, wapp->last_workspace);
822 wUnhideApplication(wapp, False, False);
823 } else {
824 wHideApplication(wapp);
829 static void
830 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
832 WApplication *wapp;
833 WAppIcon *btn = (WAppIcon*)entry->clientdata;
835 wapp = wApplicationOf(btn->icon->owner->main_window);
837 wUnhideApplication(wapp, False, True);
841 WAppIcon*
842 mainIconCreate(WScreen *scr, int type)
844 WAppIcon *btn;
845 int x_pos;
847 if (type == WM_CLIP) {
848 if (scr->clip_icon)
849 return scr->clip_icon;
850 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
851 btn->icon->core->descriptor.handle_expose = clipIconExpose;
852 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
853 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
854 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
855 x_pos = 0;
856 } else {
857 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
858 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
861 btn->xindex = 0;
862 btn->yindex = 0;
864 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
865 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
866 btn->icon->core->descriptor.parent = btn;
867 /*ChangeStackingLevel(btn->icon->core, WMDockLevel);*/
868 XMapWindow(dpy, btn->icon->core->window);
869 btn->x_pos = x_pos;
870 btn->y_pos = 0;
871 btn->docked = 1;
872 if (type == WM_CLIP)
873 scr->clip_icon = btn;
875 return btn;
879 static void
880 switchWSCommand(WMenu *menu, WMenuEntry *entry)
882 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
883 WScreen *scr = icon->icon->core->screen_ptr;
884 WDock *src, *dest;
885 WMArray *selectedIcons;
886 int x, y;
888 if (entry->order == scr->current_workspace)
889 return;
890 src = icon->dock;
891 dest = scr->workspaces[entry->order]->clip;
893 selectedIcons = getSelected(src);
895 if (WMGetArrayItemCount(selectedIcons)) {
896 WMArrayIterator iter;
898 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
899 if (wDockFindFreeSlot(dest, &x, &y)) {
900 moveIconBetweenDocks(src, dest, btn, x, y);
901 XUnmapWindow(dpy, btn->icon->core->window);
904 } else if (icon != scr->clip_icon) {
905 if (wDockFindFreeSlot(dest, &x, &y)) {
906 moveIconBetweenDocks(src, dest, icon, x, y);
907 XUnmapWindow(dpy, icon->icon->core->window);
910 WMFreeArray(selectedIcons);
915 static void
916 launchDockedApplication(WAppIcon *btn, Bool withSelection)
918 WScreen *scr = btn->icon->core->screen_ptr;
920 if (!btn->launching &&
921 ((!withSelection && btn->command!=NULL) ||
922 (withSelection && btn->paste_command!=NULL))) {
923 if (!btn->forced_dock) {
924 btn->relaunching = btn->running;
925 btn->running = 1;
927 if (btn->wm_instance || btn->wm_class) {
928 WWindowAttributes attr;
929 memset(&attr, 0, sizeof(WWindowAttributes));
930 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class,
931 &attr, NULL, True);
933 if (!attr.no_appicon && !btn->buggy_app)
934 btn->launching = 1;
935 else
936 btn->running = 0;
938 btn->drop_launch = 0;
939 btn->paste_launch = withSelection;
940 scr->last_dock = btn->dock;
941 btn->pid = execCommand(btn, (withSelection ? btn->paste_command :
942 btn->command), NULL);
943 if (btn->pid>0) {
944 if (btn->buggy_app) {
945 /* give feedback that the app was launched */
946 btn->launching = 1;
947 dockIconPaint(btn);
948 btn->launching = 0;
949 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
950 } else {
951 dockIconPaint(btn);
953 } else {
954 wwarning(_("could not launch application %s\n"), btn->command);
955 btn->launching = 0;
956 if (!btn->relaunching) {
957 btn->running = 0;
965 static void
966 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
968 WScreen *scr = menu->frame->screen_ptr;
969 char title[MAX_WORKSPACENAME_WIDTH+1];
970 int i;
972 if (!menu || !icon)
973 return;
975 for (i=0; i<scr->workspace_count; i++) {
976 if (i < menu->entry_no) {
977 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
978 wfree(menu->entries[i]->text);
979 strcpy(title, scr->workspaces[i]->name);
980 menu->entries[i]->text = wstrdup(title);
981 menu->flags.realized = 0;
983 menu->entries[i]->clientdata = (void*)icon;
984 } else {
985 strcpy(title, scr->workspaces[i]->name);
987 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
989 menu->flags.realized = 0;
991 if (i == scr->current_workspace) {
992 wMenuSetEnabled(menu, i, False);
993 } else {
994 wMenuSetEnabled(menu, i, True);
998 if (!menu->flags.realized)
999 wMenuRealize(menu);
1003 static WMenu*
1004 makeWorkspaceMenu(WScreen *scr)
1006 WMenu *menu;
1008 menu = wMenuCreate(scr, NULL, False);
1009 if (!menu)
1010 wwarning(_("could not create workspace submenu for Clip menu"));
1012 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
1014 menu->flags.realized = 0;
1015 wMenuRealize(menu);
1017 return menu;
1021 static void
1022 updateClipOptionsMenu(WMenu *menu, WDock *dock)
1024 WMenuEntry *entry;
1025 int index = 0;
1027 if (!menu || !dock)
1028 return;
1030 /* keep on top */
1031 entry = menu->entries[index];
1032 entry->flags.indicator_on = !dock->lowered;
1033 entry->clientdata = dock;
1035 /* collapsed */
1036 entry = menu->entries[++index];
1037 entry->flags.indicator_on = dock->collapsed;
1038 entry->clientdata = dock;
1040 /* auto-collapse */
1041 entry = menu->entries[++index];
1042 entry->flags.indicator_on = dock->auto_collapse;
1043 entry->clientdata = dock;
1045 /* auto-raise/lower */
1046 entry = menu->entries[++index];
1047 entry->flags.indicator_on = dock->auto_raise_lower;
1048 entry->clientdata = dock;
1049 wMenuSetEnabled(menu, index, dock->lowered);
1051 /* attract icons */
1052 entry = menu->entries[++index];
1053 entry->flags.indicator_on = dock->attract_icons;
1054 entry->clientdata = dock;
1056 menu->flags.realized = 0;
1057 wMenuRealize(menu);
1061 static WMenu*
1062 makeClipOptionsMenu(WScreen *scr)
1064 WMenu *menu;
1065 WMenuEntry *entry;
1067 menu = wMenuCreate(scr, NULL, False);
1068 if (!menu) {
1069 wwarning(_("could not create options submenu for Clip menu"));
1070 return NULL;
1073 entry = wMenuAddCallback(menu, _("Keep on Top"),
1074 toggleLoweredCallback, NULL);
1075 entry->flags.indicator = 1;
1076 entry->flags.indicator_on = 1;
1077 entry->flags.indicator_type = MI_CHECK;
1079 entry = wMenuAddCallback(menu, _("Collapsed"),
1080 toggleCollapsedCallback, NULL);
1081 entry->flags.indicator = 1;
1082 entry->flags.indicator_on = 1;
1083 entry->flags.indicator_type = MI_CHECK;
1085 entry = wMenuAddCallback(menu, _("Autocollapse"),
1086 toggleAutoCollapseCallback, NULL);
1087 entry->flags.indicator = 1;
1088 entry->flags.indicator_on = 1;
1089 entry->flags.indicator_type = MI_CHECK;
1091 entry = wMenuAddCallback(menu, _("Autoraise"),
1092 toggleAutoRaiseLowerCallback, NULL);
1093 entry->flags.indicator = 1;
1094 entry->flags.indicator_on = 1;
1095 entry->flags.indicator_type = MI_CHECK;
1097 entry = wMenuAddCallback(menu, _("Autoattract Icons"),
1098 toggleAutoAttractCallback, NULL);
1099 entry->flags.indicator = 1;
1100 entry->flags.indicator_on = 1;
1101 entry->flags.indicator_type = MI_CHECK;
1103 menu->flags.realized = 0;
1104 wMenuRealize(menu);
1106 return menu;
1110 static WMenu*
1111 dockMenuCreate(WScreen *scr, int type)
1113 WMenu *menu;
1114 WMenuEntry *entry;
1116 if (type == WM_CLIP && scr->clip_menu)
1117 return scr->clip_menu;
1119 menu = wMenuCreate(scr, NULL, False);
1120 if (type != WM_CLIP) {
1121 entry = wMenuAddCallback(menu, _("Keep on Top"),
1122 toggleLoweredCallback, NULL);
1123 entry->flags.indicator = 1;
1124 entry->flags.indicator_on = 1;
1125 entry->flags.indicator_type = MI_CHECK;
1126 } else {
1127 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1128 scr->clip_options = makeClipOptionsMenu(scr);
1129 if (scr->clip_options)
1130 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1132 entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback,
1133 NULL);
1134 wfree(entry->text);
1135 entry->text = _("Rename Workspace");
1137 entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
1138 entry->flags.indicator = 1;
1139 entry->flags.indicator_on = 1;
1140 entry->flags.indicator_type = MI_CHECK;
1142 entry = wMenuAddCallback(menu, _("Select All Icons"),
1143 selectIconsCallback, NULL);
1144 wfree(entry->text);
1145 entry->text = _("Select All Icons");
1147 entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
1148 wfree(entry->text);
1149 entry->text = _("Keep Icon");
1151 entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
1152 wfree(entry->text);
1153 entry->text = _("Move Icon To");
1154 scr->clip_submenu = makeWorkspaceMenu(scr);
1155 if (scr->clip_submenu)
1156 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1158 entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback,
1159 NULL);
1160 wfree(entry->text);
1161 entry->text = _("Remove Icon");
1163 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1166 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1168 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1170 entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1171 wfree(entry->text);
1172 entry->text = _("Hide");
1174 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1176 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1178 if (type == WM_CLIP)
1179 scr->clip_menu = menu;
1181 return menu;
1185 WDock*
1186 wDockCreate(WScreen *scr, int type)
1188 WDock *dock;
1189 WAppIcon *btn;
1190 int icon_count;
1192 make_keys();
1194 dock = wmalloc(sizeof(WDock));
1195 memset(dock, 0, sizeof(WDock));
1197 if (type == WM_CLIP)
1198 icon_count = CLIP_MAX_ICONS;
1199 else
1200 icon_count = scr->scr_height/wPreferences.icon_size;
1202 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1203 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1205 dock->max_icons = icon_count;
1207 btn = mainIconCreate(scr, type);
1209 btn->dock = dock;
1211 dock->x_pos = btn->x_pos;
1212 dock->y_pos = btn->y_pos;
1213 dock->screen_ptr = scr;
1214 dock->type = type;
1215 dock->icon_count = 1;
1216 dock->on_right_side = 1;
1217 dock->collapsed = 0;
1218 dock->auto_collapse = 0;
1219 dock->auto_collapse_magic = NULL;
1220 dock->auto_raise_lower = 0;
1221 dock->auto_lower_magic = NULL;
1222 dock->auto_raise_magic = NULL;
1223 dock->attract_icons = 0;
1224 dock->lowered = 1;
1225 dock->icon_array[0] = btn;
1226 wRaiseFrame(btn->icon->core);
1227 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1229 /* create dock menu */
1230 dock->menu = dockMenuCreate(scr, type);
1232 return dock;
1236 void
1237 wDockDestroy(WDock *dock)
1239 int i;
1240 WAppIcon *aicon;
1242 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1243 aicon = dock->icon_array[i];
1244 if (aicon) {
1245 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1246 wDockDetach(dock, aicon);
1247 if (keepit) {
1248 /* XXX: can: aicon->icon == NULL ? */
1249 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, wGetHeadForWindow(aicon->icon->owner));
1250 XMoveWindow(dpy, aicon->icon->core->window,
1251 aicon->x_pos, aicon->y_pos);
1252 if (!dock->mapped || dock->collapsed)
1253 XMapWindow(dpy, aicon->icon->core->window);
1257 if (wPreferences.auto_arrange_icons)
1258 wArrangeIcons(dock->screen_ptr, True);
1259 wfree(dock->icon_array);
1260 if (dock->menu && dock->type!=WM_CLIP)
1261 wMenuDestroy(dock->menu, True);
1262 if (dock->screen_ptr->last_dock == dock)
1263 dock->screen_ptr->last_dock = NULL;
1264 wfree(dock);
1268 void
1269 wClipIconPaint(WAppIcon *aicon)
1271 WScreen *scr = aicon->icon->core->screen_ptr;
1272 WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1273 WMColor *color;
1274 Window win = aicon->icon->core->window;
1275 int length, nlength;
1276 char *ws_name, ws_number[10];
1277 int ty, tx;
1279 wIconPaint(aicon->icon);
1281 length = strlen(workspace->name);
1282 ws_name = wmalloc(length + 1);
1283 snprintf(ws_name, length+1, "%s", workspace->name);
1284 snprintf(ws_number, sizeof(ws_number), "%i", scr->current_workspace + 1);
1285 nlength = strlen(ws_number);
1287 if (!workspace->clip->collapsed)
1288 color = scr->clip_title_color[CLIP_NORMAL];
1289 else
1290 color = scr->clip_title_color[CLIP_COLLAPSED];
1292 ty = ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
1294 tx = CLIP_BUTTON_SIZE*ICON_SIZE/64;
1296 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1297 ty, ws_name, length);
1298 /*WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, 4,
1299 2, ws_name, length);*/
1301 tx = (ICON_SIZE/2 - WMWidthOfString(scr->clip_title_font, ws_number,
1302 nlength))/2;
1304 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1305 2, ws_number, nlength);
1307 wfree(ws_name);
1309 if (aicon->launching) {
1310 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1311 0, 0, wPreferences.icon_size, wPreferences.icon_size);
1313 paintClipButtons(aicon, aicon->dock->lclip_button_pushed,
1314 aicon->dock->rclip_button_pushed);
1318 static void
1319 clipIconExpose(WObjDescriptor *desc, XEvent *event)
1321 wClipIconPaint(desc->parent);
1325 static void
1326 dockIconPaint(WAppIcon *btn)
1328 if (btn == btn->icon->core->screen_ptr->clip_icon)
1329 wClipIconPaint(btn);
1330 else
1331 wAppIconPaint(btn);
1335 static WMPropList*
1336 make_icon_state(WAppIcon *btn)
1338 WMPropList *node = NULL;
1339 WMPropList *command, *autolaunch, *lock, *name, *forced, *host;
1340 WMPropList *position, *buggy, *omnipresent;
1341 char *tmp;
1342 char buffer[64];
1344 if (btn) {
1345 if (!btn->command)
1346 command = WMCreatePLString("-");
1347 else
1348 command = WMCreatePLString(btn->command);
1350 autolaunch = btn->auto_launch ? dYes : dNo;
1352 lock = btn->lock ? dYes : dNo;
1354 tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
1356 name = WMCreatePLString(tmp);
1358 wfree(tmp);
1360 forced = btn->forced_dock ? dYes : dNo;
1362 buggy = btn->buggy_app ? dYes : dNo;
1364 if (btn == btn->icon->core->screen_ptr->clip_icon)
1365 snprintf(buffer, sizeof(buffer), "%i,%i", btn->x_pos, btn->y_pos);
1366 else
1367 snprintf(buffer, sizeof(buffer), "%hi,%hi", btn->xindex, btn->yindex);
1368 position = WMCreatePLString(buffer);
1370 node = WMCreatePLDictionary(dCommand, command,
1371 dName, name,
1372 dAutoLaunch, autolaunch,
1373 dLock, lock,
1374 dForced, forced,
1375 dBuggyApplication, buggy,
1376 dPosition, position,
1377 NULL);
1378 WMReleasePropList(command);
1379 WMReleasePropList(name);
1380 WMReleasePropList(position);
1382 omnipresent = btn->omnipresent ? dYes : dNo;
1383 if (btn->dock != btn->icon->core->screen_ptr->dock &&
1384 (btn->xindex != 0 || btn->yindex != 0))
1385 WMPutInPLDictionary(node, dOmnipresent, omnipresent);
1387 #ifdef XDND /* was OFFIX */
1388 if (btn->dnd_command) {
1389 command = WMCreatePLString(btn->dnd_command);
1390 WMPutInPLDictionary(node, dDropCommand, command);
1391 WMReleasePropList(command);
1393 #endif /* XDND */
1395 if (btn->paste_command) {
1396 command = WMCreatePLString(btn->paste_command);
1397 WMPutInPLDictionary(node, dPasteCommand, command);
1398 WMReleasePropList(command);
1401 if (btn->client_machine && btn->remote_start) {
1402 host = WMCreatePLString(btn->client_machine);
1403 WMPutInPLDictionary(node, dHost, host);
1404 WMReleasePropList(host);
1408 return node;
1412 static WMPropList*
1413 dockSaveState(WDock *dock)
1415 int i;
1416 WMPropList *icon_info;
1417 WMPropList *list=NULL, *dock_state=NULL;
1418 WMPropList *value, *key;
1419 char buffer[256];
1421 list = WMCreatePLArray(NULL);
1423 for (i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
1424 WAppIcon *btn = dock->icon_array[i];
1426 if (!btn || btn->attracted)
1427 continue;
1429 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1430 WMAddToPLArray(list, icon_info);
1431 WMReleasePropList(icon_info);
1435 dock_state = WMCreatePLDictionary(dApplications, list, NULL);
1437 if (dock->type == WM_DOCK) {
1438 snprintf(buffer, sizeof(buffer), "Applications%i", dock->screen_ptr->scr_height);
1439 key = WMCreatePLString(buffer);
1440 WMPutInPLDictionary(dock_state, key, list);
1441 WMReleasePropList(key);
1444 snprintf(buffer, sizeof(buffer), "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0),
1445 dock->y_pos);
1446 value = WMCreatePLString(buffer);
1447 WMPutInPLDictionary(dock_state, dPosition, value);
1448 WMReleasePropList(value);
1450 WMReleasePropList(list);
1453 value = (dock->lowered ? dYes : dNo);
1454 WMPutInPLDictionary(dock_state, dLowered, value);
1456 if (dock->type == WM_CLIP) {
1457 value = (dock->collapsed ? dYes : dNo);
1458 WMPutInPLDictionary(dock_state, dCollapsed, value);
1460 value = (dock->auto_collapse ? dYes : dNo);
1461 WMPutInPLDictionary(dock_state, dAutoCollapse, value);
1463 value = (dock->auto_raise_lower ? dYes : dNo);
1464 WMPutInPLDictionary(dock_state, dAutoRaiseLower, value);
1466 value = (dock->attract_icons ? dYes : dNo);
1467 WMPutInPLDictionary(dock_state, dAutoAttractIcons, value);
1470 return dock_state;
1474 void
1475 wDockSaveState(WScreen *scr, WMPropList *old_state)
1477 WMPropList *dock_state;
1478 WMPropList *keys;
1480 dock_state = dockSaveState(scr->dock);
1483 * Copy saved states of docks with different sizes.
1485 if (old_state) {
1486 int i;
1487 WMPropList *tmp;
1489 keys = WMGetPLDictionaryKeys(old_state);
1490 for (i = 0; i < WMGetPropListItemCount(keys); i++) {
1491 tmp = WMGetFromPLArray(keys, i);
1493 if (strncasecmp(WMGetFromPLString(tmp), "applications", 12) == 0
1494 && !WMGetFromPLDictionary(dock_state, tmp)) {
1496 WMPutInPLDictionary(dock_state, tmp,
1497 WMGetFromPLDictionary(old_state, tmp));
1500 WMReleasePropList(keys);
1504 WMPutInPLDictionary(scr->session_state, dDock, dock_state);
1506 WMReleasePropList(dock_state);
1510 void
1511 wClipSaveState(WScreen *scr)
1513 WMPropList *clip_state;
1515 clip_state = make_icon_state(scr->clip_icon);
1517 WMPutInPLDictionary(scr->session_state, dClip, clip_state);
1519 WMReleasePropList(clip_state);
1523 WMPropList*
1524 wClipSaveWorkspaceState(WScreen *scr, int workspace)
1526 return dockSaveState(scr->workspaces[workspace]->clip);
1530 static Bool
1531 getBooleanDockValue(WMPropList *value, WMPropList *key)
1533 if (value) {
1534 if (WMIsPLString(value)) {
1535 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1536 return True;
1537 } else {
1538 wwarning(_("bad value in docked icon state info %s"),
1539 WMGetFromPLString(key));
1542 return False;
1546 static WAppIcon*
1547 restore_icon_state(WScreen *scr, WMPropList *info, int type, int index)
1549 WAppIcon *aicon;
1550 WMPropList *cmd, *value;
1553 cmd = WMGetFromPLDictionary(info, dCommand);
1554 if (!cmd || !WMIsPLString(cmd)) {
1555 return NULL;
1558 /* parse window name */
1559 value = WMGetFromPLDictionary(info, dName);
1560 if (!value)
1561 return NULL;
1564 char *wclass, *winstance;
1565 char *command;
1567 ParseWindowName(value, &winstance, &wclass, "dock");
1569 if (!winstance && !wclass) {
1570 return NULL;
1573 /* get commands */
1575 if (cmd)
1576 command = wstrdup(WMGetFromPLString(cmd));
1577 else
1578 command = NULL;
1580 if (!command || strcmp(command, "-")==0) {
1581 if (command)
1582 wfree(command);
1583 if (wclass)
1584 wfree(wclass);
1585 if (winstance)
1586 wfree(winstance);
1588 return NULL;
1591 aicon = wAppIconCreateForDock(scr, command, winstance, wclass,
1592 TILE_NORMAL);
1593 if (wclass)
1594 wfree(wclass);
1595 if (winstance)
1596 wfree(winstance);
1597 if (command)
1598 wfree(command);
1601 aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1602 if (type == WM_CLIP) {
1603 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1604 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1606 aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1607 aicon->icon->core->descriptor.parent = aicon;
1610 #ifdef XDND /* was OFFIX */
1611 cmd = WMGetFromPLDictionary(info, dDropCommand);
1612 if (cmd)
1613 aicon->dnd_command = wstrdup(WMGetFromPLString(cmd));
1614 #endif
1616 cmd = WMGetFromPLDictionary(info, dPasteCommand);
1617 if (cmd)
1618 aicon->paste_command = wstrdup(WMGetFromPLString(cmd));
1620 /* check auto launch */
1621 value = WMGetFromPLDictionary(info, dAutoLaunch);
1623 aicon->auto_launch = getBooleanDockValue(value, dAutoLaunch);
1625 /* check lock */
1626 value = WMGetFromPLDictionary(info, dLock);
1628 aicon->lock = getBooleanDockValue(value, dLock);
1630 /* check if it wasn't normally docked */
1631 value = WMGetFromPLDictionary(info, dForced);
1633 aicon->forced_dock = getBooleanDockValue(value, dForced);
1635 /* check if we can rely on the stuff in the app */
1636 value = WMGetFromPLDictionary(info, dBuggyApplication);
1638 aicon->buggy_app = getBooleanDockValue(value, dBuggyApplication);
1640 /* get position in the dock */
1641 value = WMGetFromPLDictionary(info, dPosition);
1642 if (value && WMIsPLString(value)) {
1643 if (sscanf(WMGetFromPLString(value), "%hi,%hi", &aicon->xindex,
1644 &aicon->yindex)!=2)
1645 wwarning(_("bad value in docked icon state info %s"),
1646 WMGetFromPLString(dPosition));
1648 /* check position sanity */
1649 /* incomplete section! */
1650 if (type == WM_DOCK) {
1651 aicon->xindex = 0;
1652 if (aicon->yindex < 0)
1653 wwarning(_("bad value in docked icon position %i,%i"),
1654 aicon->xindex, aicon->yindex);
1656 } else {
1657 aicon->yindex = index;
1658 aicon->xindex = 0;
1661 /* check if icon is omnipresent */
1662 value = WMGetFromPLDictionary(info, dOmnipresent);
1664 aicon->omnipresent = getBooleanDockValue(value, dOmnipresent);
1666 aicon->running = 0;
1667 aicon->docked = 1;
1669 return aicon;
1673 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1676 WAppIcon*
1677 wClipRestoreState(WScreen *scr, WMPropList *clip_state)
1679 WAppIcon *icon;
1680 WMPropList *value;
1683 icon = mainIconCreate(scr, WM_CLIP);
1685 if (!clip_state)
1686 return icon;
1687 else
1688 WMRetainPropList(clip_state);
1690 /* restore position */
1692 value = WMGetFromPLDictionary(clip_state, dPosition);
1694 if (value) {
1695 if (!WMIsPLString(value))
1696 COMPLAIN("Position");
1697 else {
1698 WMRect rect;
1699 int flags;
1701 if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos,
1702 &icon->y_pos)!=2)
1703 COMPLAIN("Position");
1705 /* check position sanity */
1706 rect.pos.x = icon->x_pos;
1707 rect.pos.y = icon->y_pos;
1708 rect.size.width = rect.size.height = ICON_SIZE;
1710 wGetRectPlacementInfo(scr, rect, &flags);
1711 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
1712 wScreenKeepInside(scr, &icon->x_pos, &icon->y_pos,
1713 ICON_SIZE, ICON_SIZE);
1717 #ifdef XDND /* was OFFIX */
1718 value = WMGetFromPLDictionary(clip_state, dDropCommand);
1719 if (value && WMIsPLString(value))
1720 icon->dnd_command = wstrdup(WMGetFromPLString(value));
1721 #endif
1723 value = WMGetFromPLDictionary(clip_state, dPasteCommand);
1724 if (value && WMIsPLString(value))
1725 icon->paste_command = wstrdup(WMGetFromPLString(value));
1727 WMReleasePropList(clip_state);
1729 return icon;
1733 WDock*
1734 wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type)
1736 WDock *dock;
1737 WMPropList *apps;
1738 WMPropList *value;
1739 WAppIcon *aicon, *old_top;
1740 int count, i;
1743 dock = wDockCreate(scr, type);
1745 if (!dock_state)
1746 return dock;
1748 if (dock_state)
1749 WMRetainPropList(dock_state);
1752 /* restore position */
1754 value = WMGetFromPLDictionary(dock_state, dPosition);
1756 if (value) {
1757 if (!WMIsPLString(value)) {
1758 COMPLAIN("Position");
1759 } else {
1760 WMRect rect;
1761 int flags;
1763 if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos,
1764 &dock->y_pos)!=2)
1765 COMPLAIN("Position");
1767 /* check position sanity */
1768 rect.pos.x = dock->x_pos;
1769 rect.pos.y = dock->y_pos;
1770 rect.size.width = rect.size.height = ICON_SIZE;
1772 wGetRectPlacementInfo(scr, rect, &flags);
1773 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) {
1774 int x = dock->x_pos;
1775 wScreenKeepInside(scr, &x, &dock->y_pos, ICON_SIZE, ICON_SIZE);
1778 /* Is this needed any more? */
1779 if (type == WM_CLIP) {
1780 if (dock->x_pos < 0) {
1781 dock->x_pos = 0;
1782 } else if (dock->x_pos > scr->scr_width-ICON_SIZE) {
1783 dock->x_pos = scr->scr_width-ICON_SIZE;
1785 } else {
1786 if (dock->x_pos >= 0) {
1787 dock->x_pos = DOCK_EXTRA_SPACE;
1788 dock->on_right_side = 0;
1789 } else {
1790 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1791 dock->on_right_side = 1;
1797 /* restore lowered/raised state */
1799 dock->lowered = 0;
1801 value = WMGetFromPLDictionary(dock_state, dLowered);
1803 if (value) {
1804 if (!WMIsPLString(value)) {
1805 COMPLAIN("Lowered");
1806 } else {
1807 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1808 dock->lowered = 1;
1814 /* restore collapsed state */
1816 dock->collapsed = 0;
1818 value = WMGetFromPLDictionary(dock_state, dCollapsed);
1820 if (value) {
1821 if (!WMIsPLString(value)) {
1822 COMPLAIN("Collapsed");
1823 } else {
1824 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1825 dock->collapsed = 1;
1831 /* restore auto-collapsed state */
1833 value = WMGetFromPLDictionary(dock_state, dAutoCollapse);
1835 if (value) {
1836 if (!WMIsPLString(value)) {
1837 COMPLAIN("AutoCollapse");
1838 } else {
1839 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1840 dock->auto_collapse = 1;
1841 dock->collapsed = 1;
1847 /* restore auto-raise/lower state */
1849 value = WMGetFromPLDictionary(dock_state, dAutoRaiseLower);
1851 if (value) {
1852 if (!WMIsPLString(value)) {
1853 COMPLAIN("AutoRaiseLower");
1854 } else {
1855 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1856 dock->auto_raise_lower = 1;
1861 /* restore attract icons state */
1863 dock->attract_icons = 0;
1865 value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons);
1867 if (value) {
1868 if (!WMIsPLString(value)) {
1869 COMPLAIN("AutoAttractIcons");
1870 } else {
1871 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1872 dock->attract_icons = 1;
1878 /* application list */
1881 WMPropList *tmp;
1882 char buffer[64];
1885 * When saving, it saves the dock state in
1886 * Applications and Applicationsnnn
1888 * When loading, it will first try Applicationsnnn.
1889 * If it does not exist, use Applications as default.
1892 snprintf(buffer, sizeof(buffer), "Applications%i", scr->scr_height);
1894 tmp = WMCreatePLString(buffer);
1895 apps = WMGetFromPLDictionary(dock_state, tmp);
1896 WMReleasePropList(tmp);
1898 if (!apps) {
1899 apps = WMGetFromPLDictionary(dock_state, dApplications);
1903 if (!apps) {
1904 goto finish;
1907 count = WMGetPropListItemCount(apps);
1909 if (count==0)
1910 goto finish;
1912 old_top = dock->icon_array[0];
1914 /* dock->icon_count is set to 1 when dock is created.
1915 * Since Clip is already restored, we want to keep it so for clip,
1916 * but for dock we may change the default top tile, so we set it to 0.
1918 if (type == WM_DOCK)
1919 dock->icon_count = 0;
1921 for (i=0; i<count; i++) {
1922 if (dock->icon_count >= dock->max_icons) {
1923 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1924 break;
1927 value = WMGetFromPLArray(apps, i);
1928 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1930 dock->icon_array[dock->icon_count] = aicon;
1932 if (aicon) {
1933 aicon->dock = dock;
1934 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1935 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1937 if (dock->lowered)
1938 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1939 else
1940 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1942 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1943 0, 0);
1945 if (!dock->collapsed)
1946 XMapWindow(dpy, aicon->icon->core->window);
1947 wRaiseFrame(aicon->icon->core);
1949 dock->icon_count++;
1950 } else if (dock->icon_count==0 && type==WM_DOCK)
1951 dock->icon_count++;
1954 /* if the first icon is not defined, use the default */
1955 if (dock->icon_array[0]==NULL) {
1956 /* update default icon */
1957 old_top->x_pos = dock->x_pos;
1958 old_top->y_pos = dock->y_pos;
1959 if (dock->lowered)
1960 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1961 else
1962 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1963 dock->icon_array[0] = old_top;
1964 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1965 /* we don't need to increment dock->icon_count here because it was
1966 * incremented in the loop above.
1968 } else if (old_top!=dock->icon_array[0]) {
1969 if (old_top == scr->clip_icon)
1970 scr->clip_icon = dock->icon_array[0];
1971 wAppIconDestroy(old_top);
1974 finish:
1975 if (dock_state)
1976 WMReleasePropList(dock_state);
1978 return dock;
1983 void
1984 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
1986 if (btn && btn->command && !btn->running && !btn->launching) {
1988 btn->drop_launch = 0;
1989 btn->paste_launch = 0;
1991 btn->pid = execCommand(btn, btn->command, state);
1993 if (btn->pid>0) {
1994 if (!btn->forced_dock && !btn->buggy_app) {
1995 btn->launching = 1;
1996 dockIconPaint(btn);
1999 } else {
2000 wfree(state);
2005 void
2006 wDockDoAutoLaunch(WDock *dock, int workspace)
2008 WAppIcon *btn;
2009 WSavedState *state;
2010 int i;
2012 for (i = 0; i < dock->max_icons; i++) {
2013 btn = dock->icon_array[i];
2014 if (!btn || !btn->auto_launch)
2015 continue;
2017 state = wmalloc(sizeof(WSavedState));
2018 memset(state, 0, sizeof(WSavedState));
2019 state->workspace = workspace;
2020 /* TODO: this is klugy and is very difficult to understand
2021 * what's going on. Try to clean up */
2022 wDockLaunchWithState(dock, btn, state);
2027 #ifdef XDND /* was OFFIX */
2028 static WDock*
2029 findDock(WScreen *scr, XEvent *event, int *icon_pos)
2031 WDock *dock;
2032 int i;
2034 *icon_pos = -1;
2035 if ((dock = scr->dock)!=NULL) {
2036 for (i=0; i<dock->max_icons; i++) {
2037 if (dock->icon_array[i]
2038 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2039 *icon_pos = i;
2040 break;
2044 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
2045 for (i=0; i<dock->max_icons; i++) {
2046 if (dock->icon_array[i]
2047 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2048 *icon_pos = i;
2049 break;
2053 if(*icon_pos>=0)
2054 return dock;
2055 return NULL;
2060 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
2062 WDock *dock;
2063 WAppIcon *btn;
2064 int icon_pos;
2066 dock = findDock(scr, event, &icon_pos);
2067 if (!dock)
2068 return False;
2071 * Return True if the drop was on an application icon window.
2072 * In this case, let the ClientMessage handler redirect the
2073 * message to the app.
2075 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
2076 return True;
2078 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
2079 scr->flags.dnd_data_convertion_status = 0;
2081 btn = dock->icon_array[icon_pos];
2083 if (!btn->forced_dock) {
2084 btn->relaunching = btn->running;
2085 btn->running = 1;
2087 if (btn->wm_instance || btn->wm_class) {
2088 WWindowAttributes attr;
2089 memset(&attr, 0, sizeof(WWindowAttributes));
2090 wDefaultFillAttributes(btn->icon->core->screen_ptr,
2091 btn->wm_instance,
2092 btn->wm_class, &attr, NULL, True);
2094 if (!attr.no_appicon)
2095 btn->launching = 1;
2096 else
2097 btn->running = 0;
2100 btn->paste_launch = 0;
2101 btn->drop_launch = 1;
2102 scr->last_dock = dock;
2103 btn->pid = execCommand(btn, btn->dnd_command, NULL);
2104 if (btn->pid>0) {
2105 dockIconPaint(btn);
2106 } else {
2107 btn->launching = 0;
2108 if (!btn->relaunching) {
2109 btn->running = 0;
2113 return False;
2115 #endif /* XDND */
2119 Bool
2120 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2122 WWindow *wwin;
2123 int index;
2125 wwin = icon->icon->owner;
2126 if (icon->command==NULL) {
2127 char *command;
2129 icon->editing = 0;
2131 command = GetCommandForWindow(wwin->client_win);
2132 if (command) {
2133 icon->command = command;
2134 } else {
2135 /* icon->forced_dock = 1;*/
2136 if (dock->type!=WM_CLIP || !icon->attracted) {
2137 icon->editing = 1;
2138 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
2139 _("Type the command used to launch the application"),
2140 &command)) {
2141 if (command && (command[0]==0 ||
2142 (command[0]=='-' && command[1]==0))) {
2143 wfree(command);
2144 command = NULL;
2146 icon->command = command;
2147 icon->editing = 0;
2148 } else {
2149 icon->editing = 0;
2150 if (command)
2151 wfree(command);
2152 /* If the target is the dock, reject the icon. If
2153 * the target is the clip, make it an attracted icon
2155 if (dock->type==WM_CLIP) {
2156 icon->attracted = 1;
2157 if (!icon->icon->shadowed) {
2158 icon->icon->shadowed = 1;
2159 icon->icon->force_paint = 1;
2161 } else {
2162 return False;
2167 } else {
2168 icon->editing = 0;
2171 for (index=1; index<dock->max_icons; index++)
2172 if (dock->icon_array[index] == NULL)
2173 break;
2174 /* if (index == dock->max_icons)
2175 return; */
2177 assert(index < dock->max_icons);
2179 dock->icon_array[index] = icon;
2180 icon->yindex = y;
2181 icon->xindex = x;
2183 icon->omnipresent = 0;
2185 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2186 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2188 dock->icon_count++;
2190 icon->running = 1;
2191 icon->launching = 0;
2192 icon->docked = 1;
2193 icon->dock = dock;
2194 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2195 if (dock->type == WM_CLIP) {
2196 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2197 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2199 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2200 icon->icon->core->descriptor.parent = icon;
2202 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2203 icon->icon->core);
2204 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2205 wAppIconPaint(icon);
2207 if (wPreferences.auto_arrange_icons)
2208 wArrangeIcons(dock->screen_ptr, True);
2210 #ifdef XDND /* was OFFIX */
2211 if (icon->command && !icon->dnd_command) {
2212 int len = strlen(icon->command)+8;
2213 icon->dnd_command = wmalloc(len);
2214 snprintf(icon->dnd_command, len, "%s %%d", icon->command);
2216 #endif
2218 if (icon->command && !icon->paste_command) {
2219 int len = strlen(icon->command)+8;
2220 icon->paste_command = wmalloc(len);
2221 snprintf(icon->paste_command, len, "%s %%s", icon->command);
2224 return True;
2228 void
2229 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2231 int index;
2233 for(index=1; index<dock->max_icons; index++) {
2234 if(dock->icon_array[index] == icon)
2235 break;
2237 assert(index < dock->max_icons);
2239 icon->yindex = y;
2240 icon->xindex = x;
2242 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2243 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2247 Bool
2248 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2250 WWindow *wwin;
2251 char *command;
2252 int index;
2254 if (src == dest)
2255 return True; /* No move needed, we're already there */
2257 if (dest == NULL)
2258 return False;
2260 wwin = icon->icon->owner;
2263 * For the moment we can't do this if we move icons in Clip from one
2264 * workspace to other, because if we move two or more icons without
2265 * command, the dialog box will not be able to tell us to which of the
2266 * moved icons it applies. -Dan
2268 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2269 command = GetCommandForWindow(wwin->client_win);
2270 if (command) {
2271 icon->command = command;
2272 } else {
2273 icon->editing = 1;
2274 /* icon->forced_dock = 1;*/
2275 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2276 _("Type the command used to launch the application"),
2277 &command)) {
2278 if (command && (command[0]==0 ||
2279 (command[0]=='-' && command[1]==0))) {
2280 wfree(command);
2281 command = NULL;
2283 icon->command = command;
2284 } else {
2285 icon->editing = 0;
2286 if (command)
2287 wfree(command);
2288 return False;
2290 icon->editing = 0;
2294 if (dest->type == WM_DOCK)
2295 wClipMakeIconOmnipresent(icon, False);
2297 for(index=1; index<src->max_icons; index++) {
2298 if(src->icon_array[index] == icon)
2299 break;
2301 assert(index < src->max_icons);
2303 src->icon_array[index] = NULL;
2304 src->icon_count--;
2306 for(index=1; index<dest->max_icons; index++) {
2307 if(dest->icon_array[index] == NULL)
2308 break;
2310 /* if (index == dest->max_icons)
2311 return; */
2313 assert(index < dest->max_icons);
2315 dest->icon_array[index] = icon;
2316 icon->dock = dest;
2318 /* deselect the icon */
2319 if (icon->icon->selected)
2320 wIconSelect(icon->icon);
2322 if (dest->type == WM_DOCK) {
2323 icon->icon->core->descriptor.handle_enternotify = NULL;
2324 icon->icon->core->descriptor.handle_leavenotify = NULL;
2325 } else {
2326 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2327 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2330 /* set it to be kept when moving to dock.
2331 * Unless the icon does not have a command set
2333 if (icon->command && dest->type==WM_DOCK) {
2334 icon->attracted = 0;
2335 if (icon->icon->shadowed) {
2336 icon->icon->shadowed = 0;
2337 icon->icon->force_paint = 1;
2341 if (src->auto_collapse || src->auto_raise_lower)
2342 clipLeave(src);
2344 icon->yindex = y;
2345 icon->xindex = x;
2347 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2348 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2350 dest->icon_count++;
2352 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2353 icon->icon->core);
2354 wAppIconPaint(icon);
2356 return True;
2360 void
2361 wDockDetach(WDock *dock, WAppIcon *icon)
2363 int index;
2365 /* make the settings panel be closed */
2366 if (icon->panel) {
2367 DestroyDockAppSettingsPanel(icon->panel);
2370 /* This must be called before icon->dock is set to NULL.
2371 * Don't move it. -Dan
2373 wClipMakeIconOmnipresent(icon, False);
2375 icon->docked = 0;
2376 icon->dock = NULL;
2377 icon->attracted = 0;
2378 icon->auto_launch = 0;
2379 if (icon->icon->shadowed) {
2380 icon->icon->shadowed = 0;
2381 icon->icon->force_paint = 1;
2384 /* deselect the icon */
2385 if (icon->icon->selected)
2386 wIconSelect(icon->icon);
2388 if (icon->command) {
2389 wfree(icon->command);
2390 icon->command = NULL;
2392 #ifdef XDND /* was OFFIX */
2393 if (icon->dnd_command) {
2394 wfree(icon->dnd_command);
2395 icon->dnd_command = NULL;
2397 #endif
2398 if (icon->paste_command) {
2399 wfree(icon->paste_command);
2400 icon->paste_command = NULL;
2403 for (index=1; index<dock->max_icons; index++)
2404 if (dock->icon_array[index] == icon)
2405 break;
2406 assert(index < dock->max_icons);
2407 dock->icon_array[index] = NULL;
2408 icon->yindex = -1;
2409 icon->xindex = -1;
2411 dock->icon_count--;
2413 /* if the dock is not attached to an application or
2414 * the the application did not set the approriate hints yet,
2415 * destroy the icon */
2416 if (!icon->running || !wApplicationOf(icon->main_window))
2417 wAppIconDestroy(icon);
2418 else {
2419 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2420 icon->icon->core->descriptor.handle_enternotify = NULL;
2421 icon->icon->core->descriptor.handle_leavenotify = NULL;
2422 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2423 icon->icon->core->descriptor.parent = icon;
2425 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2427 wAppIconPaint(icon);
2428 if (wPreferences.auto_arrange_icons) {
2429 wArrangeIcons(dock->screen_ptr, True);
2432 if (dock->auto_collapse || dock->auto_raise_lower)
2433 clipLeave(dock);
2438 * returns the closest Dock slot index for the passed
2439 * coordinates.
2441 * Returns False if icon can't be docked.
2443 * Note: this function should NEVER alter ret_x or ret_y, unless it will
2444 * return True. -Dan
2446 Bool
2447 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2448 int *ret_x, int *ret_y, int redocking)
2450 WScreen *scr = dock->screen_ptr;
2451 int dx, dy;
2452 int ex_x, ex_y;
2453 int i, offset = ICON_SIZE/2;
2454 WAppIcon *aicon = NULL;
2455 WAppIcon *nicon = NULL;
2456 int max_y_icons, max_x_icons;
2458 /* TODO: XINERAMA, for these */
2459 max_x_icons = scr->scr_width/ICON_SIZE;
2460 max_y_icons = scr->scr_height/ICON_SIZE-1;
2462 if (wPreferences.flags.noupdates)
2463 return False;
2465 dx = dock->x_pos;
2466 dy = dock->y_pos;
2468 /* if the dock is full */
2469 if (!redocking &&
2470 (dock->icon_count >= dock->max_icons)) {
2471 return False;
2474 /* exact position */
2475 if (req_y < dy)
2476 ex_y = (req_y - offset - dy)/ICON_SIZE;
2477 else
2478 ex_y = (req_y + offset - dy)/ICON_SIZE;
2480 if (req_x < dx)
2481 ex_x = (req_x - offset - dx)/ICON_SIZE;
2482 else
2483 ex_x = (req_x + offset - dx)/ICON_SIZE;
2485 /* check if the icon is outside the screen boundaries */
2487 WMRect rect;
2488 int flags;
2490 rect.pos.x = dx + ex_x*ICON_SIZE;
2491 rect.pos.y = dy + ex_y*ICON_SIZE;
2492 rect.size.width = rect.size.height = ICON_SIZE;
2494 wGetRectPlacementInfo(scr, rect, &flags);
2495 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
2496 return False;
2499 if (dock->type == WM_DOCK) {
2500 if (icon->dock != dock && ex_x != 0)
2501 return False;
2503 aicon = NULL;
2504 for (i=0; i<dock->max_icons; i++) {
2505 nicon = dock->icon_array[i];
2506 if (nicon && nicon->yindex == ex_y) {
2507 aicon = nicon;
2508 break;
2512 if (redocking) {
2513 int sig, done, closest;
2515 /* Possible cases when redocking:
2517 * icon dragged out of range of any slot -> false
2518 * icon dragged to range of free slot
2519 * icon dragged to range of same slot
2520 * icon dragged to range of different icon
2522 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2523 return False;
2525 if (ex_y>=0 && ex_y<=max_y_icons && (aicon==icon || !aicon)) {
2526 *ret_x = 0;
2527 *ret_y = ex_y;
2528 return True;
2531 /* start looking at the upper slot or lower? */
2532 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2533 sig = 1;
2534 else
2535 sig = -1;
2537 closest = -1;
2538 done = 0;
2539 /* look for closest free slot */
2540 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2541 int j;
2543 done = 1;
2544 closest = sig*(i/2) + ex_y;
2545 /* check if this slot is used */
2546 if (closest >= 0) {
2547 for (j = 0; j<dock->max_icons; j++) {
2548 if (dock->icon_array[j]
2549 && dock->icon_array[j]->yindex==closest) {
2550 /* slot is used by someone else */
2551 if (dock->icon_array[j]!=icon)
2552 done = 0;
2553 break;
2557 sig = -sig;
2559 if (done && closest >= 0 && closest <= max_y_icons &&
2560 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2562 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2563 *ret_x = 0;
2564 *ret_y = closest;
2565 return True;
2567 } else { /* !redocking */
2569 /* if slot is free and the icon is close enough, return it */
2570 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2571 *ret_x = 0;
2572 *ret_y = ex_y;
2573 return True;
2576 } else { /* CLIP */
2577 int neighbours = 0;
2578 int start, stop, k;
2580 start = icon->omnipresent ? 0 : scr->current_workspace;
2581 stop = icon->omnipresent ? scr->workspace_count : start+1;
2583 aicon = NULL;
2584 for (k=start; k<stop; k++) {
2585 WDock *tmp = scr->workspaces[k]->clip;
2586 if (!tmp)
2587 continue;
2588 for (i=0; i<tmp->max_icons; i++) {
2589 nicon = tmp->icon_array[i];
2590 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2591 aicon = nicon;
2592 break;
2595 if (aicon)
2596 break;
2598 for (k=start; k<stop; k++) {
2599 WDock *tmp = scr->workspaces[k]->clip;
2600 if (!tmp)
2601 continue;
2602 for (i=0; i<tmp->max_icons; i++) {
2603 nicon = tmp->icon_array[i];
2604 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2605 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2606 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2607 neighbours = 1;
2608 break;
2611 if (neighbours)
2612 break;
2615 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2616 *ret_x = ex_x;
2617 *ret_y = ex_y;
2618 return True;
2621 return False;
2625 static int
2626 onScreen(WScreen *scr, int x, int y, int sx, int ex, int sy, int ey)
2628 WMRect rect = wmkrect(x, y, ICON_SIZE, ICON_SIZE);
2629 int flags;
2631 wGetRectPlacementInfo(scr, rect, &flags);
2633 return !(flags & (XFLAG_DEAD | XFLAG_PARTIAL));
2638 * returns true if it can find a free slot in the dock,
2639 * in which case it changes x_pos and y_pos accordingly.
2640 * Else returns false.
2642 Bool
2643 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2645 WScreen *scr = dock->screen_ptr;
2646 WAppIcon *btn;
2647 WAppIconChain *chain;
2648 unsigned char *slot_map;
2649 int mwidth;
2650 int r;
2651 int x, y;
2652 int i, done = False;
2653 int corner;
2654 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2655 int extra_count=0;
2657 if (dock->type == WM_CLIP &&
2658 dock != scr->workspaces[scr->current_workspace]->clip)
2659 extra_count = scr->global_icon_count;
2661 /* if the dock is full */
2662 if (dock->icon_count+extra_count >= dock->max_icons) {
2663 return False;
2666 if (!wPreferences.flags.nodock && scr->dock) {
2667 if (scr->dock->on_right_side)
2668 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2669 else
2670 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2673 if (ex < dock->x_pos)
2674 ex = dock->x_pos;
2675 if (sx > dock->x_pos+ICON_SIZE)
2676 sx = dock->x_pos+ICON_SIZE;
2677 #define C_NONE 0
2678 #define C_NW 1
2679 #define C_NE 2
2680 #define C_SW 3
2681 #define C_SE 4
2683 /* check if clip is in a corner */
2684 if (dock->type==WM_CLIP) {
2685 if (dock->x_pos < 1 && dock->y_pos < 1)
2686 corner = C_NE;
2687 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2688 corner = C_SE;
2689 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2690 corner = C_SW;
2691 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2692 corner = C_NW;
2693 else
2694 corner = C_NONE;
2695 } else
2696 corner = C_NONE;
2698 /* If the clip is in the corner, use only slots that are in the border
2699 * of the screen */
2700 if (corner!=C_NONE) {
2701 char *hmap, *vmap;
2702 int hcount, vcount;
2704 hcount = WMIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2705 vcount = WMIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2706 hmap = wmalloc(hcount+1);
2707 memset(hmap, 0, hcount+1);
2708 vmap = wmalloc(vcount+1);
2709 memset(vmap, 0, vcount+1);
2711 /* mark used positions */
2712 switch (corner) {
2713 case C_NE:
2714 for (i=0; i<dock->max_icons; i++) {
2715 btn = dock->icon_array[i];
2716 if (!btn)
2717 continue;
2719 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2720 vmap[btn->yindex] = 1;
2721 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2722 hmap[btn->xindex] = 1;
2724 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2725 btn = chain->aicon;
2726 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2727 vmap[btn->yindex] = 1;
2728 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2729 hmap[btn->xindex] = 1;
2731 break;
2732 case C_NW:
2733 for (i=0; i<dock->max_icons; i++) {
2734 btn = dock->icon_array[i];
2735 if (!btn)
2736 continue;
2738 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2739 vmap[btn->yindex] = 1;
2740 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2741 hmap[-btn->xindex] = 1;
2743 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2744 btn = chain->aicon;
2745 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2746 vmap[btn->yindex] = 1;
2747 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2748 hmap[-btn->xindex] = 1;
2750 break;
2751 case C_SE:
2752 for (i=0; i<dock->max_icons; i++) {
2753 btn = dock->icon_array[i];
2754 if (!btn)
2755 continue;
2757 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2758 vmap[-btn->yindex] = 1;
2759 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2760 hmap[btn->xindex] = 1;
2762 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2763 btn = chain->aicon;
2764 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2765 vmap[-btn->yindex] = 1;
2766 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2767 hmap[btn->xindex] = 1;
2769 break;
2770 case C_SW:
2771 default:
2772 for (i=0; i<dock->max_icons; i++) {
2773 btn = dock->icon_array[i];
2774 if (!btn)
2775 continue;
2777 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2778 vmap[-btn->yindex] = 1;
2779 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2780 hmap[-btn->xindex] = 1;
2782 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2783 btn = chain->aicon;
2784 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2785 vmap[-btn->yindex] = 1;
2786 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2787 hmap[-btn->xindex] = 1;
2790 x=0; y=0;
2791 done = 0;
2792 /* search a vacant slot */
2793 for (i=1; i<WMAX(vcount, hcount); i++) {
2794 if (i < vcount && vmap[i]==0) {
2795 /* found a slot */
2796 x = 0;
2797 y = i;
2798 done = 1;
2799 break;
2800 } else if (i < hcount && hmap[i]==0) {
2801 /* found a slot */
2802 x = i;
2803 y = 0;
2804 done = 1;
2805 break;
2808 wfree(vmap);
2809 wfree(hmap);
2810 /* If found a slot, translate and return */
2811 if (done) {
2812 if (corner==C_NW || corner==C_NE) {
2813 *y_pos = y;
2814 } else {
2815 *y_pos = -y;
2817 if (corner==C_NE || corner==C_SE) {
2818 *x_pos = x;
2819 } else {
2820 *x_pos = -x;
2822 return True;
2824 /* else, try to find a slot somewhere else */
2827 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2828 * placed outside of screen */
2829 mwidth = (int)ceil(sqrt(dock->max_icons));
2831 /* In the worst case (the clip is in the corner of the screen),
2832 * the amount of icons that fit in the clip is smaller.
2833 * Double the map to get a safe value.
2835 mwidth += mwidth;
2837 r = (mwidth-1)/2;
2839 slot_map = wmalloc(mwidth*mwidth);
2840 memset(slot_map, 0, mwidth*mwidth);
2842 #define XY2OFS(x,y) (WMAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2844 /* mark used slots in the map. If the slot falls outside the map
2845 * (for example, when all icons are placed in line), ignore them. */
2846 for (i=0; i<dock->max_icons; i++) {
2847 btn = dock->icon_array[i];
2848 if (btn)
2849 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2851 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2852 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2854 /* Find closest slot from the center that is free by scanning the
2855 * map from the center to outward in circular passes.
2856 * This will not result in a neat layout, but will be optimal
2857 * in the sense that there will not be holes left.
2859 done = 0;
2860 for (i = 1; i <= r && !done; i++) {
2861 int tx, ty;
2863 /* top and bottom parts of the ring */
2864 for (x = -i; x <= i && !done; x++) {
2865 tx = dock->x_pos + x*ICON_SIZE;
2866 y = -i;
2867 ty = dock->y_pos + y*ICON_SIZE;
2868 if (slot_map[XY2OFS(x,y)]==0
2869 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2870 *x_pos = x;
2871 *y_pos = y;
2872 done = 1;
2873 break;
2875 y = i;
2876 ty = dock->y_pos + y*ICON_SIZE;
2877 if (slot_map[XY2OFS(x,y)]==0
2878 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2879 *x_pos = x;
2880 *y_pos = y;
2881 done = 1;
2882 break;
2885 /* left and right parts of the ring */
2886 for (y = -i+1; y <= i-1; y++) {
2887 ty = dock->y_pos + y*ICON_SIZE;
2888 x = -i;
2889 tx = dock->x_pos + x*ICON_SIZE;
2890 if (slot_map[XY2OFS(x,y)]==0
2891 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2892 *x_pos = x;
2893 *y_pos = y;
2894 done = 1;
2895 break;
2897 x = i;
2898 tx = dock->x_pos + x*ICON_SIZE;
2899 if (slot_map[XY2OFS(x,y)]==0
2900 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2901 *x_pos = x;
2902 *y_pos = y;
2903 done = 1;
2904 break;
2908 wfree(slot_map);
2909 #undef XY2OFS
2910 return done;
2914 static void
2915 moveDock(WDock *dock, int new_x, int new_y)
2917 WAppIcon *btn;
2918 int i;
2920 dock->x_pos = new_x;
2921 dock->y_pos = new_y;
2922 for (i=0; i<dock->max_icons; i++) {
2923 btn = dock->icon_array[i];
2924 if (btn) {
2925 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2926 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2927 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2933 static void
2934 swapDock(WDock *dock)
2936 WScreen *scr = dock->screen_ptr;
2937 WAppIcon *btn;
2938 int x, i;
2941 if (dock->on_right_side) {
2942 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2943 } else {
2944 x = dock->x_pos = DOCK_EXTRA_SPACE;
2947 for (i=0; i<dock->max_icons; i++) {
2948 btn = dock->icon_array[i];
2949 if (btn) {
2950 btn->x_pos = x;
2951 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2955 wScreenUpdateUsableArea(scr);
2959 static pid_t
2960 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2962 WScreen *scr = btn->icon->core->screen_ptr;
2963 pid_t pid;
2964 char **argv;
2965 int argc;
2966 char *cmdline;
2968 cmdline = ExpandOptions(scr, command);
2970 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2971 if (cmdline)
2972 wfree(cmdline);
2973 if (state)
2974 wfree(state);
2975 return 0;
2978 wtokensplit(cmdline, &argv, &argc);
2980 if (!argc) {
2981 if (cmdline)
2982 wfree(cmdline);
2983 if (state)
2984 wfree(state);
2985 return 0;
2988 if ((pid=fork())==0) {
2989 char **args;
2990 int i;
2992 SetupEnvironment(scr);
2994 #ifdef HAVE_SETSID
2995 setsid();
2996 #endif
2998 args = malloc(sizeof(char*)*(argc+1));
2999 if (!args)
3000 exit(111);
3001 for (i=0; i<argc; i++) {
3002 args[i] = argv[i];
3004 args[argc] = NULL;
3005 execvp(argv[0], args);
3006 exit(111);
3008 wtokenfree(argv, argc);
3010 if (pid > 0) {
3011 if (!state) {
3012 state = wmalloc(sizeof(WSavedState));
3013 memset(state, 0, sizeof(WSavedState));
3014 state->hidden = -1;
3015 state->miniaturized = -1;
3016 state->shaded = -1;
3017 if (btn->dock==scr->dock || btn->omnipresent)
3018 state->workspace = -1;
3019 else
3020 state->workspace = scr->current_workspace;
3022 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
3023 state);
3024 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
3025 btn->dock);
3026 } else if (state) {
3027 wfree(state);
3029 wfree(cmdline);
3030 return pid;
3034 void
3035 wDockHideIcons(WDock *dock)
3037 int i;
3039 if (dock==NULL)
3040 return;
3042 for (i=1; i<dock->max_icons; i++) {
3043 if (dock->icon_array[i])
3044 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
3046 dock->mapped = 0;
3048 dockIconPaint(dock->icon_array[0]);
3052 void
3053 wDockShowIcons(WDock *dock)
3055 int i, newlevel;
3056 WAppIcon *btn;
3058 if (dock==NULL)
3059 return;
3061 btn = dock->icon_array[0];
3062 moveDock(dock, btn->x_pos, btn->y_pos);
3064 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
3065 ChangeStackingLevel(btn->icon->core, newlevel);
3067 for (i=1; i<dock->max_icons; i++) {
3068 if (dock->icon_array[i]) {
3069 MoveInStackListAbove(dock->icon_array[i]->icon->core,
3070 btn->icon->core);
3071 break;
3075 if (!dock->collapsed) {
3076 for (i=1; i<dock->max_icons; i++) {
3077 if (dock->icon_array[i]) {
3078 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
3082 dock->mapped = 1;
3084 dockIconPaint(btn);
3088 void
3089 wDockLower(WDock *dock)
3091 int i;
3093 for (i=0; i<dock->max_icons; i++) {
3094 if (dock->icon_array[i])
3095 wLowerFrame(dock->icon_array[i]->icon->core);
3100 void
3101 wDockRaise(WDock *dock)
3103 int i;
3105 for (i=dock->max_icons-1; i>=0; i--) {
3106 if (dock->icon_array[i])
3107 wRaiseFrame(dock->icon_array[i]->icon->core);
3112 void
3113 wDockRaiseLower(WDock *dock)
3115 if (!dock->icon_array[0]->icon->core->stacking->above
3116 ||(dock->icon_array[0]->icon->core->stacking->window_level
3117 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
3118 wDockLower(dock);
3119 else
3120 wDockRaise(dock);
3124 void
3125 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
3127 icon->launching = 0;
3128 icon->relaunching = 0;
3129 dockIconPaint(icon);
3133 WAppIcon*
3134 wDockFindIconForWindow(WDock *dock, Window window)
3136 WAppIcon *icon;
3137 int i;
3139 for (i=0; i<dock->max_icons; i++) {
3140 icon = dock->icon_array[i];
3141 if (icon && icon->main_window == window)
3142 return icon;
3144 return NULL;
3148 void
3149 wDockTrackWindowLaunch(WDock *dock, Window window)
3151 WAppIcon *icon;
3152 char *wm_class, *wm_instance;
3153 int i;
3154 Bool firstPass = True;
3155 Bool found = False;
3156 char *command = NULL;
3158 command = GetCommandForWindow(window);
3160 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
3161 (!wm_class && !wm_instance)) {
3163 if (command)
3164 wfree(command);
3165 return;
3168 retry:
3169 for (i=0; i<dock->max_icons; i++) {
3170 icon = dock->icon_array[i];
3171 if (!icon)
3172 continue;
3174 /* app is already attached to icon */
3175 if (icon->main_window == window) {
3176 found = True;
3177 break;
3180 if ((icon->wm_instance || icon->wm_class)
3181 && (icon->launching || !icon->running)) {
3183 if (icon->wm_instance && wm_instance &&
3184 strcmp(icon->wm_instance, wm_instance)!=0) {
3185 continue;
3187 if (icon->wm_class && wm_class &&
3188 strcmp(icon->wm_class, wm_class)!=0) {
3189 continue;
3191 if (firstPass && command && strcmp(icon->command, command)!=0) {
3192 continue;
3195 if (!icon->relaunching) {
3196 WApplication *wapp;
3198 /* Possibly an application that was docked with dockit,
3199 * but the user did not update WMState to indicate that
3200 * it was docked by force */
3201 wapp = wApplicationOf(window);
3202 if (!wapp) {
3203 icon->forced_dock = 1;
3204 icon->running = 0;
3206 if (!icon->forced_dock) {
3207 icon->main_window = window;
3210 found = True;
3211 if (!wPreferences.no_animations && !icon->launching &&
3212 !dock->screen_ptr->flags.startup && !dock->collapsed) {
3213 WAppIcon *aicon;
3214 int x0, y0;
3216 icon->launching = 1;
3217 dockIconPaint(icon);
3219 aicon = wAppIconCreateForDock(dock->screen_ptr, NULL,
3220 wm_instance, wm_class,
3221 TILE_NORMAL);
3222 /* XXX: can: aicon->icon == NULL ? */
3223 PlaceIcon(dock->screen_ptr, &x0, &y0, wGetHeadForWindow(aicon->icon->owner));
3224 wAppIconMove(aicon, x0, y0);
3225 /* Should this always be lowered? -Dan */
3226 if (dock->lowered)
3227 wLowerFrame(aicon->icon->core);
3228 XMapWindow(dpy, aicon->icon->core->window);
3229 aicon->launching = 1;
3230 wAppIconPaint(aicon);
3231 SlideWindow(aicon->icon->core->window, x0, y0,
3232 icon->x_pos, icon->y_pos);
3233 XUnmapWindow(dpy, aicon->icon->core->window);
3234 wAppIconDestroy(aicon);
3236 wDockFinishLaunch(dock, icon);
3237 break;
3241 if (firstPass && !found) {
3242 firstPass = False;
3243 goto retry;
3246 if (command)
3247 wfree(command);
3249 if (wm_class)
3250 XFree(wm_class);
3251 if (wm_instance)
3252 XFree(wm_instance);
3257 void
3258 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3260 if (!wPreferences.flags.noclip) {
3261 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3262 if (scr->current_workspace != workspace) {
3263 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3264 WAppIconChain *chain = scr->global_icons;
3266 while (chain) {
3267 moveIconBetweenDocks(chain->aicon->dock,
3268 scr->workspaces[workspace]->clip,
3269 chain->aicon, chain->aicon->xindex,
3270 chain->aicon->yindex);
3271 if (scr->workspaces[workspace]->clip->collapsed)
3272 XUnmapWindow(dpy, chain->aicon->icon->core->window);
3273 chain = chain->next;
3276 wDockHideIcons(old_clip);
3277 if (old_clip->auto_raise_lower) {
3278 if (old_clip->auto_raise_magic) {
3279 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3280 old_clip->auto_raise_magic = NULL;
3282 wDockLower(old_clip);
3284 if (old_clip->auto_collapse) {
3285 if (old_clip->auto_expand_magic) {
3286 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3287 old_clip->auto_expand_magic = NULL;
3289 old_clip->collapsed = 1;
3291 wDockShowIcons(scr->workspaces[workspace]->clip);
3293 if (scr->flags.clip_balloon_mapped)
3294 showClipBalloon(scr->clip_icon->dock, workspace);
3300 static void
3301 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3303 WAppIcon *icon;
3304 int i;
3306 for (i=0; i<dock->max_icons; i++) {
3307 icon = dock->icon_array[i];
3308 if (!icon)
3309 continue;
3311 if (icon->launching && icon->pid == pid) {
3312 if (!icon->relaunching) {
3313 icon->running = 0;
3314 icon->main_window = None;
3316 wDockFinishLaunch(dock, icon);
3317 icon->pid = 0;
3318 if (status==111) {
3319 char msg[PATH_MAX];
3320 char *cmd;
3322 #ifdef XDND
3323 if (icon->drop_launch)
3324 cmd = icon->dnd_command;
3325 else
3326 #endif
3327 if (icon->paste_launch)
3328 cmd = icon->paste_command;
3329 else
3330 cmd = icon->command;
3332 snprintf(msg, sizeof(msg),
3333 _("Could not execute command \"%s\""), cmd);
3335 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3336 _("OK"), NULL, NULL);
3338 break;
3344 static void
3345 toggleLowered(WDock *dock)
3347 WAppIcon *tmp;
3348 int newlevel, i;
3350 /* lower/raise Dock */
3351 if (!dock->lowered) {
3352 newlevel = WMNormalLevel;
3353 dock->lowered = 1;
3354 } else {
3355 newlevel = WMDockLevel;
3356 dock->lowered = 0;
3359 for (i=0; i<dock->max_icons; i++) {
3360 tmp = dock->icon_array[i];
3361 if (!tmp)
3362 continue;
3364 ChangeStackingLevel(tmp->icon->core, newlevel);
3365 if (dock->lowered)
3366 wLowerFrame(tmp->icon->core);
3369 if (dock->type == WM_DOCK)
3370 wScreenUpdateUsableArea(dock->screen_ptr);
3374 static void
3375 toggleCollapsed(WDock *dock)
3377 if (dock->collapsed) {
3378 dock->collapsed = 0;
3379 wDockShowIcons(dock);
3381 else {
3382 dock->collapsed = 1;
3383 wDockHideIcons(dock);
3388 static void
3389 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3391 WScreen *scr = dock->screen_ptr;
3392 WObjDescriptor *desc;
3393 WMenuEntry *entry;
3394 WApplication *wapp = NULL;
3395 int index = 0;
3396 int x_pos;
3397 int n_selected;
3398 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3400 if (dock->type == WM_DOCK) {
3401 /* keep on top */
3402 entry = dock->menu->entries[index];
3403 entry->flags.indicator_on = !dock->lowered;
3404 entry->clientdata = dock;
3405 dock->menu->flags.realized = 0;
3406 } else {
3407 /* clip options */
3408 if (scr->clip_options)
3409 updateClipOptionsMenu(scr->clip_options, dock);
3411 n_selected = numberOfSelectedIcons(dock);
3413 /* Rename Workspace */
3414 entry = dock->menu->entries[++index];
3415 if (aicon == scr->clip_icon) {
3416 entry->callback = renameCallback;
3417 entry->clientdata = dock;
3418 entry->flags.indicator = 0;
3419 entry->text = _("Rename Workspace");
3420 } else {
3421 entry->callback = omnipresentCallback;
3422 entry->clientdata = aicon;
3423 if (n_selected > 0) {
3424 entry->flags.indicator = 0;
3425 entry->text = _("Toggle Omnipresent");
3426 } else {
3427 entry->flags.indicator = 1;
3428 entry->flags.indicator_on = aicon->omnipresent;
3429 entry->flags.indicator_type = MI_CHECK;
3430 entry->text = _("Omnipresent");
3434 /* select/unselect icon */
3435 entry = dock->menu->entries[++index];
3436 entry->clientdata = aicon;
3437 entry->flags.indicator_on = aicon->icon->selected;
3438 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3440 /* select/unselect all icons */
3441 entry = dock->menu->entries[++index];
3442 entry->clientdata = aicon;
3443 if (n_selected > 0)
3444 entry->text = _("Unselect All Icons");
3445 else
3446 entry->text = _("Select All Icons");
3447 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3449 /* keep icon(s) */
3450 entry = dock->menu->entries[++index];
3451 entry->clientdata = aicon;
3452 if (n_selected > 1)
3453 entry->text = _("Keep Icons");
3454 else
3455 entry->text = _("Keep Icon");
3456 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3458 /* this is the workspace submenu part */
3459 entry = dock->menu->entries[++index];
3460 if (n_selected > 1)
3461 entry->text = _("Move Icons To");
3462 else
3463 entry->text = _("Move Icon To");
3464 if (scr->clip_submenu)
3465 updateWorkspaceMenu(scr->clip_submenu, aicon);
3466 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3468 /* remove icon(s) */
3469 entry = dock->menu->entries[++index];
3470 entry->clientdata = aicon;
3471 if (n_selected > 1)
3472 entry->text = _("Remove Icons");
3473 else
3474 entry->text = _("Remove Icon");
3475 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3477 /* attract icon(s) */
3478 entry = dock->menu->entries[++index];
3479 entry->clientdata = aicon;
3481 dock->menu->flags.realized = 0;
3482 wMenuRealize(dock->menu);
3486 if (aicon->icon->owner) {
3487 wapp = wApplicationOf(aicon->icon->owner->main_window);
3488 } else {
3489 wapp = NULL;
3492 /* launch */
3493 entry = dock->menu->entries[++index];
3494 entry->clientdata = aicon;
3495 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3497 /* unhide here */
3498 entry = dock->menu->entries[++index];
3499 entry->clientdata = aicon;
3500 if (wapp && wapp->flags.hidden) {
3501 entry->text = _("Unhide Here");
3502 } else {
3503 entry->text = _("Bring Here");
3505 wMenuSetEnabled(dock->menu, index, appIsRunning);
3507 /* hide */
3508 entry = dock->menu->entries[++index];
3509 entry->clientdata = aicon;
3510 if (wapp && wapp->flags.hidden) {
3511 entry->text = _("Unhide");
3512 } else {
3513 entry->text = _("Hide");
3515 wMenuSetEnabled(dock->menu, index, appIsRunning);
3517 /* settings */
3518 entry = dock->menu->entries[++index];
3519 entry->clientdata = aicon;
3520 wMenuSetEnabled(dock->menu, index, !aicon->editing
3521 && !wPreferences.flags.noupdates);
3523 /* kill */
3524 entry = dock->menu->entries[++index];
3525 entry->clientdata = aicon;
3526 wMenuSetEnabled(dock->menu, index, appIsRunning);
3528 if (!dock->menu->flags.realized)
3529 wMenuRealize(dock->menu);
3531 if (dock->type == WM_CLIP) {
3532 /*x_pos = event->xbutton.x_root+2;*/
3533 x_pos = event->xbutton.x_root - dock->menu->frame->core->width/2 - 1;
3534 if (x_pos < 0) {
3535 x_pos = 0;
3536 } else if (x_pos + dock->menu->frame->core->width > scr->scr_width-2) {
3537 x_pos = scr->scr_width - dock->menu->frame->core->width - 4;
3539 } else {
3540 x_pos = dock->on_right_side ?
3541 scr->scr_width - dock->menu->frame->core->width - 3 : 0;
3544 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3546 /* allow drag select */
3547 event->xany.send_event = True;
3548 desc = &dock->menu->menu->descriptor;
3549 (*desc->handle_mousedown)(desc, event);
3553 /******************************************************************/
3554 static void
3555 iconDblClick(WObjDescriptor *desc, XEvent *event)
3557 WAppIcon *btn = desc->parent;
3558 WDock *dock = btn->dock;
3559 WApplication *wapp = NULL;
3560 int unhideHere = 0;
3562 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3563 wapp = wApplicationOf(btn->icon->owner->main_window);
3565 assert(wapp!=NULL);
3567 unhideHere = (event->xbutton.state & ShiftMask);
3569 /* go to the last workspace that the user worked on the app */
3570 if (wapp->last_workspace != dock->screen_ptr->current_workspace
3571 && !unhideHere) {
3572 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3575 wUnhideApplication(wapp, event->xbutton.button==Button2, 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, False);
3596 } else if (btn->xindex==0 && btn->yindex==0 &&
3597 btn->dock->type==WM_DOCK) {
3598 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 x = ev.xmotion.x_root - ofs_x;
3656 y = ev.xmotion.y_root - ofs_y;
3657 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3659 moveDock(dock, x, y);
3660 } else {
3661 /* move vertically if pointer is inside the dock*/
3662 if ((dock->on_right_side &&
3663 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3664 || (!dock->on_right_side &&
3665 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3667 x = ev.xmotion.x_root - ofs_x;
3668 y = ev.xmotion.y_root - ofs_y;
3669 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3670 moveDock(dock, dock->x_pos, y);
3672 /* move horizontally to change sides */
3673 x = ev.xmotion.x_root - ofs_x;
3674 if (!dock->on_right_side) {
3676 /* is on left */
3678 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3679 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3680 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3681 if (superfluous && ghost==None) {
3682 ghost = MakeGhostDock(dock, dock->x_pos,
3683 scr->scr_width-ICON_SIZE
3684 -DOCK_EXTRA_SPACE-1,
3685 dock->y_pos);
3686 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3687 ghost);
3688 XClearWindow(dpy, scr->dock_shadow);
3690 XMapRaised(dpy, scr->dock_shadow);
3691 swapped = 1;
3692 } else {
3693 if (superfluous && ghost!=None) {
3694 XFreePixmap(dpy, ghost);
3695 ghost = None;
3697 XUnmapWindow(dpy, scr->dock_shadow);
3698 swapped = 0;
3700 } else {
3701 /* is on right */
3702 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3703 XMoveWindow(dpy, scr->dock_shadow,
3704 DOCK_EXTRA_SPACE, dock->y_pos);
3705 if (superfluous && ghost==None) {
3706 ghost = MakeGhostDock(dock, dock->x_pos,
3707 DOCK_EXTRA_SPACE, dock->y_pos);
3708 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3709 ghost);
3710 XClearWindow(dpy, scr->dock_shadow);
3712 XMapRaised(dpy, scr->dock_shadow);
3713 swapped = -1;
3714 } else {
3715 XUnmapWindow(dpy, scr->dock_shadow);
3716 swapped = 0;
3717 if (superfluous && ghost!=None) {
3718 XFreePixmap(dpy, ghost);
3719 ghost = None;
3724 break;
3726 case ButtonPress:
3727 break;
3729 case ButtonRelease:
3730 if (ev.xbutton.button != event->xbutton.button)
3731 break;
3732 XUngrabPointer(dpy, CurrentTime);
3733 XUnmapWindow(dpy, scr->dock_shadow);
3734 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3735 if (dock->type == WM_DOCK) {
3736 if (swapped!=0) {
3737 if (swapped>0)
3738 dock->on_right_side = 1;
3739 else
3740 dock->on_right_side = 0;
3741 swapDock(dock);
3742 wArrangeIcons(scr, False);
3745 done = 1;
3746 break;
3749 if (superfluous) {
3750 if (ghost!=None)
3751 XFreePixmap(dpy, ghost);
3752 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3754 #ifdef DEBUG
3755 puts("End dock move");
3756 #endif
3761 static void
3762 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3764 WScreen *scr = dock->screen_ptr;
3765 Window wins[2];
3766 WIcon *icon = aicon->icon;
3767 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3768 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3769 XEvent ev;
3770 int x = aicon->x_pos, y = aicon->y_pos;
3771 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3772 int shad_x = x, shad_y = y;
3773 int ix = aicon->xindex, iy = aicon->yindex;
3774 int tmp;
3775 Pixmap ghost = None;
3776 Bool docked;
3777 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3778 int omnipresent = aicon->omnipresent; /* this must be cached!!! */
3781 if (wPreferences.flags.noupdates)
3782 return;
3784 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3785 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3786 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3787 #ifdef DEBUG0
3788 wwarning("pointer grab failed for icon move");
3789 #endif
3792 if (!(event->xbutton.state & MOD_MASK))
3793 wRaiseFrame(icon->core);
3795 if (!wPreferences.flags.noclip)
3796 clip = scr->workspaces[scr->current_workspace]->clip;
3798 if (dock == scr->dock && !wPreferences.flags.noclip)
3799 dock2 = clip;
3800 else if (dock != scr->dock && !wPreferences.flags.nodock)
3801 dock2 = scr->dock;
3803 wins[0] = icon->core->window;
3804 wins[1] = scr->dock_shadow;
3805 XRestackWindows(dpy, wins, 2);
3806 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3807 ICON_SIZE, ICON_SIZE);
3808 if (superfluous) {
3809 if (icon->pixmap!=None)
3810 ghost = MakeGhostIcon(scr, icon->pixmap);
3811 else
3812 ghost = MakeGhostIcon(scr, icon->core->window);
3814 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3815 XClearWindow(dpy, scr->dock_shadow);
3817 XMapWindow(dpy, scr->dock_shadow);
3819 ondock = 1;
3822 while(1) {
3823 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3824 |ButtonMotionMask|ExposureMask, &ev);
3825 switch (ev.type) {
3826 case Expose:
3827 WMHandleEvent(&ev);
3828 break;
3830 case MotionNotify:
3831 if (!grabbed) {
3832 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3833 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3834 XChangeActivePointerGrab(dpy, ButtonMotionMask
3835 |ButtonReleaseMask|ButtonPressMask,
3836 wCursor[WCUR_MOVE], CurrentTime);
3837 grabbed=1;
3838 } else {
3839 break;
3843 if (omnipresent) {
3844 int i;
3845 for (i=0; i<scr->workspace_count; i++) {
3846 if (i == scr->current_workspace)
3847 continue;
3848 wDockShowIcons(scr->workspaces[i]->clip);
3852 x = ev.xmotion.x_root - ofs_x;
3853 y = ev.xmotion.y_root - ofs_y;
3854 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3855 if (tmp && dock2) {
3856 change_dock = 0;
3857 if (last_dock != dock && collapsed) {
3858 last_dock->collapsed = 1;
3859 wDockHideIcons(last_dock);
3860 collapsed = 0;
3862 if (!collapsed && (collapsed = dock->collapsed)) {
3863 dock->collapsed = 0;
3864 wDockShowIcons(dock);
3866 if (dock->auto_raise_lower)
3867 wDockRaise(dock);
3868 last_dock = dock;
3869 } else if (dock2) {
3870 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3871 if (tmp) {
3872 change_dock = 1;
3873 if (last_dock != dock2 && collapsed) {
3874 last_dock->collapsed = 1;
3875 wDockHideIcons(last_dock);
3876 collapsed = 0;
3878 if (!collapsed && (collapsed = dock2->collapsed)) {
3879 dock2->collapsed = 0;
3880 wDockShowIcons(dock2);
3882 if (dock2->auto_raise_lower)
3883 wDockRaise(dock2);
3884 last_dock = dock2;
3887 if (aicon->launching
3888 || aicon->lock
3889 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3890 || (!aicon->running && tmp)) {
3891 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3892 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3894 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3896 if (!ondock) {
3897 XMapWindow(dpy, scr->dock_shadow);
3899 ondock = 1;
3900 } else {
3901 if (ondock) {
3902 XUnmapWindow(dpy, scr->dock_shadow);
3904 ondock = 0;
3906 XMoveWindow(dpy, icon->core->window, x, y);
3907 break;
3909 case ButtonPress:
3910 break;
3912 case ButtonRelease:
3913 if (ev.xbutton.button != event->xbutton.button)
3914 break;
3915 XUngrabPointer(dpy, CurrentTime);
3916 if (ondock) {
3917 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3918 XUnmapWindow(dpy, scr->dock_shadow);
3919 if (!change_dock) {
3920 reattachIcon(dock, aicon, ix, iy);
3921 if (clip && dock!=clip && clip->auto_raise_lower)
3922 wDockLower(clip);
3923 } else {
3924 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3925 if (!docked) {
3926 /* Slide it back if dock rejected it */
3927 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3928 aicon->y_pos);
3929 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3931 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3932 collapsed = 0;
3935 } else {
3936 aicon->x_pos = x;
3937 aicon->y_pos = y;
3938 if (superfluous) {
3939 if (!aicon->running && !wPreferences.no_animations) {
3940 /* We need to deselect it, even if is deselected in
3941 * wDockDetach(), because else DoKaboom() will fail.
3943 if (aicon->icon->selected)
3944 wIconSelect(aicon->icon);
3946 wSoundPlay(WSOUND_KABOOM);
3947 DoKaboom(scr,aicon->icon->core->window, x, y);
3948 } else {
3949 wSoundPlay(WSOUND_UNDOCK);
3951 } else {
3952 wSoundPlay(WSOUND_UNDOCK);
3954 if (clip && clip->auto_raise_lower)
3955 wDockLower(clip);
3956 wDockDetach(dock, aicon);
3958 if (collapsed) {
3959 last_dock->collapsed = 1;
3960 wDockHideIcons(last_dock);
3961 collapsed = 0;
3963 if (superfluous) {
3964 if (ghost!=None)
3965 XFreePixmap(dpy, ghost);
3966 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3968 if (omnipresent) {
3969 int i;
3970 for (i=0; i<scr->workspace_count; i++) {
3971 if (i == scr->current_workspace)
3972 continue;
3973 wDockHideIcons(scr->workspaces[i]->clip);
3977 #ifdef DEBUG
3978 puts("End icon move");
3979 #endif
3980 return;
3986 static int
3987 getClipButton(int px, int py)
3989 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
3991 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
3992 return CLIP_IDLE;
3994 if (py <= pt-((int)ICON_SIZE-1-px))
3995 return CLIP_FORWARD;
3996 else if (px <= pt-((int)ICON_SIZE-1-py))
3997 return CLIP_REWIND;
3999 return CLIP_IDLE;
4003 static void
4004 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
4006 XEvent ev;
4007 int done, direction, new_ws;
4008 int new_dir;
4009 WDock *clip = scr->clip_icon->dock;
4011 direction = getClipButton(event->xbutton.x, event->xbutton.y);
4013 clip->lclip_button_pushed = direction==CLIP_REWIND;
4014 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4016 wClipIconPaint(scr->clip_icon);
4017 done = 0;
4018 while(!done) {
4019 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
4020 |ButtonPressMask, &ev);
4021 switch (ev.type) {
4022 case Expose:
4023 WMHandleEvent(&ev);
4024 break;
4026 case MotionNotify:
4027 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
4028 if (new_dir != direction) {
4029 direction = new_dir;
4030 clip->lclip_button_pushed = direction==CLIP_REWIND;
4031 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4032 wClipIconPaint(scr->clip_icon);
4034 break;
4036 case ButtonPress:
4037 break;
4039 case ButtonRelease:
4040 if (ev.xbutton.button == event->xbutton.button)
4041 done = 1;
4045 clip->lclip_button_pushed = 0;
4046 clip->rclip_button_pushed = 0;
4048 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
4050 if (direction == CLIP_FORWARD) {
4051 if (scr->current_workspace < scr->workspace_count-1)
4052 wWorkspaceChange(scr, scr->current_workspace+1);
4053 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
4054 wWorkspaceChange(scr, scr->current_workspace+1);
4055 else if (wPreferences.ws_cycle)
4056 wWorkspaceChange(scr, 0);
4058 else if (direction == CLIP_REWIND) {
4059 if (scr->current_workspace > 0)
4060 wWorkspaceChange(scr, scr->current_workspace-1);
4061 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
4062 wWorkspaceChange(scr, scr->workspace_count-1);
4065 wClipIconPaint(scr->clip_icon);
4069 static void
4070 iconMouseDown(WObjDescriptor *desc, XEvent *event)
4072 WAppIcon *aicon = desc->parent;
4073 WDock *dock = aicon->dock;
4074 WScreen *scr = aicon->icon->core->screen_ptr;
4076 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
4077 return;
4079 scr->last_dock = dock;
4081 if (dock->menu->flags.mapped)
4082 wMenuUnmap(dock->menu);
4084 if (IsDoubleClick(scr, event)) {
4085 /* double-click was not in the main clip icon */
4086 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
4087 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
4088 iconDblClick(desc, event);
4089 return;
4093 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
4094 XUnmapWindow(dpy, scr->clip_balloon);
4095 scr->flags.clip_balloon_mapped = 0;
4098 #ifdef DEBUG
4099 puts("handling dock");
4100 #endif
4101 if (event->xbutton.button == Button1) {
4102 if (event->xbutton.state & MOD_MASK)
4103 wDockLower(dock);
4104 else
4105 wDockRaise(dock);
4107 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
4108 dock->type!=WM_DOCK) {
4109 wIconSelect(aicon->icon);
4110 return;
4113 if (aicon->yindex==0 && aicon->xindex==0) {
4114 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
4115 && dock->type==WM_CLIP)
4116 handleClipChangeWorkspace(scr, event);
4117 else
4118 handleDockMove(dock, aicon, event);
4119 } else
4120 handleIconMove(dock, aicon, event);
4122 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4123 aicon==scr->clip_icon) {
4124 if (!scr->clip_ws_menu) {
4125 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
4127 if (scr->clip_ws_menu) {
4128 WMenu *wsMenu = scr->clip_ws_menu;
4129 int xpos;
4131 wWorkspaceMenuUpdate(scr, wsMenu);
4133 xpos = event->xbutton.x_root - wsMenu->frame->core->width/2 - 1;
4134 if (xpos < 0) {
4135 xpos = 0;
4136 } else if (xpos + wsMenu->frame->core->width > scr->scr_width-2) {
4137 xpos = scr->scr_width - wsMenu->frame->core->width - 4;
4139 wMenuMapAt(wsMenu, xpos, event->xbutton.y_root+2, False);
4141 desc = &wsMenu->menu->descriptor;
4142 event->xany.send_event = True;
4143 (*desc->handle_mousedown)(desc, event);
4145 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4146 (event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon) {
4147 wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
4148 } else if (event->xbutton.button == Button3) {
4149 if (event->xbutton.send_event &&
4150 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
4151 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
4152 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
4153 wwarning("pointer grab failed for dockicon menu");
4154 return;
4157 openDockMenu(dock, aicon, event);
4158 } else if (event->xbutton.button == Button2) {
4159 WAppIcon *btn = desc->parent;
4161 if (!btn->launching &&
4162 (!btn->running || (event->xbutton.state & ControlMask))) {
4163 launchDockedApplication(btn, True);
4169 static void
4170 showClipBalloon(WDock *dock, int workspace)
4172 int w, h;
4173 int x, y;
4174 WScreen *scr = dock->screen_ptr;
4175 char *text;
4176 Window stack[2];
4178 scr->flags.clip_balloon_mapped = 1;
4179 XMapWindow(dpy, scr->clip_balloon);
4181 text = scr->workspaces[workspace]->name;
4183 w = WMWidthOfString(scr->clip_title_font, text, strlen(text));
4185 h = WMFontHeight(scr->clip_title_font);
4186 XResizeWindow(dpy, scr->clip_balloon, w, h);
4188 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
4189 y = dock->y_pos + ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
4191 if (x+w > scr->scr_width) {
4192 x = scr->scr_width - w;
4193 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
4194 y = dock->y_pos - h - 1;
4195 else
4196 y = dock->y_pos + ICON_SIZE;
4197 XRaiseWindow(dpy, scr->clip_balloon);
4198 } else {
4199 stack[0] = scr->clip_icon->icon->core->window;
4200 stack[1] = scr->clip_balloon;
4201 XRestackWindows(dpy, stack, 2);
4203 XMoveWindow(dpy, scr->clip_balloon, x, y);
4204 XClearWindow(dpy, scr->clip_balloon);
4205 WMDrawString(scr->wmscreen, scr->clip_balloon,
4206 scr->clip_title_color[CLIP_NORMAL],
4207 scr->clip_title_font,
4208 0, 0, text, strlen(text));
4212 static void
4213 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
4215 WAppIcon *btn = (WAppIcon*)desc->parent;
4216 WDock *dock;
4217 WScreen *scr;
4219 assert(event->type==EnterNotify);
4221 if(desc->parent_type!=WCLASS_DOCK_ICON)
4222 return;
4224 scr = btn->icon->core->screen_ptr;
4225 if (!btn->omnipresent)
4226 dock = btn->dock;
4227 else
4228 dock = scr->workspaces[scr->current_workspace]->clip;
4230 if (!dock || dock->type!=WM_CLIP)
4231 return;
4233 /* The auto raise/lower code */
4234 if (dock->auto_lower_magic) {
4235 WMDeleteTimerHandler(dock->auto_lower_magic);
4236 dock->auto_lower_magic = NULL;
4238 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
4239 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
4240 clipAutoRaise,
4241 (void *)dock);
4244 /* The auto expand/collapse code */
4245 if (dock->auto_collapse_magic) {
4246 WMDeleteTimerHandler(dock->auto_collapse_magic);
4247 dock->auto_collapse_magic = NULL;
4249 if (dock->auto_collapse && !dock->auto_expand_magic) {
4250 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
4251 clipAutoExpand,
4252 (void *)dock);
4255 if (btn->xindex == 0 && btn->yindex == 0)
4256 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4257 else {
4258 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4259 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
4260 dock->screen_ptr->flags.clip_balloon_mapped = 0;
4266 static void
4267 clipLeave(WDock *dock)
4269 XEvent event;
4270 WObjDescriptor *desc = NULL;
4272 if (!dock || dock->type!=WM_CLIP)
4273 return;
4275 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
4276 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
4277 (XPointer *)&desc)!=XCNOENT
4278 && desc && desc->parent_type==WCLASS_DOCK_ICON
4279 && ((WAppIcon*)desc->parent)->dock
4280 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
4281 /* We didn't left the Clip yet */
4282 XPutBackEvent(dpy, &event);
4283 return;
4286 XPutBackEvent(dpy, &event);
4287 } else {
4288 /* We entered a withdrawn window, so we're still in Clip */
4289 return;
4292 if (dock->auto_raise_magic) {
4293 WMDeleteTimerHandler(dock->auto_raise_magic);
4294 dock->auto_raise_magic = NULL;
4296 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
4297 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
4298 clipAutoLower,
4299 (void *)dock);
4302 if (dock->auto_expand_magic) {
4303 WMDeleteTimerHandler(dock->auto_expand_magic);
4304 dock->auto_expand_magic = NULL;
4306 if (dock->auto_collapse && !dock->auto_collapse_magic) {
4307 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
4308 clipAutoCollapse,
4309 (void *)dock);
4314 static void
4315 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
4317 WAppIcon *btn = (WAppIcon*)desc->parent;
4319 assert(event->type==LeaveNotify);
4321 if(desc->parent_type!=WCLASS_DOCK_ICON)
4322 return;
4324 clipLeave(btn->dock);
4328 static void
4329 clipAutoCollapse(void *cdata)
4331 WDock *dock = (WDock *)cdata;
4333 if (dock->type!=WM_CLIP)
4334 return;
4336 if (dock->auto_collapse) {
4337 dock->collapsed = 1;
4338 wDockHideIcons(dock);
4340 dock->auto_collapse_magic = NULL;
4344 static void
4345 clipAutoExpand(void *cdata)
4347 WDock *dock = (WDock *)cdata;
4349 if (dock->type!=WM_CLIP)
4350 return;
4352 if (dock->auto_collapse) {
4353 dock->collapsed = 0;
4354 wDockShowIcons(dock);
4356 dock->auto_expand_magic = NULL;
4360 static void
4361 clipAutoLower(void *cdata)
4363 WDock *dock = (WDock *)cdata;
4365 if (dock->type!=WM_CLIP)
4366 return;
4368 if (dock->auto_raise_lower)
4369 wDockLower(dock);
4371 dock->auto_lower_magic = NULL;
4375 static void
4376 clipAutoRaise(void *cdata)
4378 WDock *dock = (WDock *)cdata;
4380 if (dock->type!=WM_CLIP)
4381 return;
4383 if (dock->auto_raise_lower)
4384 wDockRaise(dock);
4386 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4387 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4390 dock->auto_raise_magic = NULL;
4394 static Bool
4395 iconCanBeOmnipresent(WAppIcon *aicon)
4397 WScreen *scr = aicon->icon->core->screen_ptr;
4398 WDock *clip;
4399 WAppIcon *btn;
4400 int i, j;
4402 for (i=0; i<scr->workspace_count; i++) {
4403 clip = scr->workspaces[i]->clip;
4405 if (clip == aicon->dock)
4406 continue;
4408 if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
4409 return False; /* Clip is full in some workspace */
4411 for (j=0; j<clip->max_icons; j++) {
4412 btn = clip->icon_array[j];
4413 if(btn && btn->xindex==aicon->xindex && btn->yindex==aicon->yindex)
4414 return False;
4418 return True;
4423 wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
4425 WScreen *scr = aicon->icon->core->screen_ptr;
4426 WAppIconChain *new_entry, *tmp, *tmp1;
4427 int status = WO_SUCCESS;
4429 if ((scr->dock && aicon->dock==scr->dock) || aicon==scr->clip_icon) {
4430 return WO_NOT_APPLICABLE;
4433 if (aicon->omnipresent == omnipresent)
4434 return WO_SUCCESS;
4436 if (omnipresent) {
4437 if (iconCanBeOmnipresent(aicon)) {
4438 aicon->omnipresent = 1;
4439 new_entry = wmalloc(sizeof(WAppIconChain));
4440 new_entry->aicon = aicon;
4441 new_entry->next = scr->global_icons;
4442 scr->global_icons = new_entry;
4443 scr->global_icon_count++;
4444 } else {
4445 aicon->omnipresent = 0;
4446 status = WO_FAILED;
4448 } else {
4449 aicon->omnipresent = 0;
4450 if (aicon == scr->global_icons->aicon) {
4451 tmp = scr->global_icons->next;
4452 wfree(scr->global_icons);
4453 scr->global_icons = tmp;
4454 scr->global_icon_count--;
4455 } else {
4456 tmp = scr->global_icons;
4457 while (tmp->next) {
4458 if (tmp->next->aicon == aicon) {
4459 tmp1 = tmp->next->next;
4460 wfree(tmp->next);
4461 tmp->next = tmp1;
4462 scr->global_icon_count--;
4463 break;
4465 tmp = tmp->next;
4470 wAppIconPaint(aicon);
4472 return status;