- Fixed crashing bug in menu.c
[wmaker-crm.git] / src / dock.c
blobd142febef83f088563956daf4d28de9ca963125a
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;
84 #ifdef OFFIX_DND
85 extern Atom _XA_DND_PROTOCOL;
86 #endif
89 #define MOD_MASK wPreferences.modifier_mask
91 extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);
93 #define ICON_SIZE wPreferences.icon_size
96 /***** Local variables ****/
98 static WMPropList *dCommand=NULL;
99 static WMPropList *dPasteCommand=NULL;
100 #ifdef OFFIX_DND
101 static WMPropList *dDropCommand=NULL;
102 #endif
103 static WMPropList *dAutoLaunch, *dLock;
104 static WMPropList *dName, *dForced, *dBuggyApplication, *dYes, *dNo;
105 static WMPropList *dHost, *dDock, *dClip;
106 static WMPropList *dAutoAttractIcons;
108 static WMPropList *dPosition, *dApplications, *dLowered, *dCollapsed;
110 static WMPropList *dAutoCollapse, *dAutoRaiseLower, *dOmnipresent;
112 static void dockIconPaint(WAppIcon *btn);
114 static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
116 static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state);
118 static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);
120 static int getClipButton(int px, int py);
122 static void toggleLowered(WDock *dock);
124 static void toggleCollapsed(WDock *dock);
126 static void clipIconExpose(WObjDescriptor *desc, XEvent *event);
128 static void clipLeave(WDock *dock);
130 static void handleClipChangeWorkspace(WScreen *scr, XEvent *event);
132 Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y);
134 static void clipEnterNotify(WObjDescriptor *desc, XEvent *event);
135 static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event);
136 static void clipAutoCollapse(void *cdata);
137 static void clipAutoExpand(void *cdata);
138 static void launchDockedApplication(WAppIcon *btn, Bool withSelection);
140 static void clipAutoLower(void *cdata);
141 static void clipAutoRaise(void *cdata);
143 static void showClipBalloon(WDock *dock, int workspace);
145 #ifdef OFFIX_DND
147 #define DndNotDnd -1
148 #define DndUnknown 0
149 #define DndRawData 1
150 #define DndFile 2
151 #define DndFiles 3
152 #define DndText 4
153 #define DndDir 5
154 #define DndLink 6
155 #define DndExe 7
157 #define DndEND 8
159 #endif /* OFFIX_DND */
163 static void
164 make_keys()
166 if (dCommand!=NULL)
167 return;
169 dCommand = WMRetainPropList(WMCreatePLString("Command"));
170 dPasteCommand = WMRetainPropList(WMCreatePLString("PasteCommand"));
171 dDropCommand = WMRetainPropList(WMCreatePLString("DropCommand"));
172 dLock = WMRetainPropList(WMCreatePLString("Lock"));
173 dAutoLaunch = WMRetainPropList(WMCreatePLString("AutoLaunch"));
174 dName = WMRetainPropList(WMCreatePLString("Name"));
175 dForced = WMRetainPropList(WMCreatePLString("Forced"));
176 dBuggyApplication = WMRetainPropList(WMCreatePLString("BuggyApplication"));
177 dYes = WMRetainPropList(WMCreatePLString("Yes"));
178 dNo = WMRetainPropList(WMCreatePLString("No"));
179 dHost = WMRetainPropList(WMCreatePLString("Host"));
181 dPosition = WMCreatePLString("Position");
182 dApplications = WMCreatePLString("Applications");
183 dLowered = WMCreatePLString("Lowered");
184 dCollapsed = WMCreatePLString("Collapsed");
185 dAutoCollapse = WMCreatePLString("AutoCollapse");
186 dAutoRaiseLower = WMCreatePLString("AutoRaiseLower");
187 dAutoAttractIcons = WMCreatePLString("AutoAttractIcons");
189 dOmnipresent = WMCreatePLString("Omnipresent");
191 dDock = WMCreatePLString("Dock");
192 dClip = WMCreatePLString("Clip");
197 static void
198 renameCallback(WMenu *menu, WMenuEntry *entry)
200 WDock *dock = entry->clientdata;
201 char buffer[128];
202 int wspace;
203 char *name;
205 assert(entry->clientdata!=NULL);
207 wspace = dock->screen_ptr->current_workspace;
209 name = wstrdup(dock->screen_ptr->workspaces[wspace]->name);
211 snprintf(buffer, sizeof(buffer), _("Type the name for workspace %i:"), wspace+1);
212 if (wInputDialog(dock->screen_ptr, _("Rename Workspace"), buffer,
213 &name)) {
214 wWorkspaceRename(dock->screen_ptr, wspace, name);
216 if (name) {
217 wfree(name);
222 static void
223 toggleLoweredCallback(WMenu *menu, WMenuEntry *entry)
225 assert(entry->clientdata!=NULL);
227 toggleLowered(entry->clientdata);
229 entry->flags.indicator_on = !(((WDock*)entry->clientdata)->lowered);
231 wMenuPaint(menu);
235 static int
236 matchWindow(void *item, void *cdata)
238 return (((WFakeGroupLeader*)item)->leader == (Window)cdata);
242 static void
243 killCallback(WMenu *menu, WMenuEntry *entry)
245 WScreen *scr = menu->menu->screen_ptr;
246 WAppIcon *icon;
247 WFakeGroupLeader *fPtr;
248 char *buffer;
250 if (!WCHECK_STATE(WSTATE_NORMAL))
251 return;
253 assert(entry->clientdata!=NULL);
255 icon = (WAppIcon*)entry->clientdata;
257 icon->editing = 1;
259 WCHANGE_STATE(WSTATE_MODAL);
261 buffer = wstrconcat(icon->wm_class,
262 _(" will be forcibly closed.\n"
263 "Any unsaved changes will be lost.\n"
264 "Please confirm."));
266 if (icon->icon && icon->icon->owner) {
267 fPtr = icon->icon->owner->fake_group;
268 } else {
269 /* is this really necessary? can we kill a dock icon not running? */
270 Window win = icon->main_window;
271 int index;
273 index = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void*)win);
274 if (index != WANotFound)
275 fPtr = WMGetFromArray(scr->fakeGroupLeaders, index);
276 else
277 fPtr = NULL;
280 if (wPreferences.dont_confirm_kill
281 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
282 buffer, _("Yes"), _("No"), NULL)==WAPRDefault) {
283 if (fPtr!=NULL) {
284 WWindow *wwin, *twin;
286 wwin = scr->focused_window;
287 while (wwin) {
288 twin = wwin->prev;
289 if (wwin->fake_group == fPtr) {
290 wClientKill(wwin);
292 wwin = twin;
294 } else if (icon->icon && icon->icon->owner) {
295 wClientKill(icon->icon->owner);
299 wfree(buffer);
301 icon->editing = 0;
303 WCHANGE_STATE(WSTATE_NORMAL);
307 /* TODO: replace this function with a member of the dock struct */
308 static int
309 numberOfSelectedIcons(WDock *dock)
311 WAppIcon *aicon;
312 int i, n;
314 n = 0;
315 for (i=1; i<dock->max_icons; i++) {
316 aicon = dock->icon_array[i];
317 if (aicon && aicon->icon->selected) {
318 n++;
322 return n;
326 static WMArray*
327 getSelected(WDock *dock)
329 WMArray *ret = WMCreateArray(8);
330 WAppIcon *btn;
331 int i;
333 for (i=1; i<dock->max_icons; i++) {
334 btn = dock->icon_array[i];
335 if (btn && btn->icon->selected) {
336 WMAddToArray(ret, btn);
340 return ret;
344 static void
345 paintClipButtons(WAppIcon *clipIcon, Bool lpushed, Bool rpushed)
347 Window win = clipIcon->icon->core->window;
348 WScreen *scr = clipIcon->icon->core->screen_ptr;
349 XPoint p[4];
350 int pt = CLIP_BUTTON_SIZE*ICON_SIZE/64;
351 int tp = ICON_SIZE - pt;
352 int as = pt - 15; /* 15 = 5+5+5 */
353 GC gc = scr->draw_gc; /* maybe use WMColorGC() instead here? */
354 WMColor *color;
355 #ifdef GRADIENT_CLIP_ARROW
356 Bool collapsed = clipIcon->dock->collapsed;
357 #endif
359 if (!clipIcon->dock->collapsed)
360 color = scr->clip_title_color[CLIP_NORMAL];
361 else
362 color = scr->clip_title_color[CLIP_COLLAPSED];
364 XSetForeground(dpy, gc, WMColorPixel(color));
366 if (rpushed) {
367 p[0].x = tp+1;
368 p[0].y = 1;
369 p[1].x = ICON_SIZE-2;
370 p[1].y = 1;
371 p[2].x = ICON_SIZE-2;
372 p[2].y = pt-1;
373 } else if (lpushed) {
374 p[0].x = 1;
375 p[0].y = tp;
376 p[1].x = pt;
377 p[1].y = ICON_SIZE-2;
378 p[2].x = 1;
379 p[2].y = ICON_SIZE-2;
381 if (lpushed || rpushed) {
382 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
383 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
384 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
386 #ifdef GRADIENT_CLIP_ARROW
387 if (!collapsed) {
388 XSetFillStyle(dpy, scr->copy_gc, FillTiled);
389 XSetTile(dpy, scr->copy_gc, scr->clip_arrow_gradient);
390 XSetClipMask(dpy, scr->copy_gc, None);
391 gc = scr->copy_gc;
393 #endif /* GRADIENT_CLIP_ARROW */
395 /* top right arrow */
396 p[0].x = p[3].x = ICON_SIZE-5-as;
397 p[0].y = p[3].y = 5;
398 p[1].x = ICON_SIZE-6;
399 p[1].y = 5;
400 p[2].x = ICON_SIZE-6;
401 p[2].y = 4+as;
402 if (rpushed) {
403 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
404 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
405 } else {
406 #ifdef GRADIENT_CLIP_ARROW
407 if (!collapsed)
408 XSetTSOrigin(dpy, gc, ICON_SIZE-6-as, 5);
409 #endif
410 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
411 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
414 /* bottom left arrow */
415 p[0].x = p[3].x = 5;
416 p[0].y = p[3].y = ICON_SIZE-5-as;
417 p[1].x = 5;
418 p[1].y = ICON_SIZE-6;
419 p[2].x = 4+as;
420 p[2].y = ICON_SIZE-6;
421 if (lpushed) {
422 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
423 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
424 } else {
425 #ifdef GRADIENT_CLIP_ARROW
426 if (!collapsed)
427 XSetTSOrigin(dpy, gc, 5, ICON_SIZE-6-as);
428 #endif
429 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
430 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
432 #ifdef GRADIENT_CLIP_ARROW
433 if (!collapsed)
434 XSetFillStyle(dpy, scr->copy_gc, FillSolid);
435 #endif
439 RImage*
440 wClipMakeTile(WScreen *scr, RImage *normalTile)
442 RImage *tile = RCloneImage(normalTile);
443 RColor black;
444 RColor dark;
445 RColor light;
446 int pt, tp;
447 int as;
449 pt = CLIP_BUTTON_SIZE*wPreferences.icon_size/64;
450 tp = wPreferences.icon_size-1 - pt;
451 as = pt - 15;
453 black.alpha = 255;
454 black.red = black.green = black.blue = 0;
456 dark.alpha = 0;
457 dark.red = dark.green = dark.blue = 60;
459 light.alpha = 0;
460 light.red = light.green = light.blue = 80;
463 /* top right */
464 ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size-2,
465 pt-1, &dark);
466 RDrawLine(tile, tp-1, 0, wPreferences.icon_size-1, pt+1, &black);
467 ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size-3,
468 pt, &light);
470 /* arrow bevel */
471 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 7 - as, 4,
472 ICON_SIZE - 5, 4, &dark);
473 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 6 - as, 5,
474 ICON_SIZE - 5, 6 + as, &dark);
475 ROperateLine(tile, RAddOperation, ICON_SIZE - 5, 4, ICON_SIZE - 5, 6 + as,
476 &light);
478 /* bottom left */
479 ROperateLine(tile, RAddOperation, 2, tp+2, pt-2,
480 wPreferences.icon_size-3, &dark);
481 RDrawLine(tile, 0, tp-1, pt+1, wPreferences.icon_size-1, &black);
482 ROperateLine(tile, RSubtractOperation, 0, tp-2, pt+1,
483 wPreferences.icon_size-2, &light);
485 /* arrow bevel */
486 ROperateLine(tile, RSubtractOperation, 4, ICON_SIZE - 7 - as, 4,
487 ICON_SIZE - 5, &dark);
488 ROperateLine(tile, RSubtractOperation, 5, ICON_SIZE - 6 - as,
489 6 + as, ICON_SIZE - 5, &dark);
490 ROperateLine(tile, RAddOperation, 4, ICON_SIZE - 5, 6 + as, ICON_SIZE - 5,
491 &light);
493 return tile;
497 static void
498 omnipresentCallback(WMenu *menu, WMenuEntry *entry)
500 WAppIcon *clickedIcon = entry->clientdata;
501 WAppIcon *aicon;
502 WDock *dock;
503 WMArray *selectedIcons;
504 WMArrayIterator iter;
505 int failed;
507 assert(entry->clientdata!=NULL);
509 dock = clickedIcon->dock;
511 selectedIcons = getSelected(dock);
513 if (!WMGetArrayItemCount(selectedIcons))
514 WMAddToArray(selectedIcons, clickedIcon);
516 failed = 0;
517 WM_ITERATE_ARRAY(selectedIcons, aicon, iter) {
518 if (wClipMakeIconOmnipresent(aicon, !aicon->omnipresent) == WO_FAILED)
519 failed++;
520 else if (aicon->icon->selected)
521 wIconSelect(aicon->icon);
523 WMFreeArray(selectedIcons);
525 if (failed > 1) {
526 wMessageDialog(dock->screen_ptr, _("Warning"),
527 _("Some icons cannot be made omnipresent. "
528 "Please make sure that no other icon is "
529 "docked in the same positions on the other "
530 "workspaces and the Clip is not full in "
531 "some workspace."),
532 _("OK"), NULL, NULL);
533 } else if (failed == 1) {
534 wMessageDialog(dock->screen_ptr, _("Warning"),
535 _("Icon cannot be made omnipresent. "
536 "Please make sure that no other icon is "
537 "docked in the same position on the other "
538 "workspaces and the Clip is not full in "
539 "some workspace."),
540 _("OK"), NULL, NULL);
545 static void
546 removeIconsCallback(WMenu *menu, WMenuEntry *entry)
548 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
549 WDock *dock;
550 WAppIcon *aicon;
551 WMArray *selectedIcons;
552 int keepit;
553 WMArrayIterator it;
555 assert(clickedIcon!=NULL);
557 dock = clickedIcon->dock;
559 selectedIcons = getSelected(dock);
561 if (WMGetArrayItemCount(selectedIcons)) {
562 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
563 _("All selected icons will be removed!"),
564 _("OK"), _("Cancel"), NULL)!=WAPRDefault) {
565 WMFreeArray(selectedIcons);
566 return;
568 } else {
569 if (clickedIcon->xindex==0 && clickedIcon->yindex==0) {
570 WMFreeArray(selectedIcons);
571 return;
573 WMAddToArray(selectedIcons, clickedIcon);
576 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
577 keepit = aicon->running && wApplicationOf(aicon->main_window);
578 wDockDetach(dock, aicon);
579 if (keepit) {
580 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
581 XMoveWindow(dpy, aicon->icon->core->window,
582 aicon->x_pos, aicon->y_pos);
583 if (!dock->mapped || dock->collapsed)
584 XMapWindow(dpy, aicon->icon->core->window);
587 WMFreeArray(selectedIcons);
589 if (wPreferences.auto_arrange_icons)
590 wArrangeIcons(dock->screen_ptr, True);
594 static void
595 keepIconsCallback(WMenu *menu, WMenuEntry *entry)
597 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
598 WDock *dock;
599 WAppIcon *aicon;
600 WMArray *selectedIcons;
601 WMArrayIterator it;
603 assert(clickedIcon!=NULL);
604 dock = clickedIcon->dock;
606 selectedIcons = getSelected(dock);
608 if (!WMGetArrayItemCount(selectedIcons)
609 && clickedIcon!=dock->screen_ptr->clip_icon) {
610 char *command = NULL;
612 if (!clickedIcon->command && !clickedIcon->editing) {
613 clickedIcon->editing = 1;
614 if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
615 _("Type the command used to launch the application"),
616 &command)) {
617 if (command && (command[0]==0 ||
618 (command[0]=='-' && command[1]==0))) {
619 wfree(command);
620 command = NULL;
622 clickedIcon->command = command;
623 clickedIcon->editing = 0;
624 } else {
625 clickedIcon->editing = 0;
626 if (command)
627 wfree(command);
628 WMFreeArray(selectedIcons);
629 return;
633 WMAddToArray(selectedIcons, clickedIcon);
636 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
637 if (aicon->icon->selected)
638 wIconSelect(aicon->icon);
639 if (aicon && aicon->attracted && aicon->command) {
640 aicon->attracted = 0;
641 if (aicon->icon->shadowed) {
642 aicon->icon->shadowed = 0;
643 aicon->icon->force_paint = 1;
644 wAppIconPaint(aicon);
648 WMFreeArray(selectedIcons);
654 static void
655 toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
657 WDock *dock = (WDock*)entry->clientdata;
659 assert(entry->clientdata!=NULL);
661 dock->attract_icons = !dock->attract_icons;
662 /*if (!dock->attract_icons)
663 dock->keep_attracted = 0;*/
665 entry->flags.indicator_on = dock->attract_icons;
667 wMenuPaint(menu);
671 static void
672 selectCallback(WMenu *menu, WMenuEntry *entry)
674 WAppIcon *icon = (WAppIcon*)entry->clientdata;
676 assert(icon!=NULL);
678 wIconSelect(icon->icon);
680 wMenuPaint(menu);
684 static void
685 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
687 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
688 WDock *clip;
689 WAppIcon *aicon;
690 int x, y, x_pos, y_pos;
692 assert(entry->clientdata!=NULL);
693 clip = clickedIcon->dock;
695 aicon = clip->screen_ptr->app_icon_list;
697 while (aicon) {
698 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
699 x_pos = clip->x_pos + x*ICON_SIZE;
700 y_pos = clip->y_pos + y*ICON_SIZE;
701 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
702 #ifdef ANIMATIONS
703 if (wPreferences.no_animations) {
704 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
705 } else {
706 SlideWindow(aicon->icon->core->window,
707 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
709 #else
710 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
711 #endif /* ANIMATIONS */
713 aicon->attracted = 1;
714 if (!aicon->icon->shadowed) {
715 aicon->icon->shadowed = 1;
716 aicon->icon->force_paint = 1;
717 /* We don't do an wAppIconPaint() here because it's in
718 * wDockAttachIcon(). -Dan
721 wDockAttachIcon(clip, aicon, x, y);
722 if (clip->collapsed || !clip->mapped)
723 XUnmapWindow(dpy, aicon->icon->core->window);
725 aicon = aicon->next;
730 static void
731 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
733 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
734 WDock *dock;
735 WMArray *selectedIcons;
736 WMArrayIterator iter;
737 WAppIcon *btn;
738 int i;
740 assert(clickedIcon!=NULL);
741 dock = clickedIcon->dock;
743 selectedIcons = getSelected(dock);
745 if (!WMGetArrayItemCount(selectedIcons)) {
746 for (i=1; i<dock->max_icons; i++) {
747 btn = dock->icon_array[i];
748 if (btn && !btn->icon->selected) {
749 wIconSelect(btn->icon);
752 } else {
753 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
754 wIconSelect(btn->icon);
757 WMFreeArray(selectedIcons);
759 wMenuPaint(menu);
763 static void
764 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
766 assert(entry->clientdata!=NULL);
768 toggleCollapsed(entry->clientdata);
770 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
772 wMenuPaint(menu);
776 static void
777 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
779 WDock *dock;
780 assert(entry->clientdata!=NULL);
782 dock = (WDock*) entry->clientdata;
784 dock->auto_collapse = !dock->auto_collapse;
786 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
788 wMenuPaint(menu);
792 static void
793 toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
795 WDock *dock;
796 assert(entry->clientdata!=NULL);
798 dock = (WDock*) entry->clientdata;
800 dock->auto_raise_lower = !dock->auto_raise_lower;
802 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_raise_lower;
804 wMenuPaint(menu);
808 static void
809 launchCallback(WMenu *menu, WMenuEntry *entry)
811 WAppIcon *btn = (WAppIcon*)entry->clientdata;
813 launchDockedApplication(btn, False);
817 static void
818 settingsCallback(WMenu *menu, WMenuEntry *entry)
820 WAppIcon *btn = (WAppIcon*)entry->clientdata;
822 if (btn->editing)
823 return;
824 ShowDockAppSettingsPanel(btn);
828 static void
829 hideCallback(WMenu *menu, WMenuEntry *entry)
831 WApplication *wapp;
832 WAppIcon *btn = (WAppIcon*)entry->clientdata;
834 wapp = wApplicationOf(btn->icon->owner->main_window);
836 if (wapp->flags.hidden) {
837 wWorkspaceChange(btn->icon->core->screen_ptr, wapp->last_workspace);
838 wUnhideApplication(wapp, False, False);
839 } else {
840 wHideApplication(wapp);
845 static void
846 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
848 WApplication *wapp;
849 WAppIcon *btn = (WAppIcon*)entry->clientdata;
851 wapp = wApplicationOf(btn->icon->owner->main_window);
853 wUnhideApplication(wapp, False, True);
857 WAppIcon*
858 mainIconCreate(WScreen *scr, int type)
860 WAppIcon *btn;
861 int x_pos;
863 if (type == WM_CLIP) {
864 if (scr->clip_icon)
865 return scr->clip_icon;
866 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
867 btn->icon->core->descriptor.handle_expose = clipIconExpose;
868 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
869 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
870 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
871 x_pos = 0;
872 } else {
873 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
874 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
877 btn->xindex = 0;
878 btn->yindex = 0;
880 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
881 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
882 btn->icon->core->descriptor.parent = btn;
883 /*ChangeStackingLevel(btn->icon->core, WMDockLevel);*/
884 XMapWindow(dpy, btn->icon->core->window);
885 btn->x_pos = x_pos;
886 btn->y_pos = 0;
887 btn->docked = 1;
888 if (type == WM_CLIP)
889 scr->clip_icon = btn;
891 return btn;
895 static void
896 switchWSCommand(WMenu *menu, WMenuEntry *entry)
898 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
899 WScreen *scr = icon->icon->core->screen_ptr;
900 WDock *src, *dest;
901 WMArray *selectedIcons;
902 int x, y;
904 if (entry->order == scr->current_workspace)
905 return;
906 src = icon->dock;
907 dest = scr->workspaces[entry->order]->clip;
909 selectedIcons = getSelected(src);
911 if (WMGetArrayItemCount(selectedIcons)) {
912 WMArrayIterator iter;
914 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
915 if (wDockFindFreeSlot(dest, &x, &y)) {
916 moveIconBetweenDocks(src, dest, btn, x, y);
917 XUnmapWindow(dpy, btn->icon->core->window);
920 } else if (icon != scr->clip_icon) {
921 if (wDockFindFreeSlot(dest, &x, &y)) {
922 moveIconBetweenDocks(src, dest, icon, x, y);
923 XUnmapWindow(dpy, icon->icon->core->window);
926 WMFreeArray(selectedIcons);
931 static void
932 launchDockedApplication(WAppIcon *btn, Bool withSelection)
934 WScreen *scr = btn->icon->core->screen_ptr;
936 if (!btn->launching &&
937 ((!withSelection && btn->command!=NULL) ||
938 (withSelection && btn->paste_command!=NULL))) {
939 if (!btn->forced_dock) {
940 btn->relaunching = btn->running;
941 btn->running = 1;
943 if (btn->wm_instance || btn->wm_class) {
944 WWindowAttributes attr;
945 memset(&attr, 0, sizeof(WWindowAttributes));
946 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class,
947 &attr, NULL, True);
949 if (!attr.no_appicon && !btn->buggy_app)
950 btn->launching = 1;
951 else
952 btn->running = 0;
954 btn->drop_launch = 0;
955 btn->paste_launch = withSelection;
956 scr->last_dock = btn->dock;
957 btn->pid = execCommand(btn,
958 withSelection ? btn->paste_command : btn->command,
959 NULL);
960 if (btn->pid>0) {
961 if (btn->buggy_app) {
962 /* give feedback that the app was launched */
963 btn->launching = 1;
964 dockIconPaint(btn);
965 btn->launching = 0;
966 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
967 } else {
968 dockIconPaint(btn);
970 } else {
971 wwarning(_("could not launch application %s\n"), btn->command);
972 btn->launching = 0;
973 if (!btn->relaunching)
974 btn->running = 0;
981 static void
982 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
984 WScreen *scr = menu->frame->screen_ptr;
985 char title[MAX_WORKSPACENAME_WIDTH+1];
986 int i;
988 if (!menu || !icon)
989 return;
991 for (i=0; i<scr->workspace_count; i++) {
992 if (i < menu->entry_no) {
993 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
994 wfree(menu->entries[i]->text);
995 strcpy(title, scr->workspaces[i]->name);
996 menu->entries[i]->text = wstrdup(title);
997 menu->flags.realized = 0;
999 menu->entries[i]->clientdata = (void*)icon;
1000 } else {
1001 strcpy(title, scr->workspaces[i]->name);
1003 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
1005 menu->flags.realized = 0;
1007 if (i == scr->current_workspace) {
1008 wMenuSetEnabled(menu, i, False);
1009 } else {
1010 wMenuSetEnabled(menu, i, True);
1014 if (!menu->flags.realized)
1015 wMenuRealize(menu);
1019 static WMenu*
1020 makeWorkspaceMenu(WScreen *scr)
1022 WMenu *menu;
1024 menu = wMenuCreate(scr, NULL, False);
1025 if (!menu)
1026 wwarning(_("could not create workspace submenu for Clip menu"));
1028 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
1030 menu->flags.realized = 0;
1031 wMenuRealize(menu);
1033 return menu;
1037 static void
1038 updateClipOptionsMenu(WMenu *menu, WDock *dock)
1040 WMenuEntry *entry;
1041 int index = 0;
1043 if (!menu || !dock)
1044 return;
1046 /* keep on top */
1047 entry = menu->entries[index];
1048 entry->flags.indicator_on = !dock->lowered;
1049 entry->clientdata = dock;
1051 /* collapsed */
1052 entry = menu->entries[++index];
1053 entry->flags.indicator_on = dock->collapsed;
1054 entry->clientdata = dock;
1056 /* auto-collapse */
1057 entry = menu->entries[++index];
1058 entry->flags.indicator_on = dock->auto_collapse;
1059 entry->clientdata = dock;
1061 /* auto-raise/lower */
1062 entry = menu->entries[++index];
1063 entry->flags.indicator_on = dock->auto_raise_lower;
1064 entry->clientdata = dock;
1065 wMenuSetEnabled(menu, index, dock->lowered);
1067 /* attract icons */
1068 entry = menu->entries[++index];
1069 entry->flags.indicator_on = dock->attract_icons;
1070 entry->clientdata = dock;
1072 menu->flags.realized = 0;
1073 wMenuRealize(menu);
1077 static WMenu*
1078 makeClipOptionsMenu(WScreen *scr)
1080 WMenu *menu;
1081 WMenuEntry *entry;
1083 menu = wMenuCreate(scr, NULL, False);
1084 if (!menu) {
1085 wwarning(_("could not create options submenu for Clip menu"));
1086 return NULL;
1089 entry = wMenuAddCallback(menu, _("Keep on Top"),
1090 toggleLoweredCallback, NULL);
1091 entry->flags.indicator = 1;
1092 entry->flags.indicator_on = 1;
1093 entry->flags.indicator_type = MI_CHECK;
1095 entry = wMenuAddCallback(menu, _("Collapsed"),
1096 toggleCollapsedCallback, NULL);
1097 entry->flags.indicator = 1;
1098 entry->flags.indicator_on = 1;
1099 entry->flags.indicator_type = MI_CHECK;
1101 entry = wMenuAddCallback(menu, _("Autocollapse"),
1102 toggleAutoCollapseCallback, NULL);
1103 entry->flags.indicator = 1;
1104 entry->flags.indicator_on = 1;
1105 entry->flags.indicator_type = MI_CHECK;
1107 entry = wMenuAddCallback(menu, _("Autoraise"),
1108 toggleAutoRaiseLowerCallback, NULL);
1109 entry->flags.indicator = 1;
1110 entry->flags.indicator_on = 1;
1111 entry->flags.indicator_type = MI_CHECK;
1113 entry = wMenuAddCallback(menu, _("Autoattract Icons"),
1114 toggleAutoAttractCallback, NULL);
1115 entry->flags.indicator = 1;
1116 entry->flags.indicator_on = 1;
1117 entry->flags.indicator_type = MI_CHECK;
1119 menu->flags.realized = 0;
1120 wMenuRealize(menu);
1122 return menu;
1126 static WMenu*
1127 dockMenuCreate(WScreen *scr, int type)
1129 WMenu *menu;
1130 WMenuEntry *entry;
1132 if (type == WM_CLIP && scr->clip_menu)
1133 return scr->clip_menu;
1135 menu = wMenuCreate(scr, NULL, False);
1136 if (type != WM_CLIP) {
1137 entry = wMenuAddCallback(menu, _("Keep on Top"),
1138 toggleLoweredCallback, NULL);
1139 entry->flags.indicator = 1;
1140 entry->flags.indicator_on = 1;
1141 entry->flags.indicator_type = MI_CHECK;
1142 } else {
1143 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1144 scr->clip_options = makeClipOptionsMenu(scr);
1145 if (scr->clip_options)
1146 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1148 entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback,
1149 NULL);
1150 wfree(entry->text);
1151 entry->text = _("Rename Workspace");
1153 entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
1154 entry->flags.indicator = 1;
1155 entry->flags.indicator_on = 1;
1156 entry->flags.indicator_type = MI_CHECK;
1158 entry = wMenuAddCallback(menu, _("Select All Icons"),
1159 selectIconsCallback, NULL);
1160 wfree(entry->text);
1161 entry->text = _("Select All Icons");
1163 entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
1164 wfree(entry->text);
1165 entry->text = _("Keep Icon");
1167 entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
1168 wfree(entry->text);
1169 entry->text = _("Move Icon To");
1170 scr->clip_submenu = makeWorkspaceMenu(scr);
1171 if (scr->clip_submenu)
1172 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1174 entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback,
1175 NULL);
1176 wfree(entry->text);
1177 entry->text = _("Remove Icon");
1179 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1182 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1184 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1186 entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1187 wfree(entry->text);
1188 entry->text = _("Hide");
1190 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1192 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1194 if (type == WM_CLIP)
1195 scr->clip_menu = menu;
1197 return menu;
1201 WDock*
1202 wDockCreate(WScreen *scr, int type)
1204 WDock *dock;
1205 WAppIcon *btn;
1206 int icon_count;
1208 make_keys();
1210 dock = wmalloc(sizeof(WDock));
1211 memset(dock, 0, sizeof(WDock));
1213 if (type == WM_CLIP)
1214 icon_count = CLIP_MAX_ICONS;
1215 else
1216 icon_count = scr->scr_height/wPreferences.icon_size;
1218 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1219 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1221 dock->max_icons = icon_count;
1223 btn = mainIconCreate(scr, type);
1225 btn->dock = dock;
1227 dock->x_pos = btn->x_pos;
1228 dock->y_pos = btn->y_pos;
1229 dock->screen_ptr = scr;
1230 dock->type = type;
1231 dock->icon_count = 1;
1232 dock->on_right_side = 1;
1233 dock->collapsed = 0;
1234 dock->auto_collapse = 0;
1235 dock->auto_collapse_magic = NULL;
1236 dock->auto_raise_lower = 0;
1237 dock->auto_lower_magic = NULL;
1238 dock->auto_raise_magic = NULL;
1239 dock->attract_icons = 0;
1240 dock->lowered = 1;
1241 dock->icon_array[0] = btn;
1242 wRaiseFrame(btn->icon->core);
1243 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1245 /* create dock menu */
1246 dock->menu = dockMenuCreate(scr, type);
1248 return dock;
1252 void
1253 wDockDestroy(WDock *dock)
1255 int i;
1256 WAppIcon *aicon;
1258 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1259 aicon = dock->icon_array[i];
1260 if (aicon) {
1261 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1262 wDockDetach(dock, aicon);
1263 if (keepit) {
1264 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
1265 XMoveWindow(dpy, aicon->icon->core->window,
1266 aicon->x_pos, aicon->y_pos);
1267 if (!dock->mapped || dock->collapsed)
1268 XMapWindow(dpy, aicon->icon->core->window);
1272 if (wPreferences.auto_arrange_icons)
1273 wArrangeIcons(dock->screen_ptr, True);
1274 wfree(dock->icon_array);
1275 if (dock->menu && dock->type!=WM_CLIP)
1276 wMenuDestroy(dock->menu, True);
1277 if (dock->screen_ptr->last_dock == dock)
1278 dock->screen_ptr->last_dock = NULL;
1279 wfree(dock);
1283 void
1284 wClipIconPaint(WAppIcon *aicon)
1286 WScreen *scr = aicon->icon->core->screen_ptr;
1287 WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1288 WMColor *color;
1289 Window win = aicon->icon->core->window;
1290 int length, nlength;
1291 char *ws_name, ws_number[10];
1292 int ty, tx;
1294 wIconPaint(aicon->icon);
1296 length = strlen(workspace->name);
1297 ws_name = wmalloc(length + 1);
1298 snprintf(ws_name, length+1, "%s", workspace->name);
1299 snprintf(ws_number, sizeof(ws_number), "%i", scr->current_workspace + 1);
1300 nlength = strlen(ws_number);
1302 if (!workspace->clip->collapsed)
1303 color = scr->clip_title_color[CLIP_NORMAL];
1304 else
1305 color = scr->clip_title_color[CLIP_COLLAPSED];
1307 ty = ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
1309 tx = CLIP_BUTTON_SIZE*ICON_SIZE/64;
1311 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1312 ty, ws_name, length);
1313 /*WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, 4,
1314 2, ws_name, length);*/
1316 tx = (ICON_SIZE/2 - WMWidthOfString(scr->clip_title_font, ws_number,
1317 nlength))/2;
1319 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1320 2, ws_number, nlength);
1322 wfree(ws_name);
1324 if (aicon->launching) {
1325 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1326 0, 0, wPreferences.icon_size, wPreferences.icon_size);
1328 paintClipButtons(aicon, aicon->dock->lclip_button_pushed,
1329 aicon->dock->rclip_button_pushed);
1333 static void
1334 clipIconExpose(WObjDescriptor *desc, XEvent *event)
1336 wClipIconPaint(desc->parent);
1340 static void
1341 dockIconPaint(WAppIcon *btn)
1343 if (btn == btn->icon->core->screen_ptr->clip_icon)
1344 wClipIconPaint(btn);
1345 else
1346 wAppIconPaint(btn);
1350 static WMPropList*
1351 make_icon_state(WAppIcon *btn)
1353 WMPropList *node = NULL;
1354 WMPropList *command, *autolaunch, *lock, *name, *forced, *host;
1355 WMPropList *position, *buggy, *omnipresent;
1356 char *tmp;
1357 char buffer[64];
1359 if (btn) {
1360 if (!btn->command)
1361 command = WMCreatePLString("-");
1362 else
1363 command = WMCreatePLString(btn->command);
1365 autolaunch = btn->auto_launch ? dYes : dNo;
1367 lock = btn->lock ? dYes : dNo;
1369 tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
1371 name = WMCreatePLString(tmp);
1373 wfree(tmp);
1375 forced = btn->forced_dock ? dYes : dNo;
1377 buggy = btn->buggy_app ? dYes : dNo;
1379 if (btn == btn->icon->core->screen_ptr->clip_icon)
1380 snprintf(buffer, sizeof(buffer), "%i,%i", btn->x_pos, btn->y_pos);
1381 else
1382 snprintf(buffer, sizeof(buffer), "%hi,%hi", btn->xindex, btn->yindex);
1383 position = WMCreatePLString(buffer);
1385 node = WMCreatePLDictionary(dCommand, command,
1386 dName, name,
1387 dAutoLaunch, autolaunch,
1388 dLock, lock,
1389 dForced, forced,
1390 dBuggyApplication, buggy,
1391 dPosition, position,
1392 NULL);
1393 WMReleasePropList(command);
1394 WMReleasePropList(name);
1395 WMReleasePropList(position);
1397 omnipresent = btn->omnipresent ? dYes : dNo;
1398 if (btn->dock != btn->icon->core->screen_ptr->dock &&
1399 (btn->xindex != 0 || btn->yindex != 0))
1400 WMPutInPLDictionary(node, dOmnipresent, omnipresent);
1402 #ifdef OFFIX_DND
1403 if (btn->dnd_command) {
1404 command = WMCreatePLString(btn->dnd_command);
1405 WMPutInPLDictionary(node, dDropCommand, command);
1406 WMReleasePropList(command);
1408 #endif /* OFFIX_DND */
1410 if (btn->paste_command) {
1411 command = WMCreatePLString(btn->paste_command);
1412 WMPutInPLDictionary(node, dPasteCommand, command);
1413 WMReleasePropList(command);
1416 if (btn->client_machine && btn->remote_start) {
1417 host = WMCreatePLString(btn->client_machine);
1418 WMPutInPLDictionary(node, dHost, host);
1419 WMReleasePropList(host);
1423 return node;
1427 static WMPropList*
1428 dockSaveState(WDock *dock)
1430 int i;
1431 WMPropList *icon_info;
1432 WMPropList *list=NULL, *dock_state=NULL;
1433 WMPropList *value, *key;
1434 char buffer[256];
1436 list = WMCreatePLArray(NULL);
1438 for (i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
1439 WAppIcon *btn = dock->icon_array[i];
1441 if (!btn || btn->attracted)
1442 continue;
1444 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1445 WMAddToPLArray(list, icon_info);
1446 WMReleasePropList(icon_info);
1450 dock_state = WMCreatePLDictionary(dApplications, list,
1451 NULL);
1453 if (dock->type == WM_DOCK) {
1454 snprintf(buffer, sizeof(buffer), "Applications%i", dock->screen_ptr->scr_height);
1455 key = WMCreatePLString(buffer);
1456 WMPutInPLDictionary(dock_state, key, list);
1457 WMReleasePropList(key);
1460 snprintf(buffer, sizeof(buffer), "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0),
1461 dock->y_pos);
1462 value = WMCreatePLString(buffer);
1463 WMPutInPLDictionary(dock_state, dPosition, value);
1464 WMReleasePropList(value);
1466 WMReleasePropList(list);
1469 value = (dock->lowered ? dYes : dNo);
1470 WMPutInPLDictionary(dock_state, dLowered, value);
1472 if (dock->type == WM_CLIP) {
1473 value = (dock->collapsed ? dYes : dNo);
1474 WMPutInPLDictionary(dock_state, dCollapsed, value);
1476 value = (dock->auto_collapse ? dYes : dNo);
1477 WMPutInPLDictionary(dock_state, dAutoCollapse, value);
1479 value = (dock->auto_raise_lower ? dYes : dNo);
1480 WMPutInPLDictionary(dock_state, dAutoRaiseLower, value);
1482 value = (dock->attract_icons ? dYes : dNo);
1483 WMPutInPLDictionary(dock_state, dAutoAttractIcons, value);
1486 return dock_state;
1490 void
1491 wDockSaveState(WScreen *scr, WMPropList *old_state)
1493 WMPropList *dock_state;
1494 WMPropList *keys;
1496 dock_state = dockSaveState(scr->dock);
1499 * Copy saved states of docks with different sizes.
1501 if (old_state) {
1502 int i;
1503 WMPropList *tmp;
1505 keys = WMGetPLDictionaryKeys(old_state);
1506 for (i = 0; i < WMGetPropListItemCount(keys); i++) {
1507 tmp = WMGetFromPLArray(keys, i);
1509 if (strncasecmp(WMGetFromPLString(tmp), "applications", 12) == 0
1510 && !WMGetFromPLDictionary(dock_state, tmp)) {
1512 WMPutInPLDictionary(dock_state,
1513 tmp,
1514 WMGetFromPLDictionary(old_state, tmp));
1517 WMReleasePropList(keys);
1521 WMPutInPLDictionary(scr->session_state, dDock, dock_state);
1523 WMReleasePropList(dock_state);
1527 void
1528 wClipSaveState(WScreen *scr)
1530 WMPropList *clip_state;
1532 clip_state = make_icon_state(scr->clip_icon);
1534 WMPutInPLDictionary(scr->session_state, dClip, clip_state);
1536 WMReleasePropList(clip_state);
1540 WMPropList*
1541 wClipSaveWorkspaceState(WScreen *scr, int workspace)
1543 return dockSaveState(scr->workspaces[workspace]->clip);
1547 static Bool
1548 getBooleanDockValue(WMPropList *value, WMPropList *key)
1550 if (value) {
1551 if (WMIsPLString(value)) {
1552 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1553 return True;
1554 } else {
1555 wwarning(_("bad value in docked icon state info %s"),
1556 WMGetFromPLString(key));
1559 return False;
1563 static WAppIcon*
1564 restore_icon_state(WScreen *scr, WMPropList *info, int type, int index)
1566 WAppIcon *aicon;
1567 WMPropList *cmd, *value;
1570 cmd = WMGetFromPLDictionary(info, dCommand);
1571 if (!cmd || !WMIsPLString(cmd)) {
1572 return NULL;
1575 /* parse window name */
1576 value = WMGetFromPLDictionary(info, dName);
1577 if (!value)
1578 return NULL;
1581 char *wclass, *winstance;
1582 char *command;
1584 ParseWindowName(value, &winstance, &wclass, "dock");
1586 if (!winstance && !wclass) {
1587 return NULL;
1590 /* get commands */
1592 if (cmd)
1593 command = wstrdup(WMGetFromPLString(cmd));
1594 else
1595 command = NULL;
1597 if (!command || strcmp(command, "-")==0) {
1598 if (command)
1599 wfree(command);
1600 if (wclass)
1601 wfree(wclass);
1602 if (winstance)
1603 wfree(winstance);
1605 return NULL;
1608 aicon = wAppIconCreateForDock(scr, command, winstance, wclass,
1609 TILE_NORMAL);
1610 if (wclass)
1611 wfree(wclass);
1612 if (winstance)
1613 wfree(winstance);
1614 if (command)
1615 wfree(command);
1618 aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1619 if (type == WM_CLIP) {
1620 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1621 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1623 aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1624 aicon->icon->core->descriptor.parent = aicon;
1627 #ifdef OFFIX_DND
1628 cmd = WMGetFromPLDictionary(info, dDropCommand);
1629 if (cmd)
1630 aicon->dnd_command = wstrdup(WMGetFromPLString(cmd));
1631 #endif
1633 cmd = WMGetFromPLDictionary(info, dPasteCommand);
1634 if (cmd)
1635 aicon->paste_command = wstrdup(WMGetFromPLString(cmd));
1637 /* check auto launch */
1638 value = WMGetFromPLDictionary(info, dAutoLaunch);
1640 aicon->auto_launch = getBooleanDockValue(value, dAutoLaunch);
1642 /* check lock */
1643 value = WMGetFromPLDictionary(info, dLock);
1645 aicon->lock = getBooleanDockValue(value, dLock);
1647 /* check if it wasn't normally docked */
1648 value = WMGetFromPLDictionary(info, dForced);
1650 aicon->forced_dock = getBooleanDockValue(value, dForced);
1652 /* check if we can rely on the stuff in the app */
1653 value = WMGetFromPLDictionary(info, dBuggyApplication);
1655 aicon->buggy_app = getBooleanDockValue(value, dBuggyApplication);
1657 /* get position in the dock */
1658 value = WMGetFromPLDictionary(info, dPosition);
1659 if (value && WMIsPLString(value)) {
1660 if (sscanf(WMGetFromPLString(value), "%hi,%hi", &aicon->xindex,
1661 &aicon->yindex)!=2)
1662 wwarning(_("bad value in docked icon state info %s"),
1663 WMGetFromPLString(dPosition));
1665 /* check position sanity */
1666 /* incomplete section! */
1667 if (type == WM_DOCK) {
1668 aicon->xindex = 0;
1669 if (aicon->yindex < 0)
1670 wwarning(_("bad value in docked icon position %i,%i"),
1671 aicon->xindex, aicon->yindex);
1673 } else {
1674 aicon->yindex = index;
1675 aicon->xindex = 0;
1678 /* check if icon is omnipresent */
1679 value = WMGetFromPLDictionary(info, dOmnipresent);
1681 aicon->omnipresent = getBooleanDockValue(value, dOmnipresent);
1683 aicon->running = 0;
1684 aicon->docked = 1;
1686 return aicon;
1690 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1693 WAppIcon*
1694 wClipRestoreState(WScreen *scr, WMPropList *clip_state)
1696 WAppIcon *icon;
1697 WMPropList *value;
1700 icon = mainIconCreate(scr, WM_CLIP);
1702 if (!clip_state)
1703 return icon;
1704 else
1705 WMRetainPropList(clip_state);
1707 /* restore position */
1709 value = WMGetFromPLDictionary(clip_state, dPosition);
1711 if (value) {
1712 if (!WMIsPLString(value))
1713 COMPLAIN("Position");
1714 else {
1715 WMRect rect;
1716 int flags;
1718 if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos,
1719 &icon->y_pos)!=2)
1720 COMPLAIN("Position");
1722 /* check position sanity */
1723 rect.pos.x = icon->x_pos;
1724 rect.pos.y = icon->y_pos;
1725 rect.size.width = rect.size.height = ICON_SIZE;
1727 wGetRectPlacementInfo(scr, rect, &flags);
1728 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
1729 wScreenKeepInside(scr, &icon->x_pos, &icon->y_pos,
1730 ICON_SIZE, ICON_SIZE);
1734 #ifdef OFFIX_DND
1735 value = WMGetFromPLDictionary(clip_state, dDropCommand);
1736 if (value && WMIsPLString(value))
1737 icon->dnd_command = wstrdup(WMGetFromPLString(value));
1738 #endif
1740 value = WMGetFromPLDictionary(clip_state, dPasteCommand);
1741 if (value && WMIsPLString(value))
1742 icon->paste_command = wstrdup(WMGetFromPLString(value));
1744 WMReleasePropList(clip_state);
1746 return icon;
1750 WDock*
1751 wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type)
1753 WDock *dock;
1754 WMPropList *apps;
1755 WMPropList *value;
1756 WAppIcon *aicon, *old_top;
1757 int count, i;
1760 dock = wDockCreate(scr, type);
1762 if (!dock_state)
1763 return dock;
1765 if (dock_state)
1766 WMRetainPropList(dock_state);
1769 /* restore position */
1771 value = WMGetFromPLDictionary(dock_state, dPosition);
1773 if (value) {
1774 if (!WMIsPLString(value))
1775 COMPLAIN("Position");
1776 else {
1777 WMRect rect;
1778 int flags;
1780 if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos,
1781 &dock->y_pos)!=2)
1782 COMPLAIN("Position");
1784 /* check position sanity */
1785 rect.pos.x = dock->x_pos;
1786 rect.pos.y = dock->y_pos;
1787 rect.size.width = rect.size.height = ICON_SIZE;
1789 wGetRectPlacementInfo(scr, rect, &flags);
1790 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) {
1791 int x = dock->x_pos;
1792 wScreenKeepInside(scr, &x, &dock->y_pos, ICON_SIZE, ICON_SIZE);
1795 /* This is no more needed. ??? */
1796 if (type == WM_CLIP) {
1797 if (dock->x_pos < 0)
1798 dock->x_pos = 0;
1799 else if (dock->x_pos > scr->scr_width-ICON_SIZE)
1800 dock->x_pos = scr->scr_width-ICON_SIZE;
1801 } else {
1802 if (dock->x_pos >= 0) {
1803 dock->x_pos = DOCK_EXTRA_SPACE;
1804 dock->on_right_side = 0;
1805 } else {
1806 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1807 dock->on_right_side = 1;
1813 /* restore lowered/raised state */
1815 dock->lowered = 0;
1817 value = WMGetFromPLDictionary(dock_state, dLowered);
1819 if (value) {
1820 if (!WMIsPLString(value))
1821 COMPLAIN("Lowered");
1822 else {
1823 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1824 dock->lowered = 1;
1829 /* restore collapsed state */
1831 dock->collapsed = 0;
1833 value = WMGetFromPLDictionary(dock_state, dCollapsed);
1835 if (value) {
1836 if (!WMIsPLString(value))
1837 COMPLAIN("Collapsed");
1838 else {
1839 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1840 dock->collapsed = 1;
1845 /* restore auto-collapsed state */
1847 value = WMGetFromPLDictionary(dock_state, dAutoCollapse);
1849 if (value) {
1850 if (!WMIsPLString(value))
1851 COMPLAIN("AutoCollapse");
1852 else {
1853 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1854 dock->auto_collapse = 1;
1855 dock->collapsed = 1;
1861 /* restore auto-raise/lower state */
1863 value = WMGetFromPLDictionary(dock_state, dAutoRaiseLower);
1865 if (value) {
1866 if (!WMIsPLString(value))
1867 COMPLAIN("AutoRaiseLower");
1868 else {
1869 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1870 dock->auto_raise_lower = 1;
1875 /* restore attract icons state */
1877 dock->attract_icons = 0;
1879 value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons);
1881 if (value) {
1882 if (!WMIsPLString(value))
1883 COMPLAIN("AutoAttractIcons");
1884 else {
1885 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1886 dock->attract_icons = 1;
1891 /* application list */
1894 WMPropList *tmp;
1895 char buffer[64];
1898 * When saving, it saves the dock state in
1899 * Applications and Applicationsnnn
1901 * When loading, it will first try Applicationsnnn.
1902 * If it does not exist, use Applications as default.
1905 snprintf(buffer, sizeof(buffer), "Applications%i", scr->scr_height);
1907 tmp = WMCreatePLString(buffer);
1908 apps = WMGetFromPLDictionary(dock_state, tmp);
1909 WMReleasePropList(tmp);
1911 if (!apps) {
1912 apps = WMGetFromPLDictionary(dock_state, dApplications);
1916 if (!apps) {
1917 goto finish;
1920 count = WMGetPropListItemCount(apps);
1922 if (count==0)
1923 goto finish;
1925 old_top = dock->icon_array[0];
1927 /* dock->icon_count is set to 1 when dock is created.
1928 * Since Clip is already restored, we want to keep it so for clip,
1929 * but for dock we may change the default top tile, so we set it to 0.
1931 if (type == WM_DOCK)
1932 dock->icon_count = 0;
1934 for (i=0; i<count; i++) {
1935 if (dock->icon_count >= dock->max_icons) {
1936 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1937 break;
1940 value = WMGetFromPLArray(apps, i);
1941 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1943 dock->icon_array[dock->icon_count] = aicon;
1945 if (aicon) {
1946 aicon->dock = dock;
1947 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1948 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1950 if (dock->lowered)
1951 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1952 else
1953 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1955 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1956 0, 0);
1958 if (!dock->collapsed)
1959 XMapWindow(dpy, aicon->icon->core->window);
1960 wRaiseFrame(aicon->icon->core);
1962 dock->icon_count++;
1963 } else if (dock->icon_count==0 && type==WM_DOCK)
1964 dock->icon_count++;
1967 /* if the first icon is not defined, use the default */
1968 if (dock->icon_array[0]==NULL) {
1969 /* update default icon */
1970 old_top->x_pos = dock->x_pos;
1971 old_top->y_pos = dock->y_pos;
1972 if (dock->lowered)
1973 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1974 else
1975 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1976 dock->icon_array[0] = old_top;
1977 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1978 /* we don't need to increment dock->icon_count here because it was
1979 * incremented in the loop above.
1981 } else if (old_top!=dock->icon_array[0]) {
1982 if (old_top == scr->clip_icon)
1983 scr->clip_icon = dock->icon_array[0];
1984 wAppIconDestroy(old_top);
1987 finish:
1988 if (dock_state)
1989 WMReleasePropList(dock_state);
1991 return dock;
1996 void
1997 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
1999 if (btn && btn->command && !btn->running && !btn->launching) {
2001 btn->drop_launch = 0;
2002 btn->paste_launch = 0;
2004 btn->pid = execCommand(btn, btn->command, state);
2006 if (btn->pid>0) {
2007 if (!btn->forced_dock && !btn->buggy_app) {
2008 btn->launching = 1;
2009 dockIconPaint(btn);
2012 } else {
2013 wfree(state);
2018 void
2019 wDockDoAutoLaunch(WDock *dock, int workspace)
2021 WAppIcon *btn;
2022 WSavedState *state;
2023 int i;
2025 for (i = 0; i < dock->max_icons; i++) {
2026 btn = dock->icon_array[i];
2027 if (!btn || !btn->auto_launch)
2028 continue;
2030 state = wmalloc(sizeof(WSavedState));
2031 memset(state, 0, sizeof(WSavedState));
2032 state->workspace = workspace;
2033 /* TODO: this is klugy and is very difficult to understand
2034 * what's going on. Try to clean up */
2035 wDockLaunchWithState(dock, btn, state);
2040 #ifdef OFFIX_DND
2041 static WDock*
2042 findDock(WScreen *scr, XEvent *event, int *icon_pos)
2044 WDock *dock;
2045 int i;
2047 *icon_pos = -1;
2048 if ((dock = scr->dock)!=NULL) {
2049 for (i=0; i<dock->max_icons; i++) {
2050 if (dock->icon_array[i]
2051 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2052 *icon_pos = i;
2053 break;
2057 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
2058 for (i=0; i<dock->max_icons; i++) {
2059 if (dock->icon_array[i]
2060 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2061 *icon_pos = i;
2062 break;
2066 if(*icon_pos>=0)
2067 return dock;
2068 return NULL;
2073 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
2075 WDock *dock;
2076 WAppIcon *btn;
2077 int icon_pos;
2079 dock = findDock(scr, event, &icon_pos);
2080 if (!dock)
2081 return False;
2084 * Return True if the drop was on an application icon window.
2085 * In this case, let the ClientMessage handler redirect the
2086 * message to the app.
2088 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
2089 return True;
2091 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
2092 scr->flags.dnd_data_convertion_status = 0;
2094 btn = dock->icon_array[icon_pos];
2096 if (!btn->forced_dock) {
2097 btn->relaunching = btn->running;
2098 btn->running = 1;
2100 if (btn->wm_instance || btn->wm_class) {
2101 WWindowAttributes attr;
2102 memset(&attr, 0, sizeof(WWindowAttributes));
2103 wDefaultFillAttributes(btn->icon->core->screen_ptr,
2104 btn->wm_instance,
2105 btn->wm_class, &attr, NULL, True);
2107 if (!attr.no_appicon)
2108 btn->launching = 1;
2109 else
2110 btn->running = 0;
2113 btn->paste_launch = 0;
2114 btn->drop_launch = 1;
2115 scr->last_dock = dock;
2116 btn->pid = execCommand(btn, btn->dnd_command, NULL);
2117 if (btn->pid>0) {
2118 dockIconPaint(btn);
2119 } else {
2120 btn->launching = 0;
2121 if (!btn->relaunching) {
2122 btn->running = 0;
2126 return False;
2128 #endif /* OFFIX_DND */
2132 Bool
2133 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2135 WWindow *wwin;
2136 char **argv;
2137 int argc;
2138 int index;
2140 wwin = icon->icon->owner;
2141 if (icon->command==NULL) {
2142 icon->editing = 0;
2143 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2145 icon->command = wtokenjoin(argv, argc);
2146 XFreeStringList(argv);
2147 } else {
2148 char *command=NULL;
2150 /* icon->forced_dock = 1;*/
2151 if (dock->type!=WM_CLIP || !icon->attracted) {
2152 icon->editing = 1;
2153 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
2154 _("Type the command used to launch the application"),
2155 &command)) {
2156 if (command && (command[0]==0 ||
2157 (command[0]=='-' && command[1]==0))) {
2158 wfree(command);
2159 command = NULL;
2161 icon->command = command;
2162 icon->editing = 0;
2163 } else {
2164 icon->editing = 0;
2165 if (command)
2166 wfree(command);
2167 /* If the target is the dock, reject the icon. If
2168 * the target is the clip, make it an attracted icon
2170 if (dock->type==WM_CLIP) {
2171 icon->attracted = 1;
2172 if (!icon->icon->shadowed) {
2173 icon->icon->shadowed = 1;
2174 icon->icon->force_paint = 1;
2176 } else {
2177 return False;
2182 } else {
2183 icon->editing = 0;
2186 for (index=1; index<dock->max_icons; index++)
2187 if (dock->icon_array[index] == NULL)
2188 break;
2189 /* if (index == dock->max_icons)
2190 return; */
2192 assert(index < dock->max_icons);
2194 dock->icon_array[index] = icon;
2195 icon->yindex = y;
2196 icon->xindex = x;
2198 icon->omnipresent = 0;
2200 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2201 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2203 dock->icon_count++;
2205 icon->running = 1;
2206 icon->launching = 0;
2207 icon->docked = 1;
2208 icon->dock = dock;
2209 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2210 if (dock->type == WM_CLIP) {
2211 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2212 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2214 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2215 icon->icon->core->descriptor.parent = icon;
2217 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2218 icon->icon->core);
2219 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2220 wAppIconPaint(icon);
2222 if (wPreferences.auto_arrange_icons)
2223 wArrangeIcons(dock->screen_ptr, True);
2225 #ifdef OFFIX_DND
2226 if (icon->command && !icon->dnd_command) {
2227 int len = strlen(icon->command)+8;
2228 icon->dnd_command = wmalloc(len);
2229 snprintf(icon->dnd_command, len, "%s %%d", icon->command);
2231 #endif
2233 if (icon->command && !icon->paste_command) {
2234 int len = strlen(icon->command)+8;
2235 icon->paste_command = wmalloc(len);
2236 snprintf(icon->paste_command, len, "%s %%s", icon->command);
2239 return True;
2243 void
2244 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2246 int index;
2248 for(index=1; index<dock->max_icons; index++) {
2249 if(dock->icon_array[index] == icon)
2250 break;
2252 assert(index < dock->max_icons);
2254 icon->yindex = y;
2255 icon->xindex = x;
2257 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2258 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2262 Bool
2263 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2265 WWindow *wwin;
2266 char **argv;
2267 int argc;
2268 int index;
2270 if (src == dest)
2271 return True; /* No move needed, we're already there */
2273 if (dest == NULL)
2274 return False;
2276 wwin = icon->icon->owner;
2279 * For the moment we can't do this if we move icons in Clip from one
2280 * workspace to other, because if we move two or more icons without
2281 * command, the dialog box will not be able to tell us to which of the
2282 * moved icons it applies. -Dan
2284 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2285 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2287 icon->command = wtokenjoin(argv, argc);
2288 XFreeStringList(argv);
2289 } else {
2290 char *command=NULL;
2292 icon->editing = 1;
2293 /* icon->forced_dock = 1;*/
2294 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2295 _("Type the command used to launch the application"),
2296 &command)) {
2297 if (command && (command[0]==0 ||
2298 (command[0]=='-' && command[1]==0))) {
2299 wfree(command);
2300 command = NULL;
2302 icon->command = command;
2303 } else {
2304 icon->editing = 0;
2305 if (command)
2306 wfree(command);
2307 return False;
2309 icon->editing = 0;
2313 if (dest->type == WM_DOCK)
2314 wClipMakeIconOmnipresent(icon, False);
2316 for(index=1; index<src->max_icons; index++) {
2317 if(src->icon_array[index] == icon)
2318 break;
2320 assert(index < src->max_icons);
2322 src->icon_array[index] = NULL;
2323 src->icon_count--;
2325 for(index=1; index<dest->max_icons; index++) {
2326 if(dest->icon_array[index] == NULL)
2327 break;
2329 /* if (index == dest->max_icons)
2330 return; */
2332 assert(index < dest->max_icons);
2334 dest->icon_array[index] = icon;
2335 icon->dock = dest;
2337 /* deselect the icon */
2338 if (icon->icon->selected)
2339 wIconSelect(icon->icon);
2341 if (dest->type == WM_DOCK) {
2342 icon->icon->core->descriptor.handle_enternotify = NULL;
2343 icon->icon->core->descriptor.handle_leavenotify = NULL;
2344 } else {
2345 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2346 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2349 /* set it to be kept when moving to dock.
2350 * Unless the icon does not have a command set
2352 if (icon->command && dest->type==WM_DOCK) {
2353 icon->attracted = 0;
2354 if (icon->icon->shadowed) {
2355 icon->icon->shadowed = 0;
2356 icon->icon->force_paint = 1;
2360 if (src->auto_collapse || src->auto_raise_lower)
2361 clipLeave(src);
2363 icon->yindex = y;
2364 icon->xindex = x;
2366 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2367 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2369 dest->icon_count++;
2371 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2372 icon->icon->core);
2373 wAppIconPaint(icon);
2375 return True;
2379 void
2380 wDockDetach(WDock *dock, WAppIcon *icon)
2382 int index;
2384 /* make the settings panel be closed */
2385 if (icon->panel) {
2386 DestroyDockAppSettingsPanel(icon->panel);
2389 /* This must be called before icon->dock is set to NULL.
2390 * Don't move it. -Dan
2392 wClipMakeIconOmnipresent(icon, False);
2394 icon->docked = 0;
2395 icon->dock = NULL;
2396 icon->attracted = 0;
2397 icon->auto_launch = 0;
2398 if (icon->icon->shadowed) {
2399 icon->icon->shadowed = 0;
2400 icon->icon->force_paint = 1;
2403 /* deselect the icon */
2404 if (icon->icon->selected)
2405 wIconSelect(icon->icon);
2407 if (icon->command) {
2408 wfree(icon->command);
2409 icon->command = NULL;
2411 #ifdef OFFIX_DND
2412 if (icon->dnd_command) {
2413 wfree(icon->dnd_command);
2414 icon->dnd_command = NULL;
2416 #endif
2417 if (icon->paste_command) {
2418 wfree(icon->paste_command);
2419 icon->paste_command = NULL;
2422 for (index=1; index<dock->max_icons; index++)
2423 if (dock->icon_array[index] == icon)
2424 break;
2425 assert(index < dock->max_icons);
2426 dock->icon_array[index] = NULL;
2427 icon->yindex = -1;
2428 icon->xindex = -1;
2430 dock->icon_count--;
2432 /* if the dock is not attached to an application or
2433 * the the application did not set the approriate hints yet,
2434 * destroy the icon */
2435 if (!icon->running || !wApplicationOf(icon->main_window))
2436 wAppIconDestroy(icon);
2437 else {
2438 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2439 icon->icon->core->descriptor.handle_enternotify = NULL;
2440 icon->icon->core->descriptor.handle_leavenotify = NULL;
2441 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2442 icon->icon->core->descriptor.parent = icon;
2444 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2446 wAppIconPaint(icon);
2447 if (wPreferences.auto_arrange_icons) {
2448 wArrangeIcons(dock->screen_ptr, True);
2451 if (dock->auto_collapse || dock->auto_raise_lower)
2452 clipLeave(dock);
2457 * returns the closest Dock slot index for the passed
2458 * coordinates.
2460 * Returns False if icon can't be docked.
2462 * Note: this function should NEVER alter ret_x or ret_y, unless it will
2463 * return True. -Dan
2465 Bool
2466 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2467 int *ret_x, int *ret_y, int redocking)
2469 WScreen *scr = dock->screen_ptr;
2470 int dx, dy;
2471 int ex_x, ex_y;
2472 int i, offset = ICON_SIZE/2;
2473 WAppIcon *aicon = NULL;
2474 WAppIcon *nicon = NULL;
2475 int max_y_icons, max_x_icons;
2477 /* TODO: XINERAMA, for these */
2478 max_x_icons = scr->scr_width/ICON_SIZE;
2479 max_y_icons = scr->scr_height/ICON_SIZE-1;
2481 if (wPreferences.flags.noupdates)
2482 return False;
2484 dx = dock->x_pos;
2485 dy = dock->y_pos;
2487 /* if the dock is full */
2488 if (!redocking &&
2489 (dock->icon_count >= dock->max_icons)) {
2490 return False;
2493 /* exact position */
2494 if (req_y < dy)
2495 ex_y = (req_y - offset - dy)/ICON_SIZE;
2496 else
2497 ex_y = (req_y + offset - dy)/ICON_SIZE;
2499 if (req_x < dx)
2500 ex_x = (req_x - offset - dx)/ICON_SIZE;
2501 else
2502 ex_x = (req_x + offset - dx)/ICON_SIZE;
2504 /* check if the icon is outside the screen boundaries */
2506 WMRect rect;
2507 int flags;
2509 rect.pos.x = dx + ex_x*ICON_SIZE;
2510 rect.pos.y = dy + ex_y*ICON_SIZE;
2511 rect.size.width = rect.size.height = ICON_SIZE;
2513 wGetRectPlacementInfo(scr, rect, &flags);
2514 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
2515 return False;
2518 if (dock->type == WM_DOCK) {
2519 if (icon->dock != dock && ex_x != 0)
2520 return False;
2522 aicon = NULL;
2523 for (i=0; i<dock->max_icons; i++) {
2524 nicon = dock->icon_array[i];
2525 if (nicon && nicon->yindex == ex_y) {
2526 aicon = nicon;
2527 break;
2531 if (redocking) {
2532 int sig, done, closest;
2534 /* Possible cases when redocking:
2536 * icon dragged out of range of any slot -> false
2537 * icon dragged to range of free slot
2538 * icon dragged to range of same slot
2539 * icon dragged to range of different icon
2541 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2542 return False;
2544 if (ex_y>=0 && ex_y<=max_y_icons && (aicon==icon || !aicon)) {
2545 *ret_x = 0;
2546 *ret_y = ex_y;
2547 return True;
2550 /* start looking at the upper slot or lower? */
2551 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2552 sig = 1;
2553 else
2554 sig = -1;
2556 closest = -1;
2557 done = 0;
2558 /* look for closest free slot */
2559 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2560 int j;
2562 done = 1;
2563 closest = sig*(i/2) + ex_y;
2564 /* check if this slot is used */
2565 if (closest >= 0) {
2566 for (j = 0; j<dock->max_icons; j++) {
2567 if (dock->icon_array[j]
2568 && dock->icon_array[j]->yindex==closest) {
2569 /* slot is used by someone else */
2570 if (dock->icon_array[j]!=icon)
2571 done = 0;
2572 break;
2576 sig = -sig;
2578 if (done && closest >= 0 && closest <= max_y_icons &&
2579 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2581 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2582 *ret_x = 0;
2583 *ret_y = closest;
2584 return True;
2586 } else { /* !redocking */
2588 /* if slot is free and the icon is close enough, return it */
2589 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2590 *ret_x = 0;
2591 *ret_y = ex_y;
2592 return True;
2595 } else { /* CLIP */
2596 int neighbours = 0;
2597 int start, stop, k;
2599 start = icon->omnipresent ? 0 : scr->current_workspace;
2600 stop = icon->omnipresent ? scr->workspace_count : start+1;
2602 aicon = NULL;
2603 for (k=start; k<stop; k++) {
2604 WDock *tmp = scr->workspaces[k]->clip;
2605 if (!tmp)
2606 continue;
2607 for (i=0; i<tmp->max_icons; i++) {
2608 nicon = tmp->icon_array[i];
2609 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2610 aicon = nicon;
2611 break;
2614 if (aicon)
2615 break;
2617 for (k=start; k<stop; k++) {
2618 WDock *tmp = scr->workspaces[k]->clip;
2619 if (!tmp)
2620 continue;
2621 for (i=0; i<tmp->max_icons; i++) {
2622 nicon = tmp->icon_array[i];
2623 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2624 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2625 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2626 neighbours = 1;
2627 break;
2630 if (neighbours)
2631 break;
2634 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2635 *ret_x = ex_x;
2636 *ret_y = ex_y;
2637 return True;
2640 return False;
2644 static int onScreen(WScreen *scr, int x, int y, int sx, int ex, int sy, int ey)
2646 WMRect rect = { (x), (y), (ICON_SIZE), (ICON_SIZE) };
2647 int flags;
2649 wGetRectPlacementInfo(scr, rect, &flags);
2651 return !(flags & (XFLAG_DEAD | XFLAG_PARTIAL));
2654 #define ON_SCREEN(x, y, sx, ex, sy, ey) onScreen(scr, x, y, sx, ex, sy, ey)
2658 * returns true if it can find a free slot in the dock,
2659 * in which case it changes x_pos and y_pos accordingly.
2660 * Else returns false.
2662 Bool
2663 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2665 WScreen *scr = dock->screen_ptr;
2666 WAppIcon *btn;
2667 WAppIconChain *chain;
2668 unsigned char *slot_map;
2669 int mwidth;
2670 int r;
2671 int x, y;
2672 int i, done = False;
2673 int corner;
2674 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2675 int extra_count=0;
2677 if (dock->type == WM_CLIP &&
2678 dock != scr->workspaces[scr->current_workspace]->clip)
2679 extra_count = scr->global_icon_count;
2681 /* if the dock is full */
2682 if (dock->icon_count+extra_count >= dock->max_icons) {
2683 return False;
2686 if (!wPreferences.flags.nodock && scr->dock) {
2687 if (scr->dock->on_right_side)
2688 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2689 else
2690 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2693 if (ex < dock->x_pos)
2694 ex = dock->x_pos;
2695 if (sx > dock->x_pos+ICON_SIZE)
2696 sx = dock->x_pos+ICON_SIZE;
2697 #define C_NONE 0
2698 #define C_NW 1
2699 #define C_NE 2
2700 #define C_SW 3
2701 #define C_SE 4
2703 /* check if clip is in a corner */
2704 if (dock->type==WM_CLIP) {
2705 if (dock->x_pos < 1 && dock->y_pos < 1)
2706 corner = C_NE;
2707 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2708 corner = C_SE;
2709 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2710 corner = C_SW;
2711 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2712 corner = C_NW;
2713 else
2714 corner = C_NONE;
2715 } else
2716 corner = C_NONE;
2718 /* If the clip is in the corner, use only slots that are in the border
2719 * of the screen */
2720 if (corner!=C_NONE) {
2721 char *hmap, *vmap;
2722 int hcount, vcount;
2724 hcount = WMIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2725 vcount = WMIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2726 hmap = wmalloc(hcount+1);
2727 memset(hmap, 0, hcount+1);
2728 vmap = wmalloc(vcount+1);
2729 memset(vmap, 0, vcount+1);
2731 /* mark used positions */
2732 switch (corner) {
2733 case C_NE:
2734 for (i=0; i<dock->max_icons; i++) {
2735 btn = dock->icon_array[i];
2736 if (!btn)
2737 continue;
2739 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2740 vmap[btn->yindex] = 1;
2741 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2742 hmap[btn->xindex] = 1;
2744 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2745 btn = chain->aicon;
2746 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2747 vmap[btn->yindex] = 1;
2748 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2749 hmap[btn->xindex] = 1;
2751 break;
2752 case C_NW:
2753 for (i=0; i<dock->max_icons; i++) {
2754 btn = dock->icon_array[i];
2755 if (!btn)
2756 continue;
2758 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2759 vmap[btn->yindex] = 1;
2760 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2761 hmap[-btn->xindex] = 1;
2763 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2764 btn = chain->aicon;
2765 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2766 vmap[btn->yindex] = 1;
2767 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2768 hmap[-btn->xindex] = 1;
2770 break;
2771 case C_SE:
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;
2789 break;
2790 case C_SW:
2791 default:
2792 for (i=0; i<dock->max_icons; i++) {
2793 btn = dock->icon_array[i];
2794 if (!btn)
2795 continue;
2797 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2798 vmap[-btn->yindex] = 1;
2799 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2800 hmap[-btn->xindex] = 1;
2802 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2803 btn = chain->aicon;
2804 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2805 vmap[-btn->yindex] = 1;
2806 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2807 hmap[-btn->xindex] = 1;
2810 x=0; y=0;
2811 done = 0;
2812 /* search a vacant slot */
2813 for (i=1; i<WMAX(vcount, hcount); i++) {
2814 if (i < vcount && vmap[i]==0) {
2815 /* found a slot */
2816 x = 0;
2817 y = i;
2818 done = 1;
2819 break;
2820 } else if (i < hcount && hmap[i]==0) {
2821 /* found a slot */
2822 x = i;
2823 y = 0;
2824 done = 1;
2825 break;
2828 wfree(vmap);
2829 wfree(hmap);
2830 /* If found a slot, translate and return */
2831 if (done) {
2832 if (corner==C_NW || corner==C_NE) {
2833 *y_pos = y;
2834 } else {
2835 *y_pos = -y;
2837 if (corner==C_NE || corner==C_SE) {
2838 *x_pos = x;
2839 } else {
2840 *x_pos = -x;
2842 return True;
2844 /* else, try to find a slot somewhere else */
2847 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2848 * placed outside of screen */
2849 mwidth = (int)ceil(sqrt(dock->max_icons));
2851 /* In the worst case (the clip is in the corner of the screen),
2852 * the amount of icons that fit in the clip is smaller.
2853 * Double the map to get a safe value.
2855 mwidth += mwidth;
2857 r = (mwidth-1)/2;
2859 slot_map = wmalloc(mwidth*mwidth);
2860 memset(slot_map, 0, mwidth*mwidth);
2862 #define XY2OFS(x,y) (WMAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2864 /* mark used slots in the map. If the slot falls outside the map
2865 * (for example, when all icons are placed in line), ignore them. */
2866 for (i=0; i<dock->max_icons; i++) {
2867 btn = dock->icon_array[i];
2868 if (btn)
2869 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2871 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2872 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2874 /* Find closest slot from the center that is free by scanning the
2875 * map from the center to outward in circular passes.
2876 * This will not result in a neat layout, but will be optimal
2877 * in the sense that there will not be holes left.
2879 done = 0;
2880 for (i = 1; i <= r && !done; i++) {
2881 int tx, ty;
2883 /* top and bottom parts of the ring */
2884 for (x = -i; x <= i && !done; x++) {
2885 tx = dock->x_pos + x*ICON_SIZE;
2886 y = -i;
2887 ty = dock->y_pos + y*ICON_SIZE;
2888 if (slot_map[XY2OFS(x,y)]==0
2889 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2890 *x_pos = x;
2891 *y_pos = y;
2892 done = 1;
2893 break;
2895 y = i;
2896 ty = dock->y_pos + y*ICON_SIZE;
2897 if (slot_map[XY2OFS(x,y)]==0
2898 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2899 *x_pos = x;
2900 *y_pos = y;
2901 done = 1;
2902 break;
2905 /* left and right parts of the ring */
2906 for (y = -i+1; y <= i-1; y++) {
2907 ty = dock->y_pos + y*ICON_SIZE;
2908 x = -i;
2909 tx = dock->x_pos + x*ICON_SIZE;
2910 if (slot_map[XY2OFS(x,y)]==0
2911 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2912 *x_pos = x;
2913 *y_pos = y;
2914 done = 1;
2915 break;
2917 x = i;
2918 tx = dock->x_pos + x*ICON_SIZE;
2919 if (slot_map[XY2OFS(x,y)]==0
2920 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2921 *x_pos = x;
2922 *y_pos = y;
2923 done = 1;
2924 break;
2928 wfree(slot_map);
2929 #undef XY2OFS
2930 return done;
2934 static void
2935 moveDock(WDock *dock, int new_x, int new_y)
2937 WAppIcon *btn;
2938 int i;
2940 dock->x_pos = new_x;
2941 dock->y_pos = new_y;
2942 for (i=0; i<dock->max_icons; i++) {
2943 btn = dock->icon_array[i];
2944 if (btn) {
2945 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2946 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2947 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2953 static void
2954 swapDock(WDock *dock)
2956 WScreen *scr = dock->screen_ptr;
2957 WAppIcon *btn;
2958 int x, i;
2961 if (dock->on_right_side) {
2962 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2963 } else {
2964 x = dock->x_pos = DOCK_EXTRA_SPACE;
2967 for (i=0; i<dock->max_icons; i++) {
2968 btn = dock->icon_array[i];
2969 if (btn) {
2970 btn->x_pos = x;
2971 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2975 wScreenUpdateUsableArea(scr);
2979 static pid_t
2980 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2982 WScreen *scr = btn->icon->core->screen_ptr;
2983 pid_t pid;
2984 char **argv;
2985 int argc;
2986 char *cmdline;
2988 cmdline = ExpandOptions(scr, command);
2990 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2991 if (cmdline)
2992 wfree(cmdline);
2993 if (state)
2994 wfree(state);
2995 return 0;
2998 wtokensplit(cmdline, &argv, &argc);
3000 if (argv==NULL) {
3001 if (cmdline)
3002 wfree(cmdline);
3003 if (state)
3004 wfree(state);
3005 return 0;
3008 if ((pid=fork())==0) {
3009 char **args;
3010 int i;
3012 SetupEnvironment(scr);
3014 #ifdef HAVE_SETSID
3015 setsid();
3016 #endif
3018 args = malloc(sizeof(char*)*(argc+1));
3019 if (!args)
3020 exit(111);
3021 for (i=0; i<argc; i++) {
3022 args[i] = argv[i];
3024 args[argc] = NULL;
3025 execvp(argv[0], args);
3026 exit(111);
3028 wtokenfree(argv, argc);
3030 if (pid > 0) {
3031 if (!state) {
3032 state = wmalloc(sizeof(WSavedState));
3033 memset(state, 0, sizeof(WSavedState));
3034 state->hidden = -1;
3035 state->miniaturized = -1;
3036 state->shaded = -1;
3037 if (btn->dock==scr->dock || btn->omnipresent)
3038 state->workspace = -1;
3039 else
3040 state->workspace = scr->current_workspace;
3042 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
3043 state);
3044 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
3045 btn->dock);
3046 } else if (state) {
3047 wfree(state);
3049 wfree(cmdline);
3050 return pid;
3054 void
3055 wDockHideIcons(WDock *dock)
3057 int i;
3059 if (dock==NULL)
3060 return;
3062 for (i=1; i<dock->max_icons; i++) {
3063 if (dock->icon_array[i])
3064 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
3066 dock->mapped = 0;
3068 dockIconPaint(dock->icon_array[0]);
3072 void
3073 wDockShowIcons(WDock *dock)
3075 int i, newlevel;
3076 WAppIcon *btn;
3078 if (dock==NULL)
3079 return;
3081 btn = dock->icon_array[0];
3082 moveDock(dock, btn->x_pos, btn->y_pos);
3084 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
3085 ChangeStackingLevel(btn->icon->core, newlevel);
3087 for (i=1; i<dock->max_icons; i++) {
3088 if (dock->icon_array[i]) {
3089 MoveInStackListAbove(dock->icon_array[i]->icon->core,
3090 btn->icon->core);
3091 break;
3095 if (!dock->collapsed) {
3096 for (i=1; i<dock->max_icons; i++) {
3097 if (dock->icon_array[i]) {
3098 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
3102 dock->mapped = 1;
3104 dockIconPaint(btn);
3108 void
3109 wDockLower(WDock *dock)
3111 int i;
3113 for (i=0; i<dock->max_icons; i++) {
3114 if (dock->icon_array[i])
3115 wLowerFrame(dock->icon_array[i]->icon->core);
3120 void
3121 wDockRaise(WDock *dock)
3123 int i;
3125 for (i=dock->max_icons-1; i>=0; i--) {
3126 if (dock->icon_array[i])
3127 wRaiseFrame(dock->icon_array[i]->icon->core);
3132 void
3133 wDockRaiseLower(WDock *dock)
3135 if (!dock->icon_array[0]->icon->core->stacking->above
3136 ||(dock->icon_array[0]->icon->core->stacking->window_level
3137 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
3138 wDockLower(dock);
3139 else
3140 wDockRaise(dock);
3144 void
3145 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
3147 icon->launching = 0;
3148 icon->relaunching = 0;
3149 dockIconPaint(icon);
3153 WAppIcon*
3154 wDockFindIconForWindow(WDock *dock, Window window)
3156 WAppIcon *icon;
3157 int i;
3159 for (i=0; i<dock->max_icons; i++) {
3160 icon = dock->icon_array[i];
3161 if (icon && icon->main_window == window)
3162 return icon;
3164 return NULL;
3168 void
3169 wDockTrackWindowLaunch(WDock *dock, Window window)
3171 WAppIcon *icon;
3172 char *wm_class, *wm_instance;
3173 int i;
3174 Bool firstPass = True;
3175 Bool found = False;
3176 char *command = NULL;
3179 int argc;
3180 char **argv;
3182 if (XGetCommand(dpy, window, &argv, &argc)) {
3183 if (argc > 0 && argv != NULL)
3184 command = wtokenjoin(argv, argc);
3185 if (argv) {
3186 XFreeStringList(argv);
3191 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
3192 (!wm_class && !wm_instance))
3193 return;
3195 retry:
3196 for (i=0; i<dock->max_icons; i++) {
3197 icon = dock->icon_array[i];
3198 if (!icon)
3199 continue;
3201 /* app is already attached to icon */
3202 if (icon->main_window == window) {
3203 found = True;
3204 break;
3207 if ((icon->wm_instance || icon->wm_class)
3208 && (icon->launching || !icon->running)) {
3210 if (icon->wm_instance && wm_instance &&
3211 strcmp(icon->wm_instance, wm_instance)!=0) {
3212 continue;
3214 if (icon->wm_class && wm_class &&
3215 strcmp(icon->wm_class, wm_class)!=0) {
3216 continue;
3218 if (firstPass && command && strcmp(icon->command, command)!=0) {
3219 continue;
3222 if (!icon->relaunching) {
3223 WApplication *wapp;
3225 /* Possibly an application that was docked with dockit,
3226 * but the user did not update WMState to indicate that
3227 * it was docked by force */
3228 wapp = wApplicationOf(window);
3229 if (!wapp) {
3230 icon->forced_dock = 1;
3231 icon->running = 0;
3233 if (!icon->forced_dock)
3234 icon->main_window = window;
3237 found = True;
3238 if (!wPreferences.no_animations && !icon->launching &&
3239 !dock->screen_ptr->flags.startup && !dock->collapsed) {
3240 WAppIcon *aicon;
3241 int x0, y0;
3243 icon->launching = 1;
3244 dockIconPaint(icon);
3246 aicon = wAppIconCreateForDock(dock->screen_ptr, NULL,
3247 wm_instance, wm_class,
3248 TILE_NORMAL);
3249 PlaceIcon(dock->screen_ptr, &x0, &y0);
3250 wAppIconMove(aicon, x0, y0);
3251 /* Should this always be lowered? -Dan */
3252 if (dock->lowered)
3253 wLowerFrame(aicon->icon->core);
3254 XMapWindow(dpy, aicon->icon->core->window);
3255 aicon->launching = 1;
3256 wAppIconPaint(aicon);
3257 SlideWindow(aicon->icon->core->window, x0, y0,
3258 icon->x_pos, icon->y_pos);
3259 XUnmapWindow(dpy, aicon->icon->core->window);
3260 wAppIconDestroy(aicon);
3262 wDockFinishLaunch(dock, icon);
3263 break;
3267 if (firstPass && !found) {
3268 firstPass = False;
3269 goto retry;
3272 if (command)
3273 wfree(command);
3275 if (wm_class)
3276 XFree(wm_class);
3277 if (wm_instance)
3278 XFree(wm_instance);
3283 void
3284 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3286 if (!wPreferences.flags.noclip) {
3287 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3288 if (scr->current_workspace != workspace) {
3289 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3290 WAppIconChain *chain = scr->global_icons;
3292 while (chain) {
3293 moveIconBetweenDocks(chain->aicon->dock,
3294 scr->workspaces[workspace]->clip,
3295 chain->aicon, chain->aicon->xindex,
3296 chain->aicon->yindex);
3297 if (scr->workspaces[workspace]->clip->collapsed)
3298 XUnmapWindow(dpy, chain->aicon->icon->core->window);
3299 chain = chain->next;
3302 wDockHideIcons(old_clip);
3303 if (old_clip->auto_raise_lower) {
3304 if (old_clip->auto_raise_magic) {
3305 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3306 old_clip->auto_raise_magic = NULL;
3308 wDockLower(old_clip);
3310 if (old_clip->auto_collapse) {
3311 if (old_clip->auto_expand_magic) {
3312 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3313 old_clip->auto_expand_magic = NULL;
3315 old_clip->collapsed = 1;
3317 wDockShowIcons(scr->workspaces[workspace]->clip);
3319 if (scr->flags.clip_balloon_mapped)
3320 showClipBalloon(scr->clip_icon->dock, workspace);
3326 static void
3327 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3329 WAppIcon *icon;
3330 int i;
3332 for (i=0; i<dock->max_icons; i++) {
3333 icon = dock->icon_array[i];
3334 if (!icon)
3335 continue;
3337 if (icon->launching && icon->pid == pid) {
3338 if (!icon->relaunching) {
3339 icon->running = 0;
3340 icon->main_window = None;
3342 wDockFinishLaunch(dock, icon);
3343 icon->pid = 0;
3344 if (status==111) {
3345 char msg[PATH_MAX];
3346 char *cmd;
3348 if (icon->drop_launch)
3349 cmd = icon->dnd_command;
3350 else if (icon->paste_launch)
3351 cmd = icon->paste_command;
3352 else
3353 cmd = icon->command;
3355 snprintf(msg, sizeof(msg),
3356 _("Could not execute command \"%s\""), cmd);
3358 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3359 _("OK"), NULL, NULL);
3361 break;
3367 static void
3368 toggleLowered(WDock *dock)
3370 WAppIcon *tmp;
3371 int newlevel, i;
3373 /* lower/raise Dock */
3374 if (!dock->lowered) {
3375 newlevel = WMNormalLevel;
3376 dock->lowered = 1;
3377 } else {
3378 newlevel = WMDockLevel;
3379 dock->lowered = 0;
3382 for (i=0; i<dock->max_icons; i++) {
3383 tmp = dock->icon_array[i];
3384 if (!tmp)
3385 continue;
3387 ChangeStackingLevel(tmp->icon->core, newlevel);
3388 if (dock->lowered)
3389 wLowerFrame(tmp->icon->core);
3392 if (dock->type == WM_DOCK)
3393 wScreenUpdateUsableArea(dock->screen_ptr);
3397 static void
3398 toggleCollapsed(WDock *dock)
3400 if (dock->collapsed) {
3401 dock->collapsed = 0;
3402 wDockShowIcons(dock);
3404 else {
3405 dock->collapsed = 1;
3406 wDockHideIcons(dock);
3411 static void
3412 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3414 WScreen *scr = dock->screen_ptr;
3415 WObjDescriptor *desc;
3416 WMenuEntry *entry;
3417 WApplication *wapp = NULL;
3418 int index = 0;
3419 int x_pos;
3420 int n_selected;
3421 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3423 if (dock->type == WM_DOCK) {
3424 /* keep on top */
3425 entry = dock->menu->entries[index];
3426 entry->flags.indicator_on = !dock->lowered;
3427 entry->clientdata = dock;
3428 dock->menu->flags.realized = 0;
3429 } else {
3430 /* clip options */
3431 if (scr->clip_options)
3432 updateClipOptionsMenu(scr->clip_options, dock);
3434 n_selected = numberOfSelectedIcons(dock);
3436 /* Rename Workspace */
3437 entry = dock->menu->entries[++index];
3438 if (aicon == scr->clip_icon) {
3439 entry->callback = renameCallback;
3440 entry->clientdata = dock;
3441 entry->flags.indicator = 0;
3442 entry->text = _("Rename Workspace");
3443 } else {
3444 entry->callback = omnipresentCallback;
3445 entry->clientdata = aicon;
3446 if (n_selected > 0) {
3447 entry->flags.indicator = 0;
3448 entry->text = _("Toggle Omnipresent");
3449 } else {
3450 entry->flags.indicator = 1;
3451 entry->flags.indicator_on = aicon->omnipresent;
3452 entry->flags.indicator_type = MI_CHECK;
3453 entry->text = _("Omnipresent");
3457 /* select/unselect icon */
3458 entry = dock->menu->entries[++index];
3459 entry->clientdata = aicon;
3460 entry->flags.indicator_on = aicon->icon->selected;
3461 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3463 /* select/unselect all icons */
3464 entry = dock->menu->entries[++index];
3465 entry->clientdata = aicon;
3466 if (n_selected > 0)
3467 entry->text = _("Unselect All Icons");
3468 else
3469 entry->text = _("Select All Icons");
3470 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3472 /* keep icon(s) */
3473 entry = dock->menu->entries[++index];
3474 entry->clientdata = aicon;
3475 if (n_selected > 1)
3476 entry->text = _("Keep Icons");
3477 else
3478 entry->text = _("Keep Icon");
3479 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3481 /* this is the workspace submenu part */
3482 entry = dock->menu->entries[++index];
3483 if (n_selected > 1)
3484 entry->text = _("Move Icons To");
3485 else
3486 entry->text = _("Move Icon To");
3487 if (scr->clip_submenu)
3488 updateWorkspaceMenu(scr->clip_submenu, aicon);
3489 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3491 /* remove icon(s) */
3492 entry = dock->menu->entries[++index];
3493 entry->clientdata = aicon;
3494 if (n_selected > 1)
3495 entry->text = _("Remove Icons");
3496 else
3497 entry->text = _("Remove Icon");
3498 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3500 /* attract icon(s) */
3501 entry = dock->menu->entries[++index];
3502 entry->clientdata = aicon;
3504 dock->menu->flags.realized = 0;
3505 wMenuRealize(dock->menu);
3509 if (aicon->icon->owner) {
3510 wapp = wApplicationOf(aicon->icon->owner->main_window);
3511 } else {
3512 wapp = NULL;
3515 /* launch */
3516 entry = dock->menu->entries[++index];
3517 entry->clientdata = aicon;
3518 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3520 /* unhide here */
3521 entry = dock->menu->entries[++index];
3522 entry->clientdata = aicon;
3523 if (wapp && wapp->flags.hidden) {
3524 entry->text = _("Unhide Here");
3525 } else {
3526 entry->text = _("Bring Here");
3528 wMenuSetEnabled(dock->menu, index, appIsRunning);
3530 /* hide */
3531 entry = dock->menu->entries[++index];
3532 entry->clientdata = aicon;
3533 if (wapp && wapp->flags.hidden) {
3534 entry->text = _("Unhide");
3535 } else {
3536 entry->text = _("Hide");
3538 wMenuSetEnabled(dock->menu, index, appIsRunning);
3540 /* settings */
3541 entry = dock->menu->entries[++index];
3542 entry->clientdata = aicon;
3543 wMenuSetEnabled(dock->menu, index, !aicon->editing
3544 && !wPreferences.flags.noupdates);
3546 /* kill */
3547 entry = dock->menu->entries[++index];
3548 entry->clientdata = aicon;
3549 wMenuSetEnabled(dock->menu, index, appIsRunning);
3551 if (!dock->menu->flags.realized)
3552 wMenuRealize(dock->menu);
3554 if (dock->type == WM_CLIP) {
3555 /*x_pos = event->xbutton.x_root+2;*/
3556 x_pos = event->xbutton.x_root - dock->menu->frame->core->width/2 - 1;
3557 if (x_pos < 0) {
3558 x_pos = 0;
3559 } else if (x_pos + dock->menu->frame->core->width > scr->scr_width-2) {
3560 x_pos = scr->scr_width - dock->menu->frame->core->width - 4;
3562 } else {
3563 x_pos = dock->on_right_side ?
3564 scr->scr_width - dock->menu->frame->core->width - 3 : 0;
3567 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3569 /* allow drag select */
3570 event->xany.send_event = True;
3571 desc = &dock->menu->menu->descriptor;
3572 (*desc->handle_mousedown)(desc, event);
3576 static void
3577 openClipWorkspaceMenu(WScreen *scr, int x, int y)
3579 if (!scr->clip_ws_menu) {
3580 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
3582 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
3583 wMenuMapAt(scr->clip_ws_menu, x, y, False);
3587 /******************************************************************/
3588 static void
3589 iconDblClick(WObjDescriptor *desc, XEvent *event)
3591 WAppIcon *btn = desc->parent;
3592 WDock *dock = btn->dock;
3593 WApplication *wapp = NULL;
3594 int unhideHere = 0;
3596 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3597 wapp = wApplicationOf(btn->icon->owner->main_window);
3599 assert(wapp!=NULL);
3601 unhideHere = (event->xbutton.state & ShiftMask);
3603 /* go to the last workspace that the user worked on the app */
3604 if (wapp->last_workspace != dock->screen_ptr->current_workspace
3605 && !unhideHere) {
3606 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3609 wUnhideApplication(wapp, event->xbutton.button==Button2,
3610 unhideHere);
3612 if (event->xbutton.state & MOD_MASK) {
3613 wHideOtherApplications(btn->icon->owner);
3615 } else {
3616 if (event->xbutton.button==Button1) {
3618 if (event->xbutton.state & MOD_MASK) {
3619 /* raise/lower dock */
3620 toggleLowered(dock);
3621 } else if (btn == dock->screen_ptr->clip_icon) {
3622 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3623 toggleCollapsed(dock);
3624 else
3625 handleClipChangeWorkspace(dock->screen_ptr, event);
3626 } else if (btn->command) {
3627 if (!btn->launching &&
3628 (!btn->running || (event->xbutton.state & ControlMask))) {
3629 launchDockedApplication(btn, False);
3631 } else if (btn->xindex == 0 && btn->yindex == 0
3632 && btn->dock->type == WM_DOCK) {
3634 wShowGNUstepPanel(dock->screen_ptr);
3642 static void
3643 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3645 WScreen *scr = dock->screen_ptr;
3646 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3647 int x, y;
3648 XEvent ev;
3649 int grabbed = 0, swapped = 0, done;
3650 Pixmap ghost = None;
3651 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3653 #ifdef DEBUG
3654 puts("moving dock");
3655 #endif
3656 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3657 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3658 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3659 wwarning("pointer grab failed for dock move");
3661 y = 0;
3662 for (x=0; x<dock->max_icons; x++) {
3663 if (dock->icon_array[x]!=NULL &&
3664 dock->icon_array[x]->yindex > y)
3665 y = dock->icon_array[x]->yindex;
3667 y++;
3668 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3670 done = 0;
3671 while (!done) {
3672 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3673 |ButtonMotionMask|ExposureMask, &ev);
3674 switch (ev.type) {
3675 case Expose:
3676 WMHandleEvent(&ev);
3677 break;
3679 case MotionNotify:
3680 if (!grabbed) {
3681 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3682 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3683 XChangeActivePointerGrab(dpy, ButtonMotionMask
3684 |ButtonReleaseMask|ButtonPressMask,
3685 wCursor[WCUR_MOVE], CurrentTime);
3686 grabbed=1;
3688 break;
3690 if (dock->type == WM_CLIP) {
3691 x = ev.xmotion.x_root - ofs_x;
3692 y = ev.xmotion.y_root - ofs_y;
3693 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3695 moveDock(dock, x, y);
3696 } else {
3697 /* move vertically if pointer is inside the dock*/
3698 if ((dock->on_right_side &&
3699 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3700 || (!dock->on_right_side &&
3701 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3703 x = ev.xmotion.x_root - ofs_x;
3704 y = ev.xmotion.y_root - ofs_y;
3705 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3706 moveDock(dock, dock->x_pos, y);
3708 /* move horizontally to change sides */
3709 x = ev.xmotion.x_root - ofs_x;
3710 if (!dock->on_right_side) {
3712 /* is on left */
3714 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3715 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3716 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3717 if (superfluous && ghost==None) {
3718 ghost = MakeGhostDock(dock, dock->x_pos,
3719 scr->scr_width-ICON_SIZE
3720 -DOCK_EXTRA_SPACE-1,
3721 dock->y_pos);
3722 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3723 ghost);
3724 XClearWindow(dpy, scr->dock_shadow);
3726 XMapRaised(dpy, scr->dock_shadow);
3727 swapped = 1;
3728 } else {
3729 if (superfluous && ghost!=None) {
3730 XFreePixmap(dpy, ghost);
3731 ghost = None;
3733 XUnmapWindow(dpy, scr->dock_shadow);
3734 swapped = 0;
3736 } else {
3737 /* is on right */
3738 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3739 XMoveWindow(dpy, scr->dock_shadow,
3740 DOCK_EXTRA_SPACE, dock->y_pos);
3741 if (superfluous && ghost==None) {
3742 ghost = MakeGhostDock(dock, dock->x_pos,
3743 DOCK_EXTRA_SPACE, dock->y_pos);
3744 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3745 ghost);
3746 XClearWindow(dpy, scr->dock_shadow);
3748 XMapRaised(dpy, scr->dock_shadow);
3749 swapped = -1;
3750 } else {
3751 XUnmapWindow(dpy, scr->dock_shadow);
3752 swapped = 0;
3753 if (superfluous && ghost!=None) {
3754 XFreePixmap(dpy, ghost);
3755 ghost = None;
3760 break;
3762 case ButtonPress:
3763 break;
3765 case ButtonRelease:
3766 if (ev.xbutton.button != event->xbutton.button)
3767 break;
3768 XUngrabPointer(dpy, CurrentTime);
3769 XUnmapWindow(dpy, scr->dock_shadow);
3770 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3771 if (dock->type == WM_DOCK) {
3772 if (swapped!=0) {
3773 if (swapped>0)
3774 dock->on_right_side = 1;
3775 else
3776 dock->on_right_side = 0;
3777 swapDock(dock);
3778 wArrangeIcons(scr, False);
3781 done = 1;
3782 break;
3785 if (superfluous) {
3786 if (ghost!=None)
3787 XFreePixmap(dpy, ghost);
3788 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3790 #ifdef DEBUG
3791 puts("End dock move");
3792 #endif
3797 static void
3798 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3800 WScreen *scr = dock->screen_ptr;
3801 Window wins[2];
3802 WIcon *icon = aicon->icon;
3803 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3804 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3805 XEvent ev;
3806 int x = aicon->x_pos, y = aicon->y_pos;
3807 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3808 int shad_x = x, shad_y = y;
3809 int ix = aicon->xindex, iy = aicon->yindex;
3810 int tmp;
3811 Pixmap ghost = None;
3812 Bool docked;
3813 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3814 int omnipresent = aicon->omnipresent; /* this must be cached!!! */
3817 if (wPreferences.flags.noupdates)
3818 return;
3820 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3821 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3822 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3823 #ifdef DEBUG0
3824 wwarning("pointer grab failed for icon move");
3825 #endif
3828 if (!(event->xbutton.state & MOD_MASK))
3829 wRaiseFrame(icon->core);
3831 if (!wPreferences.flags.noclip)
3832 clip = scr->workspaces[scr->current_workspace]->clip;
3834 if (dock == scr->dock && !wPreferences.flags.noclip)
3835 dock2 = clip;
3836 else if (dock != scr->dock && !wPreferences.flags.nodock)
3837 dock2 = scr->dock;
3839 wins[0] = icon->core->window;
3840 wins[1] = scr->dock_shadow;
3841 XRestackWindows(dpy, wins, 2);
3842 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3843 ICON_SIZE, ICON_SIZE);
3844 if (superfluous) {
3845 if (icon->pixmap!=None)
3846 ghost = MakeGhostIcon(scr, icon->pixmap);
3847 else
3848 ghost = MakeGhostIcon(scr, icon->core->window);
3850 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3851 XClearWindow(dpy, scr->dock_shadow);
3853 XMapWindow(dpy, scr->dock_shadow);
3855 ondock = 1;
3858 while(1) {
3859 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3860 |ButtonMotionMask|ExposureMask, &ev);
3861 switch (ev.type) {
3862 case Expose:
3863 WMHandleEvent(&ev);
3864 break;
3866 case MotionNotify:
3867 if (!grabbed) {
3868 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3869 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3870 XChangeActivePointerGrab(dpy, ButtonMotionMask
3871 |ButtonReleaseMask|ButtonPressMask,
3872 wCursor[WCUR_MOVE], CurrentTime);
3873 grabbed=1;
3874 } else {
3875 break;
3879 if (omnipresent) {
3880 int i;
3881 for (i=0; i<scr->workspace_count; i++) {
3882 if (i == scr->current_workspace)
3883 continue;
3884 wDockShowIcons(scr->workspaces[i]->clip);
3888 x = ev.xmotion.x_root - ofs_x;
3889 y = ev.xmotion.y_root - ofs_y;
3890 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3891 if (tmp && dock2) {
3892 change_dock = 0;
3893 if (last_dock != dock && collapsed) {
3894 last_dock->collapsed = 1;
3895 wDockHideIcons(last_dock);
3896 collapsed = 0;
3898 if (!collapsed && (collapsed = dock->collapsed)) {
3899 dock->collapsed = 0;
3900 wDockShowIcons(dock);
3902 if (dock->auto_raise_lower)
3903 wDockRaise(dock);
3904 last_dock = dock;
3905 } else if (dock2) {
3906 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3907 if (tmp) {
3908 change_dock = 1;
3909 if (last_dock != dock2 && collapsed) {
3910 last_dock->collapsed = 1;
3911 wDockHideIcons(last_dock);
3912 collapsed = 0;
3914 if (!collapsed && (collapsed = dock2->collapsed)) {
3915 dock2->collapsed = 0;
3916 wDockShowIcons(dock2);
3918 if (dock2->auto_raise_lower)
3919 wDockRaise(dock2);
3920 last_dock = dock2;
3923 if (aicon->launching
3924 || aicon->lock
3925 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3926 || (!aicon->running && tmp)) {
3927 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3928 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3930 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3932 if (!ondock) {
3933 XMapWindow(dpy, scr->dock_shadow);
3935 ondock = 1;
3936 } else {
3937 if (ondock) {
3938 XUnmapWindow(dpy, scr->dock_shadow);
3940 ondock = 0;
3942 XMoveWindow(dpy, icon->core->window, x, y);
3943 break;
3945 case ButtonPress:
3946 break;
3948 case ButtonRelease:
3949 if (ev.xbutton.button != event->xbutton.button)
3950 break;
3951 XUngrabPointer(dpy, CurrentTime);
3952 if (ondock) {
3953 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3954 XUnmapWindow(dpy, scr->dock_shadow);
3955 if (!change_dock) {
3956 reattachIcon(dock, aicon, ix, iy);
3957 if (clip && dock!=clip && clip->auto_raise_lower)
3958 wDockLower(clip);
3959 } else {
3960 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3961 if (!docked) {
3962 /* Slide it back if dock rejected it */
3963 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3964 aicon->y_pos);
3965 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3967 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3968 collapsed = 0;
3971 } else {
3972 aicon->x_pos = x;
3973 aicon->y_pos = y;
3974 if (superfluous) {
3975 if (!aicon->running && !wPreferences.no_animations) {
3976 /* We need to deselect it, even if is deselected in
3977 * wDockDetach(), because else DoKaboom() will fail.
3979 if (aicon->icon->selected)
3980 wIconSelect(aicon->icon);
3982 wSoundPlay(WSOUND_KABOOM);
3983 DoKaboom(scr,aicon->icon->core->window, x, y);
3984 } else {
3985 wSoundPlay(WSOUND_UNDOCK);
3987 } else {
3988 wSoundPlay(WSOUND_UNDOCK);
3990 if (clip && clip->auto_raise_lower)
3991 wDockLower(clip);
3992 wDockDetach(dock, aicon);
3994 if (collapsed) {
3995 last_dock->collapsed = 1;
3996 wDockHideIcons(last_dock);
3997 collapsed = 0;
3999 if (superfluous) {
4000 if (ghost!=None)
4001 XFreePixmap(dpy, ghost);
4002 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
4004 if (omnipresent) {
4005 int i;
4006 for (i=0; i<scr->workspace_count; i++) {
4007 if (i == scr->current_workspace)
4008 continue;
4009 wDockHideIcons(scr->workspaces[i]->clip);
4013 #ifdef DEBUG
4014 puts("End icon move");
4015 #endif
4016 return;
4022 static int
4023 getClipButton(int px, int py)
4025 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
4027 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
4028 return CLIP_IDLE;
4030 if (py <= pt-((int)ICON_SIZE-1-px))
4031 return CLIP_FORWARD;
4032 else if (px <= pt-((int)ICON_SIZE-1-py))
4033 return CLIP_REWIND;
4035 return CLIP_IDLE;
4039 static void
4040 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
4042 XEvent ev;
4043 int done, direction, new_ws;
4044 int new_dir;
4045 WDock *clip = scr->clip_icon->dock;
4047 direction = getClipButton(event->xbutton.x, event->xbutton.y);
4049 clip->lclip_button_pushed = direction==CLIP_REWIND;
4050 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4052 wClipIconPaint(scr->clip_icon);
4053 done = 0;
4054 while(!done) {
4055 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
4056 |ButtonPressMask, &ev);
4057 switch (ev.type) {
4058 case Expose:
4059 WMHandleEvent(&ev);
4060 break;
4062 case MotionNotify:
4063 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
4064 if (new_dir != direction) {
4065 direction = new_dir;
4066 clip->lclip_button_pushed = direction==CLIP_REWIND;
4067 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4068 wClipIconPaint(scr->clip_icon);
4070 break;
4072 case ButtonPress:
4073 break;
4075 case ButtonRelease:
4076 if (ev.xbutton.button == event->xbutton.button)
4077 done = 1;
4081 clip->lclip_button_pushed = 0;
4082 clip->rclip_button_pushed = 0;
4084 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
4086 if (direction == CLIP_FORWARD) {
4087 if (scr->current_workspace < scr->workspace_count-1)
4088 wWorkspaceChange(scr, scr->current_workspace+1);
4089 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
4090 wWorkspaceChange(scr, scr->current_workspace+1);
4091 else if (wPreferences.ws_cycle)
4092 wWorkspaceChange(scr, 0);
4094 else if (direction == CLIP_REWIND) {
4095 if (scr->current_workspace > 0)
4096 wWorkspaceChange(scr, scr->current_workspace-1);
4097 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
4098 wWorkspaceChange(scr, scr->workspace_count-1);
4101 wClipIconPaint(scr->clip_icon);
4105 static void
4106 iconMouseDown(WObjDescriptor *desc, XEvent *event)
4108 WAppIcon *aicon = desc->parent;
4109 WDock *dock = aicon->dock;
4110 WScreen *scr = aicon->icon->core->screen_ptr;
4112 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
4113 return;
4115 scr->last_dock = dock;
4117 if (dock->menu->flags.mapped)
4118 wMenuUnmap(dock->menu);
4120 if (IsDoubleClick(scr, event)) {
4121 /* double-click was not in the main clip icon */
4122 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
4123 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
4124 iconDblClick(desc, event);
4125 return;
4129 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
4130 XUnmapWindow(dpy, scr->clip_balloon);
4131 scr->flags.clip_balloon_mapped = 0;
4134 #ifdef DEBUG
4135 puts("handling dock");
4136 #endif
4137 if (event->xbutton.button == Button1) {
4138 if (event->xbutton.state & MOD_MASK)
4139 wDockLower(dock);
4140 else
4141 wDockRaise(dock);
4143 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
4144 dock->type!=WM_DOCK) {
4145 wIconSelect(aicon->icon);
4146 return;
4149 if (aicon->yindex==0 && aicon->xindex==0) {
4150 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
4151 && dock->type==WM_CLIP)
4152 handleClipChangeWorkspace(scr, event);
4153 else
4154 handleDockMove(dock, aicon, event);
4155 } else
4156 handleIconMove(dock, aicon, event);
4158 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4159 aicon==scr->clip_icon) {
4160 openClipWorkspaceMenu(scr, event->xbutton.x_root+2,
4161 event->xbutton.y_root+2);
4162 if (scr->clip_ws_menu) {
4163 WMenu *menu;
4164 menu = scr->clip_ws_menu;
4165 desc = &menu->menu->descriptor;
4167 event->xany.send_event = True;
4168 (*desc->handle_mousedown)(desc, event);
4170 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4171 (event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon) {
4172 wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
4173 } else if (event->xbutton.button == Button3) {
4174 if (event->xbutton.send_event &&
4175 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
4176 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
4177 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
4178 wwarning("pointer grab failed for dockicon menu");
4179 return;
4182 openDockMenu(dock, aicon, event);
4183 } else if (event->xbutton.button == Button2) {
4184 WAppIcon *btn = desc->parent;
4186 if (!btn->launching &&
4187 (!btn->running || (event->xbutton.state & ControlMask))) {
4188 launchDockedApplication(btn, True);
4194 static void
4195 showClipBalloon(WDock *dock, int workspace)
4197 int w, h;
4198 int x, y;
4199 WScreen *scr = dock->screen_ptr;
4200 char *text;
4201 Window stack[2];
4203 scr->flags.clip_balloon_mapped = 1;
4204 XMapWindow(dpy, scr->clip_balloon);
4206 text = scr->workspaces[workspace]->name;
4208 w = WMWidthOfString(scr->clip_title_font, text, strlen(text));
4210 h = WMFontHeight(scr->clip_title_font);
4211 XResizeWindow(dpy, scr->clip_balloon, w, h);
4213 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
4214 y = dock->y_pos + ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
4216 if (x+w > scr->scr_width) {
4217 x = scr->scr_width - w;
4218 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
4219 y = dock->y_pos - h - 1;
4220 else
4221 y = dock->y_pos + ICON_SIZE;
4222 XRaiseWindow(dpy, scr->clip_balloon);
4223 } else {
4224 stack[0] = scr->clip_icon->icon->core->window;
4225 stack[1] = scr->clip_balloon;
4226 XRestackWindows(dpy, stack, 2);
4228 XMoveWindow(dpy, scr->clip_balloon, x, y);
4229 XClearWindow(dpy, scr->clip_balloon);
4230 WMDrawString(scr->wmscreen, scr->clip_balloon,
4231 scr->clip_title_color[CLIP_NORMAL],
4232 scr->clip_title_font,
4233 0, 0, text, strlen(text));
4237 static void
4238 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
4240 WAppIcon *btn = (WAppIcon*)desc->parent;
4241 WDock *dock;
4242 WScreen *scr;
4244 assert(event->type==EnterNotify);
4246 if(desc->parent_type!=WCLASS_DOCK_ICON)
4247 return;
4249 scr = btn->icon->core->screen_ptr;
4250 if (!btn->omnipresent)
4251 dock = btn->dock;
4252 else
4253 dock = scr->workspaces[scr->current_workspace]->clip;
4255 if (!dock || dock->type!=WM_CLIP)
4256 return;
4258 /* The auto raise/lower code */
4259 if (dock->auto_lower_magic) {
4260 WMDeleteTimerHandler(dock->auto_lower_magic);
4261 dock->auto_lower_magic = NULL;
4263 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
4264 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
4265 clipAutoRaise,
4266 (void *)dock);
4269 /* The auto expand/collapse code */
4270 if (dock->auto_collapse_magic) {
4271 WMDeleteTimerHandler(dock->auto_collapse_magic);
4272 dock->auto_collapse_magic = NULL;
4274 if (dock->auto_collapse && !dock->auto_expand_magic) {
4275 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
4276 clipAutoExpand,
4277 (void *)dock);
4280 if (btn->xindex == 0 && btn->yindex == 0)
4281 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4282 else {
4283 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4284 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
4285 dock->screen_ptr->flags.clip_balloon_mapped = 0;
4291 static void
4292 clipLeave(WDock *dock)
4294 XEvent event;
4295 WObjDescriptor *desc = NULL;
4297 if (!dock || dock->type!=WM_CLIP)
4298 return;
4300 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
4301 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
4302 (XPointer *)&desc)!=XCNOENT
4303 && desc && desc->parent_type==WCLASS_DOCK_ICON
4304 && ((WAppIcon*)desc->parent)->dock
4305 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
4306 /* We didn't left the Clip yet */
4307 XPutBackEvent(dpy, &event);
4308 return;
4311 XPutBackEvent(dpy, &event);
4312 } else {
4313 /* We entered a withdrawn window, so we're still in Clip */
4314 return;
4317 if (dock->auto_raise_magic) {
4318 WMDeleteTimerHandler(dock->auto_raise_magic);
4319 dock->auto_raise_magic = NULL;
4321 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
4322 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
4323 clipAutoLower,
4324 (void *)dock);
4327 if (dock->auto_expand_magic) {
4328 WMDeleteTimerHandler(dock->auto_expand_magic);
4329 dock->auto_expand_magic = NULL;
4331 if (dock->auto_collapse && !dock->auto_collapse_magic) {
4332 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
4333 clipAutoCollapse,
4334 (void *)dock);
4339 static void
4340 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
4342 WAppIcon *btn = (WAppIcon*)desc->parent;
4344 assert(event->type==LeaveNotify);
4346 if(desc->parent_type!=WCLASS_DOCK_ICON)
4347 return;
4349 clipLeave(btn->dock);
4353 static void
4354 clipAutoCollapse(void *cdata)
4356 WDock *dock = (WDock *)cdata;
4358 if (dock->type!=WM_CLIP)
4359 return;
4361 if (dock->auto_collapse) {
4362 dock->collapsed = 1;
4363 wDockHideIcons(dock);
4365 dock->auto_collapse_magic = NULL;
4369 static void
4370 clipAutoExpand(void *cdata)
4372 WDock *dock = (WDock *)cdata;
4374 if (dock->type!=WM_CLIP)
4375 return;
4377 if (dock->auto_collapse) {
4378 dock->collapsed = 0;
4379 wDockShowIcons(dock);
4381 dock->auto_expand_magic = NULL;
4385 static void
4386 clipAutoLower(void *cdata)
4388 WDock *dock = (WDock *)cdata;
4390 if (dock->type!=WM_CLIP)
4391 return;
4393 if (dock->auto_raise_lower)
4394 wDockLower(dock);
4396 dock->auto_lower_magic = NULL;
4400 static void
4401 clipAutoRaise(void *cdata)
4403 WDock *dock = (WDock *)cdata;
4405 if (dock->type!=WM_CLIP)
4406 return;
4408 if (dock->auto_raise_lower)
4409 wDockRaise(dock);
4411 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4412 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4415 dock->auto_raise_magic = NULL;
4419 static Bool
4420 iconCanBeOmnipresent(WAppIcon *aicon)
4422 WScreen *scr = aicon->icon->core->screen_ptr;
4423 WDock *clip;
4424 WAppIcon *btn;
4425 int i, j;
4427 for (i=0; i<scr->workspace_count; i++) {
4428 clip = scr->workspaces[i]->clip;
4430 if (clip == aicon->dock)
4431 continue;
4433 if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
4434 return False; /* Clip is full in some workspace */
4436 for (j=0; j<clip->max_icons; j++) {
4437 btn = clip->icon_array[j];
4438 if(btn && btn->xindex==aicon->xindex && btn->yindex==aicon->yindex)
4439 return False;
4443 return True;
4448 wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
4450 WScreen *scr = aicon->icon->core->screen_ptr;
4451 WAppIconChain *new_entry, *tmp, *tmp1;
4452 int status = WO_SUCCESS;
4454 if ((scr->dock && aicon->dock==scr->dock) || aicon==scr->clip_icon) {
4455 return WO_NOT_APPLICABLE;
4458 if (aicon->omnipresent == omnipresent)
4459 return WO_SUCCESS;
4461 if (omnipresent) {
4462 if (iconCanBeOmnipresent(aicon)) {
4463 aicon->omnipresent = 1;
4464 new_entry = wmalloc(sizeof(WAppIconChain));
4465 new_entry->aicon = aicon;
4466 new_entry->next = scr->global_icons;
4467 scr->global_icons = new_entry;
4468 scr->global_icon_count++;
4469 } else {
4470 aicon->omnipresent = 0;
4471 status = WO_FAILED;
4473 } else {
4474 aicon->omnipresent = 0;
4475 if (aicon == scr->global_icons->aicon) {
4476 tmp = scr->global_icons->next;
4477 wfree(scr->global_icons);
4478 scr->global_icons = tmp;
4479 scr->global_icon_count--;
4480 } else {
4481 tmp = scr->global_icons;
4482 while (tmp->next) {
4483 if (tmp->next->aicon == aicon) {
4484 tmp1 = tmp->next->next;
4485 wfree(tmp->next);
4486 tmp->next = tmp1;
4487 scr->global_icon_count--;
4488 break;
4490 tmp = tmp->next;
4495 wAppIconPaint(aicon);
4497 return status;