- More cleanups for obsoleted xxx_gc's and xxx_pixel's in WScreen
[wmaker-crm.git] / src / dock.c
blob127b13f2f0299a3f18415169e430599714670143
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"
59 #include <WINGs/WINGsP.h>
64 /**** Local variables ****/
65 #define CLIP_REWIND 1
66 #define CLIP_IDLE 0
67 #define CLIP_FORWARD 2
70 /**** Global variables ****/
72 /* in dockedapp.c */
73 extern void DestroyDockAppSettingsPanel();
75 extern void ShowDockAppSettingsPanel(WAppIcon *aicon);
78 extern XContext wWinContext;
80 extern Cursor wCursor[WCUR_LAST];
82 extern WPreferences wPreferences;
84 extern XContext wWinContext;
86 #ifdef OFFIX_DND
87 extern Atom _XA_DND_PROTOCOL;
88 #endif
91 #define MOD_MASK wPreferences.modifier_mask
93 extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);
95 #define ICON_SIZE wPreferences.icon_size
98 /***** Local variables ****/
100 static WMPropList *dCommand=NULL;
101 static WMPropList *dPasteCommand=NULL;
102 #ifdef OFFIX_DND
103 static WMPropList *dDropCommand=NULL;
104 #endif
105 static WMPropList *dAutoLaunch, *dLock;
106 static WMPropList *dName, *dForced, *dBuggyApplication, *dYes, *dNo;
107 static WMPropList *dHost, *dDock, *dClip;
108 static WMPropList *dAutoAttractIcons;
110 static WMPropList *dPosition, *dApplications, *dLowered, *dCollapsed;
112 static WMPropList *dAutoCollapse, *dAutoRaiseLower, *dOmnipresent;
114 static void dockIconPaint(WAppIcon *btn);
116 static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
118 static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state);
120 static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);
122 static int getClipButton(int px, int py);
124 static void toggleLowered(WDock *dock);
126 static void toggleCollapsed(WDock *dock);
128 static void clipIconExpose(WObjDescriptor *desc, XEvent *event);
130 static void clipLeave(WDock *dock);
132 static void handleClipChangeWorkspace(WScreen *scr, XEvent *event);
134 Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y);
136 static void clipEnterNotify(WObjDescriptor *desc, XEvent *event);
137 static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event);
138 static void clipAutoCollapse(void *cdata);
139 static void clipAutoExpand(void *cdata);
140 static void launchDockedApplication(WAppIcon *btn, Bool withSelection);
142 static void clipAutoLower(void *cdata);
143 static void clipAutoRaise(void *cdata);
145 static void showClipBalloon(WDock *dock, int workspace);
147 #ifdef OFFIX_DND
149 #define DndNotDnd -1
150 #define DndUnknown 0
151 #define DndRawData 1
152 #define DndFile 2
153 #define DndFiles 3
154 #define DndText 4
155 #define DndDir 5
156 #define DndLink 6
157 #define DndExe 7
159 #define DndEND 8
161 #endif /* OFFIX_DND */
165 static void
166 make_keys()
168 if (dCommand!=NULL)
169 return;
171 dCommand = WMRetainPropList(WMCreatePLString("Command"));
172 dPasteCommand = WMRetainPropList(WMCreatePLString("PasteCommand"));
173 dDropCommand = WMRetainPropList(WMCreatePLString("DropCommand"));
174 dLock = WMRetainPropList(WMCreatePLString("Lock"));
175 dAutoLaunch = WMRetainPropList(WMCreatePLString("AutoLaunch"));
176 dName = WMRetainPropList(WMCreatePLString("Name"));
177 dForced = WMRetainPropList(WMCreatePLString("Forced"));
178 dBuggyApplication = WMRetainPropList(WMCreatePLString("BuggyApplication"));
179 dYes = WMRetainPropList(WMCreatePLString("Yes"));
180 dNo = WMRetainPropList(WMCreatePLString("No"));
181 dHost = WMRetainPropList(WMCreatePLString("Host"));
183 dPosition = WMCreatePLString("Position");
184 dApplications = WMCreatePLString("Applications");
185 dLowered = WMCreatePLString("Lowered");
186 dCollapsed = WMCreatePLString("Collapsed");
187 dAutoCollapse = WMCreatePLString("AutoCollapse");
188 dAutoRaiseLower = WMCreatePLString("AutoRaiseLower");
189 dAutoAttractIcons = WMCreatePLString("AutoAttractIcons");
191 dOmnipresent = WMCreatePLString("Omnipresent");
193 dDock = WMCreatePLString("Dock");
194 dClip = WMCreatePLString("Clip");
199 static void
200 renameCallback(WMenu *menu, WMenuEntry *entry)
202 WDock *dock = entry->clientdata;
203 char buffer[128];
204 int wspace;
205 char *name;
207 assert(entry->clientdata!=NULL);
209 wspace = dock->screen_ptr->current_workspace;
211 name = wstrdup(dock->screen_ptr->workspaces[wspace]->name);
213 snprintf(buffer, sizeof(buffer), _("Type the name for workspace %i:"), wspace+1);
214 if (wInputDialog(dock->screen_ptr, _("Rename Workspace"), buffer,
215 &name)) {
216 wWorkspaceRename(dock->screen_ptr, wspace, name);
218 if (name) {
219 wfree(name);
224 static void
225 toggleLoweredCallback(WMenu *menu, WMenuEntry *entry)
227 assert(entry->clientdata!=NULL);
229 toggleLowered(entry->clientdata);
231 entry->flags.indicator_on = !(((WDock*)entry->clientdata)->lowered);
233 wMenuPaint(menu);
237 static int
238 matchWindow(void *item, void *cdata)
240 return (((WFakeGroupLeader*)item)->leader == (Window)cdata);
244 static void
245 killCallback(WMenu *menu, WMenuEntry *entry)
247 WScreen *scr = menu->menu->screen_ptr;
248 WAppIcon *icon;
249 WFakeGroupLeader *fPtr;
250 char *buffer;
252 if (!WCHECK_STATE(WSTATE_NORMAL))
253 return;
255 assert(entry->clientdata!=NULL);
257 icon = (WAppIcon*)entry->clientdata;
259 icon->editing = 1;
261 WCHANGE_STATE(WSTATE_MODAL);
263 buffer = wstrconcat(icon->wm_class,
264 _(" will be forcibly closed.\n"
265 "Any unsaved changes will be lost.\n"
266 "Please confirm."));
268 if (icon->icon && icon->icon->owner) {
269 fPtr = icon->icon->owner->fake_group;
270 } else {
271 /* is this really necessary? can we kill a dock icon not running? */
272 Window win = icon->main_window;
273 int index;
275 index = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void*)win);
276 if (index != WANotFound)
277 fPtr = WMGetFromArray(scr->fakeGroupLeaders, index);
278 else
279 fPtr = NULL;
282 if (wPreferences.dont_confirm_kill
283 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
284 buffer, _("Yes"), _("No"), NULL)==WAPRDefault) {
285 if (fPtr!=NULL) {
286 WWindow *wwin, *twin;
288 wwin = scr->focused_window;
289 while (wwin) {
290 twin = wwin->prev;
291 if (wwin->fake_group == fPtr) {
292 wClientKill(wwin);
294 wwin = twin;
296 } else if (icon->icon && icon->icon->owner) {
297 wClientKill(icon->icon->owner);
301 wfree(buffer);
303 icon->editing = 0;
305 WCHANGE_STATE(WSTATE_NORMAL);
309 /* TODO: replace this function with a member of the dock struct */
310 static int
311 numberOfSelectedIcons(WDock *dock)
313 WAppIcon *aicon;
314 int i, n;
316 n = 0;
317 for (i=1; i<dock->max_icons; i++) {
318 aicon = dock->icon_array[i];
319 if (aicon && aicon->icon->selected) {
320 n++;
324 return n;
328 static WMArray*
329 getSelected(WDock *dock)
331 WMArray *ret = WMCreateArray(8);
332 WAppIcon *btn;
333 int i;
335 for (i=1; i<dock->max_icons; i++) {
336 btn = dock->icon_array[i];
337 if (btn && btn->icon->selected) {
338 WMAddToArray(ret, btn);
342 return ret;
346 static void
347 paintClipButtons(WAppIcon *clipIcon, Bool lpushed, Bool rpushed)
349 Window win = clipIcon->icon->core->window;
350 WScreen *scr = clipIcon->icon->core->screen_ptr;
351 XPoint p[4];
352 int pt = CLIP_BUTTON_SIZE*ICON_SIZE/64;
353 int tp = ICON_SIZE - pt;
354 int as = pt - 15; /* 15 = 5+5+5 */
355 GC gc = scr->draw_gc; /* maybe use WMColorGC() instead here? */
356 WMColor *color;
357 #ifdef GRADIENT_CLIP_ARROW
358 Bool collapsed = clipIcon->dock->collapsed;
359 #endif
361 if (!clipIcon->dock->collapsed)
362 color = scr->clip_title_color[CLIP_NORMAL];
363 else
364 color = scr->clip_title_color[CLIP_COLLAPSED];
366 XSetForeground(dpy, gc, WMColorPixel(color));
368 if (rpushed) {
369 p[0].x = tp+1;
370 p[0].y = 1;
371 p[1].x = ICON_SIZE-2;
372 p[1].y = 1;
373 p[2].x = ICON_SIZE-2;
374 p[2].y = pt-1;
375 } else if (lpushed) {
376 p[0].x = 1;
377 p[0].y = tp;
378 p[1].x = pt;
379 p[1].y = ICON_SIZE-2;
380 p[2].x = 1;
381 p[2].y = ICON_SIZE-2;
383 if (lpushed || rpushed) {
384 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
385 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
386 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
388 #ifdef GRADIENT_CLIP_ARROW
389 if (!collapsed) {
390 XSetFillStyle(dpy, scr->copy_gc, FillTiled);
391 XSetTile(dpy, scr->copy_gc, scr->clip_arrow_gradient);
392 XSetClipMask(dpy, scr->copy_gc, None);
393 gc = scr->copy_gc;
395 #endif /* GRADIENT_CLIP_ARROW */
397 /* top right arrow */
398 p[0].x = p[3].x = ICON_SIZE-5-as;
399 p[0].y = p[3].y = 5;
400 p[1].x = ICON_SIZE-6;
401 p[1].y = 5;
402 p[2].x = ICON_SIZE-6;
403 p[2].y = 4+as;
404 if (rpushed) {
405 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
406 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
407 } else {
408 #ifdef GRADIENT_CLIP_ARROW
409 if (!collapsed)
410 XSetTSOrigin(dpy, gc, ICON_SIZE-6-as, 5);
411 #endif
412 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
413 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
416 /* bottom left arrow */
417 p[0].x = p[3].x = 5;
418 p[0].y = p[3].y = ICON_SIZE-5-as;
419 p[1].x = 5;
420 p[1].y = ICON_SIZE-6;
421 p[2].x = 4+as;
422 p[2].y = ICON_SIZE-6;
423 if (lpushed) {
424 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
425 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
426 } else {
427 #ifdef GRADIENT_CLIP_ARROW
428 if (!collapsed)
429 XSetTSOrigin(dpy, gc, 5, ICON_SIZE-6-as);
430 #endif
431 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
432 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
434 #ifdef GRADIENT_CLIP_ARROW
435 if (!collapsed)
436 XSetFillStyle(dpy, scr->copy_gc, FillSolid);
437 #endif
441 RImage*
442 wClipMakeTile(WScreen *scr, RImage *normalTile)
444 RImage *tile = RCloneImage(normalTile);
445 RColor black;
446 RColor dark;
447 RColor light;
448 int pt, tp;
449 int as;
451 pt = CLIP_BUTTON_SIZE*wPreferences.icon_size/64;
452 tp = wPreferences.icon_size-1 - pt;
453 as = pt - 15;
455 black.alpha = 255;
456 black.red = black.green = black.blue = 0;
458 dark.alpha = 0;
459 dark.red = dark.green = dark.blue = 60;
461 light.alpha = 0;
462 light.red = light.green = light.blue = 80;
465 /* top right */
466 ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size-2,
467 pt-1, &dark);
468 RDrawLine(tile, tp-1, 0, wPreferences.icon_size-1, pt+1, &black);
469 ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size-3,
470 pt, &light);
472 /* arrow bevel */
473 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 7 - as, 4,
474 ICON_SIZE - 5, 4, &dark);
475 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 6 - as, 5,
476 ICON_SIZE - 5, 6 + as, &dark);
477 ROperateLine(tile, RAddOperation, ICON_SIZE - 5, 4, ICON_SIZE - 5, 6 + as,
478 &light);
480 /* bottom left */
481 ROperateLine(tile, RAddOperation, 2, tp+2, pt-2,
482 wPreferences.icon_size-3, &dark);
483 RDrawLine(tile, 0, tp-1, pt+1, wPreferences.icon_size-1, &black);
484 ROperateLine(tile, RSubtractOperation, 0, tp-2, pt+1,
485 wPreferences.icon_size-2, &light);
487 /* arrow bevel */
488 ROperateLine(tile, RSubtractOperation, 4, ICON_SIZE - 7 - as, 4,
489 ICON_SIZE - 5, &dark);
490 ROperateLine(tile, RSubtractOperation, 5, ICON_SIZE - 6 - as,
491 6 + as, ICON_SIZE - 5, &dark);
492 ROperateLine(tile, RAddOperation, 4, ICON_SIZE - 5, 6 + as, ICON_SIZE - 5,
493 &light);
495 return tile;
499 static void
500 omnipresentCallback(WMenu *menu, WMenuEntry *entry)
502 WAppIcon *clickedIcon = entry->clientdata;
503 WAppIcon *aicon;
504 WDock *dock;
505 WMArray *selectedIcons;
506 WMArrayIterator iter;
507 int failed;
509 assert(entry->clientdata!=NULL);
511 dock = clickedIcon->dock;
513 selectedIcons = getSelected(dock);
515 if (!WMGetArrayItemCount(selectedIcons))
516 WMAddToArray(selectedIcons, clickedIcon);
518 failed = 0;
519 WM_ITERATE_ARRAY(selectedIcons, aicon, iter) {
520 if (wClipMakeIconOmnipresent(aicon, !aicon->omnipresent) == WO_FAILED)
521 failed++;
522 else if (aicon->icon->selected)
523 wIconSelect(aicon->icon);
525 WMFreeArray(selectedIcons);
527 if (failed > 1) {
528 wMessageDialog(dock->screen_ptr, _("Warning"),
529 _("Some icons cannot be made omnipresent. "
530 "Please make sure that no other icon is "
531 "docked in the same positions on the other "
532 "workspaces and the Clip is not full in "
533 "some workspace."),
534 _("OK"), NULL, NULL);
535 } else if (failed == 1) {
536 wMessageDialog(dock->screen_ptr, _("Warning"),
537 _("Icon cannot be made omnipresent. "
538 "Please make sure that no other icon is "
539 "docked in the same position on the other "
540 "workspaces and the Clip is not full in "
541 "some workspace."),
542 _("OK"), NULL, NULL);
547 static void
548 removeIconsCallback(WMenu *menu, WMenuEntry *entry)
550 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
551 WDock *dock;
552 WAppIcon *aicon;
553 WMArray *selectedIcons;
554 int keepit;
555 WMArrayIterator it;
557 assert(clickedIcon!=NULL);
559 dock = clickedIcon->dock;
561 selectedIcons = getSelected(dock);
563 if (WMGetArrayItemCount(selectedIcons)) {
564 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
565 _("All selected icons will be removed!"),
566 _("OK"), _("Cancel"), NULL)!=WAPRDefault) {
567 WMFreeArray(selectedIcons);
568 return;
570 } else {
571 if (clickedIcon->xindex==0 && clickedIcon->yindex==0) {
572 WMFreeArray(selectedIcons);
573 return;
575 WMAddToArray(selectedIcons, clickedIcon);
578 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
579 keepit = aicon->running && wApplicationOf(aicon->main_window);
580 wDockDetach(dock, aicon);
581 if (keepit) {
582 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
583 XMoveWindow(dpy, aicon->icon->core->window,
584 aicon->x_pos, aicon->y_pos);
585 if (!dock->mapped || dock->collapsed)
586 XMapWindow(dpy, aicon->icon->core->window);
589 WMFreeArray(selectedIcons);
591 if (wPreferences.auto_arrange_icons)
592 wArrangeIcons(dock->screen_ptr, True);
596 static void
597 keepIconsCallback(WMenu *menu, WMenuEntry *entry)
599 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
600 WDock *dock;
601 WAppIcon *aicon;
602 WMArray *selectedIcons;
603 WMArrayIterator it;
605 assert(clickedIcon!=NULL);
606 dock = clickedIcon->dock;
608 selectedIcons = getSelected(dock);
610 if (!WMGetArrayItemCount(selectedIcons)
611 && clickedIcon!=dock->screen_ptr->clip_icon) {
612 char *command = NULL;
614 if (!clickedIcon->command && !clickedIcon->editing) {
615 clickedIcon->editing = 1;
616 if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
617 _("Type the command used to launch the application"),
618 &command)) {
619 if (command && (command[0]==0 ||
620 (command[0]=='-' && command[1]==0))) {
621 wfree(command);
622 command = NULL;
624 clickedIcon->command = command;
625 clickedIcon->editing = 0;
626 } else {
627 clickedIcon->editing = 0;
628 if (command)
629 wfree(command);
630 WMFreeArray(selectedIcons);
631 return;
635 WMAddToArray(selectedIcons, clickedIcon);
638 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
639 if (aicon->icon->selected)
640 wIconSelect(aicon->icon);
641 if (aicon && aicon->attracted && aicon->command) {
642 aicon->attracted = 0;
643 if (aicon->icon->shadowed) {
644 aicon->icon->shadowed = 0;
645 aicon->icon->force_paint = 1;
646 wAppIconPaint(aicon);
650 WMFreeArray(selectedIcons);
656 static void
657 toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
659 WDock *dock = (WDock*)entry->clientdata;
661 assert(entry->clientdata!=NULL);
663 dock->attract_icons = !dock->attract_icons;
664 /*if (!dock->attract_icons)
665 dock->keep_attracted = 0;*/
667 entry->flags.indicator_on = dock->attract_icons;
669 wMenuPaint(menu);
673 static void
674 selectCallback(WMenu *menu, WMenuEntry *entry)
676 WAppIcon *icon = (WAppIcon*)entry->clientdata;
678 assert(icon!=NULL);
680 wIconSelect(icon->icon);
682 wMenuPaint(menu);
686 static void
687 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
689 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
690 WDock *clip;
691 WAppIcon *aicon;
692 int x, y, x_pos, y_pos;
694 assert(entry->clientdata!=NULL);
695 clip = clickedIcon->dock;
697 aicon = clip->screen_ptr->app_icon_list;
699 while (aicon) {
700 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
701 x_pos = clip->x_pos + x*ICON_SIZE;
702 y_pos = clip->y_pos + y*ICON_SIZE;
703 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
704 #ifdef ANIMATIONS
705 if (wPreferences.no_animations) {
706 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
707 } else {
708 SlideWindow(aicon->icon->core->window,
709 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
711 #else
712 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
713 #endif /* ANIMATIONS */
715 aicon->attracted = 1;
716 if (!aicon->icon->shadowed) {
717 aicon->icon->shadowed = 1;
718 aicon->icon->force_paint = 1;
719 /* We don't do an wAppIconPaint() here because it's in
720 * wDockAttachIcon(). -Dan
723 wDockAttachIcon(clip, aicon, x, y);
724 if (clip->collapsed || !clip->mapped)
725 XUnmapWindow(dpy, aicon->icon->core->window);
727 aicon = aicon->next;
732 static void
733 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
735 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
736 WDock *dock;
737 WMArray *selectedIcons;
738 WMArrayIterator iter;
739 WAppIcon *btn;
740 int i;
742 assert(clickedIcon!=NULL);
743 dock = clickedIcon->dock;
745 selectedIcons = getSelected(dock);
747 if (!WMGetArrayItemCount(selectedIcons)) {
748 for (i=1; i<dock->max_icons; i++) {
749 btn = dock->icon_array[i];
750 if (btn && !btn->icon->selected) {
751 wIconSelect(btn->icon);
754 } else {
755 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
756 wIconSelect(btn->icon);
759 WMFreeArray(selectedIcons);
761 wMenuPaint(menu);
765 static void
766 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
768 assert(entry->clientdata!=NULL);
770 toggleCollapsed(entry->clientdata);
772 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
774 wMenuPaint(menu);
778 static void
779 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
781 WDock *dock;
782 assert(entry->clientdata!=NULL);
784 dock = (WDock*) entry->clientdata;
786 dock->auto_collapse = !dock->auto_collapse;
788 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
790 wMenuPaint(menu);
794 static void
795 toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
797 WDock *dock;
798 assert(entry->clientdata!=NULL);
800 dock = (WDock*) entry->clientdata;
802 dock->auto_raise_lower = !dock->auto_raise_lower;
804 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_raise_lower;
806 wMenuPaint(menu);
810 static void
811 launchCallback(WMenu *menu, WMenuEntry *entry)
813 WAppIcon *btn = (WAppIcon*)entry->clientdata;
815 launchDockedApplication(btn, False);
819 static void
820 settingsCallback(WMenu *menu, WMenuEntry *entry)
822 WAppIcon *btn = (WAppIcon*)entry->clientdata;
824 if (btn->editing)
825 return;
826 ShowDockAppSettingsPanel(btn);
830 static void
831 hideCallback(WMenu *menu, WMenuEntry *entry)
833 WApplication *wapp;
834 WAppIcon *btn = (WAppIcon*)entry->clientdata;
836 wapp = wApplicationOf(btn->icon->owner->main_window);
838 if (wapp->flags.hidden) {
839 wWorkspaceChange(btn->icon->core->screen_ptr, wapp->last_workspace);
840 wUnhideApplication(wapp, False, False);
841 } else {
842 wHideApplication(wapp);
847 static void
848 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
850 WApplication *wapp;
851 WAppIcon *btn = (WAppIcon*)entry->clientdata;
853 wapp = wApplicationOf(btn->icon->owner->main_window);
855 wUnhideApplication(wapp, False, True);
859 WAppIcon*
860 mainIconCreate(WScreen *scr, int type)
862 WAppIcon *btn;
863 int x_pos;
865 if (type == WM_CLIP) {
866 if (scr->clip_icon)
867 return scr->clip_icon;
868 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
869 btn->icon->core->descriptor.handle_expose = clipIconExpose;
870 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
871 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
872 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
873 x_pos = 0;
874 } else {
875 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
876 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
879 btn->xindex = 0;
880 btn->yindex = 0;
882 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
883 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
884 btn->icon->core->descriptor.parent = btn;
885 /*ChangeStackingLevel(btn->icon->core, WMDockLevel);*/
886 XMapWindow(dpy, btn->icon->core->window);
887 btn->x_pos = x_pos;
888 btn->y_pos = 0;
889 btn->docked = 1;
890 if (type == WM_CLIP)
891 scr->clip_icon = btn;
893 return btn;
897 static void
898 switchWSCommand(WMenu *menu, WMenuEntry *entry)
900 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
901 WScreen *scr = icon->icon->core->screen_ptr;
902 WDock *src, *dest;
903 WMArray *selectedIcons;
904 int x, y;
906 if (entry->order == scr->current_workspace)
907 return;
908 src = icon->dock;
909 dest = scr->workspaces[entry->order]->clip;
911 selectedIcons = getSelected(src);
913 if (WMGetArrayItemCount(selectedIcons)) {
914 WMArrayIterator iter;
916 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
917 if (wDockFindFreeSlot(dest, &x, &y)) {
918 moveIconBetweenDocks(src, dest, btn, x, y);
919 XUnmapWindow(dpy, btn->icon->core->window);
922 } else if (icon != scr->clip_icon) {
923 if (wDockFindFreeSlot(dest, &x, &y)) {
924 moveIconBetweenDocks(src, dest, icon, x, y);
925 XUnmapWindow(dpy, icon->icon->core->window);
928 WMFreeArray(selectedIcons);
933 static void
934 launchDockedApplication(WAppIcon *btn, Bool withSelection)
936 WScreen *scr = btn->icon->core->screen_ptr;
938 if (!btn->launching &&
939 ((!withSelection && btn->command!=NULL) ||
940 (withSelection && btn->paste_command!=NULL))) {
941 if (!btn->forced_dock) {
942 btn->relaunching = btn->running;
943 btn->running = 1;
945 if (btn->wm_instance || btn->wm_class) {
946 WWindowAttributes attr;
947 memset(&attr, 0, sizeof(WWindowAttributes));
948 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class,
949 &attr, NULL, True);
951 if (!attr.no_appicon && !btn->buggy_app)
952 btn->launching = 1;
953 else
954 btn->running = 0;
956 btn->drop_launch = 0;
957 btn->paste_launch = withSelection;
958 scr->last_dock = btn->dock;
959 btn->pid = execCommand(btn,
960 withSelection ? btn->paste_command : btn->command,
961 NULL);
962 if (btn->pid>0) {
963 if (btn->buggy_app) {
964 /* give feedback that the app was launched */
965 btn->launching = 1;
966 dockIconPaint(btn);
967 btn->launching = 0;
968 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
969 } else {
970 dockIconPaint(btn);
972 } else {
973 wwarning(_("could not launch application %s\n"), btn->command);
974 btn->launching = 0;
975 if (!btn->relaunching)
976 btn->running = 0;
983 static void
984 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
986 WScreen *scr = menu->frame->screen_ptr;
987 char title[MAX_WORKSPACENAME_WIDTH+1];
988 int i;
990 if (!menu || !icon)
991 return;
993 for (i=0; i<scr->workspace_count; i++) {
994 if (i < menu->entry_no) {
995 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
996 wfree(menu->entries[i]->text);
997 strcpy(title, scr->workspaces[i]->name);
998 menu->entries[i]->text = wstrdup(title);
999 menu->flags.realized = 0;
1001 menu->entries[i]->clientdata = (void*)icon;
1002 } else {
1003 strcpy(title, scr->workspaces[i]->name);
1005 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
1007 menu->flags.realized = 0;
1009 if (i == scr->current_workspace) {
1010 wMenuSetEnabled(menu, i, False);
1011 } else {
1012 wMenuSetEnabled(menu, i, True);
1016 if (!menu->flags.realized)
1017 wMenuRealize(menu);
1021 static WMenu*
1022 makeWorkspaceMenu(WScreen *scr)
1024 WMenu *menu;
1026 menu = wMenuCreate(scr, NULL, False);
1027 if (!menu)
1028 wwarning(_("could not create workspace submenu for Clip menu"));
1030 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
1032 menu->flags.realized = 0;
1033 wMenuRealize(menu);
1035 return menu;
1039 static void
1040 updateClipOptionsMenu(WMenu *menu, WDock *dock)
1042 WMenuEntry *entry;
1043 int index = 0;
1045 if (!menu || !dock)
1046 return;
1048 /* keep on top */
1049 entry = menu->entries[index];
1050 entry->flags.indicator_on = !dock->lowered;
1051 entry->clientdata = dock;
1053 /* collapsed */
1054 entry = menu->entries[++index];
1055 entry->flags.indicator_on = dock->collapsed;
1056 entry->clientdata = dock;
1058 /* auto-collapse */
1059 entry = menu->entries[++index];
1060 entry->flags.indicator_on = dock->auto_collapse;
1061 entry->clientdata = dock;
1063 /* auto-raise/lower */
1064 entry = menu->entries[++index];
1065 entry->flags.indicator_on = dock->auto_raise_lower;
1066 entry->clientdata = dock;
1067 wMenuSetEnabled(menu, index, dock->lowered);
1069 /* attract icons */
1070 entry = menu->entries[++index];
1071 entry->flags.indicator_on = dock->attract_icons;
1072 entry->clientdata = dock;
1074 menu->flags.realized = 0;
1075 wMenuRealize(menu);
1079 static WMenu*
1080 makeClipOptionsMenu(WScreen *scr)
1082 WMenu *menu;
1083 WMenuEntry *entry;
1085 menu = wMenuCreate(scr, NULL, False);
1086 if (!menu) {
1087 wwarning(_("could not create options submenu for Clip menu"));
1088 return NULL;
1091 entry = wMenuAddCallback(menu, _("Keep on Top"),
1092 toggleLoweredCallback, NULL);
1093 entry->flags.indicator = 1;
1094 entry->flags.indicator_on = 1;
1095 entry->flags.indicator_type = MI_CHECK;
1097 entry = wMenuAddCallback(menu, _("Collapsed"),
1098 toggleCollapsedCallback, NULL);
1099 entry->flags.indicator = 1;
1100 entry->flags.indicator_on = 1;
1101 entry->flags.indicator_type = MI_CHECK;
1103 entry = wMenuAddCallback(menu, _("Autocollapse"),
1104 toggleAutoCollapseCallback, NULL);
1105 entry->flags.indicator = 1;
1106 entry->flags.indicator_on = 1;
1107 entry->flags.indicator_type = MI_CHECK;
1109 entry = wMenuAddCallback(menu, _("Autoraise"),
1110 toggleAutoRaiseLowerCallback, NULL);
1111 entry->flags.indicator = 1;
1112 entry->flags.indicator_on = 1;
1113 entry->flags.indicator_type = MI_CHECK;
1115 entry = wMenuAddCallback(menu, _("Autoattract Icons"),
1116 toggleAutoAttractCallback, NULL);
1117 entry->flags.indicator = 1;
1118 entry->flags.indicator_on = 1;
1119 entry->flags.indicator_type = MI_CHECK;
1121 menu->flags.realized = 0;
1122 wMenuRealize(menu);
1124 return menu;
1128 static WMenu*
1129 dockMenuCreate(WScreen *scr, int type)
1131 WMenu *menu;
1132 WMenuEntry *entry;
1134 if (type == WM_CLIP && scr->clip_menu)
1135 return scr->clip_menu;
1137 menu = wMenuCreate(scr, NULL, False);
1138 if (type != WM_CLIP) {
1139 entry = wMenuAddCallback(menu, _("Keep on Top"),
1140 toggleLoweredCallback, NULL);
1141 entry->flags.indicator = 1;
1142 entry->flags.indicator_on = 1;
1143 entry->flags.indicator_type = MI_CHECK;
1144 } else {
1145 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1146 scr->clip_options = makeClipOptionsMenu(scr);
1147 if (scr->clip_options)
1148 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1150 entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback,
1151 NULL);
1152 wfree(entry->text);
1153 entry->text = _("Rename Workspace");
1155 entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
1156 entry->flags.indicator = 1;
1157 entry->flags.indicator_on = 1;
1158 entry->flags.indicator_type = MI_CHECK;
1160 entry = wMenuAddCallback(menu, _("Select All Icons"),
1161 selectIconsCallback, NULL);
1162 wfree(entry->text);
1163 entry->text = _("Select All Icons");
1165 entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
1166 wfree(entry->text);
1167 entry->text = _("Keep Icon");
1169 entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
1170 wfree(entry->text);
1171 entry->text = _("Move Icon To");
1172 scr->clip_submenu = makeWorkspaceMenu(scr);
1173 if (scr->clip_submenu)
1174 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1176 entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback,
1177 NULL);
1178 wfree(entry->text);
1179 entry->text = _("Remove Icon");
1181 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1184 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1186 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1188 entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1189 wfree(entry->text);
1190 entry->text = _("Hide");
1192 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1194 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1196 if (type == WM_CLIP)
1197 scr->clip_menu = menu;
1199 return menu;
1203 WDock*
1204 wDockCreate(WScreen *scr, int type)
1206 WDock *dock;
1207 WAppIcon *btn;
1208 int icon_count;
1210 make_keys();
1212 dock = wmalloc(sizeof(WDock));
1213 memset(dock, 0, sizeof(WDock));
1215 if (type == WM_CLIP)
1216 icon_count = CLIP_MAX_ICONS;
1217 else
1218 icon_count = scr->scr_height/wPreferences.icon_size;
1220 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1221 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1223 dock->max_icons = icon_count;
1225 btn = mainIconCreate(scr, type);
1227 btn->dock = dock;
1229 dock->x_pos = btn->x_pos;
1230 dock->y_pos = btn->y_pos;
1231 dock->screen_ptr = scr;
1232 dock->type = type;
1233 dock->icon_count = 1;
1234 dock->on_right_side = 1;
1235 dock->collapsed = 0;
1236 dock->auto_collapse = 0;
1237 dock->auto_collapse_magic = NULL;
1238 dock->auto_raise_lower = 0;
1239 dock->auto_lower_magic = NULL;
1240 dock->auto_raise_magic = NULL;
1241 dock->attract_icons = 0;
1242 dock->lowered = 1;
1243 dock->icon_array[0] = btn;
1244 wRaiseFrame(btn->icon->core);
1245 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1247 /* create dock menu */
1248 dock->menu = dockMenuCreate(scr, type);
1250 return dock;
1254 void
1255 wDockDestroy(WDock *dock)
1257 int i;
1258 WAppIcon *aicon;
1260 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1261 aicon = dock->icon_array[i];
1262 if (aicon) {
1263 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1264 wDockDetach(dock, aicon);
1265 if (keepit) {
1266 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
1267 XMoveWindow(dpy, aicon->icon->core->window,
1268 aicon->x_pos, aicon->y_pos);
1269 if (!dock->mapped || dock->collapsed)
1270 XMapWindow(dpy, aicon->icon->core->window);
1274 if (wPreferences.auto_arrange_icons)
1275 wArrangeIcons(dock->screen_ptr, True);
1276 wfree(dock->icon_array);
1277 if (dock->menu && dock->type!=WM_CLIP)
1278 wMenuDestroy(dock->menu, True);
1279 if (dock->screen_ptr->last_dock == dock)
1280 dock->screen_ptr->last_dock = NULL;
1281 wfree(dock);
1285 void
1286 wClipIconPaint(WAppIcon *aicon)
1288 WScreen *scr = aicon->icon->core->screen_ptr;
1289 WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1290 WMColor *color;
1291 Window win = aicon->icon->core->window;
1292 int length, nlength;
1293 char *ws_name, ws_number[10];
1294 int ty, tx;
1296 wIconPaint(aicon->icon);
1298 length = strlen(workspace->name);
1299 ws_name = wmalloc(length + 1);
1300 snprintf(ws_name, length+1, "%s", workspace->name);
1301 snprintf(ws_number, sizeof(ws_number), "%i", scr->current_workspace + 1);
1302 nlength = strlen(ws_number);
1304 if (!workspace->clip->collapsed)
1305 color = scr->clip_title_color[CLIP_NORMAL];
1306 else
1307 color = scr->clip_title_color[CLIP_COLLAPSED];
1309 ty = ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
1311 tx = CLIP_BUTTON_SIZE*ICON_SIZE/64;
1313 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1314 ty, ws_name, length);
1315 /*WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, 4,
1316 2, ws_name, length);*/
1318 tx = (ICON_SIZE/2 - WMWidthOfString(scr->clip_title_font, ws_number,
1319 nlength))/2;
1321 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1322 2, ws_number, nlength);
1324 wfree(ws_name);
1326 if (aicon->launching) {
1327 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1328 0, 0, wPreferences.icon_size, wPreferences.icon_size);
1330 paintClipButtons(aicon, aicon->dock->lclip_button_pushed,
1331 aicon->dock->rclip_button_pushed);
1335 static void
1336 clipIconExpose(WObjDescriptor *desc, XEvent *event)
1338 wClipIconPaint(desc->parent);
1342 static void
1343 dockIconPaint(WAppIcon *btn)
1345 if (btn == btn->icon->core->screen_ptr->clip_icon)
1346 wClipIconPaint(btn);
1347 else
1348 wAppIconPaint(btn);
1352 static WMPropList*
1353 make_icon_state(WAppIcon *btn)
1355 WMPropList *node = NULL;
1356 WMPropList *command, *autolaunch, *lock, *name, *forced, *host;
1357 WMPropList *position, *buggy, *omnipresent;
1358 char *tmp;
1359 char buffer[64];
1361 if (btn) {
1362 if (!btn->command)
1363 command = WMCreatePLString("-");
1364 else
1365 command = WMCreatePLString(btn->command);
1367 autolaunch = btn->auto_launch ? dYes : dNo;
1369 lock = btn->lock ? dYes : dNo;
1371 tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
1373 name = WMCreatePLString(tmp);
1375 wfree(tmp);
1377 forced = btn->forced_dock ? dYes : dNo;
1379 buggy = btn->buggy_app ? dYes : dNo;
1381 if (btn == btn->icon->core->screen_ptr->clip_icon)
1382 snprintf(buffer, sizeof(buffer), "%i,%i", btn->x_pos, btn->y_pos);
1383 else
1384 snprintf(buffer, sizeof(buffer), "%hi,%hi", btn->xindex, btn->yindex);
1385 position = WMCreatePLString(buffer);
1387 node = WMCreatePLDictionary(dCommand, command,
1388 dName, name,
1389 dAutoLaunch, autolaunch,
1390 dLock, lock,
1391 dForced, forced,
1392 dBuggyApplication, buggy,
1393 dPosition, position,
1394 NULL);
1395 WMReleasePropList(command);
1396 WMReleasePropList(name);
1397 WMReleasePropList(position);
1399 omnipresent = btn->omnipresent ? dYes : dNo;
1400 if (btn->dock != btn->icon->core->screen_ptr->dock &&
1401 (btn->xindex != 0 || btn->yindex != 0))
1402 WMPutInPLDictionary(node, dOmnipresent, omnipresent);
1404 #ifdef OFFIX_DND
1405 if (btn->dnd_command) {
1406 command = WMCreatePLString(btn->dnd_command);
1407 WMPutInPLDictionary(node, dDropCommand, command);
1408 WMReleasePropList(command);
1410 #endif /* OFFIX_DND */
1412 if (btn->paste_command) {
1413 command = WMCreatePLString(btn->paste_command);
1414 WMPutInPLDictionary(node, dPasteCommand, command);
1415 WMReleasePropList(command);
1418 if (btn->client_machine && btn->remote_start) {
1419 host = WMCreatePLString(btn->client_machine);
1420 WMPutInPLDictionary(node, dHost, host);
1421 WMReleasePropList(host);
1425 return node;
1429 static WMPropList*
1430 dockSaveState(WDock *dock)
1432 int i;
1433 WMPropList *icon_info;
1434 WMPropList *list=NULL, *dock_state=NULL;
1435 WMPropList *value, *key;
1436 char buffer[256];
1438 list = WMCreatePLArray(NULL);
1440 for (i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
1441 WAppIcon *btn = dock->icon_array[i];
1443 if (!btn || btn->attracted)
1444 continue;
1446 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1447 WMAddToPLArray(list, icon_info);
1448 WMReleasePropList(icon_info);
1452 dock_state = WMCreatePLDictionary(dApplications, list,
1453 NULL);
1455 if (dock->type == WM_DOCK) {
1456 snprintf(buffer, sizeof(buffer), "Applications%i", dock->screen_ptr->scr_height);
1457 key = WMCreatePLString(buffer);
1458 WMPutInPLDictionary(dock_state, key, list);
1459 WMReleasePropList(key);
1462 snprintf(buffer, sizeof(buffer), "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0),
1463 dock->y_pos);
1464 value = WMCreatePLString(buffer);
1465 WMPutInPLDictionary(dock_state, dPosition, value);
1466 WMReleasePropList(value);
1468 WMReleasePropList(list);
1471 value = (dock->lowered ? dYes : dNo);
1472 WMPutInPLDictionary(dock_state, dLowered, value);
1474 if (dock->type == WM_CLIP) {
1475 value = (dock->collapsed ? dYes : dNo);
1476 WMPutInPLDictionary(dock_state, dCollapsed, value);
1478 value = (dock->auto_collapse ? dYes : dNo);
1479 WMPutInPLDictionary(dock_state, dAutoCollapse, value);
1481 value = (dock->auto_raise_lower ? dYes : dNo);
1482 WMPutInPLDictionary(dock_state, dAutoRaiseLower, value);
1484 value = (dock->attract_icons ? dYes : dNo);
1485 WMPutInPLDictionary(dock_state, dAutoAttractIcons, value);
1488 return dock_state;
1492 void
1493 wDockSaveState(WScreen *scr, WMPropList *old_state)
1495 WMPropList *dock_state;
1496 WMPropList *keys;
1498 dock_state = dockSaveState(scr->dock);
1501 * Copy saved states of docks with different sizes.
1503 if (old_state) {
1504 int i;
1505 WMPropList *tmp;
1507 keys = WMGetPLDictionaryKeys(old_state);
1508 for (i = 0; i < WMGetPropListItemCount(keys); i++) {
1509 tmp = WMGetFromPLArray(keys, i);
1511 if (strncasecmp(WMGetFromPLString(tmp), "applications", 12) == 0
1512 && !WMGetFromPLDictionary(dock_state, tmp)) {
1514 WMPutInPLDictionary(dock_state,
1515 tmp,
1516 WMGetFromPLDictionary(old_state, tmp));
1519 WMReleasePropList(keys);
1523 WMPutInPLDictionary(scr->session_state, dDock, dock_state);
1525 WMReleasePropList(dock_state);
1529 void
1530 wClipSaveState(WScreen *scr)
1532 WMPropList *clip_state;
1534 clip_state = make_icon_state(scr->clip_icon);
1536 WMPutInPLDictionary(scr->session_state, dClip, clip_state);
1538 WMReleasePropList(clip_state);
1542 WMPropList*
1543 wClipSaveWorkspaceState(WScreen *scr, int workspace)
1545 return dockSaveState(scr->workspaces[workspace]->clip);
1549 static Bool
1550 getBooleanDockValue(WMPropList *value, WMPropList *key)
1552 if (value) {
1553 if (WMIsPLString(value)) {
1554 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1555 return True;
1556 } else {
1557 wwarning(_("bad value in docked icon state info %s"),
1558 WMGetFromPLString(key));
1561 return False;
1565 static WAppIcon*
1566 restore_icon_state(WScreen *scr, WMPropList *info, int type, int index)
1568 WAppIcon *aicon;
1569 WMPropList *cmd, *value;
1572 cmd = WMGetFromPLDictionary(info, dCommand);
1573 if (!cmd || !WMIsPLString(cmd)) {
1574 return NULL;
1577 /* parse window name */
1578 value = WMGetFromPLDictionary(info, dName);
1579 if (!value)
1580 return NULL;
1583 char *wclass, *winstance;
1584 char *command;
1586 ParseWindowName(value, &winstance, &wclass, "dock");
1588 if (!winstance && !wclass) {
1589 return NULL;
1592 /* get commands */
1594 if (cmd)
1595 command = wstrdup(WMGetFromPLString(cmd));
1596 else
1597 command = NULL;
1599 if (!command || strcmp(command, "-")==0) {
1600 if (command)
1601 wfree(command);
1602 if (wclass)
1603 wfree(wclass);
1604 if (winstance)
1605 wfree(winstance);
1607 return NULL;
1610 aicon = wAppIconCreateForDock(scr, command, winstance, wclass,
1611 TILE_NORMAL);
1612 if (wclass)
1613 wfree(wclass);
1614 if (winstance)
1615 wfree(winstance);
1616 if (command)
1617 wfree(command);
1620 aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1621 if (type == WM_CLIP) {
1622 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1623 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1625 aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1626 aicon->icon->core->descriptor.parent = aicon;
1629 #ifdef OFFIX_DND
1630 cmd = WMGetFromPLDictionary(info, dDropCommand);
1631 if (cmd)
1632 aicon->dnd_command = wstrdup(WMGetFromPLString(cmd));
1633 #endif
1635 cmd = WMGetFromPLDictionary(info, dPasteCommand);
1636 if (cmd)
1637 aicon->paste_command = wstrdup(WMGetFromPLString(cmd));
1639 /* check auto launch */
1640 value = WMGetFromPLDictionary(info, dAutoLaunch);
1642 aicon->auto_launch = getBooleanDockValue(value, dAutoLaunch);
1644 /* check lock */
1645 value = WMGetFromPLDictionary(info, dLock);
1647 aicon->lock = getBooleanDockValue(value, dLock);
1649 /* check if it wasn't normally docked */
1650 value = WMGetFromPLDictionary(info, dForced);
1652 aicon->forced_dock = getBooleanDockValue(value, dForced);
1654 /* check if we can rely on the stuff in the app */
1655 value = WMGetFromPLDictionary(info, dBuggyApplication);
1657 aicon->buggy_app = getBooleanDockValue(value, dBuggyApplication);
1659 /* get position in the dock */
1660 value = WMGetFromPLDictionary(info, dPosition);
1661 if (value && WMIsPLString(value)) {
1662 if (sscanf(WMGetFromPLString(value), "%hi,%hi", &aicon->xindex,
1663 &aicon->yindex)!=2)
1664 wwarning(_("bad value in docked icon state info %s"),
1665 WMGetFromPLString(dPosition));
1667 /* check position sanity */
1668 /* incomplete section! */
1669 if (type == WM_DOCK) {
1670 aicon->xindex = 0;
1671 if (aicon->yindex < 0)
1672 wwarning(_("bad value in docked icon position %i,%i"),
1673 aicon->xindex, aicon->yindex);
1675 } else {
1676 aicon->yindex = index;
1677 aicon->xindex = 0;
1680 /* check if icon is omnipresent */
1681 value = WMGetFromPLDictionary(info, dOmnipresent);
1683 aicon->omnipresent = getBooleanDockValue(value, dOmnipresent);
1685 aicon->running = 0;
1686 aicon->docked = 1;
1688 return aicon;
1692 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1695 WAppIcon*
1696 wClipRestoreState(WScreen *scr, WMPropList *clip_state)
1698 WAppIcon *icon;
1699 WMPropList *value;
1702 icon = mainIconCreate(scr, WM_CLIP);
1704 if (!clip_state)
1705 return icon;
1706 else
1707 WMRetainPropList(clip_state);
1709 /* restore position */
1711 value = WMGetFromPLDictionary(clip_state, dPosition);
1713 if (value) {
1714 if (!WMIsPLString(value))
1715 COMPLAIN("Position");
1716 else {
1717 if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos,
1718 &icon->y_pos)!=2)
1719 COMPLAIN("Position");
1721 /* check position sanity */
1722 if (icon->y_pos < 0)
1723 icon->y_pos = 0;
1724 else if (icon->y_pos > scr->scr_height-ICON_SIZE)
1725 icon->y_pos = scr->scr_height-ICON_SIZE;
1727 if (icon->x_pos < 0)
1728 icon->x_pos = 0;
1729 else if (icon->x_pos > scr->scr_width-ICON_SIZE)
1730 icon->x_pos = scr->scr_width-ICON_SIZE;
1734 #ifdef OFFIX_DND
1735 value = WMGetFromPLDictionary(clip_state, dDropCommand);
1736 if (value && WMIsPLString(value))
1737 icon->dnd_command = wstrdup(WMGetFromPLString(value));
1738 #endif
1740 value = WMGetFromPLDictionary(clip_state, dPasteCommand);
1741 if (value && WMIsPLString(value))
1742 icon->paste_command = wstrdup(WMGetFromPLString(value));
1744 WMReleasePropList(clip_state);
1746 return icon;
1750 WDock*
1751 wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type)
1753 WDock *dock;
1754 WMPropList *apps;
1755 WMPropList *value;
1756 WAppIcon *aicon, *old_top;
1757 int count, i;
1760 dock = wDockCreate(scr, type);
1762 if (!dock_state)
1763 return dock;
1765 if (dock_state)
1766 WMRetainPropList(dock_state);
1769 /* restore position */
1771 value = WMGetFromPLDictionary(dock_state, dPosition);
1773 if (value) {
1774 if (!WMIsPLString(value))
1775 COMPLAIN("Position");
1776 else {
1777 if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos,
1778 &dock->y_pos)!=2)
1779 COMPLAIN("Position");
1781 /* check position sanity */
1782 if (dock->y_pos < 0)
1783 dock->y_pos = 0;
1784 else if (dock->y_pos > scr->scr_height-ICON_SIZE)
1785 dock->y_pos = scr->scr_height - ICON_SIZE;
1787 /* This is no more needed. ??? */
1788 if (type == WM_CLIP) {
1789 if (dock->x_pos < 0)
1790 dock->x_pos = 0;
1791 else if (dock->x_pos > scr->scr_width-ICON_SIZE)
1792 dock->x_pos = scr->scr_width-ICON_SIZE;
1793 } else {
1794 if (dock->x_pos >= 0) {
1795 dock->x_pos = DOCK_EXTRA_SPACE;
1796 dock->on_right_side = 0;
1797 } else {
1798 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1799 dock->on_right_side = 1;
1805 /* restore lowered/raised state */
1807 dock->lowered = 0;
1809 value = WMGetFromPLDictionary(dock_state, dLowered);
1811 if (value) {
1812 if (!WMIsPLString(value))
1813 COMPLAIN("Lowered");
1814 else {
1815 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1816 dock->lowered = 1;
1821 /* restore collapsed state */
1823 dock->collapsed = 0;
1825 value = WMGetFromPLDictionary(dock_state, dCollapsed);
1827 if (value) {
1828 if (!WMIsPLString(value))
1829 COMPLAIN("Collapsed");
1830 else {
1831 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1832 dock->collapsed = 1;
1837 /* restore auto-collapsed state */
1839 value = WMGetFromPLDictionary(dock_state, dAutoCollapse);
1841 if (value) {
1842 if (!WMIsPLString(value))
1843 COMPLAIN("AutoCollapse");
1844 else {
1845 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1846 dock->auto_collapse = 1;
1847 dock->collapsed = 1;
1853 /* restore auto-raise/lower state */
1855 value = WMGetFromPLDictionary(dock_state, dAutoRaiseLower);
1857 if (value) {
1858 if (!WMIsPLString(value))
1859 COMPLAIN("AutoRaiseLower");
1860 else {
1861 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1862 dock->auto_raise_lower = 1;
1867 /* restore attract icons state */
1869 dock->attract_icons = 0;
1871 value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons);
1873 if (value) {
1874 if (!WMIsPLString(value))
1875 COMPLAIN("AutoAttractIcons");
1876 else {
1877 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1878 dock->attract_icons = 1;
1883 /* application list */
1886 WMPropList *tmp;
1887 char buffer[64];
1890 * When saving, it saves the dock state in
1891 * Applications and Applicationsnnn
1893 * When loading, it will first try Applicationsnnn.
1894 * If it does not exist, use Applications as default.
1897 snprintf(buffer, sizeof(buffer), "Applications%i", scr->scr_height);
1899 tmp = WMCreatePLString(buffer);
1900 apps = WMGetFromPLDictionary(dock_state, tmp);
1901 WMReleasePropList(tmp);
1903 if (!apps) {
1904 apps = WMGetFromPLDictionary(dock_state, dApplications);
1908 if (!apps) {
1909 goto finish;
1912 count = WMGetPropListItemCount(apps);
1914 if (count==0)
1915 goto finish;
1917 old_top = dock->icon_array[0];
1919 /* dock->icon_count is set to 1 when dock is created.
1920 * Since Clip is already restored, we want to keep it so for clip,
1921 * but for dock we may change the default top tile, so we set it to 0.
1923 if (type == WM_DOCK)
1924 dock->icon_count = 0;
1926 for (i=0; i<count; i++) {
1927 if (dock->icon_count >= dock->max_icons) {
1928 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1929 break;
1932 value = WMGetFromPLArray(apps, i);
1933 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1935 dock->icon_array[dock->icon_count] = aicon;
1937 if (aicon) {
1938 aicon->dock = dock;
1939 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1940 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1942 if (dock->lowered)
1943 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1944 else
1945 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1947 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1948 0, 0);
1950 if (!dock->collapsed)
1951 XMapWindow(dpy, aicon->icon->core->window);
1952 wRaiseFrame(aicon->icon->core);
1954 dock->icon_count++;
1955 } else if (dock->icon_count==0 && type==WM_DOCK)
1956 dock->icon_count++;
1959 /* if the first icon is not defined, use the default */
1960 if (dock->icon_array[0]==NULL) {
1961 /* update default icon */
1962 old_top->x_pos = dock->x_pos;
1963 old_top->y_pos = dock->y_pos;
1964 if (dock->lowered)
1965 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1966 else
1967 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1968 dock->icon_array[0] = old_top;
1969 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1970 /* we don't need to increment dock->icon_count here because it was
1971 * incremented in the loop above.
1973 } else if (old_top!=dock->icon_array[0]) {
1974 if (old_top == scr->clip_icon)
1975 scr->clip_icon = dock->icon_array[0];
1976 wAppIconDestroy(old_top);
1979 finish:
1980 if (dock_state)
1981 WMReleasePropList(dock_state);
1983 return dock;
1988 void
1989 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
1991 if (btn && btn->command && !btn->running && !btn->launching) {
1993 btn->drop_launch = 0;
1994 btn->paste_launch = 0;
1996 btn->pid = execCommand(btn, btn->command, state);
1998 if (btn->pid>0) {
1999 if (!btn->forced_dock && !btn->buggy_app) {
2000 btn->launching = 1;
2001 dockIconPaint(btn);
2004 } else {
2005 wfree(state);
2010 void
2011 wDockDoAutoLaunch(WDock *dock, int workspace)
2013 WAppIcon *btn;
2014 WSavedState *state;
2015 int i;
2017 for (i = 0; i < dock->max_icons; i++) {
2018 btn = dock->icon_array[i];
2019 if (!btn || !btn->auto_launch)
2020 continue;
2022 state = wmalloc(sizeof(WSavedState));
2023 memset(state, 0, sizeof(WSavedState));
2024 state->workspace = workspace;
2025 /* TODO: this is klugy and is very difficult to understand
2026 * what's going on. Try to clean up */
2027 wDockLaunchWithState(dock, btn, state);
2032 #ifdef OFFIX_DND
2033 static WDock*
2034 findDock(WScreen *scr, XEvent *event, int *icon_pos)
2036 WDock *dock;
2037 int i;
2039 *icon_pos = -1;
2040 if ((dock = scr->dock)!=NULL) {
2041 for (i=0; i<dock->max_icons; i++) {
2042 if (dock->icon_array[i]
2043 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2044 *icon_pos = i;
2045 break;
2049 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
2050 for (i=0; i<dock->max_icons; i++) {
2051 if (dock->icon_array[i]
2052 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2053 *icon_pos = i;
2054 break;
2058 if(*icon_pos>=0)
2059 return dock;
2060 return NULL;
2065 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
2067 WDock *dock;
2068 WAppIcon *btn;
2069 int icon_pos;
2071 dock = findDock(scr, event, &icon_pos);
2072 if (!dock)
2073 return False;
2076 * Return True if the drop was on an application icon window.
2077 * In this case, let the ClientMessage handler redirect the
2078 * message to the app.
2080 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
2081 return True;
2083 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
2084 scr->flags.dnd_data_convertion_status = 0;
2086 btn = dock->icon_array[icon_pos];
2088 if (!btn->forced_dock) {
2089 btn->relaunching = btn->running;
2090 btn->running = 1;
2092 if (btn->wm_instance || btn->wm_class) {
2093 WWindowAttributes attr;
2094 memset(&attr, 0, sizeof(WWindowAttributes));
2095 wDefaultFillAttributes(btn->icon->core->screen_ptr,
2096 btn->wm_instance,
2097 btn->wm_class, &attr, NULL, True);
2099 if (!attr.no_appicon)
2100 btn->launching = 1;
2101 else
2102 btn->running = 0;
2105 btn->paste_launch = 0;
2106 btn->drop_launch = 1;
2107 scr->last_dock = dock;
2108 btn->pid = execCommand(btn, btn->dnd_command, NULL);
2109 if (btn->pid>0) {
2110 dockIconPaint(btn);
2111 } else {
2112 btn->launching = 0;
2113 if (!btn->relaunching) {
2114 btn->running = 0;
2118 return False;
2120 #endif /* OFFIX_DND */
2124 Bool
2125 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2127 WWindow *wwin;
2128 char **argv;
2129 int argc;
2130 int index;
2132 wwin = icon->icon->owner;
2133 if (icon->command==NULL) {
2134 icon->editing = 0;
2135 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2137 icon->command = wtokenjoin(argv, argc);
2138 XFreeStringList(argv);
2139 } else {
2140 char *command=NULL;
2142 /* icon->forced_dock = 1;*/
2143 if (dock->type!=WM_CLIP || !icon->attracted) {
2144 icon->editing = 1;
2145 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
2146 _("Type the command used to launch the application"),
2147 &command)) {
2148 if (command && (command[0]==0 ||
2149 (command[0]=='-' && command[1]==0))) {
2150 wfree(command);
2151 command = NULL;
2153 icon->command = command;
2154 icon->editing = 0;
2155 } else {
2156 icon->editing = 0;
2157 if (command)
2158 wfree(command);
2159 /* If the target is the dock, reject the icon. If
2160 * the target is the clip, make it an attracted icon
2162 if (dock->type==WM_CLIP) {
2163 icon->attracted = 1;
2164 if (!icon->icon->shadowed) {
2165 icon->icon->shadowed = 1;
2166 icon->icon->force_paint = 1;
2168 } else {
2169 return False;
2174 } else {
2175 icon->editing = 0;
2178 for (index=1; index<dock->max_icons; index++)
2179 if (dock->icon_array[index] == NULL)
2180 break;
2181 /* if (index == dock->max_icons)
2182 return; */
2184 assert(index < dock->max_icons);
2186 dock->icon_array[index] = icon;
2187 icon->yindex = y;
2188 icon->xindex = x;
2190 icon->omnipresent = 0;
2192 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2193 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2195 dock->icon_count++;
2197 icon->running = 1;
2198 icon->launching = 0;
2199 icon->docked = 1;
2200 icon->dock = dock;
2201 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2202 if (dock->type == WM_CLIP) {
2203 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2204 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2206 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2207 icon->icon->core->descriptor.parent = icon;
2209 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2210 icon->icon->core);
2211 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2212 wAppIconPaint(icon);
2214 if (wPreferences.auto_arrange_icons)
2215 wArrangeIcons(dock->screen_ptr, True);
2217 #ifdef OFFIX_DND
2218 if (icon->command && !icon->dnd_command) {
2219 int len = strlen(icon->command)+8;
2220 icon->dnd_command = wmalloc(len);
2221 snprintf(icon->dnd_command, len, "%s %%d", icon->command);
2223 #endif
2225 if (icon->command && !icon->paste_command) {
2226 int len = strlen(icon->command)+8;
2227 icon->paste_command = wmalloc(len);
2228 snprintf(icon->paste_command, len, "%s %%s", icon->command);
2231 return True;
2235 void
2236 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2238 int index;
2240 for(index=1; index<dock->max_icons; index++) {
2241 if(dock->icon_array[index] == icon)
2242 break;
2244 assert(index < dock->max_icons);
2246 icon->yindex = y;
2247 icon->xindex = x;
2249 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2250 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2254 Bool
2255 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2257 WWindow *wwin;
2258 char **argv;
2259 int argc;
2260 int index;
2262 if (src == dest)
2263 return True; /* No move needed, we're already there */
2265 if (dest == NULL)
2266 return False;
2268 wwin = icon->icon->owner;
2271 * For the moment we can't do this if we move icons in Clip from one
2272 * workspace to other, because if we move two or more icons without
2273 * command, the dialog box will not be able to tell us to which of the
2274 * moved icons it applies. -Dan
2276 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2277 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2279 icon->command = wtokenjoin(argv, argc);
2280 XFreeStringList(argv);
2281 } else {
2282 char *command=NULL;
2284 icon->editing = 1;
2285 /* icon->forced_dock = 1;*/
2286 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2287 _("Type the command used to launch the application"),
2288 &command)) {
2289 if (command && (command[0]==0 ||
2290 (command[0]=='-' && command[1]==0))) {
2291 wfree(command);
2292 command = NULL;
2294 icon->command = command;
2295 } else {
2296 icon->editing = 0;
2297 if (command)
2298 wfree(command);
2299 return False;
2301 icon->editing = 0;
2305 if (dest->type == WM_DOCK)
2306 wClipMakeIconOmnipresent(icon, False);
2308 for(index=1; index<src->max_icons; index++) {
2309 if(src->icon_array[index] == icon)
2310 break;
2312 assert(index < src->max_icons);
2314 src->icon_array[index] = NULL;
2315 src->icon_count--;
2317 for(index=1; index<dest->max_icons; index++) {
2318 if(dest->icon_array[index] == NULL)
2319 break;
2321 /* if (index == dest->max_icons)
2322 return; */
2324 assert(index < dest->max_icons);
2326 dest->icon_array[index] = icon;
2327 icon->dock = dest;
2329 /* deselect the icon */
2330 if (icon->icon->selected)
2331 wIconSelect(icon->icon);
2333 if (dest->type == WM_DOCK) {
2334 icon->icon->core->descriptor.handle_enternotify = NULL;
2335 icon->icon->core->descriptor.handle_leavenotify = NULL;
2336 } else {
2337 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2338 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2341 /* set it to be kept when moving to dock.
2342 * Unless the icon does not have a command set
2344 if (icon->command && dest->type==WM_DOCK) {
2345 icon->attracted = 0;
2346 if (icon->icon->shadowed) {
2347 icon->icon->shadowed = 0;
2348 icon->icon->force_paint = 1;
2352 if (src->auto_collapse || src->auto_raise_lower)
2353 clipLeave(src);
2355 icon->yindex = y;
2356 icon->xindex = x;
2358 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2359 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2361 dest->icon_count++;
2363 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2364 icon->icon->core);
2365 wAppIconPaint(icon);
2367 return True;
2371 void
2372 wDockDetach(WDock *dock, WAppIcon *icon)
2374 int index;
2376 /* make the settings panel be closed */
2377 if (icon->panel) {
2378 DestroyDockAppSettingsPanel(icon->panel);
2381 /* This must be called before icon->dock is set to NULL.
2382 * Don't move it. -Dan
2384 wClipMakeIconOmnipresent(icon, False);
2386 icon->docked = 0;
2387 icon->dock = NULL;
2388 icon->attracted = 0;
2389 icon->auto_launch = 0;
2390 if (icon->icon->shadowed) {
2391 icon->icon->shadowed = 0;
2392 icon->icon->force_paint = 1;
2395 /* deselect the icon */
2396 if (icon->icon->selected)
2397 wIconSelect(icon->icon);
2399 if (icon->command) {
2400 wfree(icon->command);
2401 icon->command = NULL;
2403 #ifdef OFFIX_DND
2404 if (icon->dnd_command) {
2405 wfree(icon->dnd_command);
2406 icon->dnd_command = NULL;
2408 #endif
2409 if (icon->paste_command) {
2410 wfree(icon->paste_command);
2411 icon->paste_command = NULL;
2414 for (index=1; index<dock->max_icons; index++)
2415 if (dock->icon_array[index] == icon)
2416 break;
2417 assert(index < dock->max_icons);
2418 dock->icon_array[index] = NULL;
2419 icon->yindex = -1;
2420 icon->xindex = -1;
2422 dock->icon_count--;
2424 /* if the dock is not attached to an application or
2425 * the the application did not set the approriate hints yet,
2426 * destroy the icon */
2427 if (!icon->running || !wApplicationOf(icon->main_window))
2428 wAppIconDestroy(icon);
2429 else {
2430 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2431 icon->icon->core->descriptor.handle_enternotify = NULL;
2432 icon->icon->core->descriptor.handle_leavenotify = NULL;
2433 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2434 icon->icon->core->descriptor.parent = icon;
2436 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2438 wAppIconPaint(icon);
2439 if (wPreferences.auto_arrange_icons) {
2440 wArrangeIcons(dock->screen_ptr, True);
2443 if (dock->auto_collapse || dock->auto_raise_lower)
2444 clipLeave(dock);
2449 * returns the closest Dock slot index for the passed
2450 * coordinates.
2452 * Returns False if icon can't be docked.
2454 * Note: this function should NEVER alter ret_x or ret_y, unless it will
2455 * return True. -Dan
2457 Bool
2458 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2459 int *ret_x, int *ret_y, int redocking)
2461 WScreen *scr = dock->screen_ptr;
2462 int dx, dy;
2463 int ex_x, ex_y;
2464 int i, offset = ICON_SIZE/2;
2465 WAppIcon *aicon = NULL;
2466 WAppIcon *nicon = NULL;
2467 int max_y_icons, max_x_icons;
2469 max_x_icons = scr->scr_width/ICON_SIZE;
2470 max_y_icons = scr->scr_height/ICON_SIZE-1;
2472 if (wPreferences.flags.noupdates)
2473 return False;
2475 dx = dock->x_pos;
2476 dy = dock->y_pos;
2478 /* if the dock is full */
2479 if (!redocking &&
2480 (dock->icon_count >= dock->max_icons)) {
2481 return False;
2484 /* exact position */
2485 if (req_y < dy)
2486 ex_y = (req_y - offset - dy)/ICON_SIZE;
2487 else
2488 ex_y = (req_y + offset - dy)/ICON_SIZE;
2490 if (req_x < dx)
2491 ex_x = (req_x - offset - dx)/ICON_SIZE;
2492 else
2493 ex_x = (req_x + offset - dx)/ICON_SIZE;
2495 /* check if the icon is outside the screen boundaries */
2496 if (dx + ex_x*ICON_SIZE < -ICON_SIZE+2 ||
2497 dx + ex_x*ICON_SIZE >= scr->scr_width-1 ||
2498 dy + ex_y*ICON_SIZE < -ICON_SIZE+2 ||
2499 dy + ex_y*ICON_SIZE >= scr->scr_height-1)
2500 return False;
2502 if (dock->type == WM_DOCK) {
2503 if (icon->dock != dock && ex_x != 0)
2504 return False;
2506 aicon = NULL;
2507 for (i=0; i<dock->max_icons; i++) {
2508 nicon = dock->icon_array[i];
2509 if (nicon && nicon->yindex == ex_y) {
2510 aicon = nicon;
2511 break;
2515 if (redocking) {
2516 int sig, done, closest;
2518 /* Possible cases when redocking:
2520 * icon dragged out of range of any slot -> false
2521 * icon dragged to range of free slot
2522 * icon dragged to range of same slot
2523 * icon dragged to range of different icon
2525 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2526 return False;
2528 if (ex_y>=0 && ex_y<=max_y_icons && (aicon==icon || !aicon)) {
2529 *ret_x = 0;
2530 *ret_y = ex_y;
2531 return True;
2534 /* start looking at the upper slot or lower? */
2535 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2536 sig = 1;
2537 else
2538 sig = -1;
2540 closest = -1;
2541 done = 0;
2542 /* look for closest free slot */
2543 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2544 int j;
2546 done = 1;
2547 closest = sig*(i/2) + ex_y;
2548 /* check if this slot is used */
2549 if (closest >= 0) {
2550 for (j = 0; j<dock->max_icons; j++) {
2551 if (dock->icon_array[j]
2552 && dock->icon_array[j]->yindex==closest) {
2553 /* slot is used by someone else */
2554 if (dock->icon_array[j]!=icon)
2555 done = 0;
2556 break;
2560 sig = -sig;
2562 if (done && closest >= 0 && closest <= max_y_icons &&
2563 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2565 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2566 *ret_x = 0;
2567 *ret_y = closest;
2568 return True;
2570 } else { /* !redocking */
2572 /* if slot is free and the icon is close enough, return it */
2573 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2574 *ret_x = 0;
2575 *ret_y = ex_y;
2576 return True;
2579 } else { /* CLIP */
2580 int neighbours = 0;
2581 int start, stop, k;
2583 start = icon->omnipresent ? 0 : scr->current_workspace;
2584 stop = icon->omnipresent ? scr->workspace_count : start+1;
2586 aicon = NULL;
2587 for (k=start; k<stop; k++) {
2588 WDock *tmp = scr->workspaces[k]->clip;
2589 if (!tmp)
2590 continue;
2591 for (i=0; i<tmp->max_icons; i++) {
2592 nicon = tmp->icon_array[i];
2593 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2594 aicon = nicon;
2595 break;
2598 if (aicon)
2599 break;
2601 for (k=start; k<stop; k++) {
2602 WDock *tmp = scr->workspaces[k]->clip;
2603 if (!tmp)
2604 continue;
2605 for (i=0; i<tmp->max_icons; i++) {
2606 nicon = tmp->icon_array[i];
2607 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2608 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2609 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2610 neighbours = 1;
2611 break;
2614 if (neighbours)
2615 break;
2618 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2619 *ret_x = ex_x;
2620 *ret_y = ex_y;
2621 return True;
2624 return False;
2628 #define ON_SCREEN(x, y, sx, ex, sy, ey) \
2629 ((((x)+ICON_SIZE/2) >= (sx)) && (((y)+ICON_SIZE/2) >= (sy)) && \
2630 (((x) + (ICON_SIZE/2)) <= (ex)) && (((y) + (ICON_SIZE/2)) <= ey))
2634 * returns true if it can find a free slot in the dock,
2635 * in which case it changes x_pos and y_pos accordingly.
2636 * Else returns false.
2638 Bool
2639 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2641 WScreen *scr = dock->screen_ptr;
2642 WAppIcon *btn;
2643 WAppIconChain *chain;
2644 unsigned char *slot_map;
2645 int mwidth;
2646 int r;
2647 int x, y;
2648 int i, done = False;
2649 int corner;
2650 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2651 int extra_count=0;
2653 if (dock->type == WM_CLIP &&
2654 dock != scr->workspaces[scr->current_workspace]->clip)
2655 extra_count = scr->global_icon_count;
2657 /* if the dock is full */
2658 if (dock->icon_count+extra_count >= dock->max_icons) {
2659 return False;
2662 if (!wPreferences.flags.nodock && scr->dock) {
2663 if (scr->dock->on_right_side)
2664 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2665 else
2666 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2669 if (ex < dock->x_pos)
2670 ex = dock->x_pos;
2671 if (sx > dock->x_pos+ICON_SIZE)
2672 sx = dock->x_pos+ICON_SIZE;
2673 #define C_NONE 0
2674 #define C_NW 1
2675 #define C_NE 2
2676 #define C_SW 3
2677 #define C_SE 4
2679 /* check if clip is in a corner */
2680 if (dock->type==WM_CLIP) {
2681 if (dock->x_pos < 1 && dock->y_pos < 1)
2682 corner = C_NE;
2683 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2684 corner = C_SE;
2685 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2686 corner = C_SW;
2687 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2688 corner = C_NW;
2689 else
2690 corner = C_NONE;
2691 } else
2692 corner = C_NONE;
2694 /* If the clip is in the corner, use only slots that are in the border
2695 * of the screen */
2696 if (corner!=C_NONE) {
2697 char *hmap, *vmap;
2698 int hcount, vcount;
2700 hcount = WMIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2701 vcount = WMIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2702 hmap = wmalloc(hcount+1);
2703 memset(hmap, 0, hcount+1);
2704 vmap = wmalloc(vcount+1);
2705 memset(vmap, 0, vcount+1);
2707 /* mark used positions */
2708 switch (corner) {
2709 case C_NE:
2710 for (i=0; i<dock->max_icons; i++) {
2711 btn = dock->icon_array[i];
2712 if (!btn)
2713 continue;
2715 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2716 vmap[btn->yindex] = 1;
2717 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2718 hmap[btn->xindex] = 1;
2720 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2721 btn = chain->aicon;
2722 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2723 vmap[btn->yindex] = 1;
2724 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2725 hmap[btn->xindex] = 1;
2727 break;
2728 case C_NW:
2729 for (i=0; i<dock->max_icons; i++) {
2730 btn = dock->icon_array[i];
2731 if (!btn)
2732 continue;
2734 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2735 vmap[btn->yindex] = 1;
2736 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2737 hmap[-btn->xindex] = 1;
2739 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2740 btn = chain->aicon;
2741 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2742 vmap[btn->yindex] = 1;
2743 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2744 hmap[-btn->xindex] = 1;
2746 break;
2747 case C_SE:
2748 for (i=0; i<dock->max_icons; i++) {
2749 btn = dock->icon_array[i];
2750 if (!btn)
2751 continue;
2753 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2754 vmap[-btn->yindex] = 1;
2755 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2756 hmap[btn->xindex] = 1;
2758 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2759 btn = chain->aicon;
2760 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2761 vmap[-btn->yindex] = 1;
2762 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2763 hmap[btn->xindex] = 1;
2765 break;
2766 case C_SW:
2767 default:
2768 for (i=0; i<dock->max_icons; i++) {
2769 btn = dock->icon_array[i];
2770 if (!btn)
2771 continue;
2773 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2774 vmap[-btn->yindex] = 1;
2775 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2776 hmap[-btn->xindex] = 1;
2778 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2779 btn = chain->aicon;
2780 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2781 vmap[-btn->yindex] = 1;
2782 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2783 hmap[-btn->xindex] = 1;
2786 x=0; y=0;
2787 done = 0;
2788 /* search a vacant slot */
2789 for (i=1; i<WMAX(vcount, hcount); i++) {
2790 if (i < vcount && vmap[i]==0) {
2791 /* found a slot */
2792 x = 0;
2793 y = i;
2794 done = 1;
2795 break;
2796 } else if (i < hcount && hmap[i]==0) {
2797 /* found a slot */
2798 x = i;
2799 y = 0;
2800 done = 1;
2801 break;
2804 wfree(vmap);
2805 wfree(hmap);
2806 /* If found a slot, translate and return */
2807 if (done) {
2808 if (corner==C_NW || corner==C_NE) {
2809 *y_pos = y;
2810 } else {
2811 *y_pos = -y;
2813 if (corner==C_NE || corner==C_SE) {
2814 *x_pos = x;
2815 } else {
2816 *x_pos = -x;
2818 return True;
2820 /* else, try to find a slot somewhere else */
2823 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2824 * placed outside of screen */
2825 mwidth = (int)ceil(sqrt(dock->max_icons));
2827 /* In the worst case (the clip is in the corner of the screen),
2828 * the amount of icons that fit in the clip is smaller.
2829 * Double the map to get a safe value.
2831 mwidth += mwidth;
2833 r = (mwidth-1)/2;
2835 slot_map = wmalloc(mwidth*mwidth);
2836 memset(slot_map, 0, mwidth*mwidth);
2838 #define XY2OFS(x,y) (WMAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2840 /* mark used slots in the map. If the slot falls outside the map
2841 * (for example, when all icons are placed in line), ignore them. */
2842 for (i=0; i<dock->max_icons; i++) {
2843 btn = dock->icon_array[i];
2844 if (btn)
2845 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2847 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2848 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2850 /* Find closest slot from the center that is free by scanning the
2851 * map from the center to outward in circular passes.
2852 * This will not result in a neat layout, but will be optimal
2853 * in the sense that there will not be holes left.
2855 done = 0;
2856 for (i = 1; i <= r && !done; i++) {
2857 int tx, ty;
2859 /* top and bottom parts of the ring */
2860 for (x = -i; x <= i && !done; x++) {
2861 tx = dock->x_pos + x*ICON_SIZE;
2862 y = -i;
2863 ty = dock->y_pos + y*ICON_SIZE;
2864 if (slot_map[XY2OFS(x,y)]==0
2865 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2866 *x_pos = x;
2867 *y_pos = y;
2868 done = 1;
2869 break;
2871 y = i;
2872 ty = dock->y_pos + y*ICON_SIZE;
2873 if (slot_map[XY2OFS(x,y)]==0
2874 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2875 *x_pos = x;
2876 *y_pos = y;
2877 done = 1;
2878 break;
2881 /* left and right parts of the ring */
2882 for (y = -i+1; y <= i-1; y++) {
2883 ty = dock->y_pos + y*ICON_SIZE;
2884 x = -i;
2885 tx = dock->x_pos + x*ICON_SIZE;
2886 if (slot_map[XY2OFS(x,y)]==0
2887 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2888 *x_pos = x;
2889 *y_pos = y;
2890 done = 1;
2891 break;
2893 x = i;
2894 tx = dock->x_pos + x*ICON_SIZE;
2895 if (slot_map[XY2OFS(x,y)]==0
2896 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2897 *x_pos = x;
2898 *y_pos = y;
2899 done = 1;
2900 break;
2904 wfree(slot_map);
2905 #undef XY2OFS
2906 return done;
2910 static void
2911 moveDock(WDock *dock, int new_x, int new_y)
2913 WAppIcon *btn;
2914 int i;
2916 dock->x_pos = new_x;
2917 dock->y_pos = new_y;
2918 for (i=0; i<dock->max_icons; i++) {
2919 btn = dock->icon_array[i];
2920 if (btn) {
2921 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2922 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2923 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2929 static void
2930 swapDock(WDock *dock)
2932 WScreen *scr = dock->screen_ptr;
2933 WAppIcon *btn;
2934 int x, i;
2937 if (dock->on_right_side) {
2938 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2939 } else {
2940 x = dock->x_pos = DOCK_EXTRA_SPACE;
2943 for (i=0; i<dock->max_icons; i++) {
2944 btn = dock->icon_array[i];
2945 if (btn) {
2946 btn->x_pos = x;
2947 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2951 wScreenUpdateUsableArea(scr);
2955 static pid_t
2956 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2958 WScreen *scr = btn->icon->core->screen_ptr;
2959 pid_t pid;
2960 char **argv;
2961 int argc;
2962 char *cmdline;
2964 cmdline = ExpandOptions(scr, command);
2966 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2967 if (cmdline)
2968 wfree(cmdline);
2969 if (state)
2970 wfree(state);
2971 return 0;
2974 wtokensplit(cmdline, &argv, &argc);
2976 if (argv==NULL) {
2977 if (cmdline)
2978 wfree(cmdline);
2979 if (state)
2980 wfree(state);
2981 return 0;
2984 if ((pid=fork())==0) {
2985 char **args;
2986 int i;
2988 SetupEnvironment(scr);
2990 #ifdef HAVE_SETSID
2991 setsid();
2992 #endif
2994 args = malloc(sizeof(char*)*(argc+1));
2995 if (!args)
2996 exit(111);
2997 for (i=0; i<argc; i++) {
2998 args[i] = argv[i];
3000 args[argc] = NULL;
3001 execvp(argv[0], args);
3002 exit(111);
3004 wtokenfree(argv, argc);
3006 if (pid > 0) {
3007 if (!state) {
3008 state = wmalloc(sizeof(WSavedState));
3009 memset(state, 0, sizeof(WSavedState));
3010 state->hidden = -1;
3011 state->miniaturized = -1;
3012 state->shaded = -1;
3013 if (btn->dock==scr->dock || btn->omnipresent)
3014 state->workspace = -1;
3015 else
3016 state->workspace = scr->current_workspace;
3018 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
3019 state);
3020 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
3021 btn->dock);
3022 } else if (state) {
3023 wfree(state);
3025 wfree(cmdline);
3026 return pid;
3030 void
3031 wDockHideIcons(WDock *dock)
3033 int i;
3035 if (dock==NULL)
3036 return;
3038 for (i=1; i<dock->max_icons; i++) {
3039 if (dock->icon_array[i])
3040 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
3042 dock->mapped = 0;
3044 dockIconPaint(dock->icon_array[0]);
3048 void
3049 wDockShowIcons(WDock *dock)
3051 int i, newlevel;
3052 WAppIcon *btn;
3054 if (dock==NULL)
3055 return;
3057 btn = dock->icon_array[0];
3058 moveDock(dock, btn->x_pos, btn->y_pos);
3060 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
3061 ChangeStackingLevel(btn->icon->core, newlevel);
3063 for (i=1; i<dock->max_icons; i++) {
3064 if (dock->icon_array[i]) {
3065 MoveInStackListAbove(dock->icon_array[i]->icon->core,
3066 btn->icon->core);
3067 break;
3071 if (!dock->collapsed) {
3072 for (i=1; i<dock->max_icons; i++) {
3073 if (dock->icon_array[i]) {
3074 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
3078 dock->mapped = 1;
3080 dockIconPaint(btn);
3084 void
3085 wDockLower(WDock *dock)
3087 int i;
3089 for (i=0; i<dock->max_icons; i++) {
3090 if (dock->icon_array[i])
3091 wLowerFrame(dock->icon_array[i]->icon->core);
3096 void
3097 wDockRaise(WDock *dock)
3099 int i;
3101 for (i=dock->max_icons-1; i>=0; i--) {
3102 if (dock->icon_array[i])
3103 wRaiseFrame(dock->icon_array[i]->icon->core);
3108 void
3109 wDockRaiseLower(WDock *dock)
3111 if (!dock->icon_array[0]->icon->core->stacking->above
3112 ||(dock->icon_array[0]->icon->core->stacking->window_level
3113 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
3114 wDockLower(dock);
3115 else
3116 wDockRaise(dock);
3120 void
3121 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
3123 icon->launching = 0;
3124 icon->relaunching = 0;
3125 dockIconPaint(icon);
3129 WAppIcon*
3130 wDockFindIconForWindow(WDock *dock, Window window)
3132 WAppIcon *icon;
3133 int i;
3135 for (i=0; i<dock->max_icons; i++) {
3136 icon = dock->icon_array[i];
3137 if (icon && icon->main_window == window)
3138 return icon;
3140 return NULL;
3144 void
3145 wDockTrackWindowLaunch(WDock *dock, Window window)
3147 WAppIcon *icon;
3148 char *wm_class, *wm_instance;
3149 int i;
3150 Bool firstPass = True;
3151 Bool found = False;
3152 char *command = NULL;
3155 int argc;
3156 char **argv;
3158 if (XGetCommand(dpy, window, &argv, &argc)) {
3159 if (argc > 0 && argv != NULL)
3160 command = wtokenjoin(argv, argc);
3161 if (argv) {
3162 XFreeStringList(argv);
3167 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
3168 (!wm_class && !wm_instance))
3169 return;
3171 retry:
3172 for (i=0; i<dock->max_icons; i++) {
3173 icon = dock->icon_array[i];
3174 if (!icon)
3175 continue;
3177 /* app is already attached to icon */
3178 if (icon->main_window == window) {
3179 found = True;
3180 break;
3183 if ((icon->wm_instance || icon->wm_class)
3184 && (icon->launching || !icon->running)) {
3186 if (icon->wm_instance && wm_instance &&
3187 strcmp(icon->wm_instance, wm_instance)!=0) {
3188 continue;
3190 if (icon->wm_class && wm_class &&
3191 strcmp(icon->wm_class, wm_class)!=0) {
3192 continue;
3194 if (firstPass && command && strcmp(icon->command, command)!=0) {
3195 continue;
3198 if (!icon->relaunching) {
3199 WApplication *wapp;
3201 /* Possibly an application that was docked with dockit,
3202 * but the user did not update WMState to indicate that
3203 * it was docked by force */
3204 wapp = wApplicationOf(window);
3205 if (!wapp) {
3206 icon->forced_dock = 1;
3207 icon->running = 0;
3209 if (!icon->forced_dock)
3210 icon->main_window = window;
3213 found = True;
3214 if (!wPreferences.no_animations && !icon->launching &&
3215 !dock->screen_ptr->flags.startup && !dock->collapsed) {
3216 WAppIcon *aicon;
3217 int x0, y0;
3219 icon->launching = 1;
3220 dockIconPaint(icon);
3222 aicon = wAppIconCreateForDock(dock->screen_ptr, NULL,
3223 wm_instance, wm_class,
3224 TILE_NORMAL);
3225 PlaceIcon(dock->screen_ptr, &x0, &y0);
3226 wAppIconMove(aicon, x0, y0);
3227 /* Should this always be lowered? -Dan */
3228 if (dock->lowered)
3229 wLowerFrame(aicon->icon->core);
3230 XMapWindow(dpy, aicon->icon->core->window);
3231 aicon->launching = 1;
3232 wAppIconPaint(aicon);
3233 SlideWindow(aicon->icon->core->window, x0, y0,
3234 icon->x_pos, icon->y_pos);
3235 XUnmapWindow(dpy, aicon->icon->core->window);
3236 wAppIconDestroy(aicon);
3238 wDockFinishLaunch(dock, icon);
3239 break;
3243 if (firstPass && !found) {
3244 firstPass = False;
3245 goto retry;
3248 if (command)
3249 wfree(command);
3251 if (wm_class)
3252 XFree(wm_class);
3253 if (wm_instance)
3254 XFree(wm_instance);
3259 void
3260 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3262 if (!wPreferences.flags.noclip) {
3263 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3264 if (scr->current_workspace != workspace) {
3265 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3266 WAppIconChain *chain = scr->global_icons;
3268 while (chain) {
3269 moveIconBetweenDocks(chain->aicon->dock,
3270 scr->workspaces[workspace]->clip,
3271 chain->aicon, chain->aicon->xindex,
3272 chain->aicon->yindex);
3273 if (scr->workspaces[workspace]->clip->collapsed)
3274 XUnmapWindow(dpy, chain->aicon->icon->core->window);
3275 chain = chain->next;
3278 wDockHideIcons(old_clip);
3279 if (old_clip->auto_raise_lower) {
3280 if (old_clip->auto_raise_magic) {
3281 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3282 old_clip->auto_raise_magic = NULL;
3284 wDockLower(old_clip);
3286 if (old_clip->auto_collapse) {
3287 if (old_clip->auto_expand_magic) {
3288 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3289 old_clip->auto_expand_magic = NULL;
3291 old_clip->collapsed = 1;
3293 wDockShowIcons(scr->workspaces[workspace]->clip);
3295 if (scr->flags.clip_balloon_mapped)
3296 showClipBalloon(scr->clip_icon->dock, workspace);
3302 static void
3303 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3305 WAppIcon *icon;
3306 int i;
3308 for (i=0; i<dock->max_icons; i++) {
3309 icon = dock->icon_array[i];
3310 if (!icon)
3311 continue;
3313 if (icon->launching && icon->pid == pid) {
3314 if (!icon->relaunching) {
3315 icon->running = 0;
3316 icon->main_window = None;
3318 wDockFinishLaunch(dock, icon);
3319 icon->pid = 0;
3320 if (status==111) {
3321 char msg[PATH_MAX];
3322 char *cmd;
3324 if (icon->drop_launch)
3325 cmd = icon->dnd_command;
3326 else if (icon->paste_launch)
3327 cmd = icon->paste_command;
3328 else
3329 cmd = icon->command;
3331 snprintf(msg, sizeof(msg),
3332 _("Could not execute command \"%s\""), cmd);
3334 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3335 _("OK"), NULL, NULL);
3337 break;
3343 static void
3344 toggleLowered(WDock *dock)
3346 WAppIcon *tmp;
3347 int newlevel, i;
3349 /* lower/raise Dock */
3350 if (!dock->lowered) {
3351 newlevel = WMNormalLevel;
3352 dock->lowered = 1;
3353 } else {
3354 newlevel = WMDockLevel;
3355 dock->lowered = 0;
3358 for (i=0; i<dock->max_icons; i++) {
3359 tmp = dock->icon_array[i];
3360 if (!tmp)
3361 continue;
3363 ChangeStackingLevel(tmp->icon->core, newlevel);
3364 if (dock->lowered)
3365 wLowerFrame(tmp->icon->core);
3368 if (dock->type == WM_DOCK)
3369 wScreenUpdateUsableArea(dock->screen_ptr);
3373 static void
3374 toggleCollapsed(WDock *dock)
3376 if (dock->collapsed) {
3377 dock->collapsed = 0;
3378 wDockShowIcons(dock);
3380 else {
3381 dock->collapsed = 1;
3382 wDockHideIcons(dock);
3387 static void
3388 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3390 WScreen *scr = dock->screen_ptr;
3391 WObjDescriptor *desc;
3392 WMenuEntry *entry;
3393 WApplication *wapp = NULL;
3394 int index = 0;
3395 int x_pos;
3396 int n_selected;
3397 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3399 if (dock->type == WM_DOCK) {
3400 /* keep on top */
3401 entry = dock->menu->entries[index];
3402 entry->flags.indicator_on = !dock->lowered;
3403 entry->clientdata = dock;
3404 dock->menu->flags.realized = 0;
3405 } else {
3406 /* clip options */
3407 if (scr->clip_options)
3408 updateClipOptionsMenu(scr->clip_options, dock);
3410 n_selected = numberOfSelectedIcons(dock);
3412 /* Rename Workspace */
3413 entry = dock->menu->entries[++index];
3414 if (aicon == scr->clip_icon) {
3415 entry->callback = renameCallback;
3416 entry->clientdata = dock;
3417 entry->flags.indicator = 0;
3418 entry->text = _("Rename Workspace");
3419 } else {
3420 entry->callback = omnipresentCallback;
3421 entry->clientdata = aicon;
3422 if (n_selected > 0) {
3423 entry->flags.indicator = 0;
3424 entry->text = _("Toggle Omnipresent");
3425 } else {
3426 entry->flags.indicator = 1;
3427 entry->flags.indicator_on = aicon->omnipresent;
3428 entry->flags.indicator_type = MI_CHECK;
3429 entry->text = _("Omnipresent");
3433 /* select/unselect icon */
3434 entry = dock->menu->entries[++index];
3435 entry->clientdata = aicon;
3436 entry->flags.indicator_on = aicon->icon->selected;
3437 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3439 /* select/unselect all icons */
3440 entry = dock->menu->entries[++index];
3441 entry->clientdata = aicon;
3442 if (n_selected > 0)
3443 entry->text = _("Unselect All Icons");
3444 else
3445 entry->text = _("Select All Icons");
3446 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3448 /* keep icon(s) */
3449 entry = dock->menu->entries[++index];
3450 entry->clientdata = aicon;
3451 if (n_selected > 1)
3452 entry->text = _("Keep Icons");
3453 else
3454 entry->text = _("Keep Icon");
3455 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3457 /* this is the workspace submenu part */
3458 entry = dock->menu->entries[++index];
3459 if (n_selected > 1)
3460 entry->text = _("Move Icons To");
3461 else
3462 entry->text = _("Move Icon To");
3463 if (scr->clip_submenu)
3464 updateWorkspaceMenu(scr->clip_submenu, aicon);
3465 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3467 /* remove icon(s) */
3468 entry = dock->menu->entries[++index];
3469 entry->clientdata = aicon;
3470 if (n_selected > 1)
3471 entry->text = _("Remove Icons");
3472 else
3473 entry->text = _("Remove Icon");
3474 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3476 /* attract icon(s) */
3477 entry = dock->menu->entries[++index];
3478 entry->clientdata = aicon;
3480 dock->menu->flags.realized = 0;
3481 wMenuRealize(dock->menu);
3485 if (aicon->icon->owner) {
3486 wapp = wApplicationOf(aicon->icon->owner->main_window);
3487 } else {
3488 wapp = NULL;
3491 /* launch */
3492 entry = dock->menu->entries[++index];
3493 entry->clientdata = aicon;
3494 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3496 /* unhide here */
3497 entry = dock->menu->entries[++index];
3498 entry->clientdata = aicon;
3499 if (wapp && wapp->flags.hidden) {
3500 entry->text = _("Unhide Here");
3501 } else {
3502 entry->text = _("Bring Here");
3504 wMenuSetEnabled(dock->menu, index, appIsRunning);
3506 /* hide */
3507 entry = dock->menu->entries[++index];
3508 entry->clientdata = aicon;
3509 if (wapp && wapp->flags.hidden) {
3510 entry->text = _("Unhide");
3511 } else {
3512 entry->text = _("Hide");
3514 wMenuSetEnabled(dock->menu, index, appIsRunning);
3516 /* settings */
3517 entry = dock->menu->entries[++index];
3518 entry->clientdata = aicon;
3519 wMenuSetEnabled(dock->menu, index, !aicon->editing
3520 && !wPreferences.flags.noupdates);
3522 /* kill */
3523 entry = dock->menu->entries[++index];
3524 entry->clientdata = aicon;
3525 wMenuSetEnabled(dock->menu, index, appIsRunning);
3527 if (!dock->menu->flags.realized)
3528 wMenuRealize(dock->menu);
3530 if (dock->type == WM_CLIP) {
3531 /*x_pos = event->xbutton.x_root+2;*/
3532 x_pos = event->xbutton.x_root - dock->menu->frame->core->width/2 - 1;
3533 if (x_pos < 0) {
3534 x_pos = 0;
3535 } else if (x_pos + dock->menu->frame->core->width > scr->scr_width-2) {
3536 x_pos = scr->scr_width - dock->menu->frame->core->width - 4;
3538 } else {
3539 x_pos = dock->on_right_side ?
3540 scr->scr_width - dock->menu->frame->core->width - 3 : 0;
3543 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3545 /* allow drag select */
3546 event->xany.send_event = True;
3547 desc = &dock->menu->menu->descriptor;
3548 (*desc->handle_mousedown)(desc, event);
3552 static void
3553 openClipWorkspaceMenu(WScreen *scr, int x, int y)
3555 if (!scr->clip_ws_menu) {
3556 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
3558 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
3559 wMenuMapAt(scr->clip_ws_menu, x, y, False);
3563 /******************************************************************/
3564 static void
3565 iconDblClick(WObjDescriptor *desc, XEvent *event)
3567 WAppIcon *btn = desc->parent;
3568 WDock *dock = btn->dock;
3569 WApplication *wapp = NULL;
3570 int unhideHere = 0;
3572 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3573 wapp = wApplicationOf(btn->icon->owner->main_window);
3575 assert(wapp!=NULL);
3577 unhideHere = (event->xbutton.state & ShiftMask);
3579 /* go to the last workspace that the user worked on the app */
3580 if (wapp->last_workspace != dock->screen_ptr->current_workspace
3581 && !unhideHere) {
3582 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3585 wUnhideApplication(wapp, event->xbutton.button==Button2,
3586 unhideHere);
3588 if (event->xbutton.state & MOD_MASK) {
3589 wHideOtherApplications(btn->icon->owner);
3591 } else {
3592 if (event->xbutton.button==Button1) {
3594 if (event->xbutton.state & MOD_MASK) {
3595 /* raise/lower dock */
3596 toggleLowered(dock);
3597 } else if (btn == dock->screen_ptr->clip_icon) {
3598 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3599 toggleCollapsed(dock);
3600 else
3601 handleClipChangeWorkspace(dock->screen_ptr, event);
3602 } else if (btn->command) {
3603 if (!btn->launching &&
3604 (!btn->running || (event->xbutton.state & ControlMask))) {
3605 launchDockedApplication(btn, False);
3607 } else if (btn->xindex == 0 && btn->yindex == 0
3608 && btn->dock->type == WM_DOCK) {
3610 wShowGNUstepPanel(dock->screen_ptr);
3618 static void
3619 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3621 WScreen *scr = dock->screen_ptr;
3622 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3623 int x, y;
3624 XEvent ev;
3625 int grabbed = 0, swapped = 0, done;
3626 Pixmap ghost = None;
3627 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3629 #ifdef DEBUG
3630 puts("moving dock");
3631 #endif
3632 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3633 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3634 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3635 wwarning("pointer grab failed for dock move");
3637 y = 0;
3638 for (x=0; x<dock->max_icons; x++) {
3639 if (dock->icon_array[x]!=NULL &&
3640 dock->icon_array[x]->yindex > y)
3641 y = dock->icon_array[x]->yindex;
3643 y++;
3644 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3646 done = 0;
3647 while (!done) {
3648 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3649 |ButtonMotionMask|ExposureMask, &ev);
3650 switch (ev.type) {
3651 case Expose:
3652 WMHandleEvent(&ev);
3653 break;
3655 case MotionNotify:
3656 if (!grabbed) {
3657 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3658 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3659 XChangeActivePointerGrab(dpy, ButtonMotionMask
3660 |ButtonReleaseMask|ButtonPressMask,
3661 wCursor[WCUR_MOVE], CurrentTime);
3662 grabbed=1;
3664 break;
3666 if (dock->type == WM_CLIP) {
3667 if (ev.xmotion.x_root - ofs_x < 0) {
3668 x = 0;
3669 } else if (ev.xmotion.x_root - ofs_x + ICON_SIZE >
3670 scr->scr_width) {
3671 x = scr->scr_width - ICON_SIZE;
3672 } else {
3673 x = ev.xmotion.x_root - ofs_x;
3675 if (ev.xmotion.y_root - ofs_y < 0) {
3676 y = 0;
3677 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3678 scr->scr_height) {
3679 y = scr->scr_height - ICON_SIZE;
3680 } else {
3681 y = ev.xmotion.y_root - ofs_y;
3683 moveDock(dock, x, y);
3684 } else {
3685 /* move vertically if pointer is inside the dock*/
3686 if ((dock->on_right_side &&
3687 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3688 || (!dock->on_right_side &&
3689 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3691 if (ev.xmotion.y_root - ofs_y < 0) {
3692 y = 0;
3693 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3694 scr->scr_height) {
3695 y = scr->scr_height - ICON_SIZE;
3696 } else {
3697 y = ev.xmotion.y_root - ofs_y;
3699 moveDock(dock, dock->x_pos, y);
3701 /* move horizontally to change sides */
3702 x = ev.xmotion.x_root - ofs_x;
3703 if (!dock->on_right_side) {
3705 /* is on left */
3707 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3708 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3709 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3710 if (superfluous && ghost==None) {
3711 ghost = MakeGhostDock(dock, dock->x_pos,
3712 scr->scr_width-ICON_SIZE
3713 -DOCK_EXTRA_SPACE-1,
3714 dock->y_pos);
3715 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3716 ghost);
3717 XClearWindow(dpy, scr->dock_shadow);
3719 XMapRaised(dpy, scr->dock_shadow);
3720 swapped = 1;
3721 } else {
3722 if (superfluous && ghost!=None) {
3723 XFreePixmap(dpy, ghost);
3724 ghost = None;
3726 XUnmapWindow(dpy, scr->dock_shadow);
3727 swapped = 0;
3729 } else {
3730 /* is on right */
3731 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3732 XMoveWindow(dpy, scr->dock_shadow,
3733 DOCK_EXTRA_SPACE, dock->y_pos);
3734 if (superfluous && ghost==None) {
3735 ghost = MakeGhostDock(dock, dock->x_pos,
3736 DOCK_EXTRA_SPACE, dock->y_pos);
3737 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3738 ghost);
3739 XClearWindow(dpy, scr->dock_shadow);
3741 XMapRaised(dpy, scr->dock_shadow);
3742 swapped = -1;
3743 } else {
3744 XUnmapWindow(dpy, scr->dock_shadow);
3745 swapped = 0;
3746 if (superfluous && ghost!=None) {
3747 XFreePixmap(dpy, ghost);
3748 ghost = None;
3753 break;
3755 case ButtonPress:
3756 break;
3758 case ButtonRelease:
3759 if (ev.xbutton.button != event->xbutton.button)
3760 break;
3761 XUngrabPointer(dpy, CurrentTime);
3762 XUnmapWindow(dpy, scr->dock_shadow);
3763 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3764 if (dock->type == WM_DOCK) {
3765 if (swapped!=0) {
3766 if (swapped>0)
3767 dock->on_right_side = 1;
3768 else
3769 dock->on_right_side = 0;
3770 swapDock(dock);
3771 wArrangeIcons(scr, False);
3774 done = 1;
3775 break;
3778 if (superfluous) {
3779 if (ghost!=None)
3780 XFreePixmap(dpy, ghost);
3781 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3783 #ifdef DEBUG
3784 puts("End dock move");
3785 #endif
3790 static void
3791 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3793 WScreen *scr = dock->screen_ptr;
3794 Window wins[2];
3795 WIcon *icon = aicon->icon;
3796 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3797 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3798 XEvent ev;
3799 int x = aicon->x_pos, y = aicon->y_pos;
3800 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3801 int shad_x = x, shad_y = y;
3802 int ix = aicon->xindex, iy = aicon->yindex;
3803 int tmp;
3804 Pixmap ghost = None;
3805 Bool docked;
3806 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3807 int omnipresent = aicon->omnipresent; /* this must be cached!!! */
3810 if (wPreferences.flags.noupdates)
3811 return;
3813 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3814 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3815 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3816 #ifdef DEBUG0
3817 wwarning("pointer grab failed for icon move");
3818 #endif
3821 if (!(event->xbutton.state & MOD_MASK))
3822 wRaiseFrame(icon->core);
3824 if (!wPreferences.flags.noclip)
3825 clip = scr->workspaces[scr->current_workspace]->clip;
3827 if (dock == scr->dock && !wPreferences.flags.noclip)
3828 dock2 = clip;
3829 else if (dock != scr->dock && !wPreferences.flags.nodock)
3830 dock2 = scr->dock;
3832 wins[0] = icon->core->window;
3833 wins[1] = scr->dock_shadow;
3834 XRestackWindows(dpy, wins, 2);
3835 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3836 ICON_SIZE, ICON_SIZE);
3837 if (superfluous) {
3838 if (icon->pixmap!=None)
3839 ghost = MakeGhostIcon(scr, icon->pixmap);
3840 else
3841 ghost = MakeGhostIcon(scr, icon->core->window);
3843 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3844 XClearWindow(dpy, scr->dock_shadow);
3846 XMapWindow(dpy, scr->dock_shadow);
3848 ondock = 1;
3851 while(1) {
3852 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3853 |ButtonMotionMask|ExposureMask, &ev);
3854 switch (ev.type) {
3855 case Expose:
3856 WMHandleEvent(&ev);
3857 break;
3859 case MotionNotify:
3860 if (!grabbed) {
3861 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3862 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3863 XChangeActivePointerGrab(dpy, ButtonMotionMask
3864 |ButtonReleaseMask|ButtonPressMask,
3865 wCursor[WCUR_MOVE], CurrentTime);
3866 grabbed=1;
3867 } else {
3868 break;
3872 if (omnipresent) {
3873 int i;
3874 for (i=0; i<scr->workspace_count; i++) {
3875 if (i == scr->current_workspace)
3876 continue;
3877 wDockShowIcons(scr->workspaces[i]->clip);
3881 x = ev.xmotion.x_root - ofs_x;
3882 y = ev.xmotion.y_root - ofs_y;
3883 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3884 if (tmp && dock2) {
3885 change_dock = 0;
3886 if (last_dock != dock && collapsed) {
3887 last_dock->collapsed = 1;
3888 wDockHideIcons(last_dock);
3889 collapsed = 0;
3891 if (!collapsed && (collapsed = dock->collapsed)) {
3892 dock->collapsed = 0;
3893 wDockShowIcons(dock);
3895 if (dock->auto_raise_lower)
3896 wDockRaise(dock);
3897 last_dock = dock;
3898 } else if (dock2) {
3899 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3900 if (tmp) {
3901 change_dock = 1;
3902 if (last_dock != dock2 && collapsed) {
3903 last_dock->collapsed = 1;
3904 wDockHideIcons(last_dock);
3905 collapsed = 0;
3907 if (!collapsed && (collapsed = dock2->collapsed)) {
3908 dock2->collapsed = 0;
3909 wDockShowIcons(dock2);
3911 if (dock2->auto_raise_lower)
3912 wDockRaise(dock2);
3913 last_dock = dock2;
3916 if (aicon->launching
3917 || aicon->lock
3918 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3919 || (!aicon->running && tmp)) {
3920 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3921 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3923 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3925 if (!ondock) {
3926 XMapWindow(dpy, scr->dock_shadow);
3928 ondock = 1;
3929 } else {
3930 if (ondock) {
3931 XUnmapWindow(dpy, scr->dock_shadow);
3933 ondock = 0;
3935 XMoveWindow(dpy, icon->core->window, x, y);
3936 break;
3938 case ButtonPress:
3939 break;
3941 case ButtonRelease:
3942 if (ev.xbutton.button != event->xbutton.button)
3943 break;
3944 XUngrabPointer(dpy, CurrentTime);
3945 if (ondock) {
3946 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3947 XUnmapWindow(dpy, scr->dock_shadow);
3948 if (!change_dock) {
3949 reattachIcon(dock, aicon, ix, iy);
3950 if (clip && dock!=clip && clip->auto_raise_lower)
3951 wDockLower(clip);
3952 } else {
3953 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3954 if (!docked) {
3955 /* Slide it back if dock rejected it */
3956 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3957 aicon->y_pos);
3958 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3960 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3961 collapsed = 0;
3964 } else {
3965 aicon->x_pos = x;
3966 aicon->y_pos = y;
3967 if (superfluous) {
3968 if (!aicon->running && !wPreferences.no_animations) {
3969 /* We need to deselect it, even if is deselected in
3970 * wDockDetach(), because else DoKaboom() will fail.
3972 if (aicon->icon->selected)
3973 wIconSelect(aicon->icon);
3975 wSoundPlay(WSOUND_KABOOM);
3976 DoKaboom(scr,aicon->icon->core->window, x, y);
3977 } else {
3978 wSoundPlay(WSOUND_UNDOCK);
3980 } else {
3981 wSoundPlay(WSOUND_UNDOCK);
3983 if (clip && clip->auto_raise_lower)
3984 wDockLower(clip);
3985 wDockDetach(dock, aicon);
3987 if (collapsed) {
3988 last_dock->collapsed = 1;
3989 wDockHideIcons(last_dock);
3990 collapsed = 0;
3992 if (superfluous) {
3993 if (ghost!=None)
3994 XFreePixmap(dpy, ghost);
3995 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3997 if (omnipresent) {
3998 int i;
3999 for (i=0; i<scr->workspace_count; i++) {
4000 if (i == scr->current_workspace)
4001 continue;
4002 wDockHideIcons(scr->workspaces[i]->clip);
4006 #ifdef DEBUG
4007 puts("End icon move");
4008 #endif
4009 return;
4015 static int
4016 getClipButton(int px, int py)
4018 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
4020 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
4021 return CLIP_IDLE;
4023 if (py <= pt-((int)ICON_SIZE-1-px))
4024 return CLIP_FORWARD;
4025 else if (px <= pt-((int)ICON_SIZE-1-py))
4026 return CLIP_REWIND;
4028 return CLIP_IDLE;
4032 static void
4033 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
4035 XEvent ev;
4036 int done, direction, new_ws;
4037 int new_dir;
4038 WDock *clip = scr->clip_icon->dock;
4040 direction = getClipButton(event->xbutton.x, event->xbutton.y);
4042 clip->lclip_button_pushed = direction==CLIP_REWIND;
4043 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4045 wClipIconPaint(scr->clip_icon);
4046 done = 0;
4047 while(!done) {
4048 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
4049 |ButtonPressMask, &ev);
4050 switch (ev.type) {
4051 case Expose:
4052 WMHandleEvent(&ev);
4053 break;
4055 case MotionNotify:
4056 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
4057 if (new_dir != direction) {
4058 direction = new_dir;
4059 clip->lclip_button_pushed = direction==CLIP_REWIND;
4060 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4061 wClipIconPaint(scr->clip_icon);
4063 break;
4065 case ButtonPress:
4066 break;
4068 case ButtonRelease:
4069 if (ev.xbutton.button == event->xbutton.button)
4070 done = 1;
4074 clip->lclip_button_pushed = 0;
4075 clip->rclip_button_pushed = 0;
4077 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
4079 if (direction == CLIP_FORWARD) {
4080 if (scr->current_workspace < scr->workspace_count-1)
4081 wWorkspaceChange(scr, scr->current_workspace+1);
4082 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
4083 wWorkspaceChange(scr, scr->current_workspace+1);
4084 else if (wPreferences.ws_cycle)
4085 wWorkspaceChange(scr, 0);
4087 else if (direction == CLIP_REWIND) {
4088 if (scr->current_workspace > 0)
4089 wWorkspaceChange(scr, scr->current_workspace-1);
4090 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
4091 wWorkspaceChange(scr, scr->workspace_count-1);
4094 wClipIconPaint(scr->clip_icon);
4098 static void
4099 iconMouseDown(WObjDescriptor *desc, XEvent *event)
4101 WAppIcon *aicon = desc->parent;
4102 WDock *dock = aicon->dock;
4103 WScreen *scr = aicon->icon->core->screen_ptr;
4105 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
4106 return;
4108 scr->last_dock = dock;
4110 if (dock->menu->flags.mapped)
4111 wMenuUnmap(dock->menu);
4113 if (IsDoubleClick(scr, event)) {
4114 /* double-click was not in the main clip icon */
4115 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
4116 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
4117 iconDblClick(desc, event);
4118 return;
4122 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
4123 XUnmapWindow(dpy, scr->clip_balloon);
4124 scr->flags.clip_balloon_mapped = 0;
4127 #ifdef DEBUG
4128 puts("handling dock");
4129 #endif
4130 if (event->xbutton.button == Button1) {
4131 if (event->xbutton.state & MOD_MASK)
4132 wDockLower(dock);
4133 else
4134 wDockRaise(dock);
4136 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
4137 dock->type!=WM_DOCK) {
4138 wIconSelect(aicon->icon);
4139 return;
4142 if (aicon->yindex==0 && aicon->xindex==0) {
4143 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
4144 && dock->type==WM_CLIP)
4145 handleClipChangeWorkspace(scr, event);
4146 else
4147 handleDockMove(dock, aicon, event);
4148 } else
4149 handleIconMove(dock, aicon, event);
4151 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4152 aicon==scr->clip_icon) {
4153 openClipWorkspaceMenu(scr, event->xbutton.x_root+2,
4154 event->xbutton.y_root+2);
4155 if (scr->clip_ws_menu) {
4156 WMenu *menu;
4157 menu = scr->clip_ws_menu;
4158 desc = &menu->menu->descriptor;
4160 event->xany.send_event = True;
4161 (*desc->handle_mousedown)(desc, event);
4163 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4164 (event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon) {
4165 wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
4166 } else if (event->xbutton.button == Button3) {
4167 if (event->xbutton.send_event &&
4168 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
4169 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
4170 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
4171 wwarning("pointer grab failed for dockicon menu");
4172 return;
4175 openDockMenu(dock, aicon, event);
4176 } else if (event->xbutton.button == Button2) {
4177 WAppIcon *btn = desc->parent;
4179 if (!btn->launching &&
4180 (!btn->running || (event->xbutton.state & ControlMask))) {
4181 launchDockedApplication(btn, True);
4187 static void
4188 showClipBalloon(WDock *dock, int workspace)
4190 int w, h;
4191 int x, y;
4192 WScreen *scr = dock->screen_ptr;
4193 char *text;
4194 Window stack[2];
4196 scr->flags.clip_balloon_mapped = 1;
4197 XMapWindow(dpy, scr->clip_balloon);
4199 text = scr->workspaces[workspace]->name;
4201 w = WMWidthOfString(scr->clip_title_font, text, strlen(text));
4203 h = WMFontHeight(scr->clip_title_font);
4204 XResizeWindow(dpy, scr->clip_balloon, w, h);
4206 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
4207 y = dock->y_pos + ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
4209 if (x+w > scr->scr_width) {
4210 x = scr->scr_width - w;
4211 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
4212 y = dock->y_pos - h - 1;
4213 else
4214 y = dock->y_pos + ICON_SIZE;
4215 XRaiseWindow(dpy, scr->clip_balloon);
4216 } else {
4217 stack[0] = scr->clip_icon->icon->core->window;
4218 stack[1] = scr->clip_balloon;
4219 XRestackWindows(dpy, stack, 2);
4221 XMoveWindow(dpy, scr->clip_balloon, x, y);
4222 XClearWindow(dpy, scr->clip_balloon);
4223 WMDrawString(scr->wmscreen, scr->clip_balloon,
4224 scr->clip_title_color[CLIP_NORMAL],
4225 scr->clip_title_font,
4226 0, 0, text, strlen(text));
4230 static void
4231 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
4233 WAppIcon *btn = (WAppIcon*)desc->parent;
4234 WDock *dock;
4235 WScreen *scr;
4237 assert(event->type==EnterNotify);
4239 if(desc->parent_type!=WCLASS_DOCK_ICON)
4240 return;
4242 scr = btn->icon->core->screen_ptr;
4243 if (!btn->omnipresent)
4244 dock = btn->dock;
4245 else
4246 dock = scr->workspaces[scr->current_workspace]->clip;
4248 if (!dock || dock->type!=WM_CLIP)
4249 return;
4251 /* The auto raise/lower code */
4252 if (dock->auto_lower_magic) {
4253 WMDeleteTimerHandler(dock->auto_lower_magic);
4254 dock->auto_lower_magic = NULL;
4256 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
4257 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
4258 clipAutoRaise,
4259 (void *)dock);
4262 /* The auto expand/collapse code */
4263 if (dock->auto_collapse_magic) {
4264 WMDeleteTimerHandler(dock->auto_collapse_magic);
4265 dock->auto_collapse_magic = NULL;
4267 if (dock->auto_collapse && !dock->auto_expand_magic) {
4268 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
4269 clipAutoExpand,
4270 (void *)dock);
4273 if (btn->xindex == 0 && btn->yindex == 0)
4274 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4275 else {
4276 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4277 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
4278 dock->screen_ptr->flags.clip_balloon_mapped = 0;
4284 static void
4285 clipLeave(WDock *dock)
4287 XEvent event;
4288 WObjDescriptor *desc = NULL;
4290 if (!dock || dock->type!=WM_CLIP)
4291 return;
4293 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
4294 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
4295 (XPointer *)&desc)!=XCNOENT
4296 && desc && desc->parent_type==WCLASS_DOCK_ICON
4297 && ((WAppIcon*)desc->parent)->dock
4298 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
4299 /* We didn't left the Clip yet */
4300 XPutBackEvent(dpy, &event);
4301 return;
4304 XPutBackEvent(dpy, &event);
4305 } else {
4306 /* We entered a withdrawn window, so we're still in Clip */
4307 return;
4310 if (dock->auto_raise_magic) {
4311 WMDeleteTimerHandler(dock->auto_raise_magic);
4312 dock->auto_raise_magic = NULL;
4314 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
4315 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
4316 clipAutoLower,
4317 (void *)dock);
4320 if (dock->auto_expand_magic) {
4321 WMDeleteTimerHandler(dock->auto_expand_magic);
4322 dock->auto_expand_magic = NULL;
4324 if (dock->auto_collapse && !dock->auto_collapse_magic) {
4325 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
4326 clipAutoCollapse,
4327 (void *)dock);
4332 static void
4333 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
4335 WAppIcon *btn = (WAppIcon*)desc->parent;
4337 assert(event->type==LeaveNotify);
4339 if(desc->parent_type!=WCLASS_DOCK_ICON)
4340 return;
4342 clipLeave(btn->dock);
4346 static void
4347 clipAutoCollapse(void *cdata)
4349 WDock *dock = (WDock *)cdata;
4351 if (dock->type!=WM_CLIP)
4352 return;
4354 if (dock->auto_collapse) {
4355 dock->collapsed = 1;
4356 wDockHideIcons(dock);
4358 dock->auto_collapse_magic = NULL;
4362 static void
4363 clipAutoExpand(void *cdata)
4365 WDock *dock = (WDock *)cdata;
4367 if (dock->type!=WM_CLIP)
4368 return;
4370 if (dock->auto_collapse) {
4371 dock->collapsed = 0;
4372 wDockShowIcons(dock);
4374 dock->auto_expand_magic = NULL;
4378 static void
4379 clipAutoLower(void *cdata)
4381 WDock *dock = (WDock *)cdata;
4383 if (dock->type!=WM_CLIP)
4384 return;
4386 if (dock->auto_raise_lower)
4387 wDockLower(dock);
4389 dock->auto_lower_magic = NULL;
4393 static void
4394 clipAutoRaise(void *cdata)
4396 WDock *dock = (WDock *)cdata;
4398 if (dock->type!=WM_CLIP)
4399 return;
4401 if (dock->auto_raise_lower)
4402 wDockRaise(dock);
4404 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4405 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4408 dock->auto_raise_magic = NULL;
4412 static Bool
4413 iconCanBeOmnipresent(WAppIcon *aicon)
4415 WScreen *scr = aicon->icon->core->screen_ptr;
4416 WDock *clip;
4417 WAppIcon *btn;
4418 int i, j;
4420 for (i=0; i<scr->workspace_count; i++) {
4421 clip = scr->workspaces[i]->clip;
4423 if (clip == aicon->dock)
4424 continue;
4426 if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
4427 return False; /* Clip is full in some workspace */
4429 for (j=0; j<clip->max_icons; j++) {
4430 btn = clip->icon_array[j];
4431 if(btn && btn->xindex==aicon->xindex && btn->yindex==aicon->yindex)
4432 return False;
4436 return True;
4441 wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
4443 WScreen *scr = aicon->icon->core->screen_ptr;
4444 WAppIconChain *new_entry, *tmp, *tmp1;
4445 int status = WO_SUCCESS;
4447 if ((scr->dock && aicon->dock==scr->dock) || aicon==scr->clip_icon) {
4448 return WO_NOT_APPLICABLE;
4451 if (aicon->omnipresent == omnipresent)
4452 return WO_SUCCESS;
4454 if (omnipresent) {
4455 if (iconCanBeOmnipresent(aicon)) {
4456 aicon->omnipresent = 1;
4457 new_entry = wmalloc(sizeof(WAppIconChain));
4458 new_entry->aicon = aicon;
4459 new_entry->next = scr->global_icons;
4460 scr->global_icons = new_entry;
4461 scr->global_icon_count++;
4462 } else {
4463 aicon->omnipresent = 0;
4464 status = WO_FAILED;
4466 } else {
4467 aicon->omnipresent = 0;
4468 if (aicon == scr->global_icons->aicon) {
4469 tmp = scr->global_icons->next;
4470 wfree(scr->global_icons);
4471 scr->global_icons = tmp;
4472 scr->global_icon_count--;
4473 } else {
4474 tmp = scr->global_icons;
4475 while (tmp->next) {
4476 if (tmp->next->aicon == aicon) {
4477 tmp1 = tmp->next->next;
4478 wfree(tmp->next);
4479 tmp->next = tmp1;
4480 scr->global_icon_count--;
4481 break;
4483 tmp = tmp->next;
4488 wAppIconPaint(aicon);
4490 return status;