- removed unnecessary include of WINGsP.h in some places
[wmaker-crm.git] / src / dock.c
blob168aa80983fc45203a7a09a5924c8bdf99df5488
1 /* dock.c- built-in Dock module for WindowMaker
3 * Window Maker window manager
5 * Copyright (c) 1997-2002 Alfredo K. Kojima
6 * Copyright (c) 1998-2002 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"
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 if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos,
1716 &icon->y_pos)!=2)
1717 COMPLAIN("Position");
1719 /* check position sanity */
1720 if (icon->y_pos < 0)
1721 icon->y_pos = 0;
1722 else if (icon->y_pos > scr->scr_height-ICON_SIZE)
1723 icon->y_pos = scr->scr_height-ICON_SIZE;
1725 if (icon->x_pos < 0)
1726 icon->x_pos = 0;
1727 else if (icon->x_pos > scr->scr_width-ICON_SIZE)
1728 icon->x_pos = scr->scr_width-ICON_SIZE;
1732 #ifdef OFFIX_DND
1733 value = WMGetFromPLDictionary(clip_state, dDropCommand);
1734 if (value && WMIsPLString(value))
1735 icon->dnd_command = wstrdup(WMGetFromPLString(value));
1736 #endif
1738 value = WMGetFromPLDictionary(clip_state, dPasteCommand);
1739 if (value && WMIsPLString(value))
1740 icon->paste_command = wstrdup(WMGetFromPLString(value));
1742 WMReleasePropList(clip_state);
1744 return icon;
1748 WDock*
1749 wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type)
1751 WDock *dock;
1752 WMPropList *apps;
1753 WMPropList *value;
1754 WAppIcon *aicon, *old_top;
1755 int count, i;
1758 dock = wDockCreate(scr, type);
1760 if (!dock_state)
1761 return dock;
1763 if (dock_state)
1764 WMRetainPropList(dock_state);
1767 /* restore position */
1769 value = WMGetFromPLDictionary(dock_state, dPosition);
1771 if (value) {
1772 if (!WMIsPLString(value))
1773 COMPLAIN("Position");
1774 else {
1775 if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos,
1776 &dock->y_pos)!=2)
1777 COMPLAIN("Position");
1779 /* check position sanity */
1780 if (dock->y_pos < 0)
1781 dock->y_pos = 0;
1782 else if (dock->y_pos > scr->scr_height-ICON_SIZE)
1783 dock->y_pos = scr->scr_height - ICON_SIZE;
1785 /* This is no more needed. ??? */
1786 if (type == WM_CLIP) {
1787 if (dock->x_pos < 0)
1788 dock->x_pos = 0;
1789 else if (dock->x_pos > scr->scr_width-ICON_SIZE)
1790 dock->x_pos = scr->scr_width-ICON_SIZE;
1791 } else {
1792 if (dock->x_pos >= 0) {
1793 dock->x_pos = DOCK_EXTRA_SPACE;
1794 dock->on_right_side = 0;
1795 } else {
1796 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1797 dock->on_right_side = 1;
1803 /* restore lowered/raised state */
1805 dock->lowered = 0;
1807 value = WMGetFromPLDictionary(dock_state, dLowered);
1809 if (value) {
1810 if (!WMIsPLString(value))
1811 COMPLAIN("Lowered");
1812 else {
1813 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1814 dock->lowered = 1;
1819 /* restore collapsed state */
1821 dock->collapsed = 0;
1823 value = WMGetFromPLDictionary(dock_state, dCollapsed);
1825 if (value) {
1826 if (!WMIsPLString(value))
1827 COMPLAIN("Collapsed");
1828 else {
1829 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1830 dock->collapsed = 1;
1835 /* restore auto-collapsed state */
1837 value = WMGetFromPLDictionary(dock_state, dAutoCollapse);
1839 if (value) {
1840 if (!WMIsPLString(value))
1841 COMPLAIN("AutoCollapse");
1842 else {
1843 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1844 dock->auto_collapse = 1;
1845 dock->collapsed = 1;
1851 /* restore auto-raise/lower state */
1853 value = WMGetFromPLDictionary(dock_state, dAutoRaiseLower);
1855 if (value) {
1856 if (!WMIsPLString(value))
1857 COMPLAIN("AutoRaiseLower");
1858 else {
1859 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1860 dock->auto_raise_lower = 1;
1865 /* restore attract icons state */
1867 dock->attract_icons = 0;
1869 value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons);
1871 if (value) {
1872 if (!WMIsPLString(value))
1873 COMPLAIN("AutoAttractIcons");
1874 else {
1875 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1876 dock->attract_icons = 1;
1881 /* application list */
1884 WMPropList *tmp;
1885 char buffer[64];
1888 * When saving, it saves the dock state in
1889 * Applications and Applicationsnnn
1891 * When loading, it will first try Applicationsnnn.
1892 * If it does not exist, use Applications as default.
1895 snprintf(buffer, sizeof(buffer), "Applications%i", scr->scr_height);
1897 tmp = WMCreatePLString(buffer);
1898 apps = WMGetFromPLDictionary(dock_state, tmp);
1899 WMReleasePropList(tmp);
1901 if (!apps) {
1902 apps = WMGetFromPLDictionary(dock_state, dApplications);
1906 if (!apps) {
1907 goto finish;
1910 count = WMGetPropListItemCount(apps);
1912 if (count==0)
1913 goto finish;
1915 old_top = dock->icon_array[0];
1917 /* dock->icon_count is set to 1 when dock is created.
1918 * Since Clip is already restored, we want to keep it so for clip,
1919 * but for dock we may change the default top tile, so we set it to 0.
1921 if (type == WM_DOCK)
1922 dock->icon_count = 0;
1924 for (i=0; i<count; i++) {
1925 if (dock->icon_count >= dock->max_icons) {
1926 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1927 break;
1930 value = WMGetFromPLArray(apps, i);
1931 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1933 dock->icon_array[dock->icon_count] = aicon;
1935 if (aicon) {
1936 aicon->dock = dock;
1937 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1938 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1940 if (dock->lowered)
1941 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1942 else
1943 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1945 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1946 0, 0);
1948 if (!dock->collapsed)
1949 XMapWindow(dpy, aicon->icon->core->window);
1950 wRaiseFrame(aicon->icon->core);
1952 dock->icon_count++;
1953 } else if (dock->icon_count==0 && type==WM_DOCK)
1954 dock->icon_count++;
1957 /* if the first icon is not defined, use the default */
1958 if (dock->icon_array[0]==NULL) {
1959 /* update default icon */
1960 old_top->x_pos = dock->x_pos;
1961 old_top->y_pos = dock->y_pos;
1962 if (dock->lowered)
1963 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1964 else
1965 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1966 dock->icon_array[0] = old_top;
1967 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1968 /* we don't need to increment dock->icon_count here because it was
1969 * incremented in the loop above.
1971 } else if (old_top!=dock->icon_array[0]) {
1972 if (old_top == scr->clip_icon)
1973 scr->clip_icon = dock->icon_array[0];
1974 wAppIconDestroy(old_top);
1977 finish:
1978 if (dock_state)
1979 WMReleasePropList(dock_state);
1981 return dock;
1986 void
1987 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
1989 if (btn && btn->command && !btn->running && !btn->launching) {
1991 btn->drop_launch = 0;
1992 btn->paste_launch = 0;
1994 btn->pid = execCommand(btn, btn->command, state);
1996 if (btn->pid>0) {
1997 if (!btn->forced_dock && !btn->buggy_app) {
1998 btn->launching = 1;
1999 dockIconPaint(btn);
2002 } else {
2003 wfree(state);
2008 void
2009 wDockDoAutoLaunch(WDock *dock, int workspace)
2011 WAppIcon *btn;
2012 WSavedState *state;
2013 int i;
2015 for (i = 0; i < dock->max_icons; i++) {
2016 btn = dock->icon_array[i];
2017 if (!btn || !btn->auto_launch)
2018 continue;
2020 state = wmalloc(sizeof(WSavedState));
2021 memset(state, 0, sizeof(WSavedState));
2022 state->workspace = workspace;
2023 /* TODO: this is klugy and is very difficult to understand
2024 * what's going on. Try to clean up */
2025 wDockLaunchWithState(dock, btn, state);
2030 #ifdef OFFIX_DND
2031 static WDock*
2032 findDock(WScreen *scr, XEvent *event, int *icon_pos)
2034 WDock *dock;
2035 int i;
2037 *icon_pos = -1;
2038 if ((dock = scr->dock)!=NULL) {
2039 for (i=0; i<dock->max_icons; i++) {
2040 if (dock->icon_array[i]
2041 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2042 *icon_pos = i;
2043 break;
2047 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
2048 for (i=0; i<dock->max_icons; i++) {
2049 if (dock->icon_array[i]
2050 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2051 *icon_pos = i;
2052 break;
2056 if(*icon_pos>=0)
2057 return dock;
2058 return NULL;
2063 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
2065 WDock *dock;
2066 WAppIcon *btn;
2067 int icon_pos;
2069 dock = findDock(scr, event, &icon_pos);
2070 if (!dock)
2071 return False;
2074 * Return True if the drop was on an application icon window.
2075 * In this case, let the ClientMessage handler redirect the
2076 * message to the app.
2078 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
2079 return True;
2081 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
2082 scr->flags.dnd_data_convertion_status = 0;
2084 btn = dock->icon_array[icon_pos];
2086 if (!btn->forced_dock) {
2087 btn->relaunching = btn->running;
2088 btn->running = 1;
2090 if (btn->wm_instance || btn->wm_class) {
2091 WWindowAttributes attr;
2092 memset(&attr, 0, sizeof(WWindowAttributes));
2093 wDefaultFillAttributes(btn->icon->core->screen_ptr,
2094 btn->wm_instance,
2095 btn->wm_class, &attr, NULL, True);
2097 if (!attr.no_appicon)
2098 btn->launching = 1;
2099 else
2100 btn->running = 0;
2103 btn->paste_launch = 0;
2104 btn->drop_launch = 1;
2105 scr->last_dock = dock;
2106 btn->pid = execCommand(btn, btn->dnd_command, NULL);
2107 if (btn->pid>0) {
2108 dockIconPaint(btn);
2109 } else {
2110 btn->launching = 0;
2111 if (!btn->relaunching) {
2112 btn->running = 0;
2116 return False;
2118 #endif /* OFFIX_DND */
2122 Bool
2123 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2125 WWindow *wwin;
2126 char **argv;
2127 int argc;
2128 int index;
2130 wwin = icon->icon->owner;
2131 if (icon->command==NULL) {
2132 icon->editing = 0;
2133 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2135 icon->command = wtokenjoin(argv, argc);
2136 XFreeStringList(argv);
2137 } else {
2138 char *command=NULL;
2140 /* icon->forced_dock = 1;*/
2141 if (dock->type!=WM_CLIP || !icon->attracted) {
2142 icon->editing = 1;
2143 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
2144 _("Type the command used to launch the application"),
2145 &command)) {
2146 if (command && (command[0]==0 ||
2147 (command[0]=='-' && command[1]==0))) {
2148 wfree(command);
2149 command = NULL;
2151 icon->command = command;
2152 icon->editing = 0;
2153 } else {
2154 icon->editing = 0;
2155 if (command)
2156 wfree(command);
2157 /* If the target is the dock, reject the icon. If
2158 * the target is the clip, make it an attracted icon
2160 if (dock->type==WM_CLIP) {
2161 icon->attracted = 1;
2162 if (!icon->icon->shadowed) {
2163 icon->icon->shadowed = 1;
2164 icon->icon->force_paint = 1;
2166 } else {
2167 return False;
2172 } else {
2173 icon->editing = 0;
2176 for (index=1; index<dock->max_icons; index++)
2177 if (dock->icon_array[index] == NULL)
2178 break;
2179 /* if (index == dock->max_icons)
2180 return; */
2182 assert(index < dock->max_icons);
2184 dock->icon_array[index] = icon;
2185 icon->yindex = y;
2186 icon->xindex = x;
2188 icon->omnipresent = 0;
2190 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2191 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2193 dock->icon_count++;
2195 icon->running = 1;
2196 icon->launching = 0;
2197 icon->docked = 1;
2198 icon->dock = dock;
2199 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2200 if (dock->type == WM_CLIP) {
2201 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2202 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2204 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2205 icon->icon->core->descriptor.parent = icon;
2207 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2208 icon->icon->core);
2209 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2210 wAppIconPaint(icon);
2212 if (wPreferences.auto_arrange_icons)
2213 wArrangeIcons(dock->screen_ptr, True);
2215 #ifdef OFFIX_DND
2216 if (icon->command && !icon->dnd_command) {
2217 int len = strlen(icon->command)+8;
2218 icon->dnd_command = wmalloc(len);
2219 snprintf(icon->dnd_command, len, "%s %%d", icon->command);
2221 #endif
2223 if (icon->command && !icon->paste_command) {
2224 int len = strlen(icon->command)+8;
2225 icon->paste_command = wmalloc(len);
2226 snprintf(icon->paste_command, len, "%s %%s", icon->command);
2229 return True;
2233 void
2234 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2236 int index;
2238 for(index=1; index<dock->max_icons; index++) {
2239 if(dock->icon_array[index] == icon)
2240 break;
2242 assert(index < dock->max_icons);
2244 icon->yindex = y;
2245 icon->xindex = x;
2247 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2248 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2252 Bool
2253 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2255 WWindow *wwin;
2256 char **argv;
2257 int argc;
2258 int index;
2260 if (src == dest)
2261 return True; /* No move needed, we're already there */
2263 if (dest == NULL)
2264 return False;
2266 wwin = icon->icon->owner;
2269 * For the moment we can't do this if we move icons in Clip from one
2270 * workspace to other, because if we move two or more icons without
2271 * command, the dialog box will not be able to tell us to which of the
2272 * moved icons it applies. -Dan
2274 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2275 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2277 icon->command = wtokenjoin(argv, argc);
2278 XFreeStringList(argv);
2279 } else {
2280 char *command=NULL;
2282 icon->editing = 1;
2283 /* icon->forced_dock = 1;*/
2284 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2285 _("Type the command used to launch the application"),
2286 &command)) {
2287 if (command && (command[0]==0 ||
2288 (command[0]=='-' && command[1]==0))) {
2289 wfree(command);
2290 command = NULL;
2292 icon->command = command;
2293 } else {
2294 icon->editing = 0;
2295 if (command)
2296 wfree(command);
2297 return False;
2299 icon->editing = 0;
2303 if (dest->type == WM_DOCK)
2304 wClipMakeIconOmnipresent(icon, False);
2306 for(index=1; index<src->max_icons; index++) {
2307 if(src->icon_array[index] == icon)
2308 break;
2310 assert(index < src->max_icons);
2312 src->icon_array[index] = NULL;
2313 src->icon_count--;
2315 for(index=1; index<dest->max_icons; index++) {
2316 if(dest->icon_array[index] == NULL)
2317 break;
2319 /* if (index == dest->max_icons)
2320 return; */
2322 assert(index < dest->max_icons);
2324 dest->icon_array[index] = icon;
2325 icon->dock = dest;
2327 /* deselect the icon */
2328 if (icon->icon->selected)
2329 wIconSelect(icon->icon);
2331 if (dest->type == WM_DOCK) {
2332 icon->icon->core->descriptor.handle_enternotify = NULL;
2333 icon->icon->core->descriptor.handle_leavenotify = NULL;
2334 } else {
2335 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2336 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2339 /* set it to be kept when moving to dock.
2340 * Unless the icon does not have a command set
2342 if (icon->command && dest->type==WM_DOCK) {
2343 icon->attracted = 0;
2344 if (icon->icon->shadowed) {
2345 icon->icon->shadowed = 0;
2346 icon->icon->force_paint = 1;
2350 if (src->auto_collapse || src->auto_raise_lower)
2351 clipLeave(src);
2353 icon->yindex = y;
2354 icon->xindex = x;
2356 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2357 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2359 dest->icon_count++;
2361 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2362 icon->icon->core);
2363 wAppIconPaint(icon);
2365 return True;
2369 void
2370 wDockDetach(WDock *dock, WAppIcon *icon)
2372 int index;
2374 /* make the settings panel be closed */
2375 if (icon->panel) {
2376 DestroyDockAppSettingsPanel(icon->panel);
2379 /* This must be called before icon->dock is set to NULL.
2380 * Don't move it. -Dan
2382 wClipMakeIconOmnipresent(icon, False);
2384 icon->docked = 0;
2385 icon->dock = NULL;
2386 icon->attracted = 0;
2387 icon->auto_launch = 0;
2388 if (icon->icon->shadowed) {
2389 icon->icon->shadowed = 0;
2390 icon->icon->force_paint = 1;
2393 /* deselect the icon */
2394 if (icon->icon->selected)
2395 wIconSelect(icon->icon);
2397 if (icon->command) {
2398 wfree(icon->command);
2399 icon->command = NULL;
2401 #ifdef OFFIX_DND
2402 if (icon->dnd_command) {
2403 wfree(icon->dnd_command);
2404 icon->dnd_command = NULL;
2406 #endif
2407 if (icon->paste_command) {
2408 wfree(icon->paste_command);
2409 icon->paste_command = NULL;
2412 for (index=1; index<dock->max_icons; index++)
2413 if (dock->icon_array[index] == icon)
2414 break;
2415 assert(index < dock->max_icons);
2416 dock->icon_array[index] = NULL;
2417 icon->yindex = -1;
2418 icon->xindex = -1;
2420 dock->icon_count--;
2422 /* if the dock is not attached to an application or
2423 * the the application did not set the approriate hints yet,
2424 * destroy the icon */
2425 if (!icon->running || !wApplicationOf(icon->main_window))
2426 wAppIconDestroy(icon);
2427 else {
2428 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2429 icon->icon->core->descriptor.handle_enternotify = NULL;
2430 icon->icon->core->descriptor.handle_leavenotify = NULL;
2431 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2432 icon->icon->core->descriptor.parent = icon;
2434 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2436 wAppIconPaint(icon);
2437 if (wPreferences.auto_arrange_icons) {
2438 wArrangeIcons(dock->screen_ptr, True);
2441 if (dock->auto_collapse || dock->auto_raise_lower)
2442 clipLeave(dock);
2447 * returns the closest Dock slot index for the passed
2448 * coordinates.
2450 * Returns False if icon can't be docked.
2452 * Note: this function should NEVER alter ret_x or ret_y, unless it will
2453 * return True. -Dan
2455 Bool
2456 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2457 int *ret_x, int *ret_y, int redocking)
2459 WScreen *scr = dock->screen_ptr;
2460 int dx, dy;
2461 int ex_x, ex_y;
2462 int i, offset = ICON_SIZE/2;
2463 WAppIcon *aicon = NULL;
2464 WAppIcon *nicon = NULL;
2465 int max_y_icons, max_x_icons;
2467 max_x_icons = scr->scr_width/ICON_SIZE;
2468 max_y_icons = scr->scr_height/ICON_SIZE-1;
2470 if (wPreferences.flags.noupdates)
2471 return False;
2473 dx = dock->x_pos;
2474 dy = dock->y_pos;
2476 /* if the dock is full */
2477 if (!redocking &&
2478 (dock->icon_count >= dock->max_icons)) {
2479 return False;
2482 /* exact position */
2483 if (req_y < dy)
2484 ex_y = (req_y - offset - dy)/ICON_SIZE;
2485 else
2486 ex_y = (req_y + offset - dy)/ICON_SIZE;
2488 if (req_x < dx)
2489 ex_x = (req_x - offset - dx)/ICON_SIZE;
2490 else
2491 ex_x = (req_x + offset - dx)/ICON_SIZE;
2493 /* check if the icon is outside the screen boundaries */
2494 if (dx + ex_x*ICON_SIZE < -ICON_SIZE+2 ||
2495 dx + ex_x*ICON_SIZE >= scr->scr_width-1 ||
2496 dy + ex_y*ICON_SIZE < -ICON_SIZE+2 ||
2497 dy + ex_y*ICON_SIZE >= scr->scr_height-1)
2498 return False;
2500 if (dock->type == WM_DOCK) {
2501 if (icon->dock != dock && ex_x != 0)
2502 return False;
2504 aicon = NULL;
2505 for (i=0; i<dock->max_icons; i++) {
2506 nicon = dock->icon_array[i];
2507 if (nicon && nicon->yindex == ex_y) {
2508 aicon = nicon;
2509 break;
2513 if (redocking) {
2514 int sig, done, closest;
2516 /* Possible cases when redocking:
2518 * icon dragged out of range of any slot -> false
2519 * icon dragged to range of free slot
2520 * icon dragged to range of same slot
2521 * icon dragged to range of different icon
2523 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2524 return False;
2526 if (ex_y>=0 && ex_y<=max_y_icons && (aicon==icon || !aicon)) {
2527 *ret_x = 0;
2528 *ret_y = ex_y;
2529 return True;
2532 /* start looking at the upper slot or lower? */
2533 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2534 sig = 1;
2535 else
2536 sig = -1;
2538 closest = -1;
2539 done = 0;
2540 /* look for closest free slot */
2541 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2542 int j;
2544 done = 1;
2545 closest = sig*(i/2) + ex_y;
2546 /* check if this slot is used */
2547 if (closest >= 0) {
2548 for (j = 0; j<dock->max_icons; j++) {
2549 if (dock->icon_array[j]
2550 && dock->icon_array[j]->yindex==closest) {
2551 /* slot is used by someone else */
2552 if (dock->icon_array[j]!=icon)
2553 done = 0;
2554 break;
2558 sig = -sig;
2560 if (done && closest >= 0 && closest <= max_y_icons &&
2561 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2563 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2564 *ret_x = 0;
2565 *ret_y = closest;
2566 return True;
2568 } else { /* !redocking */
2570 /* if slot is free and the icon is close enough, return it */
2571 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2572 *ret_x = 0;
2573 *ret_y = ex_y;
2574 return True;
2577 } else { /* CLIP */
2578 int neighbours = 0;
2579 int start, stop, k;
2581 start = icon->omnipresent ? 0 : scr->current_workspace;
2582 stop = icon->omnipresent ? scr->workspace_count : start+1;
2584 aicon = NULL;
2585 for (k=start; k<stop; k++) {
2586 WDock *tmp = scr->workspaces[k]->clip;
2587 if (!tmp)
2588 continue;
2589 for (i=0; i<tmp->max_icons; i++) {
2590 nicon = tmp->icon_array[i];
2591 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2592 aicon = nicon;
2593 break;
2596 if (aicon)
2597 break;
2599 for (k=start; k<stop; k++) {
2600 WDock *tmp = scr->workspaces[k]->clip;
2601 if (!tmp)
2602 continue;
2603 for (i=0; i<tmp->max_icons; i++) {
2604 nicon = tmp->icon_array[i];
2605 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2606 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2607 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2608 neighbours = 1;
2609 break;
2612 if (neighbours)
2613 break;
2616 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2617 *ret_x = ex_x;
2618 *ret_y = ex_y;
2619 return True;
2622 return False;
2626 #define ON_SCREEN(x, y, sx, ex, sy, ey) \
2627 ((((x)+ICON_SIZE/2) >= (sx)) && (((y)+ICON_SIZE/2) >= (sy)) && \
2628 (((x) + (ICON_SIZE/2)) <= (ex)) && (((y) + (ICON_SIZE/2)) <= ey))
2632 * returns true if it can find a free slot in the dock,
2633 * in which case it changes x_pos and y_pos accordingly.
2634 * Else returns false.
2636 Bool
2637 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2639 WScreen *scr = dock->screen_ptr;
2640 WAppIcon *btn;
2641 WAppIconChain *chain;
2642 unsigned char *slot_map;
2643 int mwidth;
2644 int r;
2645 int x, y;
2646 int i, done = False;
2647 int corner;
2648 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2649 int extra_count=0;
2651 if (dock->type == WM_CLIP &&
2652 dock != scr->workspaces[scr->current_workspace]->clip)
2653 extra_count = scr->global_icon_count;
2655 /* if the dock is full */
2656 if (dock->icon_count+extra_count >= dock->max_icons) {
2657 return False;
2660 if (!wPreferences.flags.nodock && scr->dock) {
2661 if (scr->dock->on_right_side)
2662 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2663 else
2664 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2667 if (ex < dock->x_pos)
2668 ex = dock->x_pos;
2669 if (sx > dock->x_pos+ICON_SIZE)
2670 sx = dock->x_pos+ICON_SIZE;
2671 #define C_NONE 0
2672 #define C_NW 1
2673 #define C_NE 2
2674 #define C_SW 3
2675 #define C_SE 4
2677 /* check if clip is in a corner */
2678 if (dock->type==WM_CLIP) {
2679 if (dock->x_pos < 1 && dock->y_pos < 1)
2680 corner = C_NE;
2681 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2682 corner = C_SE;
2683 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2684 corner = C_SW;
2685 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2686 corner = C_NW;
2687 else
2688 corner = C_NONE;
2689 } else
2690 corner = C_NONE;
2692 /* If the clip is in the corner, use only slots that are in the border
2693 * of the screen */
2694 if (corner!=C_NONE) {
2695 char *hmap, *vmap;
2696 int hcount, vcount;
2698 hcount = WMIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2699 vcount = WMIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2700 hmap = wmalloc(hcount+1);
2701 memset(hmap, 0, hcount+1);
2702 vmap = wmalloc(vcount+1);
2703 memset(vmap, 0, vcount+1);
2705 /* mark used positions */
2706 switch (corner) {
2707 case C_NE:
2708 for (i=0; i<dock->max_icons; i++) {
2709 btn = dock->icon_array[i];
2710 if (!btn)
2711 continue;
2713 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2714 vmap[btn->yindex] = 1;
2715 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2716 hmap[btn->xindex] = 1;
2718 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2719 btn = chain->aicon;
2720 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2721 vmap[btn->yindex] = 1;
2722 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2723 hmap[btn->xindex] = 1;
2725 break;
2726 case C_NW:
2727 for (i=0; i<dock->max_icons; i++) {
2728 btn = dock->icon_array[i];
2729 if (!btn)
2730 continue;
2732 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2733 vmap[btn->yindex] = 1;
2734 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2735 hmap[-btn->xindex] = 1;
2737 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2738 btn = chain->aicon;
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 break;
2745 case C_SE:
2746 for (i=0; i<dock->max_icons; i++) {
2747 btn = dock->icon_array[i];
2748 if (!btn)
2749 continue;
2751 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2752 vmap[-btn->yindex] = 1;
2753 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2754 hmap[btn->xindex] = 1;
2756 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2757 btn = chain->aicon;
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 break;
2764 case C_SW:
2765 default:
2766 for (i=0; i<dock->max_icons; i++) {
2767 btn = dock->icon_array[i];
2768 if (!btn)
2769 continue;
2771 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2772 vmap[-btn->yindex] = 1;
2773 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2774 hmap[-btn->xindex] = 1;
2776 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2777 btn = chain->aicon;
2778 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2779 vmap[-btn->yindex] = 1;
2780 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2781 hmap[-btn->xindex] = 1;
2784 x=0; y=0;
2785 done = 0;
2786 /* search a vacant slot */
2787 for (i=1; i<WMAX(vcount, hcount); i++) {
2788 if (i < vcount && vmap[i]==0) {
2789 /* found a slot */
2790 x = 0;
2791 y = i;
2792 done = 1;
2793 break;
2794 } else if (i < hcount && hmap[i]==0) {
2795 /* found a slot */
2796 x = i;
2797 y = 0;
2798 done = 1;
2799 break;
2802 wfree(vmap);
2803 wfree(hmap);
2804 /* If found a slot, translate and return */
2805 if (done) {
2806 if (corner==C_NW || corner==C_NE) {
2807 *y_pos = y;
2808 } else {
2809 *y_pos = -y;
2811 if (corner==C_NE || corner==C_SE) {
2812 *x_pos = x;
2813 } else {
2814 *x_pos = -x;
2816 return True;
2818 /* else, try to find a slot somewhere else */
2821 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2822 * placed outside of screen */
2823 mwidth = (int)ceil(sqrt(dock->max_icons));
2825 /* In the worst case (the clip is in the corner of the screen),
2826 * the amount of icons that fit in the clip is smaller.
2827 * Double the map to get a safe value.
2829 mwidth += mwidth;
2831 r = (mwidth-1)/2;
2833 slot_map = wmalloc(mwidth*mwidth);
2834 memset(slot_map, 0, mwidth*mwidth);
2836 #define XY2OFS(x,y) (WMAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2838 /* mark used slots in the map. If the slot falls outside the map
2839 * (for example, when all icons are placed in line), ignore them. */
2840 for (i=0; i<dock->max_icons; i++) {
2841 btn = dock->icon_array[i];
2842 if (btn)
2843 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2845 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2846 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2848 /* Find closest slot from the center that is free by scanning the
2849 * map from the center to outward in circular passes.
2850 * This will not result in a neat layout, but will be optimal
2851 * in the sense that there will not be holes left.
2853 done = 0;
2854 for (i = 1; i <= r && !done; i++) {
2855 int tx, ty;
2857 /* top and bottom parts of the ring */
2858 for (x = -i; x <= i && !done; x++) {
2859 tx = dock->x_pos + x*ICON_SIZE;
2860 y = -i;
2861 ty = dock->y_pos + y*ICON_SIZE;
2862 if (slot_map[XY2OFS(x,y)]==0
2863 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2864 *x_pos = x;
2865 *y_pos = y;
2866 done = 1;
2867 break;
2869 y = i;
2870 ty = dock->y_pos + y*ICON_SIZE;
2871 if (slot_map[XY2OFS(x,y)]==0
2872 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2873 *x_pos = x;
2874 *y_pos = y;
2875 done = 1;
2876 break;
2879 /* left and right parts of the ring */
2880 for (y = -i+1; y <= i-1; y++) {
2881 ty = dock->y_pos + y*ICON_SIZE;
2882 x = -i;
2883 tx = dock->x_pos + x*ICON_SIZE;
2884 if (slot_map[XY2OFS(x,y)]==0
2885 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2886 *x_pos = x;
2887 *y_pos = y;
2888 done = 1;
2889 break;
2891 x = i;
2892 tx = dock->x_pos + x*ICON_SIZE;
2893 if (slot_map[XY2OFS(x,y)]==0
2894 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2895 *x_pos = x;
2896 *y_pos = y;
2897 done = 1;
2898 break;
2902 wfree(slot_map);
2903 #undef XY2OFS
2904 return done;
2908 static void
2909 moveDock(WDock *dock, int new_x, int new_y)
2911 WAppIcon *btn;
2912 int i;
2914 dock->x_pos = new_x;
2915 dock->y_pos = new_y;
2916 for (i=0; i<dock->max_icons; i++) {
2917 btn = dock->icon_array[i];
2918 if (btn) {
2919 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2920 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2921 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2927 static void
2928 swapDock(WDock *dock)
2930 WScreen *scr = dock->screen_ptr;
2931 WAppIcon *btn;
2932 int x, i;
2935 if (dock->on_right_side) {
2936 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2937 } else {
2938 x = dock->x_pos = DOCK_EXTRA_SPACE;
2941 for (i=0; i<dock->max_icons; i++) {
2942 btn = dock->icon_array[i];
2943 if (btn) {
2944 btn->x_pos = x;
2945 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2949 wScreenUpdateUsableArea(scr);
2953 static pid_t
2954 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2956 WScreen *scr = btn->icon->core->screen_ptr;
2957 pid_t pid;
2958 char **argv;
2959 int argc;
2960 char *cmdline;
2962 cmdline = ExpandOptions(scr, command);
2964 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2965 if (cmdline)
2966 wfree(cmdline);
2967 if (state)
2968 wfree(state);
2969 return 0;
2972 wtokensplit(cmdline, &argv, &argc);
2974 if (argv==NULL) {
2975 if (cmdline)
2976 wfree(cmdline);
2977 if (state)
2978 wfree(state);
2979 return 0;
2982 if ((pid=fork())==0) {
2983 char **args;
2984 int i;
2986 SetupEnvironment(scr);
2988 #ifdef HAVE_SETSID
2989 setsid();
2990 #endif
2992 args = malloc(sizeof(char*)*(argc+1));
2993 if (!args)
2994 exit(111);
2995 for (i=0; i<argc; i++) {
2996 args[i] = argv[i];
2998 args[argc] = NULL;
2999 execvp(argv[0], args);
3000 exit(111);
3002 wtokenfree(argv, argc);
3004 if (pid > 0) {
3005 if (!state) {
3006 state = wmalloc(sizeof(WSavedState));
3007 memset(state, 0, sizeof(WSavedState));
3008 state->hidden = -1;
3009 state->miniaturized = -1;
3010 state->shaded = -1;
3011 if (btn->dock==scr->dock || btn->omnipresent)
3012 state->workspace = -1;
3013 else
3014 state->workspace = scr->current_workspace;
3016 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
3017 state);
3018 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
3019 btn->dock);
3020 } else if (state) {
3021 wfree(state);
3023 wfree(cmdline);
3024 return pid;
3028 void
3029 wDockHideIcons(WDock *dock)
3031 int i;
3033 if (dock==NULL)
3034 return;
3036 for (i=1; i<dock->max_icons; i++) {
3037 if (dock->icon_array[i])
3038 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
3040 dock->mapped = 0;
3042 dockIconPaint(dock->icon_array[0]);
3046 void
3047 wDockShowIcons(WDock *dock)
3049 int i, newlevel;
3050 WAppIcon *btn;
3052 if (dock==NULL)
3053 return;
3055 btn = dock->icon_array[0];
3056 moveDock(dock, btn->x_pos, btn->y_pos);
3058 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
3059 ChangeStackingLevel(btn->icon->core, newlevel);
3061 for (i=1; i<dock->max_icons; i++) {
3062 if (dock->icon_array[i]) {
3063 MoveInStackListAbove(dock->icon_array[i]->icon->core,
3064 btn->icon->core);
3065 break;
3069 if (!dock->collapsed) {
3070 for (i=1; i<dock->max_icons; i++) {
3071 if (dock->icon_array[i]) {
3072 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
3076 dock->mapped = 1;
3078 dockIconPaint(btn);
3082 void
3083 wDockLower(WDock *dock)
3085 int i;
3087 for (i=0; i<dock->max_icons; i++) {
3088 if (dock->icon_array[i])
3089 wLowerFrame(dock->icon_array[i]->icon->core);
3094 void
3095 wDockRaise(WDock *dock)
3097 int i;
3099 for (i=dock->max_icons-1; i>=0; i--) {
3100 if (dock->icon_array[i])
3101 wRaiseFrame(dock->icon_array[i]->icon->core);
3106 void
3107 wDockRaiseLower(WDock *dock)
3109 if (!dock->icon_array[0]->icon->core->stacking->above
3110 ||(dock->icon_array[0]->icon->core->stacking->window_level
3111 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
3112 wDockLower(dock);
3113 else
3114 wDockRaise(dock);
3118 void
3119 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
3121 icon->launching = 0;
3122 icon->relaunching = 0;
3123 dockIconPaint(icon);
3127 WAppIcon*
3128 wDockFindIconForWindow(WDock *dock, Window window)
3130 WAppIcon *icon;
3131 int i;
3133 for (i=0; i<dock->max_icons; i++) {
3134 icon = dock->icon_array[i];
3135 if (icon && icon->main_window == window)
3136 return icon;
3138 return NULL;
3142 void
3143 wDockTrackWindowLaunch(WDock *dock, Window window)
3145 WAppIcon *icon;
3146 char *wm_class, *wm_instance;
3147 int i;
3148 Bool firstPass = True;
3149 Bool found = False;
3150 char *command = NULL;
3153 int argc;
3154 char **argv;
3156 if (XGetCommand(dpy, window, &argv, &argc)) {
3157 if (argc > 0 && argv != NULL)
3158 command = wtokenjoin(argv, argc);
3159 if (argv) {
3160 XFreeStringList(argv);
3165 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
3166 (!wm_class && !wm_instance))
3167 return;
3169 retry:
3170 for (i=0; i<dock->max_icons; i++) {
3171 icon = dock->icon_array[i];
3172 if (!icon)
3173 continue;
3175 /* app is already attached to icon */
3176 if (icon->main_window == window) {
3177 found = True;
3178 break;
3181 if ((icon->wm_instance || icon->wm_class)
3182 && (icon->launching || !icon->running)) {
3184 if (icon->wm_instance && wm_instance &&
3185 strcmp(icon->wm_instance, wm_instance)!=0) {
3186 continue;
3188 if (icon->wm_class && wm_class &&
3189 strcmp(icon->wm_class, wm_class)!=0) {
3190 continue;
3192 if (firstPass && command && strcmp(icon->command, command)!=0) {
3193 continue;
3196 if (!icon->relaunching) {
3197 WApplication *wapp;
3199 /* Possibly an application that was docked with dockit,
3200 * but the user did not update WMState to indicate that
3201 * it was docked by force */
3202 wapp = wApplicationOf(window);
3203 if (!wapp) {
3204 icon->forced_dock = 1;
3205 icon->running = 0;
3207 if (!icon->forced_dock)
3208 icon->main_window = window;
3211 found = True;
3212 if (!wPreferences.no_animations && !icon->launching &&
3213 !dock->screen_ptr->flags.startup && !dock->collapsed) {
3214 WAppIcon *aicon;
3215 int x0, y0;
3217 icon->launching = 1;
3218 dockIconPaint(icon);
3220 aicon = wAppIconCreateForDock(dock->screen_ptr, NULL,
3221 wm_instance, wm_class,
3222 TILE_NORMAL);
3223 PlaceIcon(dock->screen_ptr, &x0, &y0);
3224 wAppIconMove(aicon, x0, y0);
3225 /* Should this always be lowered? -Dan */
3226 if (dock->lowered)
3227 wLowerFrame(aicon->icon->core);
3228 XMapWindow(dpy, aicon->icon->core->window);
3229 aicon->launching = 1;
3230 wAppIconPaint(aicon);
3231 SlideWindow(aicon->icon->core->window, x0, y0,
3232 icon->x_pos, icon->y_pos);
3233 XUnmapWindow(dpy, aicon->icon->core->window);
3234 wAppIconDestroy(aicon);
3236 wDockFinishLaunch(dock, icon);
3237 break;
3241 if (firstPass && !found) {
3242 firstPass = False;
3243 goto retry;
3246 if (command)
3247 wfree(command);
3249 if (wm_class)
3250 XFree(wm_class);
3251 if (wm_instance)
3252 XFree(wm_instance);
3257 void
3258 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3260 if (!wPreferences.flags.noclip) {
3261 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3262 if (scr->current_workspace != workspace) {
3263 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3264 WAppIconChain *chain = scr->global_icons;
3266 while (chain) {
3267 moveIconBetweenDocks(chain->aicon->dock,
3268 scr->workspaces[workspace]->clip,
3269 chain->aicon, chain->aicon->xindex,
3270 chain->aicon->yindex);
3271 if (scr->workspaces[workspace]->clip->collapsed)
3272 XUnmapWindow(dpy, chain->aicon->icon->core->window);
3273 chain = chain->next;
3276 wDockHideIcons(old_clip);
3277 if (old_clip->auto_raise_lower) {
3278 if (old_clip->auto_raise_magic) {
3279 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3280 old_clip->auto_raise_magic = NULL;
3282 wDockLower(old_clip);
3284 if (old_clip->auto_collapse) {
3285 if (old_clip->auto_expand_magic) {
3286 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3287 old_clip->auto_expand_magic = NULL;
3289 old_clip->collapsed = 1;
3291 wDockShowIcons(scr->workspaces[workspace]->clip);
3293 if (scr->flags.clip_balloon_mapped)
3294 showClipBalloon(scr->clip_icon->dock, workspace);
3300 static void
3301 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3303 WAppIcon *icon;
3304 int i;
3306 for (i=0; i<dock->max_icons; i++) {
3307 icon = dock->icon_array[i];
3308 if (!icon)
3309 continue;
3311 if (icon->launching && icon->pid == pid) {
3312 if (!icon->relaunching) {
3313 icon->running = 0;
3314 icon->main_window = None;
3316 wDockFinishLaunch(dock, icon);
3317 icon->pid = 0;
3318 if (status==111) {
3319 char msg[PATH_MAX];
3320 char *cmd;
3322 if (icon->drop_launch)
3323 cmd = icon->dnd_command;
3324 else if (icon->paste_launch)
3325 cmd = icon->paste_command;
3326 else
3327 cmd = icon->command;
3329 snprintf(msg, sizeof(msg),
3330 _("Could not execute command \"%s\""), cmd);
3332 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3333 _("OK"), NULL, NULL);
3335 break;
3341 static void
3342 toggleLowered(WDock *dock)
3344 WAppIcon *tmp;
3345 int newlevel, i;
3347 /* lower/raise Dock */
3348 if (!dock->lowered) {
3349 newlevel = WMNormalLevel;
3350 dock->lowered = 1;
3351 } else {
3352 newlevel = WMDockLevel;
3353 dock->lowered = 0;
3356 for (i=0; i<dock->max_icons; i++) {
3357 tmp = dock->icon_array[i];
3358 if (!tmp)
3359 continue;
3361 ChangeStackingLevel(tmp->icon->core, newlevel);
3362 if (dock->lowered)
3363 wLowerFrame(tmp->icon->core);
3366 if (dock->type == WM_DOCK)
3367 wScreenUpdateUsableArea(dock->screen_ptr);
3371 static void
3372 toggleCollapsed(WDock *dock)
3374 if (dock->collapsed) {
3375 dock->collapsed = 0;
3376 wDockShowIcons(dock);
3378 else {
3379 dock->collapsed = 1;
3380 wDockHideIcons(dock);
3385 static void
3386 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3388 WScreen *scr = dock->screen_ptr;
3389 WObjDescriptor *desc;
3390 WMenuEntry *entry;
3391 WApplication *wapp = NULL;
3392 int index = 0;
3393 int x_pos;
3394 int n_selected;
3395 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3397 if (dock->type == WM_DOCK) {
3398 /* keep on top */
3399 entry = dock->menu->entries[index];
3400 entry->flags.indicator_on = !dock->lowered;
3401 entry->clientdata = dock;
3402 dock->menu->flags.realized = 0;
3403 } else {
3404 /* clip options */
3405 if (scr->clip_options)
3406 updateClipOptionsMenu(scr->clip_options, dock);
3408 n_selected = numberOfSelectedIcons(dock);
3410 /* Rename Workspace */
3411 entry = dock->menu->entries[++index];
3412 if (aicon == scr->clip_icon) {
3413 entry->callback = renameCallback;
3414 entry->clientdata = dock;
3415 entry->flags.indicator = 0;
3416 entry->text = _("Rename Workspace");
3417 } else {
3418 entry->callback = omnipresentCallback;
3419 entry->clientdata = aicon;
3420 if (n_selected > 0) {
3421 entry->flags.indicator = 0;
3422 entry->text = _("Toggle Omnipresent");
3423 } else {
3424 entry->flags.indicator = 1;
3425 entry->flags.indicator_on = aicon->omnipresent;
3426 entry->flags.indicator_type = MI_CHECK;
3427 entry->text = _("Omnipresent");
3431 /* select/unselect icon */
3432 entry = dock->menu->entries[++index];
3433 entry->clientdata = aicon;
3434 entry->flags.indicator_on = aicon->icon->selected;
3435 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3437 /* select/unselect all icons */
3438 entry = dock->menu->entries[++index];
3439 entry->clientdata = aicon;
3440 if (n_selected > 0)
3441 entry->text = _("Unselect All Icons");
3442 else
3443 entry->text = _("Select All Icons");
3444 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3446 /* keep icon(s) */
3447 entry = dock->menu->entries[++index];
3448 entry->clientdata = aicon;
3449 if (n_selected > 1)
3450 entry->text = _("Keep Icons");
3451 else
3452 entry->text = _("Keep Icon");
3453 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3455 /* this is the workspace submenu part */
3456 entry = dock->menu->entries[++index];
3457 if (n_selected > 1)
3458 entry->text = _("Move Icons To");
3459 else
3460 entry->text = _("Move Icon To");
3461 if (scr->clip_submenu)
3462 updateWorkspaceMenu(scr->clip_submenu, aicon);
3463 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3465 /* remove icon(s) */
3466 entry = dock->menu->entries[++index];
3467 entry->clientdata = aicon;
3468 if (n_selected > 1)
3469 entry->text = _("Remove Icons");
3470 else
3471 entry->text = _("Remove Icon");
3472 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3474 /* attract icon(s) */
3475 entry = dock->menu->entries[++index];
3476 entry->clientdata = aicon;
3478 dock->menu->flags.realized = 0;
3479 wMenuRealize(dock->menu);
3483 if (aicon->icon->owner) {
3484 wapp = wApplicationOf(aicon->icon->owner->main_window);
3485 } else {
3486 wapp = NULL;
3489 /* launch */
3490 entry = dock->menu->entries[++index];
3491 entry->clientdata = aicon;
3492 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3494 /* unhide here */
3495 entry = dock->menu->entries[++index];
3496 entry->clientdata = aicon;
3497 if (wapp && wapp->flags.hidden) {
3498 entry->text = _("Unhide Here");
3499 } else {
3500 entry->text = _("Bring Here");
3502 wMenuSetEnabled(dock->menu, index, appIsRunning);
3504 /* hide */
3505 entry = dock->menu->entries[++index];
3506 entry->clientdata = aicon;
3507 if (wapp && wapp->flags.hidden) {
3508 entry->text = _("Unhide");
3509 } else {
3510 entry->text = _("Hide");
3512 wMenuSetEnabled(dock->menu, index, appIsRunning);
3514 /* settings */
3515 entry = dock->menu->entries[++index];
3516 entry->clientdata = aicon;
3517 wMenuSetEnabled(dock->menu, index, !aicon->editing
3518 && !wPreferences.flags.noupdates);
3520 /* kill */
3521 entry = dock->menu->entries[++index];
3522 entry->clientdata = aicon;
3523 wMenuSetEnabled(dock->menu, index, appIsRunning);
3525 if (!dock->menu->flags.realized)
3526 wMenuRealize(dock->menu);
3528 if (dock->type == WM_CLIP) {
3529 /*x_pos = event->xbutton.x_root+2;*/
3530 x_pos = event->xbutton.x_root - dock->menu->frame->core->width/2 - 1;
3531 if (x_pos < 0) {
3532 x_pos = 0;
3533 } else if (x_pos + dock->menu->frame->core->width > scr->scr_width-2) {
3534 x_pos = scr->scr_width - dock->menu->frame->core->width - 4;
3536 } else {
3537 x_pos = dock->on_right_side ?
3538 scr->scr_width - dock->menu->frame->core->width - 3 : 0;
3541 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3543 /* allow drag select */
3544 event->xany.send_event = True;
3545 desc = &dock->menu->menu->descriptor;
3546 (*desc->handle_mousedown)(desc, event);
3550 static void
3551 openClipWorkspaceMenu(WScreen *scr, int x, int y)
3553 if (!scr->clip_ws_menu) {
3554 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
3556 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
3557 wMenuMapAt(scr->clip_ws_menu, x, y, False);
3561 /******************************************************************/
3562 static void
3563 iconDblClick(WObjDescriptor *desc, XEvent *event)
3565 WAppIcon *btn = desc->parent;
3566 WDock *dock = btn->dock;
3567 WApplication *wapp = NULL;
3568 int unhideHere = 0;
3570 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3571 wapp = wApplicationOf(btn->icon->owner->main_window);
3573 assert(wapp!=NULL);
3575 unhideHere = (event->xbutton.state & ShiftMask);
3577 /* go to the last workspace that the user worked on the app */
3578 if (wapp->last_workspace != dock->screen_ptr->current_workspace
3579 && !unhideHere) {
3580 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3583 wUnhideApplication(wapp, event->xbutton.button==Button2,
3584 unhideHere);
3586 if (event->xbutton.state & MOD_MASK) {
3587 wHideOtherApplications(btn->icon->owner);
3589 } else {
3590 if (event->xbutton.button==Button1) {
3592 if (event->xbutton.state & MOD_MASK) {
3593 /* raise/lower dock */
3594 toggleLowered(dock);
3595 } else if (btn == dock->screen_ptr->clip_icon) {
3596 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3597 toggleCollapsed(dock);
3598 else
3599 handleClipChangeWorkspace(dock->screen_ptr, event);
3600 } else if (btn->command) {
3601 if (!btn->launching &&
3602 (!btn->running || (event->xbutton.state & ControlMask))) {
3603 launchDockedApplication(btn, False);
3605 } else if (btn->xindex == 0 && btn->yindex == 0
3606 && btn->dock->type == WM_DOCK) {
3608 wShowGNUstepPanel(dock->screen_ptr);
3616 static void
3617 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3619 WScreen *scr = dock->screen_ptr;
3620 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3621 int x, y;
3622 XEvent ev;
3623 int grabbed = 0, swapped = 0, done;
3624 Pixmap ghost = None;
3625 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3627 #ifdef DEBUG
3628 puts("moving dock");
3629 #endif
3630 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3631 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3632 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3633 wwarning("pointer grab failed for dock move");
3635 y = 0;
3636 for (x=0; x<dock->max_icons; x++) {
3637 if (dock->icon_array[x]!=NULL &&
3638 dock->icon_array[x]->yindex > y)
3639 y = dock->icon_array[x]->yindex;
3641 y++;
3642 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3644 done = 0;
3645 while (!done) {
3646 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3647 |ButtonMotionMask|ExposureMask, &ev);
3648 switch (ev.type) {
3649 case Expose:
3650 WMHandleEvent(&ev);
3651 break;
3653 case MotionNotify:
3654 if (!grabbed) {
3655 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3656 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3657 XChangeActivePointerGrab(dpy, ButtonMotionMask
3658 |ButtonReleaseMask|ButtonPressMask,
3659 wCursor[WCUR_MOVE], CurrentTime);
3660 grabbed=1;
3662 break;
3664 if (dock->type == WM_CLIP) {
3665 if (ev.xmotion.x_root - ofs_x < 0) {
3666 x = 0;
3667 } else if (ev.xmotion.x_root - ofs_x + ICON_SIZE >
3668 scr->scr_width) {
3669 x = scr->scr_width - ICON_SIZE;
3670 } else {
3671 x = ev.xmotion.x_root - ofs_x;
3673 if (ev.xmotion.y_root - ofs_y < 0) {
3674 y = 0;
3675 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3676 scr->scr_height) {
3677 y = scr->scr_height - ICON_SIZE;
3678 } else {
3679 y = ev.xmotion.y_root - ofs_y;
3681 moveDock(dock, x, y);
3682 } else {
3683 /* move vertically if pointer is inside the dock*/
3684 if ((dock->on_right_side &&
3685 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3686 || (!dock->on_right_side &&
3687 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3689 if (ev.xmotion.y_root - ofs_y < 0) {
3690 y = 0;
3691 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3692 scr->scr_height) {
3693 y = scr->scr_height - ICON_SIZE;
3694 } else {
3695 y = ev.xmotion.y_root - ofs_y;
3697 moveDock(dock, dock->x_pos, y);
3699 /* move horizontally to change sides */
3700 x = ev.xmotion.x_root - ofs_x;
3701 if (!dock->on_right_side) {
3703 /* is on left */
3705 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3706 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3707 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3708 if (superfluous && ghost==None) {
3709 ghost = MakeGhostDock(dock, dock->x_pos,
3710 scr->scr_width-ICON_SIZE
3711 -DOCK_EXTRA_SPACE-1,
3712 dock->y_pos);
3713 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3714 ghost);
3715 XClearWindow(dpy, scr->dock_shadow);
3717 XMapRaised(dpy, scr->dock_shadow);
3718 swapped = 1;
3719 } else {
3720 if (superfluous && ghost!=None) {
3721 XFreePixmap(dpy, ghost);
3722 ghost = None;
3724 XUnmapWindow(dpy, scr->dock_shadow);
3725 swapped = 0;
3727 } else {
3728 /* is on right */
3729 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3730 XMoveWindow(dpy, scr->dock_shadow,
3731 DOCK_EXTRA_SPACE, dock->y_pos);
3732 if (superfluous && ghost==None) {
3733 ghost = MakeGhostDock(dock, dock->x_pos,
3734 DOCK_EXTRA_SPACE, dock->y_pos);
3735 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3736 ghost);
3737 XClearWindow(dpy, scr->dock_shadow);
3739 XMapRaised(dpy, scr->dock_shadow);
3740 swapped = -1;
3741 } else {
3742 XUnmapWindow(dpy, scr->dock_shadow);
3743 swapped = 0;
3744 if (superfluous && ghost!=None) {
3745 XFreePixmap(dpy, ghost);
3746 ghost = None;
3751 break;
3753 case ButtonPress:
3754 break;
3756 case ButtonRelease:
3757 if (ev.xbutton.button != event->xbutton.button)
3758 break;
3759 XUngrabPointer(dpy, CurrentTime);
3760 XUnmapWindow(dpy, scr->dock_shadow);
3761 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3762 if (dock->type == WM_DOCK) {
3763 if (swapped!=0) {
3764 if (swapped>0)
3765 dock->on_right_side = 1;
3766 else
3767 dock->on_right_side = 0;
3768 swapDock(dock);
3769 wArrangeIcons(scr, False);
3772 done = 1;
3773 break;
3776 if (superfluous) {
3777 if (ghost!=None)
3778 XFreePixmap(dpy, ghost);
3779 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3781 #ifdef DEBUG
3782 puts("End dock move");
3783 #endif
3788 static void
3789 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3791 WScreen *scr = dock->screen_ptr;
3792 Window wins[2];
3793 WIcon *icon = aicon->icon;
3794 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3795 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3796 XEvent ev;
3797 int x = aicon->x_pos, y = aicon->y_pos;
3798 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3799 int shad_x = x, shad_y = y;
3800 int ix = aicon->xindex, iy = aicon->yindex;
3801 int tmp;
3802 Pixmap ghost = None;
3803 Bool docked;
3804 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3805 int omnipresent = aicon->omnipresent; /* this must be cached!!! */
3808 if (wPreferences.flags.noupdates)
3809 return;
3811 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3812 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3813 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3814 #ifdef DEBUG0
3815 wwarning("pointer grab failed for icon move");
3816 #endif
3819 if (!(event->xbutton.state & MOD_MASK))
3820 wRaiseFrame(icon->core);
3822 if (!wPreferences.flags.noclip)
3823 clip = scr->workspaces[scr->current_workspace]->clip;
3825 if (dock == scr->dock && !wPreferences.flags.noclip)
3826 dock2 = clip;
3827 else if (dock != scr->dock && !wPreferences.flags.nodock)
3828 dock2 = scr->dock;
3830 wins[0] = icon->core->window;
3831 wins[1] = scr->dock_shadow;
3832 XRestackWindows(dpy, wins, 2);
3833 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3834 ICON_SIZE, ICON_SIZE);
3835 if (superfluous) {
3836 if (icon->pixmap!=None)
3837 ghost = MakeGhostIcon(scr, icon->pixmap);
3838 else
3839 ghost = MakeGhostIcon(scr, icon->core->window);
3841 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3842 XClearWindow(dpy, scr->dock_shadow);
3844 XMapWindow(dpy, scr->dock_shadow);
3846 ondock = 1;
3849 while(1) {
3850 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3851 |ButtonMotionMask|ExposureMask, &ev);
3852 switch (ev.type) {
3853 case Expose:
3854 WMHandleEvent(&ev);
3855 break;
3857 case MotionNotify:
3858 if (!grabbed) {
3859 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3860 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3861 XChangeActivePointerGrab(dpy, ButtonMotionMask
3862 |ButtonReleaseMask|ButtonPressMask,
3863 wCursor[WCUR_MOVE], CurrentTime);
3864 grabbed=1;
3865 } else {
3866 break;
3870 if (omnipresent) {
3871 int i;
3872 for (i=0; i<scr->workspace_count; i++) {
3873 if (i == scr->current_workspace)
3874 continue;
3875 wDockShowIcons(scr->workspaces[i]->clip);
3879 x = ev.xmotion.x_root - ofs_x;
3880 y = ev.xmotion.y_root - ofs_y;
3881 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3882 if (tmp && dock2) {
3883 change_dock = 0;
3884 if (last_dock != dock && collapsed) {
3885 last_dock->collapsed = 1;
3886 wDockHideIcons(last_dock);
3887 collapsed = 0;
3889 if (!collapsed && (collapsed = dock->collapsed)) {
3890 dock->collapsed = 0;
3891 wDockShowIcons(dock);
3893 if (dock->auto_raise_lower)
3894 wDockRaise(dock);
3895 last_dock = dock;
3896 } else if (dock2) {
3897 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3898 if (tmp) {
3899 change_dock = 1;
3900 if (last_dock != dock2 && collapsed) {
3901 last_dock->collapsed = 1;
3902 wDockHideIcons(last_dock);
3903 collapsed = 0;
3905 if (!collapsed && (collapsed = dock2->collapsed)) {
3906 dock2->collapsed = 0;
3907 wDockShowIcons(dock2);
3909 if (dock2->auto_raise_lower)
3910 wDockRaise(dock2);
3911 last_dock = dock2;
3914 if (aicon->launching
3915 || aicon->lock
3916 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3917 || (!aicon->running && tmp)) {
3918 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3919 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3921 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3923 if (!ondock) {
3924 XMapWindow(dpy, scr->dock_shadow);
3926 ondock = 1;
3927 } else {
3928 if (ondock) {
3929 XUnmapWindow(dpy, scr->dock_shadow);
3931 ondock = 0;
3933 XMoveWindow(dpy, icon->core->window, x, y);
3934 break;
3936 case ButtonPress:
3937 break;
3939 case ButtonRelease:
3940 if (ev.xbutton.button != event->xbutton.button)
3941 break;
3942 XUngrabPointer(dpy, CurrentTime);
3943 if (ondock) {
3944 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3945 XUnmapWindow(dpy, scr->dock_shadow);
3946 if (!change_dock) {
3947 reattachIcon(dock, aicon, ix, iy);
3948 if (clip && dock!=clip && clip->auto_raise_lower)
3949 wDockLower(clip);
3950 } else {
3951 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3952 if (!docked) {
3953 /* Slide it back if dock rejected it */
3954 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3955 aicon->y_pos);
3956 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3958 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3959 collapsed = 0;
3962 } else {
3963 aicon->x_pos = x;
3964 aicon->y_pos = y;
3965 if (superfluous) {
3966 if (!aicon->running && !wPreferences.no_animations) {
3967 /* We need to deselect it, even if is deselected in
3968 * wDockDetach(), because else DoKaboom() will fail.
3970 if (aicon->icon->selected)
3971 wIconSelect(aicon->icon);
3973 wSoundPlay(WSOUND_KABOOM);
3974 DoKaboom(scr,aicon->icon->core->window, x, y);
3975 } else {
3976 wSoundPlay(WSOUND_UNDOCK);
3978 } else {
3979 wSoundPlay(WSOUND_UNDOCK);
3981 if (clip && clip->auto_raise_lower)
3982 wDockLower(clip);
3983 wDockDetach(dock, aicon);
3985 if (collapsed) {
3986 last_dock->collapsed = 1;
3987 wDockHideIcons(last_dock);
3988 collapsed = 0;
3990 if (superfluous) {
3991 if (ghost!=None)
3992 XFreePixmap(dpy, ghost);
3993 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3995 if (omnipresent) {
3996 int i;
3997 for (i=0; i<scr->workspace_count; i++) {
3998 if (i == scr->current_workspace)
3999 continue;
4000 wDockHideIcons(scr->workspaces[i]->clip);
4004 #ifdef DEBUG
4005 puts("End icon move");
4006 #endif
4007 return;
4013 static int
4014 getClipButton(int px, int py)
4016 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
4018 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
4019 return CLIP_IDLE;
4021 if (py <= pt-((int)ICON_SIZE-1-px))
4022 return CLIP_FORWARD;
4023 else if (px <= pt-((int)ICON_SIZE-1-py))
4024 return CLIP_REWIND;
4026 return CLIP_IDLE;
4030 static void
4031 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
4033 XEvent ev;
4034 int done, direction, new_ws;
4035 int new_dir;
4036 WDock *clip = scr->clip_icon->dock;
4038 direction = getClipButton(event->xbutton.x, event->xbutton.y);
4040 clip->lclip_button_pushed = direction==CLIP_REWIND;
4041 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4043 wClipIconPaint(scr->clip_icon);
4044 done = 0;
4045 while(!done) {
4046 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
4047 |ButtonPressMask, &ev);
4048 switch (ev.type) {
4049 case Expose:
4050 WMHandleEvent(&ev);
4051 break;
4053 case MotionNotify:
4054 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
4055 if (new_dir != direction) {
4056 direction = new_dir;
4057 clip->lclip_button_pushed = direction==CLIP_REWIND;
4058 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4059 wClipIconPaint(scr->clip_icon);
4061 break;
4063 case ButtonPress:
4064 break;
4066 case ButtonRelease:
4067 if (ev.xbutton.button == event->xbutton.button)
4068 done = 1;
4072 clip->lclip_button_pushed = 0;
4073 clip->rclip_button_pushed = 0;
4075 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
4077 if (direction == CLIP_FORWARD) {
4078 if (scr->current_workspace < scr->workspace_count-1)
4079 wWorkspaceChange(scr, scr->current_workspace+1);
4080 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
4081 wWorkspaceChange(scr, scr->current_workspace+1);
4082 else if (wPreferences.ws_cycle)
4083 wWorkspaceChange(scr, 0);
4085 else if (direction == CLIP_REWIND) {
4086 if (scr->current_workspace > 0)
4087 wWorkspaceChange(scr, scr->current_workspace-1);
4088 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
4089 wWorkspaceChange(scr, scr->workspace_count-1);
4092 wClipIconPaint(scr->clip_icon);
4096 static void
4097 iconMouseDown(WObjDescriptor *desc, XEvent *event)
4099 WAppIcon *aicon = desc->parent;
4100 WDock *dock = aicon->dock;
4101 WScreen *scr = aicon->icon->core->screen_ptr;
4103 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
4104 return;
4106 scr->last_dock = dock;
4108 if (dock->menu->flags.mapped)
4109 wMenuUnmap(dock->menu);
4111 if (IsDoubleClick(scr, event)) {
4112 /* double-click was not in the main clip icon */
4113 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
4114 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
4115 iconDblClick(desc, event);
4116 return;
4120 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
4121 XUnmapWindow(dpy, scr->clip_balloon);
4122 scr->flags.clip_balloon_mapped = 0;
4125 #ifdef DEBUG
4126 puts("handling dock");
4127 #endif
4128 if (event->xbutton.button == Button1) {
4129 if (event->xbutton.state & MOD_MASK)
4130 wDockLower(dock);
4131 else
4132 wDockRaise(dock);
4134 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
4135 dock->type!=WM_DOCK) {
4136 wIconSelect(aicon->icon);
4137 return;
4140 if (aicon->yindex==0 && aicon->xindex==0) {
4141 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
4142 && dock->type==WM_CLIP)
4143 handleClipChangeWorkspace(scr, event);
4144 else
4145 handleDockMove(dock, aicon, event);
4146 } else
4147 handleIconMove(dock, aicon, event);
4149 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4150 aicon==scr->clip_icon) {
4151 openClipWorkspaceMenu(scr, event->xbutton.x_root+2,
4152 event->xbutton.y_root+2);
4153 if (scr->clip_ws_menu) {
4154 WMenu *menu;
4155 menu = scr->clip_ws_menu;
4156 desc = &menu->menu->descriptor;
4158 event->xany.send_event = True;
4159 (*desc->handle_mousedown)(desc, event);
4161 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4162 (event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon) {
4163 wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
4164 } else if (event->xbutton.button == Button3) {
4165 if (event->xbutton.send_event &&
4166 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
4167 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
4168 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
4169 wwarning("pointer grab failed for dockicon menu");
4170 return;
4173 openDockMenu(dock, aicon, event);
4174 } else if (event->xbutton.button == Button2) {
4175 WAppIcon *btn = desc->parent;
4177 if (!btn->launching &&
4178 (!btn->running || (event->xbutton.state & ControlMask))) {
4179 launchDockedApplication(btn, True);
4185 static void
4186 showClipBalloon(WDock *dock, int workspace)
4188 int w, h;
4189 int x, y;
4190 WScreen *scr = dock->screen_ptr;
4191 char *text;
4192 Window stack[2];
4194 scr->flags.clip_balloon_mapped = 1;
4195 XMapWindow(dpy, scr->clip_balloon);
4197 text = scr->workspaces[workspace]->name;
4199 w = WMWidthOfString(scr->clip_title_font, text, strlen(text));
4201 h = WMFontHeight(scr->clip_title_font);
4202 XResizeWindow(dpy, scr->clip_balloon, w, h);
4204 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
4205 y = dock->y_pos + ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
4207 if (x+w > scr->scr_width) {
4208 x = scr->scr_width - w;
4209 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
4210 y = dock->y_pos - h - 1;
4211 else
4212 y = dock->y_pos + ICON_SIZE;
4213 XRaiseWindow(dpy, scr->clip_balloon);
4214 } else {
4215 stack[0] = scr->clip_icon->icon->core->window;
4216 stack[1] = scr->clip_balloon;
4217 XRestackWindows(dpy, stack, 2);
4219 XMoveWindow(dpy, scr->clip_balloon, x, y);
4220 XClearWindow(dpy, scr->clip_balloon);
4221 WMDrawString(scr->wmscreen, scr->clip_balloon,
4222 scr->clip_title_color[CLIP_NORMAL],
4223 scr->clip_title_font,
4224 0, 0, text, strlen(text));
4228 static void
4229 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
4231 WAppIcon *btn = (WAppIcon*)desc->parent;
4232 WDock *dock;
4233 WScreen *scr;
4235 assert(event->type==EnterNotify);
4237 if(desc->parent_type!=WCLASS_DOCK_ICON)
4238 return;
4240 scr = btn->icon->core->screen_ptr;
4241 if (!btn->omnipresent)
4242 dock = btn->dock;
4243 else
4244 dock = scr->workspaces[scr->current_workspace]->clip;
4246 if (!dock || dock->type!=WM_CLIP)
4247 return;
4249 /* The auto raise/lower code */
4250 if (dock->auto_lower_magic) {
4251 WMDeleteTimerHandler(dock->auto_lower_magic);
4252 dock->auto_lower_magic = NULL;
4254 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
4255 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
4256 clipAutoRaise,
4257 (void *)dock);
4260 /* The auto expand/collapse code */
4261 if (dock->auto_collapse_magic) {
4262 WMDeleteTimerHandler(dock->auto_collapse_magic);
4263 dock->auto_collapse_magic = NULL;
4265 if (dock->auto_collapse && !dock->auto_expand_magic) {
4266 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
4267 clipAutoExpand,
4268 (void *)dock);
4271 if (btn->xindex == 0 && btn->yindex == 0)
4272 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4273 else {
4274 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4275 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
4276 dock->screen_ptr->flags.clip_balloon_mapped = 0;
4282 static void
4283 clipLeave(WDock *dock)
4285 XEvent event;
4286 WObjDescriptor *desc = NULL;
4288 if (!dock || dock->type!=WM_CLIP)
4289 return;
4291 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
4292 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
4293 (XPointer *)&desc)!=XCNOENT
4294 && desc && desc->parent_type==WCLASS_DOCK_ICON
4295 && ((WAppIcon*)desc->parent)->dock
4296 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
4297 /* We didn't left the Clip yet */
4298 XPutBackEvent(dpy, &event);
4299 return;
4302 XPutBackEvent(dpy, &event);
4303 } else {
4304 /* We entered a withdrawn window, so we're still in Clip */
4305 return;
4308 if (dock->auto_raise_magic) {
4309 WMDeleteTimerHandler(dock->auto_raise_magic);
4310 dock->auto_raise_magic = NULL;
4312 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
4313 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
4314 clipAutoLower,
4315 (void *)dock);
4318 if (dock->auto_expand_magic) {
4319 WMDeleteTimerHandler(dock->auto_expand_magic);
4320 dock->auto_expand_magic = NULL;
4322 if (dock->auto_collapse && !dock->auto_collapse_magic) {
4323 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
4324 clipAutoCollapse,
4325 (void *)dock);
4330 static void
4331 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
4333 WAppIcon *btn = (WAppIcon*)desc->parent;
4335 assert(event->type==LeaveNotify);
4337 if(desc->parent_type!=WCLASS_DOCK_ICON)
4338 return;
4340 clipLeave(btn->dock);
4344 static void
4345 clipAutoCollapse(void *cdata)
4347 WDock *dock = (WDock *)cdata;
4349 if (dock->type!=WM_CLIP)
4350 return;
4352 if (dock->auto_collapse) {
4353 dock->collapsed = 1;
4354 wDockHideIcons(dock);
4356 dock->auto_collapse_magic = NULL;
4360 static void
4361 clipAutoExpand(void *cdata)
4363 WDock *dock = (WDock *)cdata;
4365 if (dock->type!=WM_CLIP)
4366 return;
4368 if (dock->auto_collapse) {
4369 dock->collapsed = 0;
4370 wDockShowIcons(dock);
4372 dock->auto_expand_magic = NULL;
4376 static void
4377 clipAutoLower(void *cdata)
4379 WDock *dock = (WDock *)cdata;
4381 if (dock->type!=WM_CLIP)
4382 return;
4384 if (dock->auto_raise_lower)
4385 wDockLower(dock);
4387 dock->auto_lower_magic = NULL;
4391 static void
4392 clipAutoRaise(void *cdata)
4394 WDock *dock = (WDock *)cdata;
4396 if (dock->type!=WM_CLIP)
4397 return;
4399 if (dock->auto_raise_lower)
4400 wDockRaise(dock);
4402 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4403 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4406 dock->auto_raise_magic = NULL;
4410 static Bool
4411 iconCanBeOmnipresent(WAppIcon *aicon)
4413 WScreen *scr = aicon->icon->core->screen_ptr;
4414 WDock *clip;
4415 WAppIcon *btn;
4416 int i, j;
4418 for (i=0; i<scr->workspace_count; i++) {
4419 clip = scr->workspaces[i]->clip;
4421 if (clip == aicon->dock)
4422 continue;
4424 if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
4425 return False; /* Clip is full in some workspace */
4427 for (j=0; j<clip->max_icons; j++) {
4428 btn = clip->icon_array[j];
4429 if(btn && btn->xindex==aicon->xindex && btn->yindex==aicon->yindex)
4430 return False;
4434 return True;
4439 wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
4441 WScreen *scr = aicon->icon->core->screen_ptr;
4442 WAppIconChain *new_entry, *tmp, *tmp1;
4443 int status = WO_SUCCESS;
4445 if ((scr->dock && aicon->dock==scr->dock) || aicon==scr->clip_icon) {
4446 return WO_NOT_APPLICABLE;
4449 if (aicon->omnipresent == omnipresent)
4450 return WO_SUCCESS;
4452 if (omnipresent) {
4453 if (iconCanBeOmnipresent(aicon)) {
4454 aicon->omnipresent = 1;
4455 new_entry = wmalloc(sizeof(WAppIconChain));
4456 new_entry->aicon = aicon;
4457 new_entry->next = scr->global_icons;
4458 scr->global_icons = new_entry;
4459 scr->global_icon_count++;
4460 } else {
4461 aicon->omnipresent = 0;
4462 status = WO_FAILED;
4464 } else {
4465 aicon->omnipresent = 0;
4466 if (aicon == scr->global_icons->aicon) {
4467 tmp = scr->global_icons->next;
4468 wfree(scr->global_icons);
4469 scr->global_icons = tmp;
4470 scr->global_icon_count--;
4471 } else {
4472 tmp = scr->global_icons;
4473 while (tmp->next) {
4474 if (tmp->next->aicon == aicon) {
4475 tmp1 = tmp->next->next;
4476 wfree(tmp->next);
4477 tmp->next = tmp1;
4478 scr->global_icon_count--;
4479 break;
4481 tmp = tmp->next;
4486 wAppIconPaint(aicon);
4488 return status;