fixes to comply to ANSI C
[wmaker-crm.git] / src / dock.c
blobf44ea5adb9a630fbb9cf60c8d41beab8a69382a4
1 /* dock.c- built-in Dock module for WindowMaker
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 * USA.
25 #include "wconfig.h"
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <math.h>
33 #include <limits.h>
35 #ifndef PATH_MAX
36 #define PATH_MAX DEFAULT_PATH_MAX
37 #endif
39 #include "WindowMaker.h"
40 #include "wcore.h"
41 #include "window.h"
42 #include "icon.h"
43 #include "appicon.h"
44 #include "actions.h"
45 #include "stacking.h"
46 #include "dock.h"
47 #include "dialog.h"
48 #include "funcs.h"
49 #include "properties.h"
50 #include "menu.h"
51 #include "client.h"
52 #include "defaults.h"
53 #include "workspace.h"
54 #include "framewin.h"
55 #include "superfluous.h"
56 #include "wsound.h"
57 #include "xinerama.h"
62 /**** Local variables ****/
63 #define CLIP_REWIND 1
64 #define CLIP_IDLE 0
65 #define CLIP_FORWARD 2
68 /**** Global variables ****/
70 /* in dockedapp.c */
71 extern void DestroyDockAppSettingsPanel();
73 extern void ShowDockAppSettingsPanel(WAppIcon *aicon);
76 extern XContext wWinContext;
78 extern Cursor wCursor[WCUR_LAST];
80 extern WPreferences wPreferences;
82 extern XContext wWinContext;
84 #ifdef OFFIX_DND
85 extern Atom _XA_DND_PROTOCOL;
86 #endif
89 #define MOD_MASK wPreferences.modifier_mask
91 extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);
93 #define ICON_SIZE wPreferences.icon_size
96 /***** Local variables ****/
98 static WMPropList *dCommand=NULL;
99 static WMPropList *dPasteCommand=NULL;
100 #ifdef OFFIX_DND
101 static WMPropList *dDropCommand=NULL;
102 #endif
103 static WMPropList *dAutoLaunch, *dLock;
104 static WMPropList *dName, *dForced, *dBuggyApplication, *dYes, *dNo;
105 static WMPropList *dHost, *dDock, *dClip;
106 static WMPropList *dAutoAttractIcons;
108 static WMPropList *dPosition, *dApplications, *dLowered, *dCollapsed;
110 static WMPropList *dAutoCollapse, *dAutoRaiseLower, *dOmnipresent;
112 static void dockIconPaint(WAppIcon *btn);
114 static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
116 static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state);
118 static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);
120 static int getClipButton(int px, int py);
122 static void toggleLowered(WDock *dock);
124 static void toggleCollapsed(WDock *dock);
126 static void clipIconExpose(WObjDescriptor *desc, XEvent *event);
128 static void clipLeave(WDock *dock);
130 static void handleClipChangeWorkspace(WScreen *scr, XEvent *event);
132 Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y);
134 static void clipEnterNotify(WObjDescriptor *desc, XEvent *event);
135 static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event);
136 static void clipAutoCollapse(void *cdata);
137 static void clipAutoExpand(void *cdata);
138 static void launchDockedApplication(WAppIcon *btn, Bool withSelection);
140 static void clipAutoLower(void *cdata);
141 static void clipAutoRaise(void *cdata);
143 static void showClipBalloon(WDock *dock, int workspace);
145 #ifdef OFFIX_DND
147 #define DndNotDnd -1
148 #define DndUnknown 0
149 #define DndRawData 1
150 #define DndFile 2
151 #define DndFiles 3
152 #define DndText 4
153 #define DndDir 5
154 #define DndLink 6
155 #define DndExe 7
157 #define DndEND 8
159 #endif /* OFFIX_DND */
163 static void
164 make_keys()
166 if (dCommand!=NULL)
167 return;
169 dCommand = WMRetainPropList(WMCreatePLString("Command"));
170 dPasteCommand = WMRetainPropList(WMCreatePLString("PasteCommand"));
171 dDropCommand = WMRetainPropList(WMCreatePLString("DropCommand"));
172 dLock = WMRetainPropList(WMCreatePLString("Lock"));
173 dAutoLaunch = WMRetainPropList(WMCreatePLString("AutoLaunch"));
174 dName = WMRetainPropList(WMCreatePLString("Name"));
175 dForced = WMRetainPropList(WMCreatePLString("Forced"));
176 dBuggyApplication = WMRetainPropList(WMCreatePLString("BuggyApplication"));
177 dYes = WMRetainPropList(WMCreatePLString("Yes"));
178 dNo = WMRetainPropList(WMCreatePLString("No"));
179 dHost = WMRetainPropList(WMCreatePLString("Host"));
181 dPosition = WMCreatePLString("Position");
182 dApplications = WMCreatePLString("Applications");
183 dLowered = WMCreatePLString("Lowered");
184 dCollapsed = WMCreatePLString("Collapsed");
185 dAutoCollapse = WMCreatePLString("AutoCollapse");
186 dAutoRaiseLower = WMCreatePLString("AutoRaiseLower");
187 dAutoAttractIcons = WMCreatePLString("AutoAttractIcons");
189 dOmnipresent = WMCreatePLString("Omnipresent");
191 dDock = WMCreatePLString("Dock");
192 dClip = WMCreatePLString("Clip");
197 static void
198 renameCallback(WMenu *menu, WMenuEntry *entry)
200 WDock *dock = entry->clientdata;
201 char buffer[128];
202 int wspace;
203 char *name;
205 assert(entry->clientdata!=NULL);
207 wspace = dock->screen_ptr->current_workspace;
209 name = wstrdup(dock->screen_ptr->workspaces[wspace]->name);
211 snprintf(buffer, sizeof(buffer), _("Type the name for workspace %i:"), wspace+1);
212 if (wInputDialog(dock->screen_ptr, _("Rename Workspace"), buffer,
213 &name)) {
214 wWorkspaceRename(dock->screen_ptr, wspace, name);
216 if (name) {
217 wfree(name);
222 static void
223 toggleLoweredCallback(WMenu *menu, WMenuEntry *entry)
225 assert(entry->clientdata!=NULL);
227 toggleLowered(entry->clientdata);
229 entry->flags.indicator_on = !(((WDock*)entry->clientdata)->lowered);
231 wMenuPaint(menu);
235 static int
236 matchWindow(void *item, void *cdata)
238 return (((WFakeGroupLeader*)item)->leader == (Window)cdata);
242 static void
243 killCallback(WMenu *menu, WMenuEntry *entry)
245 WScreen *scr = menu->menu->screen_ptr;
246 WAppIcon *icon;
247 WFakeGroupLeader *fPtr;
248 char *buffer;
250 if (!WCHECK_STATE(WSTATE_NORMAL))
251 return;
253 assert(entry->clientdata!=NULL);
255 icon = (WAppIcon*)entry->clientdata;
257 icon->editing = 1;
259 WCHANGE_STATE(WSTATE_MODAL);
261 buffer = wstrconcat(icon->wm_class,
262 _(" will be forcibly closed.\n"
263 "Any unsaved changes will be lost.\n"
264 "Please confirm."));
266 if (icon->icon && icon->icon->owner) {
267 fPtr = icon->icon->owner->fake_group;
268 } else {
269 /* is this really necessary? can we kill a dock icon not running? */
270 Window win = icon->main_window;
271 int index;
273 index = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void*)win);
274 if (index != WANotFound)
275 fPtr = WMGetFromArray(scr->fakeGroupLeaders, index);
276 else
277 fPtr = NULL;
280 if (wPreferences.dont_confirm_kill
281 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
282 buffer, _("Yes"), _("No"), NULL)==WAPRDefault) {
283 if (fPtr!=NULL) {
284 WWindow *wwin, *twin;
286 wwin = scr->focused_window;
287 while (wwin) {
288 twin = wwin->prev;
289 if (wwin->fake_group == fPtr) {
290 wClientKill(wwin);
292 wwin = twin;
294 } else if (icon->icon && icon->icon->owner) {
295 wClientKill(icon->icon->owner);
299 wfree(buffer);
301 icon->editing = 0;
303 WCHANGE_STATE(WSTATE_NORMAL);
307 /* TODO: replace this function with a member of the dock struct */
308 static int
309 numberOfSelectedIcons(WDock *dock)
311 WAppIcon *aicon;
312 int i, n;
314 n = 0;
315 for (i=1; i<dock->max_icons; i++) {
316 aicon = dock->icon_array[i];
317 if (aicon && aicon->icon->selected) {
318 n++;
322 return n;
326 static WMArray*
327 getSelected(WDock *dock)
329 WMArray *ret = WMCreateArray(8);
330 WAppIcon *btn;
331 int i;
333 for (i=1; i<dock->max_icons; i++) {
334 btn = dock->icon_array[i];
335 if (btn && btn->icon->selected) {
336 WMAddToArray(ret, btn);
340 return ret;
344 static void
345 paintClipButtons(WAppIcon *clipIcon, Bool lpushed, Bool rpushed)
347 Window win = clipIcon->icon->core->window;
348 WScreen *scr = clipIcon->icon->core->screen_ptr;
349 XPoint p[4];
350 int pt = CLIP_BUTTON_SIZE*ICON_SIZE/64;
351 int tp = ICON_SIZE - pt;
352 int as = pt - 15; /* 15 = 5+5+5 */
353 GC gc = scr->draw_gc; /* maybe use WMColorGC() instead here? */
354 WMColor *color;
355 #ifdef GRADIENT_CLIP_ARROW
356 Bool collapsed = clipIcon->dock->collapsed;
357 #endif
359 if (!clipIcon->dock->collapsed)
360 color = scr->clip_title_color[CLIP_NORMAL];
361 else
362 color = scr->clip_title_color[CLIP_COLLAPSED];
364 XSetForeground(dpy, gc, WMColorPixel(color));
366 if (rpushed) {
367 p[0].x = tp+1;
368 p[0].y = 1;
369 p[1].x = ICON_SIZE-2;
370 p[1].y = 1;
371 p[2].x = ICON_SIZE-2;
372 p[2].y = pt-1;
373 } else if (lpushed) {
374 p[0].x = 1;
375 p[0].y = tp;
376 p[1].x = pt;
377 p[1].y = ICON_SIZE-2;
378 p[2].x = 1;
379 p[2].y = ICON_SIZE-2;
381 if (lpushed || rpushed) {
382 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
383 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
384 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
386 #ifdef GRADIENT_CLIP_ARROW
387 if (!collapsed) {
388 XSetFillStyle(dpy, scr->copy_gc, FillTiled);
389 XSetTile(dpy, scr->copy_gc, scr->clip_arrow_gradient);
390 XSetClipMask(dpy, scr->copy_gc, None);
391 gc = scr->copy_gc;
393 #endif /* GRADIENT_CLIP_ARROW */
395 /* top right arrow */
396 p[0].x = p[3].x = ICON_SIZE-5-as;
397 p[0].y = p[3].y = 5;
398 p[1].x = ICON_SIZE-6;
399 p[1].y = 5;
400 p[2].x = ICON_SIZE-6;
401 p[2].y = 4+as;
402 if (rpushed) {
403 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
404 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
405 } else {
406 #ifdef GRADIENT_CLIP_ARROW
407 if (!collapsed)
408 XSetTSOrigin(dpy, gc, ICON_SIZE-6-as, 5);
409 #endif
410 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
411 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
414 /* bottom left arrow */
415 p[0].x = p[3].x = 5;
416 p[0].y = p[3].y = ICON_SIZE-5-as;
417 p[1].x = 5;
418 p[1].y = ICON_SIZE-6;
419 p[2].x = 4+as;
420 p[2].y = ICON_SIZE-6;
421 if (lpushed) {
422 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
423 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
424 } else {
425 #ifdef GRADIENT_CLIP_ARROW
426 if (!collapsed)
427 XSetTSOrigin(dpy, gc, 5, ICON_SIZE-6-as);
428 #endif
429 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
430 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
432 #ifdef GRADIENT_CLIP_ARROW
433 if (!collapsed)
434 XSetFillStyle(dpy, scr->copy_gc, FillSolid);
435 #endif
439 RImage*
440 wClipMakeTile(WScreen *scr, RImage *normalTile)
442 RImage *tile = RCloneImage(normalTile);
443 RColor black;
444 RColor dark;
445 RColor light;
446 int pt, tp;
447 int as;
449 pt = CLIP_BUTTON_SIZE*wPreferences.icon_size/64;
450 tp = wPreferences.icon_size-1 - pt;
451 as = pt - 15;
453 black.alpha = 255;
454 black.red = black.green = black.blue = 0;
456 dark.alpha = 0;
457 dark.red = dark.green = dark.blue = 60;
459 light.alpha = 0;
460 light.red = light.green = light.blue = 80;
463 /* top right */
464 ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size-2,
465 pt-1, &dark);
466 RDrawLine(tile, tp-1, 0, wPreferences.icon_size-1, pt+1, &black);
467 ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size-3,
468 pt, &light);
470 /* arrow bevel */
471 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 7 - as, 4,
472 ICON_SIZE - 5, 4, &dark);
473 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 6 - as, 5,
474 ICON_SIZE - 5, 6 + as, &dark);
475 ROperateLine(tile, RAddOperation, ICON_SIZE - 5, 4, ICON_SIZE - 5, 6 + as,
476 &light);
478 /* bottom left */
479 ROperateLine(tile, RAddOperation, 2, tp+2, pt-2,
480 wPreferences.icon_size-3, &dark);
481 RDrawLine(tile, 0, tp-1, pt+1, wPreferences.icon_size-1, &black);
482 ROperateLine(tile, RSubtractOperation, 0, tp-2, pt+1,
483 wPreferences.icon_size-2, &light);
485 /* arrow bevel */
486 ROperateLine(tile, RSubtractOperation, 4, ICON_SIZE - 7 - as, 4,
487 ICON_SIZE - 5, &dark);
488 ROperateLine(tile, RSubtractOperation, 5, ICON_SIZE - 6 - as,
489 6 + as, ICON_SIZE - 5, &dark);
490 ROperateLine(tile, RAddOperation, 4, ICON_SIZE - 5, 6 + as, ICON_SIZE - 5,
491 &light);
493 return tile;
497 static void
498 omnipresentCallback(WMenu *menu, WMenuEntry *entry)
500 WAppIcon *clickedIcon = entry->clientdata;
501 WAppIcon *aicon;
502 WDock *dock;
503 WMArray *selectedIcons;
504 WMArrayIterator iter;
505 int failed;
507 assert(entry->clientdata!=NULL);
509 dock = clickedIcon->dock;
511 selectedIcons = getSelected(dock);
513 if (!WMGetArrayItemCount(selectedIcons))
514 WMAddToArray(selectedIcons, clickedIcon);
516 failed = 0;
517 WM_ITERATE_ARRAY(selectedIcons, aicon, iter) {
518 if (wClipMakeIconOmnipresent(aicon, !aicon->omnipresent) == WO_FAILED)
519 failed++;
520 else if (aicon->icon->selected)
521 wIconSelect(aicon->icon);
523 WMFreeArray(selectedIcons);
525 if (failed > 1) {
526 wMessageDialog(dock->screen_ptr, _("Warning"),
527 _("Some icons cannot be made omnipresent. "
528 "Please make sure that no other icon is "
529 "docked in the same positions on the other "
530 "workspaces and the Clip is not full in "
531 "some workspace."),
532 _("OK"), NULL, NULL);
533 } else if (failed == 1) {
534 wMessageDialog(dock->screen_ptr, _("Warning"),
535 _("Icon cannot be made omnipresent. "
536 "Please make sure that no other icon is "
537 "docked in the same position on the other "
538 "workspaces and the Clip is not full in "
539 "some workspace."),
540 _("OK"), NULL, NULL);
545 static void
546 removeIconsCallback(WMenu *menu, WMenuEntry *entry)
548 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
549 WDock *dock;
550 WAppIcon *aicon;
551 WMArray *selectedIcons;
552 int keepit;
553 WMArrayIterator it;
555 assert(clickedIcon!=NULL);
557 dock = clickedIcon->dock;
559 selectedIcons = getSelected(dock);
561 if (WMGetArrayItemCount(selectedIcons)) {
562 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
563 _("All selected icons will be removed!"),
564 _("OK"), _("Cancel"), NULL)!=WAPRDefault) {
565 WMFreeArray(selectedIcons);
566 return;
568 } else {
569 if (clickedIcon->xindex==0 && clickedIcon->yindex==0) {
570 WMFreeArray(selectedIcons);
571 return;
573 WMAddToArray(selectedIcons, clickedIcon);
576 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
577 keepit = aicon->running && wApplicationOf(aicon->main_window);
578 wDockDetach(dock, aicon);
579 if (keepit) {
580 /* XXX: can: aicon->icon == NULL ? */
581 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, wGetHeadForWindow(aicon->icon->owner));
582 XMoveWindow(dpy, aicon->icon->core->window,
583 aicon->x_pos, aicon->y_pos);
584 if (!dock->mapped || dock->collapsed)
585 XMapWindow(dpy, aicon->icon->core->window);
588 WMFreeArray(selectedIcons);
590 if (wPreferences.auto_arrange_icons)
591 wArrangeIcons(dock->screen_ptr, True);
595 static void
596 keepIconsCallback(WMenu *menu, WMenuEntry *entry)
598 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
599 WDock *dock;
600 WAppIcon *aicon;
601 WMArray *selectedIcons;
602 WMArrayIterator it;
604 assert(clickedIcon!=NULL);
605 dock = clickedIcon->dock;
607 selectedIcons = getSelected(dock);
609 if (!WMGetArrayItemCount(selectedIcons)
610 && clickedIcon!=dock->screen_ptr->clip_icon) {
611 char *command = NULL;
613 if (!clickedIcon->command && !clickedIcon->editing) {
614 clickedIcon->editing = 1;
615 if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
616 _("Type the command used to launch the application"),
617 &command)) {
618 if (command && (command[0]==0 ||
619 (command[0]=='-' && command[1]==0))) {
620 wfree(command);
621 command = NULL;
623 clickedIcon->command = command;
624 clickedIcon->editing = 0;
625 } else {
626 clickedIcon->editing = 0;
627 if (command)
628 wfree(command);
629 WMFreeArray(selectedIcons);
630 return;
634 WMAddToArray(selectedIcons, clickedIcon);
637 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
638 if (aicon->icon->selected)
639 wIconSelect(aicon->icon);
640 if (aicon && aicon->attracted && aicon->command) {
641 aicon->attracted = 0;
642 if (aicon->icon->shadowed) {
643 aicon->icon->shadowed = 0;
644 aicon->icon->force_paint = 1;
645 wAppIconPaint(aicon);
649 WMFreeArray(selectedIcons);
655 static void
656 toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
658 WDock *dock = (WDock*)entry->clientdata;
660 assert(entry->clientdata!=NULL);
662 dock->attract_icons = !dock->attract_icons;
663 /*if (!dock->attract_icons)
664 dock->keep_attracted = 0;*/
666 entry->flags.indicator_on = dock->attract_icons;
668 wMenuPaint(menu);
672 static void
673 selectCallback(WMenu *menu, WMenuEntry *entry)
675 WAppIcon *icon = (WAppIcon*)entry->clientdata;
677 assert(icon!=NULL);
679 wIconSelect(icon->icon);
681 wMenuPaint(menu);
685 static void
686 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
688 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
689 WDock *clip;
690 WAppIcon *aicon;
691 int x, y, x_pos, y_pos;
693 assert(entry->clientdata!=NULL);
694 clip = clickedIcon->dock;
696 aicon = clip->screen_ptr->app_icon_list;
698 while (aicon) {
699 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
700 x_pos = clip->x_pos + x*ICON_SIZE;
701 y_pos = clip->y_pos + y*ICON_SIZE;
702 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
703 #ifdef ANIMATIONS
704 if (wPreferences.no_animations) {
705 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
706 } else {
707 SlideWindow(aicon->icon->core->window,
708 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
710 #else
711 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
712 #endif /* ANIMATIONS */
714 aicon->attracted = 1;
715 if (!aicon->icon->shadowed) {
716 aicon->icon->shadowed = 1;
717 aicon->icon->force_paint = 1;
718 /* We don't do an wAppIconPaint() here because it's in
719 * wDockAttachIcon(). -Dan
722 wDockAttachIcon(clip, aicon, x, y);
723 if (clip->collapsed || !clip->mapped)
724 XUnmapWindow(dpy, aicon->icon->core->window);
726 aicon = aicon->next;
731 static void
732 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
734 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
735 WDock *dock;
736 WMArray *selectedIcons;
737 WMArrayIterator iter;
738 WAppIcon *btn;
739 int i;
741 assert(clickedIcon!=NULL);
742 dock = clickedIcon->dock;
744 selectedIcons = getSelected(dock);
746 if (!WMGetArrayItemCount(selectedIcons)) {
747 for (i=1; i<dock->max_icons; i++) {
748 btn = dock->icon_array[i];
749 if (btn && !btn->icon->selected) {
750 wIconSelect(btn->icon);
753 } else {
754 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
755 wIconSelect(btn->icon);
758 WMFreeArray(selectedIcons);
760 wMenuPaint(menu);
764 static void
765 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
767 assert(entry->clientdata!=NULL);
769 toggleCollapsed(entry->clientdata);
771 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
773 wMenuPaint(menu);
777 static void
778 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
780 WDock *dock;
781 assert(entry->clientdata!=NULL);
783 dock = (WDock*) entry->clientdata;
785 dock->auto_collapse = !dock->auto_collapse;
787 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
789 wMenuPaint(menu);
793 static void
794 toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
796 WDock *dock;
797 assert(entry->clientdata!=NULL);
799 dock = (WDock*) entry->clientdata;
801 dock->auto_raise_lower = !dock->auto_raise_lower;
803 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_raise_lower;
805 wMenuPaint(menu);
809 static void
810 launchCallback(WMenu *menu, WMenuEntry *entry)
812 WAppIcon *btn = (WAppIcon*)entry->clientdata;
814 launchDockedApplication(btn, False);
818 static void
819 settingsCallback(WMenu *menu, WMenuEntry *entry)
821 WAppIcon *btn = (WAppIcon*)entry->clientdata;
823 if (btn->editing)
824 return;
825 ShowDockAppSettingsPanel(btn);
829 static void
830 hideCallback(WMenu *menu, WMenuEntry *entry)
832 WApplication *wapp;
833 WAppIcon *btn = (WAppIcon*)entry->clientdata;
835 wapp = wApplicationOf(btn->icon->owner->main_window);
837 if (wapp->flags.hidden) {
838 wWorkspaceChange(btn->icon->core->screen_ptr, wapp->last_workspace);
839 wUnhideApplication(wapp, False, False);
840 } else {
841 wHideApplication(wapp);
846 static void
847 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
849 WApplication *wapp;
850 WAppIcon *btn = (WAppIcon*)entry->clientdata;
852 wapp = wApplicationOf(btn->icon->owner->main_window);
854 wUnhideApplication(wapp, False, True);
858 WAppIcon*
859 mainIconCreate(WScreen *scr, int type)
861 WAppIcon *btn;
862 int x_pos;
864 if (type == WM_CLIP) {
865 if (scr->clip_icon)
866 return scr->clip_icon;
867 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
868 btn->icon->core->descriptor.handle_expose = clipIconExpose;
869 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
870 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
871 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
872 x_pos = 0;
873 } else {
874 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
875 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
878 btn->xindex = 0;
879 btn->yindex = 0;
881 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
882 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
883 btn->icon->core->descriptor.parent = btn;
884 /*ChangeStackingLevel(btn->icon->core, WMDockLevel);*/
885 XMapWindow(dpy, btn->icon->core->window);
886 btn->x_pos = x_pos;
887 btn->y_pos = 0;
888 btn->docked = 1;
889 if (type == WM_CLIP)
890 scr->clip_icon = btn;
892 return btn;
896 static void
897 switchWSCommand(WMenu *menu, WMenuEntry *entry)
899 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
900 WScreen *scr = icon->icon->core->screen_ptr;
901 WDock *src, *dest;
902 WMArray *selectedIcons;
903 int x, y;
905 if (entry->order == scr->current_workspace)
906 return;
907 src = icon->dock;
908 dest = scr->workspaces[entry->order]->clip;
910 selectedIcons = getSelected(src);
912 if (WMGetArrayItemCount(selectedIcons)) {
913 WMArrayIterator iter;
915 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
916 if (wDockFindFreeSlot(dest, &x, &y)) {
917 moveIconBetweenDocks(src, dest, btn, x, y);
918 XUnmapWindow(dpy, btn->icon->core->window);
921 } else if (icon != scr->clip_icon) {
922 if (wDockFindFreeSlot(dest, &x, &y)) {
923 moveIconBetweenDocks(src, dest, icon, x, y);
924 XUnmapWindow(dpy, icon->icon->core->window);
927 WMFreeArray(selectedIcons);
932 static void
933 launchDockedApplication(WAppIcon *btn, Bool withSelection)
935 WScreen *scr = btn->icon->core->screen_ptr;
937 if (!btn->launching &&
938 ((!withSelection && btn->command!=NULL) ||
939 (withSelection && btn->paste_command!=NULL))) {
940 if (!btn->forced_dock) {
941 btn->relaunching = btn->running;
942 btn->running = 1;
944 if (btn->wm_instance || btn->wm_class) {
945 WWindowAttributes attr;
946 memset(&attr, 0, sizeof(WWindowAttributes));
947 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class,
948 &attr, NULL, True);
950 if (!attr.no_appicon && !btn->buggy_app)
951 btn->launching = 1;
952 else
953 btn->running = 0;
955 btn->drop_launch = 0;
956 btn->paste_launch = withSelection;
957 scr->last_dock = btn->dock;
958 btn->pid = execCommand(btn,
959 withSelection ? btn->paste_command : btn->command,
960 NULL);
961 if (btn->pid>0) {
962 if (btn->buggy_app) {
963 /* give feedback that the app was launched */
964 btn->launching = 1;
965 dockIconPaint(btn);
966 btn->launching = 0;
967 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
968 } else {
969 dockIconPaint(btn);
971 } else {
972 wwarning(_("could not launch application %s\n"), btn->command);
973 btn->launching = 0;
974 if (!btn->relaunching)
975 btn->running = 0;
982 static void
983 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
985 WScreen *scr = menu->frame->screen_ptr;
986 char title[MAX_WORKSPACENAME_WIDTH+1];
987 int i;
989 if (!menu || !icon)
990 return;
992 for (i=0; i<scr->workspace_count; i++) {
993 if (i < menu->entry_no) {
994 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
995 wfree(menu->entries[i]->text);
996 strcpy(title, scr->workspaces[i]->name);
997 menu->entries[i]->text = wstrdup(title);
998 menu->flags.realized = 0;
1000 menu->entries[i]->clientdata = (void*)icon;
1001 } else {
1002 strcpy(title, scr->workspaces[i]->name);
1004 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
1006 menu->flags.realized = 0;
1008 if (i == scr->current_workspace) {
1009 wMenuSetEnabled(menu, i, False);
1010 } else {
1011 wMenuSetEnabled(menu, i, True);
1015 if (!menu->flags.realized)
1016 wMenuRealize(menu);
1020 static WMenu*
1021 makeWorkspaceMenu(WScreen *scr)
1023 WMenu *menu;
1025 menu = wMenuCreate(scr, NULL, False);
1026 if (!menu)
1027 wwarning(_("could not create workspace submenu for Clip menu"));
1029 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
1031 menu->flags.realized = 0;
1032 wMenuRealize(menu);
1034 return menu;
1038 static void
1039 updateClipOptionsMenu(WMenu *menu, WDock *dock)
1041 WMenuEntry *entry;
1042 int index = 0;
1044 if (!menu || !dock)
1045 return;
1047 /* keep on top */
1048 entry = menu->entries[index];
1049 entry->flags.indicator_on = !dock->lowered;
1050 entry->clientdata = dock;
1052 /* collapsed */
1053 entry = menu->entries[++index];
1054 entry->flags.indicator_on = dock->collapsed;
1055 entry->clientdata = dock;
1057 /* auto-collapse */
1058 entry = menu->entries[++index];
1059 entry->flags.indicator_on = dock->auto_collapse;
1060 entry->clientdata = dock;
1062 /* auto-raise/lower */
1063 entry = menu->entries[++index];
1064 entry->flags.indicator_on = dock->auto_raise_lower;
1065 entry->clientdata = dock;
1066 wMenuSetEnabled(menu, index, dock->lowered);
1068 /* attract icons */
1069 entry = menu->entries[++index];
1070 entry->flags.indicator_on = dock->attract_icons;
1071 entry->clientdata = dock;
1073 menu->flags.realized = 0;
1074 wMenuRealize(menu);
1078 static WMenu*
1079 makeClipOptionsMenu(WScreen *scr)
1081 WMenu *menu;
1082 WMenuEntry *entry;
1084 menu = wMenuCreate(scr, NULL, False);
1085 if (!menu) {
1086 wwarning(_("could not create options submenu for Clip menu"));
1087 return NULL;
1090 entry = wMenuAddCallback(menu, _("Keep on Top"),
1091 toggleLoweredCallback, NULL);
1092 entry->flags.indicator = 1;
1093 entry->flags.indicator_on = 1;
1094 entry->flags.indicator_type = MI_CHECK;
1096 entry = wMenuAddCallback(menu, _("Collapsed"),
1097 toggleCollapsedCallback, NULL);
1098 entry->flags.indicator = 1;
1099 entry->flags.indicator_on = 1;
1100 entry->flags.indicator_type = MI_CHECK;
1102 entry = wMenuAddCallback(menu, _("Autocollapse"),
1103 toggleAutoCollapseCallback, NULL);
1104 entry->flags.indicator = 1;
1105 entry->flags.indicator_on = 1;
1106 entry->flags.indicator_type = MI_CHECK;
1108 entry = wMenuAddCallback(menu, _("Autoraise"),
1109 toggleAutoRaiseLowerCallback, NULL);
1110 entry->flags.indicator = 1;
1111 entry->flags.indicator_on = 1;
1112 entry->flags.indicator_type = MI_CHECK;
1114 entry = wMenuAddCallback(menu, _("Autoattract Icons"),
1115 toggleAutoAttractCallback, NULL);
1116 entry->flags.indicator = 1;
1117 entry->flags.indicator_on = 1;
1118 entry->flags.indicator_type = MI_CHECK;
1120 menu->flags.realized = 0;
1121 wMenuRealize(menu);
1123 return menu;
1127 static WMenu*
1128 dockMenuCreate(WScreen *scr, int type)
1130 WMenu *menu;
1131 WMenuEntry *entry;
1133 if (type == WM_CLIP && scr->clip_menu)
1134 return scr->clip_menu;
1136 menu = wMenuCreate(scr, NULL, False);
1137 if (type != WM_CLIP) {
1138 entry = wMenuAddCallback(menu, _("Keep on Top"),
1139 toggleLoweredCallback, NULL);
1140 entry->flags.indicator = 1;
1141 entry->flags.indicator_on = 1;
1142 entry->flags.indicator_type = MI_CHECK;
1143 } else {
1144 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1145 scr->clip_options = makeClipOptionsMenu(scr);
1146 if (scr->clip_options)
1147 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1149 entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback,
1150 NULL);
1151 wfree(entry->text);
1152 entry->text = _("Rename Workspace");
1154 entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
1155 entry->flags.indicator = 1;
1156 entry->flags.indicator_on = 1;
1157 entry->flags.indicator_type = MI_CHECK;
1159 entry = wMenuAddCallback(menu, _("Select All Icons"),
1160 selectIconsCallback, NULL);
1161 wfree(entry->text);
1162 entry->text = _("Select All Icons");
1164 entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
1165 wfree(entry->text);
1166 entry->text = _("Keep Icon");
1168 entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
1169 wfree(entry->text);
1170 entry->text = _("Move Icon To");
1171 scr->clip_submenu = makeWorkspaceMenu(scr);
1172 if (scr->clip_submenu)
1173 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1175 entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback,
1176 NULL);
1177 wfree(entry->text);
1178 entry->text = _("Remove Icon");
1180 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1183 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1185 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1187 entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1188 wfree(entry->text);
1189 entry->text = _("Hide");
1191 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1193 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1195 if (type == WM_CLIP)
1196 scr->clip_menu = menu;
1198 return menu;
1202 WDock*
1203 wDockCreate(WScreen *scr, int type)
1205 WDock *dock;
1206 WAppIcon *btn;
1207 int icon_count;
1209 make_keys();
1211 dock = wmalloc(sizeof(WDock));
1212 memset(dock, 0, sizeof(WDock));
1214 if (type == WM_CLIP)
1215 icon_count = CLIP_MAX_ICONS;
1216 else
1217 icon_count = scr->scr_height/wPreferences.icon_size;
1219 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1220 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1222 dock->max_icons = icon_count;
1224 btn = mainIconCreate(scr, type);
1226 btn->dock = dock;
1228 dock->x_pos = btn->x_pos;
1229 dock->y_pos = btn->y_pos;
1230 dock->screen_ptr = scr;
1231 dock->type = type;
1232 dock->icon_count = 1;
1233 dock->on_right_side = 1;
1234 dock->collapsed = 0;
1235 dock->auto_collapse = 0;
1236 dock->auto_collapse_magic = NULL;
1237 dock->auto_raise_lower = 0;
1238 dock->auto_lower_magic = NULL;
1239 dock->auto_raise_magic = NULL;
1240 dock->attract_icons = 0;
1241 dock->lowered = 1;
1242 dock->icon_array[0] = btn;
1243 wRaiseFrame(btn->icon->core);
1244 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1246 /* create dock menu */
1247 dock->menu = dockMenuCreate(scr, type);
1249 return dock;
1253 void
1254 wDockDestroy(WDock *dock)
1256 int i;
1257 WAppIcon *aicon;
1259 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1260 aicon = dock->icon_array[i];
1261 if (aicon) {
1262 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1263 wDockDetach(dock, aicon);
1264 if (keepit) {
1265 /* XXX: can: aicon->icon == NULL ? */
1266 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, wGetHeadForWindow(aicon->icon->owner));
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 WMRect rect;
1718 int flags;
1720 if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos,
1721 &icon->y_pos)!=2)
1722 COMPLAIN("Position");
1724 /* check position sanity */
1725 rect.pos.x = icon->x_pos;
1726 rect.pos.y = icon->y_pos;
1727 rect.size.width = rect.size.height = ICON_SIZE;
1729 wGetRectPlacementInfo(scr, rect, &flags);
1730 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
1731 wScreenKeepInside(scr, &icon->x_pos, &icon->y_pos,
1732 ICON_SIZE, ICON_SIZE);
1736 #ifdef OFFIX_DND
1737 value = WMGetFromPLDictionary(clip_state, dDropCommand);
1738 if (value && WMIsPLString(value))
1739 icon->dnd_command = wstrdup(WMGetFromPLString(value));
1740 #endif
1742 value = WMGetFromPLDictionary(clip_state, dPasteCommand);
1743 if (value && WMIsPLString(value))
1744 icon->paste_command = wstrdup(WMGetFromPLString(value));
1746 WMReleasePropList(clip_state);
1748 return icon;
1752 WDock*
1753 wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type)
1755 WDock *dock;
1756 WMPropList *apps;
1757 WMPropList *value;
1758 WAppIcon *aicon, *old_top;
1759 int count, i;
1762 dock = wDockCreate(scr, type);
1764 if (!dock_state)
1765 return dock;
1767 if (dock_state)
1768 WMRetainPropList(dock_state);
1771 /* restore position */
1773 value = WMGetFromPLDictionary(dock_state, dPosition);
1775 if (value) {
1776 if (!WMIsPLString(value))
1777 COMPLAIN("Position");
1778 else {
1779 WMRect rect;
1780 int flags;
1782 if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos,
1783 &dock->y_pos)!=2)
1784 COMPLAIN("Position");
1786 /* check position sanity */
1787 rect.pos.x = dock->x_pos;
1788 rect.pos.y = dock->y_pos;
1789 rect.size.width = rect.size.height = ICON_SIZE;
1791 wGetRectPlacementInfo(scr, rect, &flags);
1792 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) {
1793 int x = dock->x_pos;
1794 wScreenKeepInside(scr, &x, &dock->y_pos, ICON_SIZE, ICON_SIZE);
1797 /* This is no more needed. ??? */
1798 if (type == WM_CLIP) {
1799 if (dock->x_pos < 0)
1800 dock->x_pos = 0;
1801 else if (dock->x_pos > scr->scr_width-ICON_SIZE)
1802 dock->x_pos = scr->scr_width-ICON_SIZE;
1803 } else {
1804 if (dock->x_pos >= 0) {
1805 dock->x_pos = DOCK_EXTRA_SPACE;
1806 dock->on_right_side = 0;
1807 } else {
1808 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1809 dock->on_right_side = 1;
1815 /* restore lowered/raised state */
1817 dock->lowered = 0;
1819 value = WMGetFromPLDictionary(dock_state, dLowered);
1821 if (value) {
1822 if (!WMIsPLString(value))
1823 COMPLAIN("Lowered");
1824 else {
1825 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1826 dock->lowered = 1;
1831 /* restore collapsed state */
1833 dock->collapsed = 0;
1835 value = WMGetFromPLDictionary(dock_state, dCollapsed);
1837 if (value) {
1838 if (!WMIsPLString(value))
1839 COMPLAIN("Collapsed");
1840 else {
1841 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1842 dock->collapsed = 1;
1847 /* restore auto-collapsed state */
1849 value = WMGetFromPLDictionary(dock_state, dAutoCollapse);
1851 if (value) {
1852 if (!WMIsPLString(value))
1853 COMPLAIN("AutoCollapse");
1854 else {
1855 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1856 dock->auto_collapse = 1;
1857 dock->collapsed = 1;
1863 /* restore auto-raise/lower state */
1865 value = WMGetFromPLDictionary(dock_state, dAutoRaiseLower);
1867 if (value) {
1868 if (!WMIsPLString(value))
1869 COMPLAIN("AutoRaiseLower");
1870 else {
1871 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1872 dock->auto_raise_lower = 1;
1877 /* restore attract icons state */
1879 dock->attract_icons = 0;
1881 value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons);
1883 if (value) {
1884 if (!WMIsPLString(value))
1885 COMPLAIN("AutoAttractIcons");
1886 else {
1887 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1888 dock->attract_icons = 1;
1893 /* application list */
1896 WMPropList *tmp;
1897 char buffer[64];
1900 * When saving, it saves the dock state in
1901 * Applications and Applicationsnnn
1903 * When loading, it will first try Applicationsnnn.
1904 * If it does not exist, use Applications as default.
1907 snprintf(buffer, sizeof(buffer), "Applications%i", scr->scr_height);
1909 tmp = WMCreatePLString(buffer);
1910 apps = WMGetFromPLDictionary(dock_state, tmp);
1911 WMReleasePropList(tmp);
1913 if (!apps) {
1914 apps = WMGetFromPLDictionary(dock_state, dApplications);
1918 if (!apps) {
1919 goto finish;
1922 count = WMGetPropListItemCount(apps);
1924 if (count==0)
1925 goto finish;
1927 old_top = dock->icon_array[0];
1929 /* dock->icon_count is set to 1 when dock is created.
1930 * Since Clip is already restored, we want to keep it so for clip,
1931 * but for dock we may change the default top tile, so we set it to 0.
1933 if (type == WM_DOCK)
1934 dock->icon_count = 0;
1936 for (i=0; i<count; i++) {
1937 if (dock->icon_count >= dock->max_icons) {
1938 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1939 break;
1942 value = WMGetFromPLArray(apps, i);
1943 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1945 dock->icon_array[dock->icon_count] = aicon;
1947 if (aicon) {
1948 aicon->dock = dock;
1949 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1950 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1952 if (dock->lowered)
1953 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1954 else
1955 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1957 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1958 0, 0);
1960 if (!dock->collapsed)
1961 XMapWindow(dpy, aicon->icon->core->window);
1962 wRaiseFrame(aicon->icon->core);
1964 dock->icon_count++;
1965 } else if (dock->icon_count==0 && type==WM_DOCK)
1966 dock->icon_count++;
1969 /* if the first icon is not defined, use the default */
1970 if (dock->icon_array[0]==NULL) {
1971 /* update default icon */
1972 old_top->x_pos = dock->x_pos;
1973 old_top->y_pos = dock->y_pos;
1974 if (dock->lowered)
1975 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1976 else
1977 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1978 dock->icon_array[0] = old_top;
1979 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1980 /* we don't need to increment dock->icon_count here because it was
1981 * incremented in the loop above.
1983 } else if (old_top!=dock->icon_array[0]) {
1984 if (old_top == scr->clip_icon)
1985 scr->clip_icon = dock->icon_array[0];
1986 wAppIconDestroy(old_top);
1989 finish:
1990 if (dock_state)
1991 WMReleasePropList(dock_state);
1993 return dock;
1998 void
1999 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
2001 if (btn && btn->command && !btn->running && !btn->launching) {
2003 btn->drop_launch = 0;
2004 btn->paste_launch = 0;
2006 btn->pid = execCommand(btn, btn->command, state);
2008 if (btn->pid>0) {
2009 if (!btn->forced_dock && !btn->buggy_app) {
2010 btn->launching = 1;
2011 dockIconPaint(btn);
2014 } else {
2015 wfree(state);
2020 void
2021 wDockDoAutoLaunch(WDock *dock, int workspace)
2023 WAppIcon *btn;
2024 WSavedState *state;
2025 int i;
2027 for (i = 0; i < dock->max_icons; i++) {
2028 btn = dock->icon_array[i];
2029 if (!btn || !btn->auto_launch)
2030 continue;
2032 state = wmalloc(sizeof(WSavedState));
2033 memset(state, 0, sizeof(WSavedState));
2034 state->workspace = workspace;
2035 /* TODO: this is klugy and is very difficult to understand
2036 * what's going on. Try to clean up */
2037 wDockLaunchWithState(dock, btn, state);
2042 #ifdef OFFIX_DND
2043 static WDock*
2044 findDock(WScreen *scr, XEvent *event, int *icon_pos)
2046 WDock *dock;
2047 int i;
2049 *icon_pos = -1;
2050 if ((dock = scr->dock)!=NULL) {
2051 for (i=0; i<dock->max_icons; i++) {
2052 if (dock->icon_array[i]
2053 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2054 *icon_pos = i;
2055 break;
2059 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
2060 for (i=0; i<dock->max_icons; i++) {
2061 if (dock->icon_array[i]
2062 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2063 *icon_pos = i;
2064 break;
2068 if(*icon_pos>=0)
2069 return dock;
2070 return NULL;
2075 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
2077 WDock *dock;
2078 WAppIcon *btn;
2079 int icon_pos;
2081 dock = findDock(scr, event, &icon_pos);
2082 if (!dock)
2083 return False;
2086 * Return True if the drop was on an application icon window.
2087 * In this case, let the ClientMessage handler redirect the
2088 * message to the app.
2090 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
2091 return True;
2093 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
2094 scr->flags.dnd_data_convertion_status = 0;
2096 btn = dock->icon_array[icon_pos];
2098 if (!btn->forced_dock) {
2099 btn->relaunching = btn->running;
2100 btn->running = 1;
2102 if (btn->wm_instance || btn->wm_class) {
2103 WWindowAttributes attr;
2104 memset(&attr, 0, sizeof(WWindowAttributes));
2105 wDefaultFillAttributes(btn->icon->core->screen_ptr,
2106 btn->wm_instance,
2107 btn->wm_class, &attr, NULL, True);
2109 if (!attr.no_appicon)
2110 btn->launching = 1;
2111 else
2112 btn->running = 0;
2115 btn->paste_launch = 0;
2116 btn->drop_launch = 1;
2117 scr->last_dock = dock;
2118 btn->pid = execCommand(btn, btn->dnd_command, NULL);
2119 if (btn->pid>0) {
2120 dockIconPaint(btn);
2121 } else {
2122 btn->launching = 0;
2123 if (!btn->relaunching) {
2124 btn->running = 0;
2128 return False;
2130 #endif /* OFFIX_DND */
2134 Bool
2135 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2137 WWindow *wwin;
2138 char **argv;
2139 int argc;
2140 int index;
2142 wwin = icon->icon->owner;
2143 if (icon->command==NULL) {
2144 icon->editing = 0;
2145 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2147 icon->command = wtokenjoin(argv, argc);
2148 XFreeStringList(argv);
2149 } else {
2150 char *command=NULL;
2152 /* icon->forced_dock = 1;*/
2153 if (dock->type!=WM_CLIP || !icon->attracted) {
2154 icon->editing = 1;
2155 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
2156 _("Type the command used to launch the application"),
2157 &command)) {
2158 if (command && (command[0]==0 ||
2159 (command[0]=='-' && command[1]==0))) {
2160 wfree(command);
2161 command = NULL;
2163 icon->command = command;
2164 icon->editing = 0;
2165 } else {
2166 icon->editing = 0;
2167 if (command)
2168 wfree(command);
2169 /* If the target is the dock, reject the icon. If
2170 * the target is the clip, make it an attracted icon
2172 if (dock->type==WM_CLIP) {
2173 icon->attracted = 1;
2174 if (!icon->icon->shadowed) {
2175 icon->icon->shadowed = 1;
2176 icon->icon->force_paint = 1;
2178 } else {
2179 return False;
2184 } else {
2185 icon->editing = 0;
2188 for (index=1; index<dock->max_icons; index++)
2189 if (dock->icon_array[index] == NULL)
2190 break;
2191 /* if (index == dock->max_icons)
2192 return; */
2194 assert(index < dock->max_icons);
2196 dock->icon_array[index] = icon;
2197 icon->yindex = y;
2198 icon->xindex = x;
2200 icon->omnipresent = 0;
2202 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2203 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2205 dock->icon_count++;
2207 icon->running = 1;
2208 icon->launching = 0;
2209 icon->docked = 1;
2210 icon->dock = dock;
2211 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2212 if (dock->type == WM_CLIP) {
2213 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2214 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2216 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2217 icon->icon->core->descriptor.parent = icon;
2219 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2220 icon->icon->core);
2221 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2222 wAppIconPaint(icon);
2224 if (wPreferences.auto_arrange_icons)
2225 wArrangeIcons(dock->screen_ptr, True);
2227 #ifdef OFFIX_DND
2228 if (icon->command && !icon->dnd_command) {
2229 int len = strlen(icon->command)+8;
2230 icon->dnd_command = wmalloc(len);
2231 snprintf(icon->dnd_command, len, "%s %%d", icon->command);
2233 #endif
2235 if (icon->command && !icon->paste_command) {
2236 int len = strlen(icon->command)+8;
2237 icon->paste_command = wmalloc(len);
2238 snprintf(icon->paste_command, len, "%s %%s", icon->command);
2241 return True;
2245 void
2246 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2248 int index;
2250 for(index=1; index<dock->max_icons; index++) {
2251 if(dock->icon_array[index] == icon)
2252 break;
2254 assert(index < dock->max_icons);
2256 icon->yindex = y;
2257 icon->xindex = x;
2259 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2260 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2264 Bool
2265 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2267 WWindow *wwin;
2268 char **argv;
2269 int argc;
2270 int index;
2272 if (src == dest)
2273 return True; /* No move needed, we're already there */
2275 if (dest == NULL)
2276 return False;
2278 wwin = icon->icon->owner;
2281 * For the moment we can't do this if we move icons in Clip from one
2282 * workspace to other, because if we move two or more icons without
2283 * command, the dialog box will not be able to tell us to which of the
2284 * moved icons it applies. -Dan
2286 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2287 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2289 icon->command = wtokenjoin(argv, argc);
2290 XFreeStringList(argv);
2291 } else {
2292 char *command=NULL;
2294 icon->editing = 1;
2295 /* icon->forced_dock = 1;*/
2296 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2297 _("Type the command used to launch the application"),
2298 &command)) {
2299 if (command && (command[0]==0 ||
2300 (command[0]=='-' && command[1]==0))) {
2301 wfree(command);
2302 command = NULL;
2304 icon->command = command;
2305 } else {
2306 icon->editing = 0;
2307 if (command)
2308 wfree(command);
2309 return False;
2311 icon->editing = 0;
2315 if (dest->type == WM_DOCK)
2316 wClipMakeIconOmnipresent(icon, False);
2318 for(index=1; index<src->max_icons; index++) {
2319 if(src->icon_array[index] == icon)
2320 break;
2322 assert(index < src->max_icons);
2324 src->icon_array[index] = NULL;
2325 src->icon_count--;
2327 for(index=1; index<dest->max_icons; index++) {
2328 if(dest->icon_array[index] == NULL)
2329 break;
2331 /* if (index == dest->max_icons)
2332 return; */
2334 assert(index < dest->max_icons);
2336 dest->icon_array[index] = icon;
2337 icon->dock = dest;
2339 /* deselect the icon */
2340 if (icon->icon->selected)
2341 wIconSelect(icon->icon);
2343 if (dest->type == WM_DOCK) {
2344 icon->icon->core->descriptor.handle_enternotify = NULL;
2345 icon->icon->core->descriptor.handle_leavenotify = NULL;
2346 } else {
2347 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2348 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2351 /* set it to be kept when moving to dock.
2352 * Unless the icon does not have a command set
2354 if (icon->command && dest->type==WM_DOCK) {
2355 icon->attracted = 0;
2356 if (icon->icon->shadowed) {
2357 icon->icon->shadowed = 0;
2358 icon->icon->force_paint = 1;
2362 if (src->auto_collapse || src->auto_raise_lower)
2363 clipLeave(src);
2365 icon->yindex = y;
2366 icon->xindex = x;
2368 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2369 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2371 dest->icon_count++;
2373 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2374 icon->icon->core);
2375 wAppIconPaint(icon);
2377 return True;
2381 void
2382 wDockDetach(WDock *dock, WAppIcon *icon)
2384 int index;
2386 /* make the settings panel be closed */
2387 if (icon->panel) {
2388 DestroyDockAppSettingsPanel(icon->panel);
2391 /* This must be called before icon->dock is set to NULL.
2392 * Don't move it. -Dan
2394 wClipMakeIconOmnipresent(icon, False);
2396 icon->docked = 0;
2397 icon->dock = NULL;
2398 icon->attracted = 0;
2399 icon->auto_launch = 0;
2400 if (icon->icon->shadowed) {
2401 icon->icon->shadowed = 0;
2402 icon->icon->force_paint = 1;
2405 /* deselect the icon */
2406 if (icon->icon->selected)
2407 wIconSelect(icon->icon);
2409 if (icon->command) {
2410 wfree(icon->command);
2411 icon->command = NULL;
2413 #ifdef OFFIX_DND
2414 if (icon->dnd_command) {
2415 wfree(icon->dnd_command);
2416 icon->dnd_command = NULL;
2418 #endif
2419 if (icon->paste_command) {
2420 wfree(icon->paste_command);
2421 icon->paste_command = NULL;
2424 for (index=1; index<dock->max_icons; index++)
2425 if (dock->icon_array[index] == icon)
2426 break;
2427 assert(index < dock->max_icons);
2428 dock->icon_array[index] = NULL;
2429 icon->yindex = -1;
2430 icon->xindex = -1;
2432 dock->icon_count--;
2434 /* if the dock is not attached to an application or
2435 * the the application did not set the approriate hints yet,
2436 * destroy the icon */
2437 if (!icon->running || !wApplicationOf(icon->main_window))
2438 wAppIconDestroy(icon);
2439 else {
2440 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2441 icon->icon->core->descriptor.handle_enternotify = NULL;
2442 icon->icon->core->descriptor.handle_leavenotify = NULL;
2443 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2444 icon->icon->core->descriptor.parent = icon;
2446 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2448 wAppIconPaint(icon);
2449 if (wPreferences.auto_arrange_icons) {
2450 wArrangeIcons(dock->screen_ptr, True);
2453 if (dock->auto_collapse || dock->auto_raise_lower)
2454 clipLeave(dock);
2459 * returns the closest Dock slot index for the passed
2460 * coordinates.
2462 * Returns False if icon can't be docked.
2464 * Note: this function should NEVER alter ret_x or ret_y, unless it will
2465 * return True. -Dan
2467 Bool
2468 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2469 int *ret_x, int *ret_y, int redocking)
2471 WScreen *scr = dock->screen_ptr;
2472 int dx, dy;
2473 int ex_x, ex_y;
2474 int i, offset = ICON_SIZE/2;
2475 WAppIcon *aicon = NULL;
2476 WAppIcon *nicon = NULL;
2477 int max_y_icons, max_x_icons;
2479 /* TODO: XINERAMA, for these */
2480 max_x_icons = scr->scr_width/ICON_SIZE;
2481 max_y_icons = scr->scr_height/ICON_SIZE-1;
2483 if (wPreferences.flags.noupdates)
2484 return False;
2486 dx = dock->x_pos;
2487 dy = dock->y_pos;
2489 /* if the dock is full */
2490 if (!redocking &&
2491 (dock->icon_count >= dock->max_icons)) {
2492 return False;
2495 /* exact position */
2496 if (req_y < dy)
2497 ex_y = (req_y - offset - dy)/ICON_SIZE;
2498 else
2499 ex_y = (req_y + offset - dy)/ICON_SIZE;
2501 if (req_x < dx)
2502 ex_x = (req_x - offset - dx)/ICON_SIZE;
2503 else
2504 ex_x = (req_x + offset - dx)/ICON_SIZE;
2506 /* check if the icon is outside the screen boundaries */
2508 WMRect rect;
2509 int flags;
2511 rect.pos.x = dx + ex_x*ICON_SIZE;
2512 rect.pos.y = dy + ex_y*ICON_SIZE;
2513 rect.size.width = rect.size.height = ICON_SIZE;
2515 wGetRectPlacementInfo(scr, rect, &flags);
2516 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
2517 return False;
2520 if (dock->type == WM_DOCK) {
2521 if (icon->dock != dock && ex_x != 0)
2522 return False;
2524 aicon = NULL;
2525 for (i=0; i<dock->max_icons; i++) {
2526 nicon = dock->icon_array[i];
2527 if (nicon && nicon->yindex == ex_y) {
2528 aicon = nicon;
2529 break;
2533 if (redocking) {
2534 int sig, done, closest;
2536 /* Possible cases when redocking:
2538 * icon dragged out of range of any slot -> false
2539 * icon dragged to range of free slot
2540 * icon dragged to range of same slot
2541 * icon dragged to range of different icon
2543 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2544 return False;
2546 if (ex_y>=0 && ex_y<=max_y_icons && (aicon==icon || !aicon)) {
2547 *ret_x = 0;
2548 *ret_y = ex_y;
2549 return True;
2552 /* start looking at the upper slot or lower? */
2553 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2554 sig = 1;
2555 else
2556 sig = -1;
2558 closest = -1;
2559 done = 0;
2560 /* look for closest free slot */
2561 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2562 int j;
2564 done = 1;
2565 closest = sig*(i/2) + ex_y;
2566 /* check if this slot is used */
2567 if (closest >= 0) {
2568 for (j = 0; j<dock->max_icons; j++) {
2569 if (dock->icon_array[j]
2570 && dock->icon_array[j]->yindex==closest) {
2571 /* slot is used by someone else */
2572 if (dock->icon_array[j]!=icon)
2573 done = 0;
2574 break;
2578 sig = -sig;
2580 if (done && closest >= 0 && closest <= max_y_icons &&
2581 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2583 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2584 *ret_x = 0;
2585 *ret_y = closest;
2586 return True;
2588 } else { /* !redocking */
2590 /* if slot is free and the icon is close enough, return it */
2591 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2592 *ret_x = 0;
2593 *ret_y = ex_y;
2594 return True;
2597 } else { /* CLIP */
2598 int neighbours = 0;
2599 int start, stop, k;
2601 start = icon->omnipresent ? 0 : scr->current_workspace;
2602 stop = icon->omnipresent ? scr->workspace_count : start+1;
2604 aicon = NULL;
2605 for (k=start; k<stop; k++) {
2606 WDock *tmp = scr->workspaces[k]->clip;
2607 if (!tmp)
2608 continue;
2609 for (i=0; i<tmp->max_icons; i++) {
2610 nicon = tmp->icon_array[i];
2611 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2612 aicon = nicon;
2613 break;
2616 if (aicon)
2617 break;
2619 for (k=start; k<stop; k++) {
2620 WDock *tmp = scr->workspaces[k]->clip;
2621 if (!tmp)
2622 continue;
2623 for (i=0; i<tmp->max_icons; i++) {
2624 nicon = tmp->icon_array[i];
2625 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2626 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2627 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2628 neighbours = 1;
2629 break;
2632 if (neighbours)
2633 break;
2636 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2637 *ret_x = ex_x;
2638 *ret_y = ex_y;
2639 return True;
2642 return False;
2646 static int
2647 onScreen(WScreen *scr, int x, int y, int sx, int ex, int sy, int ey)
2649 WMRect rect = wmkrect(x, y, ICON_SIZE, ICON_SIZE);
2650 int flags;
2652 wGetRectPlacementInfo(scr, rect, &flags);
2654 return !(flags & (XFLAG_DEAD | XFLAG_PARTIAL));
2659 * returns true if it can find a free slot in the dock,
2660 * in which case it changes x_pos and y_pos accordingly.
2661 * Else returns false.
2663 Bool
2664 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2666 WScreen *scr = dock->screen_ptr;
2667 WAppIcon *btn;
2668 WAppIconChain *chain;
2669 unsigned char *slot_map;
2670 int mwidth;
2671 int r;
2672 int x, y;
2673 int i, done = False;
2674 int corner;
2675 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2676 int extra_count=0;
2678 if (dock->type == WM_CLIP &&
2679 dock != scr->workspaces[scr->current_workspace]->clip)
2680 extra_count = scr->global_icon_count;
2682 /* if the dock is full */
2683 if (dock->icon_count+extra_count >= dock->max_icons) {
2684 return False;
2687 if (!wPreferences.flags.nodock && scr->dock) {
2688 if (scr->dock->on_right_side)
2689 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2690 else
2691 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2694 if (ex < dock->x_pos)
2695 ex = dock->x_pos;
2696 if (sx > dock->x_pos+ICON_SIZE)
2697 sx = dock->x_pos+ICON_SIZE;
2698 #define C_NONE 0
2699 #define C_NW 1
2700 #define C_NE 2
2701 #define C_SW 3
2702 #define C_SE 4
2704 /* check if clip is in a corner */
2705 if (dock->type==WM_CLIP) {
2706 if (dock->x_pos < 1 && dock->y_pos < 1)
2707 corner = C_NE;
2708 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2709 corner = C_SE;
2710 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2711 corner = C_SW;
2712 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2713 corner = C_NW;
2714 else
2715 corner = C_NONE;
2716 } else
2717 corner = C_NONE;
2719 /* If the clip is in the corner, use only slots that are in the border
2720 * of the screen */
2721 if (corner!=C_NONE) {
2722 char *hmap, *vmap;
2723 int hcount, vcount;
2725 hcount = WMIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2726 vcount = WMIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2727 hmap = wmalloc(hcount+1);
2728 memset(hmap, 0, hcount+1);
2729 vmap = wmalloc(vcount+1);
2730 memset(vmap, 0, vcount+1);
2732 /* mark used positions */
2733 switch (corner) {
2734 case C_NE:
2735 for (i=0; i<dock->max_icons; i++) {
2736 btn = dock->icon_array[i];
2737 if (!btn)
2738 continue;
2740 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2741 vmap[btn->yindex] = 1;
2742 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2743 hmap[btn->xindex] = 1;
2745 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2746 btn = chain->aicon;
2747 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2748 vmap[btn->yindex] = 1;
2749 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2750 hmap[btn->xindex] = 1;
2752 break;
2753 case C_NW:
2754 for (i=0; i<dock->max_icons; i++) {
2755 btn = dock->icon_array[i];
2756 if (!btn)
2757 continue;
2759 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2760 vmap[btn->yindex] = 1;
2761 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2762 hmap[-btn->xindex] = 1;
2764 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2765 btn = chain->aicon;
2766 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2767 vmap[btn->yindex] = 1;
2768 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2769 hmap[-btn->xindex] = 1;
2771 break;
2772 case C_SE:
2773 for (i=0; i<dock->max_icons; i++) {
2774 btn = dock->icon_array[i];
2775 if (!btn)
2776 continue;
2778 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2779 vmap[-btn->yindex] = 1;
2780 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2781 hmap[btn->xindex] = 1;
2783 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2784 btn = chain->aicon;
2785 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2786 vmap[-btn->yindex] = 1;
2787 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2788 hmap[btn->xindex] = 1;
2790 break;
2791 case C_SW:
2792 default:
2793 for (i=0; i<dock->max_icons; i++) {
2794 btn = dock->icon_array[i];
2795 if (!btn)
2796 continue;
2798 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2799 vmap[-btn->yindex] = 1;
2800 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2801 hmap[-btn->xindex] = 1;
2803 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2804 btn = chain->aicon;
2805 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2806 vmap[-btn->yindex] = 1;
2807 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2808 hmap[-btn->xindex] = 1;
2811 x=0; y=0;
2812 done = 0;
2813 /* search a vacant slot */
2814 for (i=1; i<WMAX(vcount, hcount); i++) {
2815 if (i < vcount && vmap[i]==0) {
2816 /* found a slot */
2817 x = 0;
2818 y = i;
2819 done = 1;
2820 break;
2821 } else if (i < hcount && hmap[i]==0) {
2822 /* found a slot */
2823 x = i;
2824 y = 0;
2825 done = 1;
2826 break;
2829 wfree(vmap);
2830 wfree(hmap);
2831 /* If found a slot, translate and return */
2832 if (done) {
2833 if (corner==C_NW || corner==C_NE) {
2834 *y_pos = y;
2835 } else {
2836 *y_pos = -y;
2838 if (corner==C_NE || corner==C_SE) {
2839 *x_pos = x;
2840 } else {
2841 *x_pos = -x;
2843 return True;
2845 /* else, try to find a slot somewhere else */
2848 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2849 * placed outside of screen */
2850 mwidth = (int)ceil(sqrt(dock->max_icons));
2852 /* In the worst case (the clip is in the corner of the screen),
2853 * the amount of icons that fit in the clip is smaller.
2854 * Double the map to get a safe value.
2856 mwidth += mwidth;
2858 r = (mwidth-1)/2;
2860 slot_map = wmalloc(mwidth*mwidth);
2861 memset(slot_map, 0, mwidth*mwidth);
2863 #define XY2OFS(x,y) (WMAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2865 /* mark used slots in the map. If the slot falls outside the map
2866 * (for example, when all icons are placed in line), ignore them. */
2867 for (i=0; i<dock->max_icons; i++) {
2868 btn = dock->icon_array[i];
2869 if (btn)
2870 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2872 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2873 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2875 /* Find closest slot from the center that is free by scanning the
2876 * map from the center to outward in circular passes.
2877 * This will not result in a neat layout, but will be optimal
2878 * in the sense that there will not be holes left.
2880 done = 0;
2881 for (i = 1; i <= r && !done; i++) {
2882 int tx, ty;
2884 /* top and bottom parts of the ring */
2885 for (x = -i; x <= i && !done; x++) {
2886 tx = dock->x_pos + x*ICON_SIZE;
2887 y = -i;
2888 ty = dock->y_pos + y*ICON_SIZE;
2889 if (slot_map[XY2OFS(x,y)]==0
2890 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2891 *x_pos = x;
2892 *y_pos = y;
2893 done = 1;
2894 break;
2896 y = i;
2897 ty = dock->y_pos + y*ICON_SIZE;
2898 if (slot_map[XY2OFS(x,y)]==0
2899 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2900 *x_pos = x;
2901 *y_pos = y;
2902 done = 1;
2903 break;
2906 /* left and right parts of the ring */
2907 for (y = -i+1; y <= i-1; y++) {
2908 ty = dock->y_pos + y*ICON_SIZE;
2909 x = -i;
2910 tx = dock->x_pos + x*ICON_SIZE;
2911 if (slot_map[XY2OFS(x,y)]==0
2912 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2913 *x_pos = x;
2914 *y_pos = y;
2915 done = 1;
2916 break;
2918 x = i;
2919 tx = dock->x_pos + x*ICON_SIZE;
2920 if (slot_map[XY2OFS(x,y)]==0
2921 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2922 *x_pos = x;
2923 *y_pos = y;
2924 done = 1;
2925 break;
2929 wfree(slot_map);
2930 #undef XY2OFS
2931 return done;
2935 static void
2936 moveDock(WDock *dock, int new_x, int new_y)
2938 WAppIcon *btn;
2939 int i;
2941 dock->x_pos = new_x;
2942 dock->y_pos = new_y;
2943 for (i=0; i<dock->max_icons; i++) {
2944 btn = dock->icon_array[i];
2945 if (btn) {
2946 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2947 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2948 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2954 static void
2955 swapDock(WDock *dock)
2957 WScreen *scr = dock->screen_ptr;
2958 WAppIcon *btn;
2959 int x, i;
2962 if (dock->on_right_side) {
2963 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2964 } else {
2965 x = dock->x_pos = DOCK_EXTRA_SPACE;
2968 for (i=0; i<dock->max_icons; i++) {
2969 btn = dock->icon_array[i];
2970 if (btn) {
2971 btn->x_pos = x;
2972 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2976 wScreenUpdateUsableArea(scr);
2980 static pid_t
2981 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2983 WScreen *scr = btn->icon->core->screen_ptr;
2984 pid_t pid;
2985 char **argv;
2986 int argc;
2987 char *cmdline;
2989 cmdline = ExpandOptions(scr, command);
2991 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2992 if (cmdline)
2993 wfree(cmdline);
2994 if (state)
2995 wfree(state);
2996 return 0;
2999 wtokensplit(cmdline, &argv, &argc);
3001 if (argv==NULL) {
3002 if (cmdline)
3003 wfree(cmdline);
3004 if (state)
3005 wfree(state);
3006 return 0;
3009 if ((pid=fork())==0) {
3010 char **args;
3011 int i;
3013 SetupEnvironment(scr);
3015 #ifdef HAVE_SETSID
3016 setsid();
3017 #endif
3019 args = malloc(sizeof(char*)*(argc+1));
3020 if (!args)
3021 exit(111);
3022 for (i=0; i<argc; i++) {
3023 args[i] = argv[i];
3025 args[argc] = NULL;
3026 execvp(argv[0], args);
3027 exit(111);
3029 wtokenfree(argv, argc);
3031 if (pid > 0) {
3032 if (!state) {
3033 state = wmalloc(sizeof(WSavedState));
3034 memset(state, 0, sizeof(WSavedState));
3035 state->hidden = -1;
3036 state->miniaturized = -1;
3037 state->shaded = -1;
3038 if (btn->dock==scr->dock || btn->omnipresent)
3039 state->workspace = -1;
3040 else
3041 state->workspace = scr->current_workspace;
3043 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
3044 state);
3045 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
3046 btn->dock);
3047 } else if (state) {
3048 wfree(state);
3050 wfree(cmdline);
3051 return pid;
3055 void
3056 wDockHideIcons(WDock *dock)
3058 int i;
3060 if (dock==NULL)
3061 return;
3063 for (i=1; i<dock->max_icons; i++) {
3064 if (dock->icon_array[i])
3065 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
3067 dock->mapped = 0;
3069 dockIconPaint(dock->icon_array[0]);
3073 void
3074 wDockShowIcons(WDock *dock)
3076 int i, newlevel;
3077 WAppIcon *btn;
3079 if (dock==NULL)
3080 return;
3082 btn = dock->icon_array[0];
3083 moveDock(dock, btn->x_pos, btn->y_pos);
3085 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
3086 ChangeStackingLevel(btn->icon->core, newlevel);
3088 for (i=1; i<dock->max_icons; i++) {
3089 if (dock->icon_array[i]) {
3090 MoveInStackListAbove(dock->icon_array[i]->icon->core,
3091 btn->icon->core);
3092 break;
3096 if (!dock->collapsed) {
3097 for (i=1; i<dock->max_icons; i++) {
3098 if (dock->icon_array[i]) {
3099 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
3103 dock->mapped = 1;
3105 dockIconPaint(btn);
3109 void
3110 wDockLower(WDock *dock)
3112 int i;
3114 for (i=0; i<dock->max_icons; i++) {
3115 if (dock->icon_array[i])
3116 wLowerFrame(dock->icon_array[i]->icon->core);
3121 void
3122 wDockRaise(WDock *dock)
3124 int i;
3126 for (i=dock->max_icons-1; i>=0; i--) {
3127 if (dock->icon_array[i])
3128 wRaiseFrame(dock->icon_array[i]->icon->core);
3133 void
3134 wDockRaiseLower(WDock *dock)
3136 if (!dock->icon_array[0]->icon->core->stacking->above
3137 ||(dock->icon_array[0]->icon->core->stacking->window_level
3138 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
3139 wDockLower(dock);
3140 else
3141 wDockRaise(dock);
3145 void
3146 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
3148 icon->launching = 0;
3149 icon->relaunching = 0;
3150 dockIconPaint(icon);
3154 WAppIcon*
3155 wDockFindIconForWindow(WDock *dock, Window window)
3157 WAppIcon *icon;
3158 int i;
3160 for (i=0; i<dock->max_icons; i++) {
3161 icon = dock->icon_array[i];
3162 if (icon && icon->main_window == window)
3163 return icon;
3165 return NULL;
3169 void
3170 wDockTrackWindowLaunch(WDock *dock, Window window)
3172 WAppIcon *icon;
3173 char *wm_class, *wm_instance;
3174 int i;
3175 Bool firstPass = True;
3176 Bool found = False;
3177 char *command = NULL;
3180 int argc;
3181 char **argv;
3183 if (XGetCommand(dpy, window, &argv, &argc)) {
3184 if (argc > 0 && argv != NULL)
3185 command = wtokenjoin(argv, argc);
3186 if (argv) {
3187 XFreeStringList(argv);
3192 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
3193 (!wm_class && !wm_instance))
3194 return;
3196 retry:
3197 for (i=0; i<dock->max_icons; i++) {
3198 icon = dock->icon_array[i];
3199 if (!icon)
3200 continue;
3202 /* app is already attached to icon */
3203 if (icon->main_window == window) {
3204 found = True;
3205 break;
3208 if ((icon->wm_instance || icon->wm_class)
3209 && (icon->launching || !icon->running)) {
3211 if (icon->wm_instance && wm_instance &&
3212 strcmp(icon->wm_instance, wm_instance)!=0) {
3213 continue;
3215 if (icon->wm_class && wm_class &&
3216 strcmp(icon->wm_class, wm_class)!=0) {
3217 continue;
3219 if (firstPass && command && strcmp(icon->command, command)!=0) {
3220 continue;
3223 if (!icon->relaunching) {
3224 WApplication *wapp;
3226 /* Possibly an application that was docked with dockit,
3227 * but the user did not update WMState to indicate that
3228 * it was docked by force */
3229 wapp = wApplicationOf(window);
3230 if (!wapp) {
3231 icon->forced_dock = 1;
3232 icon->running = 0;
3234 if (!icon->forced_dock)
3235 icon->main_window = window;
3238 found = True;
3239 if (!wPreferences.no_animations && !icon->launching &&
3240 !dock->screen_ptr->flags.startup && !dock->collapsed) {
3241 WAppIcon *aicon;
3242 int x0, y0;
3244 icon->launching = 1;
3245 dockIconPaint(icon);
3247 aicon = wAppIconCreateForDock(dock->screen_ptr, NULL,
3248 wm_instance, wm_class,
3249 TILE_NORMAL);
3250 /* XXX: can: aicon->icon == NULL ? */
3251 PlaceIcon(dock->screen_ptr, &x0, &y0, wGetHeadForWindow(aicon->icon->owner));
3252 wAppIconMove(aicon, x0, y0);
3253 /* Should this always be lowered? -Dan */
3254 if (dock->lowered)
3255 wLowerFrame(aicon->icon->core);
3256 XMapWindow(dpy, aicon->icon->core->window);
3257 aicon->launching = 1;
3258 wAppIconPaint(aicon);
3259 SlideWindow(aicon->icon->core->window, x0, y0,
3260 icon->x_pos, icon->y_pos);
3261 XUnmapWindow(dpy, aicon->icon->core->window);
3262 wAppIconDestroy(aicon);
3264 wDockFinishLaunch(dock, icon);
3265 break;
3269 if (firstPass && !found) {
3270 firstPass = False;
3271 goto retry;
3274 if (command)
3275 wfree(command);
3277 if (wm_class)
3278 XFree(wm_class);
3279 if (wm_instance)
3280 XFree(wm_instance);
3285 void
3286 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3288 if (!wPreferences.flags.noclip) {
3289 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3290 if (scr->current_workspace != workspace) {
3291 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3292 WAppIconChain *chain = scr->global_icons;
3294 while (chain) {
3295 moveIconBetweenDocks(chain->aicon->dock,
3296 scr->workspaces[workspace]->clip,
3297 chain->aicon, chain->aicon->xindex,
3298 chain->aicon->yindex);
3299 if (scr->workspaces[workspace]->clip->collapsed)
3300 XUnmapWindow(dpy, chain->aicon->icon->core->window);
3301 chain = chain->next;
3304 wDockHideIcons(old_clip);
3305 if (old_clip->auto_raise_lower) {
3306 if (old_clip->auto_raise_magic) {
3307 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3308 old_clip->auto_raise_magic = NULL;
3310 wDockLower(old_clip);
3312 if (old_clip->auto_collapse) {
3313 if (old_clip->auto_expand_magic) {
3314 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3315 old_clip->auto_expand_magic = NULL;
3317 old_clip->collapsed = 1;
3319 wDockShowIcons(scr->workspaces[workspace]->clip);
3321 if (scr->flags.clip_balloon_mapped)
3322 showClipBalloon(scr->clip_icon->dock, workspace);
3328 static void
3329 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3331 WAppIcon *icon;
3332 int i;
3334 for (i=0; i<dock->max_icons; i++) {
3335 icon = dock->icon_array[i];
3336 if (!icon)
3337 continue;
3339 if (icon->launching && icon->pid == pid) {
3340 if (!icon->relaunching) {
3341 icon->running = 0;
3342 icon->main_window = None;
3344 wDockFinishLaunch(dock, icon);
3345 icon->pid = 0;
3346 if (status==111) {
3347 char msg[PATH_MAX];
3348 char *cmd;
3350 if (icon->drop_launch)
3351 cmd = icon->dnd_command;
3352 else if (icon->paste_launch)
3353 cmd = icon->paste_command;
3354 else
3355 cmd = icon->command;
3357 snprintf(msg, sizeof(msg),
3358 _("Could not execute command \"%s\""), cmd);
3360 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3361 _("OK"), NULL, NULL);
3363 break;
3369 static void
3370 toggleLowered(WDock *dock)
3372 WAppIcon *tmp;
3373 int newlevel, i;
3375 /* lower/raise Dock */
3376 if (!dock->lowered) {
3377 newlevel = WMNormalLevel;
3378 dock->lowered = 1;
3379 } else {
3380 newlevel = WMDockLevel;
3381 dock->lowered = 0;
3384 for (i=0; i<dock->max_icons; i++) {
3385 tmp = dock->icon_array[i];
3386 if (!tmp)
3387 continue;
3389 ChangeStackingLevel(tmp->icon->core, newlevel);
3390 if (dock->lowered)
3391 wLowerFrame(tmp->icon->core);
3394 if (dock->type == WM_DOCK)
3395 wScreenUpdateUsableArea(dock->screen_ptr);
3399 static void
3400 toggleCollapsed(WDock *dock)
3402 if (dock->collapsed) {
3403 dock->collapsed = 0;
3404 wDockShowIcons(dock);
3406 else {
3407 dock->collapsed = 1;
3408 wDockHideIcons(dock);
3413 static void
3414 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3416 WScreen *scr = dock->screen_ptr;
3417 WObjDescriptor *desc;
3418 WMenuEntry *entry;
3419 WApplication *wapp = NULL;
3420 int index = 0;
3421 int x_pos;
3422 int n_selected;
3423 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3425 if (dock->type == WM_DOCK) {
3426 /* keep on top */
3427 entry = dock->menu->entries[index];
3428 entry->flags.indicator_on = !dock->lowered;
3429 entry->clientdata = dock;
3430 dock->menu->flags.realized = 0;
3431 } else {
3432 /* clip options */
3433 if (scr->clip_options)
3434 updateClipOptionsMenu(scr->clip_options, dock);
3436 n_selected = numberOfSelectedIcons(dock);
3438 /* Rename Workspace */
3439 entry = dock->menu->entries[++index];
3440 if (aicon == scr->clip_icon) {
3441 entry->callback = renameCallback;
3442 entry->clientdata = dock;
3443 entry->flags.indicator = 0;
3444 entry->text = _("Rename Workspace");
3445 } else {
3446 entry->callback = omnipresentCallback;
3447 entry->clientdata = aicon;
3448 if (n_selected > 0) {
3449 entry->flags.indicator = 0;
3450 entry->text = _("Toggle Omnipresent");
3451 } else {
3452 entry->flags.indicator = 1;
3453 entry->flags.indicator_on = aicon->omnipresent;
3454 entry->flags.indicator_type = MI_CHECK;
3455 entry->text = _("Omnipresent");
3459 /* select/unselect icon */
3460 entry = dock->menu->entries[++index];
3461 entry->clientdata = aicon;
3462 entry->flags.indicator_on = aicon->icon->selected;
3463 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3465 /* select/unselect all icons */
3466 entry = dock->menu->entries[++index];
3467 entry->clientdata = aicon;
3468 if (n_selected > 0)
3469 entry->text = _("Unselect All Icons");
3470 else
3471 entry->text = _("Select All Icons");
3472 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3474 /* keep icon(s) */
3475 entry = dock->menu->entries[++index];
3476 entry->clientdata = aicon;
3477 if (n_selected > 1)
3478 entry->text = _("Keep Icons");
3479 else
3480 entry->text = _("Keep Icon");
3481 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3483 /* this is the workspace submenu part */
3484 entry = dock->menu->entries[++index];
3485 if (n_selected > 1)
3486 entry->text = _("Move Icons To");
3487 else
3488 entry->text = _("Move Icon To");
3489 if (scr->clip_submenu)
3490 updateWorkspaceMenu(scr->clip_submenu, aicon);
3491 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3493 /* remove icon(s) */
3494 entry = dock->menu->entries[++index];
3495 entry->clientdata = aicon;
3496 if (n_selected > 1)
3497 entry->text = _("Remove Icons");
3498 else
3499 entry->text = _("Remove Icon");
3500 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3502 /* attract icon(s) */
3503 entry = dock->menu->entries[++index];
3504 entry->clientdata = aicon;
3506 dock->menu->flags.realized = 0;
3507 wMenuRealize(dock->menu);
3511 if (aicon->icon->owner) {
3512 wapp = wApplicationOf(aicon->icon->owner->main_window);
3513 } else {
3514 wapp = NULL;
3517 /* launch */
3518 entry = dock->menu->entries[++index];
3519 entry->clientdata = aicon;
3520 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3522 /* unhide here */
3523 entry = dock->menu->entries[++index];
3524 entry->clientdata = aicon;
3525 if (wapp && wapp->flags.hidden) {
3526 entry->text = _("Unhide Here");
3527 } else {
3528 entry->text = _("Bring Here");
3530 wMenuSetEnabled(dock->menu, index, appIsRunning);
3532 /* hide */
3533 entry = dock->menu->entries[++index];
3534 entry->clientdata = aicon;
3535 if (wapp && wapp->flags.hidden) {
3536 entry->text = _("Unhide");
3537 } else {
3538 entry->text = _("Hide");
3540 wMenuSetEnabled(dock->menu, index, appIsRunning);
3542 /* settings */
3543 entry = dock->menu->entries[++index];
3544 entry->clientdata = aicon;
3545 wMenuSetEnabled(dock->menu, index, !aicon->editing
3546 && !wPreferences.flags.noupdates);
3548 /* kill */
3549 entry = dock->menu->entries[++index];
3550 entry->clientdata = aicon;
3551 wMenuSetEnabled(dock->menu, index, appIsRunning);
3553 if (!dock->menu->flags.realized)
3554 wMenuRealize(dock->menu);
3556 if (dock->type == WM_CLIP) {
3557 /*x_pos = event->xbutton.x_root+2;*/
3558 x_pos = event->xbutton.x_root - dock->menu->frame->core->width/2 - 1;
3559 if (x_pos < 0) {
3560 x_pos = 0;
3561 } else if (x_pos + dock->menu->frame->core->width > scr->scr_width-2) {
3562 x_pos = scr->scr_width - dock->menu->frame->core->width - 4;
3564 } else {
3565 x_pos = dock->on_right_side ?
3566 scr->scr_width - dock->menu->frame->core->width - 3 : 0;
3569 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3571 /* allow drag select */
3572 event->xany.send_event = True;
3573 desc = &dock->menu->menu->descriptor;
3574 (*desc->handle_mousedown)(desc, event);
3578 /******************************************************************/
3579 static void
3580 iconDblClick(WObjDescriptor *desc, XEvent *event)
3582 WAppIcon *btn = desc->parent;
3583 WDock *dock = btn->dock;
3584 WApplication *wapp = NULL;
3585 int unhideHere = 0;
3587 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3588 wapp = wApplicationOf(btn->icon->owner->main_window);
3590 assert(wapp!=NULL);
3592 unhideHere = (event->xbutton.state & ShiftMask);
3594 /* go to the last workspace that the user worked on the app */
3595 if (wapp->last_workspace != dock->screen_ptr->current_workspace
3596 && !unhideHere) {
3597 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3600 wUnhideApplication(wapp, event->xbutton.button==Button2,
3601 unhideHere);
3603 if (event->xbutton.state & MOD_MASK) {
3604 wHideOtherApplications(btn->icon->owner);
3606 } else {
3607 if (event->xbutton.button==Button1) {
3609 if (event->xbutton.state & MOD_MASK) {
3610 /* raise/lower dock */
3611 toggleLowered(dock);
3612 } else if (btn == dock->screen_ptr->clip_icon) {
3613 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3614 toggleCollapsed(dock);
3615 else
3616 handleClipChangeWorkspace(dock->screen_ptr, event);
3617 } else if (btn->command) {
3618 if (!btn->launching &&
3619 (!btn->running || (event->xbutton.state & ControlMask))) {
3620 launchDockedApplication(btn, False);
3622 } else if (btn->xindex == 0 && btn->yindex == 0
3623 && btn->dock->type == WM_DOCK) {
3625 wShowGNUstepPanel(dock->screen_ptr);
3633 static void
3634 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3636 WScreen *scr = dock->screen_ptr;
3637 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3638 int x, y;
3639 XEvent ev;
3640 int grabbed = 0, swapped = 0, done;
3641 Pixmap ghost = None;
3642 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3644 #ifdef DEBUG
3645 puts("moving dock");
3646 #endif
3647 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3648 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3649 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3650 wwarning("pointer grab failed for dock move");
3652 y = 0;
3653 for (x=0; x<dock->max_icons; x++) {
3654 if (dock->icon_array[x]!=NULL &&
3655 dock->icon_array[x]->yindex > y)
3656 y = dock->icon_array[x]->yindex;
3658 y++;
3659 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3661 done = 0;
3662 while (!done) {
3663 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3664 |ButtonMotionMask|ExposureMask, &ev);
3665 switch (ev.type) {
3666 case Expose:
3667 WMHandleEvent(&ev);
3668 break;
3670 case MotionNotify:
3671 if (!grabbed) {
3672 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3673 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3674 XChangeActivePointerGrab(dpy, ButtonMotionMask
3675 |ButtonReleaseMask|ButtonPressMask,
3676 wCursor[WCUR_MOVE], CurrentTime);
3677 grabbed=1;
3679 break;
3681 if (dock->type == WM_CLIP) {
3682 x = ev.xmotion.x_root - ofs_x;
3683 y = ev.xmotion.y_root - ofs_y;
3684 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3686 moveDock(dock, x, y);
3687 } else {
3688 /* move vertically if pointer is inside the dock*/
3689 if ((dock->on_right_side &&
3690 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3691 || (!dock->on_right_side &&
3692 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3694 x = ev.xmotion.x_root - ofs_x;
3695 y = ev.xmotion.y_root - ofs_y;
3696 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3697 moveDock(dock, dock->x_pos, y);
3699 /* move horizontally to change sides */
3700 x = ev.xmotion.x_root - ofs_x;
3701 if (!dock->on_right_side) {
3703 /* is on left */
3705 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3706 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3707 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3708 if (superfluous && ghost==None) {
3709 ghost = MakeGhostDock(dock, dock->x_pos,
3710 scr->scr_width-ICON_SIZE
3711 -DOCK_EXTRA_SPACE-1,
3712 dock->y_pos);
3713 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3714 ghost);
3715 XClearWindow(dpy, scr->dock_shadow);
3717 XMapRaised(dpy, scr->dock_shadow);
3718 swapped = 1;
3719 } else {
3720 if (superfluous && ghost!=None) {
3721 XFreePixmap(dpy, ghost);
3722 ghost = None;
3724 XUnmapWindow(dpy, scr->dock_shadow);
3725 swapped = 0;
3727 } else {
3728 /* is on right */
3729 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3730 XMoveWindow(dpy, scr->dock_shadow,
3731 DOCK_EXTRA_SPACE, dock->y_pos);
3732 if (superfluous && ghost==None) {
3733 ghost = MakeGhostDock(dock, dock->x_pos,
3734 DOCK_EXTRA_SPACE, dock->y_pos);
3735 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3736 ghost);
3737 XClearWindow(dpy, scr->dock_shadow);
3739 XMapRaised(dpy, scr->dock_shadow);
3740 swapped = -1;
3741 } else {
3742 XUnmapWindow(dpy, scr->dock_shadow);
3743 swapped = 0;
3744 if (superfluous && ghost!=None) {
3745 XFreePixmap(dpy, ghost);
3746 ghost = None;
3751 break;
3753 case ButtonPress:
3754 break;
3756 case ButtonRelease:
3757 if (ev.xbutton.button != event->xbutton.button)
3758 break;
3759 XUngrabPointer(dpy, CurrentTime);
3760 XUnmapWindow(dpy, scr->dock_shadow);
3761 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3762 if (dock->type == WM_DOCK) {
3763 if (swapped!=0) {
3764 if (swapped>0)
3765 dock->on_right_side = 1;
3766 else
3767 dock->on_right_side = 0;
3768 swapDock(dock);
3769 wArrangeIcons(scr, False);
3772 done = 1;
3773 break;
3776 if (superfluous) {
3777 if (ghost!=None)
3778 XFreePixmap(dpy, ghost);
3779 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3781 #ifdef DEBUG
3782 puts("End dock move");
3783 #endif
3788 static void
3789 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3791 WScreen *scr = dock->screen_ptr;
3792 Window wins[2];
3793 WIcon *icon = aicon->icon;
3794 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3795 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3796 XEvent ev;
3797 int x = aicon->x_pos, y = aicon->y_pos;
3798 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3799 int shad_x = x, shad_y = y;
3800 int ix = aicon->xindex, iy = aicon->yindex;
3801 int tmp;
3802 Pixmap ghost = None;
3803 Bool docked;
3804 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3805 int omnipresent = aicon->omnipresent; /* this must be cached!!! */
3808 if (wPreferences.flags.noupdates)
3809 return;
3811 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3812 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3813 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3814 #ifdef DEBUG0
3815 wwarning("pointer grab failed for icon move");
3816 #endif
3819 if (!(event->xbutton.state & MOD_MASK))
3820 wRaiseFrame(icon->core);
3822 if (!wPreferences.flags.noclip)
3823 clip = scr->workspaces[scr->current_workspace]->clip;
3825 if (dock == scr->dock && !wPreferences.flags.noclip)
3826 dock2 = clip;
3827 else if (dock != scr->dock && !wPreferences.flags.nodock)
3828 dock2 = scr->dock;
3830 wins[0] = icon->core->window;
3831 wins[1] = scr->dock_shadow;
3832 XRestackWindows(dpy, wins, 2);
3833 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3834 ICON_SIZE, ICON_SIZE);
3835 if (superfluous) {
3836 if (icon->pixmap!=None)
3837 ghost = MakeGhostIcon(scr, icon->pixmap);
3838 else
3839 ghost = MakeGhostIcon(scr, icon->core->window);
3841 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3842 XClearWindow(dpy, scr->dock_shadow);
3844 XMapWindow(dpy, scr->dock_shadow);
3846 ondock = 1;
3849 while(1) {
3850 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3851 |ButtonMotionMask|ExposureMask, &ev);
3852 switch (ev.type) {
3853 case Expose:
3854 WMHandleEvent(&ev);
3855 break;
3857 case MotionNotify:
3858 if (!grabbed) {
3859 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3860 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3861 XChangeActivePointerGrab(dpy, ButtonMotionMask
3862 |ButtonReleaseMask|ButtonPressMask,
3863 wCursor[WCUR_MOVE], CurrentTime);
3864 grabbed=1;
3865 } else {
3866 break;
3870 if (omnipresent) {
3871 int i;
3872 for (i=0; i<scr->workspace_count; i++) {
3873 if (i == scr->current_workspace)
3874 continue;
3875 wDockShowIcons(scr->workspaces[i]->clip);
3879 x = ev.xmotion.x_root - ofs_x;
3880 y = ev.xmotion.y_root - ofs_y;
3881 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3882 if (tmp && dock2) {
3883 change_dock = 0;
3884 if (last_dock != dock && collapsed) {
3885 last_dock->collapsed = 1;
3886 wDockHideIcons(last_dock);
3887 collapsed = 0;
3889 if (!collapsed && (collapsed = dock->collapsed)) {
3890 dock->collapsed = 0;
3891 wDockShowIcons(dock);
3893 if (dock->auto_raise_lower)
3894 wDockRaise(dock);
3895 last_dock = dock;
3896 } else if (dock2) {
3897 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3898 if (tmp) {
3899 change_dock = 1;
3900 if (last_dock != dock2 && collapsed) {
3901 last_dock->collapsed = 1;
3902 wDockHideIcons(last_dock);
3903 collapsed = 0;
3905 if (!collapsed && (collapsed = dock2->collapsed)) {
3906 dock2->collapsed = 0;
3907 wDockShowIcons(dock2);
3909 if (dock2->auto_raise_lower)
3910 wDockRaise(dock2);
3911 last_dock = dock2;
3914 if (aicon->launching
3915 || aicon->lock
3916 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3917 || (!aicon->running && tmp)) {
3918 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3919 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3921 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3923 if (!ondock) {
3924 XMapWindow(dpy, scr->dock_shadow);
3926 ondock = 1;
3927 } else {
3928 if (ondock) {
3929 XUnmapWindow(dpy, scr->dock_shadow);
3931 ondock = 0;
3933 XMoveWindow(dpy, icon->core->window, x, y);
3934 break;
3936 case ButtonPress:
3937 break;
3939 case ButtonRelease:
3940 if (ev.xbutton.button != event->xbutton.button)
3941 break;
3942 XUngrabPointer(dpy, CurrentTime);
3943 if (ondock) {
3944 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3945 XUnmapWindow(dpy, scr->dock_shadow);
3946 if (!change_dock) {
3947 reattachIcon(dock, aicon, ix, iy);
3948 if (clip && dock!=clip && clip->auto_raise_lower)
3949 wDockLower(clip);
3950 } else {
3951 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3952 if (!docked) {
3953 /* Slide it back if dock rejected it */
3954 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3955 aicon->y_pos);
3956 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3958 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3959 collapsed = 0;
3962 } else {
3963 aicon->x_pos = x;
3964 aicon->y_pos = y;
3965 if (superfluous) {
3966 if (!aicon->running && !wPreferences.no_animations) {
3967 /* We need to deselect it, even if is deselected in
3968 * wDockDetach(), because else DoKaboom() will fail.
3970 if (aicon->icon->selected)
3971 wIconSelect(aicon->icon);
3973 wSoundPlay(WSOUND_KABOOM);
3974 DoKaboom(scr,aicon->icon->core->window, x, y);
3975 } else {
3976 wSoundPlay(WSOUND_UNDOCK);
3978 } else {
3979 wSoundPlay(WSOUND_UNDOCK);
3981 if (clip && clip->auto_raise_lower)
3982 wDockLower(clip);
3983 wDockDetach(dock, aicon);
3985 if (collapsed) {
3986 last_dock->collapsed = 1;
3987 wDockHideIcons(last_dock);
3988 collapsed = 0;
3990 if (superfluous) {
3991 if (ghost!=None)
3992 XFreePixmap(dpy, ghost);
3993 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3995 if (omnipresent) {
3996 int i;
3997 for (i=0; i<scr->workspace_count; i++) {
3998 if (i == scr->current_workspace)
3999 continue;
4000 wDockHideIcons(scr->workspaces[i]->clip);
4004 #ifdef DEBUG
4005 puts("End icon move");
4006 #endif
4007 return;
4013 static int
4014 getClipButton(int px, int py)
4016 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
4018 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
4019 return CLIP_IDLE;
4021 if (py <= pt-((int)ICON_SIZE-1-px))
4022 return CLIP_FORWARD;
4023 else if (px <= pt-((int)ICON_SIZE-1-py))
4024 return CLIP_REWIND;
4026 return CLIP_IDLE;
4030 static void
4031 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
4033 XEvent ev;
4034 int done, direction, new_ws;
4035 int new_dir;
4036 WDock *clip = scr->clip_icon->dock;
4038 direction = getClipButton(event->xbutton.x, event->xbutton.y);
4040 clip->lclip_button_pushed = direction==CLIP_REWIND;
4041 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4043 wClipIconPaint(scr->clip_icon);
4044 done = 0;
4045 while(!done) {
4046 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
4047 |ButtonPressMask, &ev);
4048 switch (ev.type) {
4049 case Expose:
4050 WMHandleEvent(&ev);
4051 break;
4053 case MotionNotify:
4054 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
4055 if (new_dir != direction) {
4056 direction = new_dir;
4057 clip->lclip_button_pushed = direction==CLIP_REWIND;
4058 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4059 wClipIconPaint(scr->clip_icon);
4061 break;
4063 case ButtonPress:
4064 break;
4066 case ButtonRelease:
4067 if (ev.xbutton.button == event->xbutton.button)
4068 done = 1;
4072 clip->lclip_button_pushed = 0;
4073 clip->rclip_button_pushed = 0;
4075 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
4077 if (direction == CLIP_FORWARD) {
4078 if (scr->current_workspace < scr->workspace_count-1)
4079 wWorkspaceChange(scr, scr->current_workspace+1);
4080 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
4081 wWorkspaceChange(scr, scr->current_workspace+1);
4082 else if (wPreferences.ws_cycle)
4083 wWorkspaceChange(scr, 0);
4085 else if (direction == CLIP_REWIND) {
4086 if (scr->current_workspace > 0)
4087 wWorkspaceChange(scr, scr->current_workspace-1);
4088 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
4089 wWorkspaceChange(scr, scr->workspace_count-1);
4092 wClipIconPaint(scr->clip_icon);
4096 static void
4097 iconMouseDown(WObjDescriptor *desc, XEvent *event)
4099 WAppIcon *aicon = desc->parent;
4100 WDock *dock = aicon->dock;
4101 WScreen *scr = aicon->icon->core->screen_ptr;
4103 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
4104 return;
4106 scr->last_dock = dock;
4108 if (dock->menu->flags.mapped)
4109 wMenuUnmap(dock->menu);
4111 if (IsDoubleClick(scr, event)) {
4112 /* double-click was not in the main clip icon */
4113 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
4114 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
4115 iconDblClick(desc, event);
4116 return;
4120 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
4121 XUnmapWindow(dpy, scr->clip_balloon);
4122 scr->flags.clip_balloon_mapped = 0;
4125 #ifdef DEBUG
4126 puts("handling dock");
4127 #endif
4128 if (event->xbutton.button == Button1) {
4129 if (event->xbutton.state & MOD_MASK)
4130 wDockLower(dock);
4131 else
4132 wDockRaise(dock);
4134 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
4135 dock->type!=WM_DOCK) {
4136 wIconSelect(aicon->icon);
4137 return;
4140 if (aicon->yindex==0 && aicon->xindex==0) {
4141 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
4142 && dock->type==WM_CLIP)
4143 handleClipChangeWorkspace(scr, event);
4144 else
4145 handleDockMove(dock, aicon, event);
4146 } else
4147 handleIconMove(dock, aicon, event);
4149 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4150 aicon==scr->clip_icon) {
4151 if (!scr->clip_ws_menu) {
4152 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
4154 if (scr->clip_ws_menu) {
4155 WMenu *wsMenu = scr->clip_ws_menu;
4156 int xpos;
4158 wWorkspaceMenuUpdate(scr, wsMenu);
4160 xpos = event->xbutton.x_root - wsMenu->frame->core->width/2 - 1;
4161 if (xpos < 0) {
4162 xpos = 0;
4163 } else if (xpos + wsMenu->frame->core->width > scr->scr_width-2) {
4164 xpos = scr->scr_width - wsMenu->frame->core->width - 4;
4166 wMenuMapAt(wsMenu, xpos, event->xbutton.y_root+2, False);
4168 desc = &wsMenu->menu->descriptor;
4169 event->xany.send_event = True;
4170 (*desc->handle_mousedown)(desc, event);
4172 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4173 (event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon) {
4174 wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
4175 } else if (event->xbutton.button == Button3) {
4176 if (event->xbutton.send_event &&
4177 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
4178 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
4179 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
4180 wwarning("pointer grab failed for dockicon menu");
4181 return;
4184 openDockMenu(dock, aicon, event);
4185 } else if (event->xbutton.button == Button2) {
4186 WAppIcon *btn = desc->parent;
4188 if (!btn->launching &&
4189 (!btn->running || (event->xbutton.state & ControlMask))) {
4190 launchDockedApplication(btn, True);
4196 static void
4197 showClipBalloon(WDock *dock, int workspace)
4199 int w, h;
4200 int x, y;
4201 WScreen *scr = dock->screen_ptr;
4202 char *text;
4203 Window stack[2];
4205 scr->flags.clip_balloon_mapped = 1;
4206 XMapWindow(dpy, scr->clip_balloon);
4208 text = scr->workspaces[workspace]->name;
4210 w = WMWidthOfString(scr->clip_title_font, text, strlen(text));
4212 h = WMFontHeight(scr->clip_title_font);
4213 XResizeWindow(dpy, scr->clip_balloon, w, h);
4215 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
4216 y = dock->y_pos + ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
4218 if (x+w > scr->scr_width) {
4219 x = scr->scr_width - w;
4220 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
4221 y = dock->y_pos - h - 1;
4222 else
4223 y = dock->y_pos + ICON_SIZE;
4224 XRaiseWindow(dpy, scr->clip_balloon);
4225 } else {
4226 stack[0] = scr->clip_icon->icon->core->window;
4227 stack[1] = scr->clip_balloon;
4228 XRestackWindows(dpy, stack, 2);
4230 XMoveWindow(dpy, scr->clip_balloon, x, y);
4231 XClearWindow(dpy, scr->clip_balloon);
4232 WMDrawString(scr->wmscreen, scr->clip_balloon,
4233 scr->clip_title_color[CLIP_NORMAL],
4234 scr->clip_title_font,
4235 0, 0, text, strlen(text));
4239 static void
4240 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
4242 WAppIcon *btn = (WAppIcon*)desc->parent;
4243 WDock *dock;
4244 WScreen *scr;
4246 assert(event->type==EnterNotify);
4248 if(desc->parent_type!=WCLASS_DOCK_ICON)
4249 return;
4251 scr = btn->icon->core->screen_ptr;
4252 if (!btn->omnipresent)
4253 dock = btn->dock;
4254 else
4255 dock = scr->workspaces[scr->current_workspace]->clip;
4257 if (!dock || dock->type!=WM_CLIP)
4258 return;
4260 /* The auto raise/lower code */
4261 if (dock->auto_lower_magic) {
4262 WMDeleteTimerHandler(dock->auto_lower_magic);
4263 dock->auto_lower_magic = NULL;
4265 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
4266 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
4267 clipAutoRaise,
4268 (void *)dock);
4271 /* The auto expand/collapse code */
4272 if (dock->auto_collapse_magic) {
4273 WMDeleteTimerHandler(dock->auto_collapse_magic);
4274 dock->auto_collapse_magic = NULL;
4276 if (dock->auto_collapse && !dock->auto_expand_magic) {
4277 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
4278 clipAutoExpand,
4279 (void *)dock);
4282 if (btn->xindex == 0 && btn->yindex == 0)
4283 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4284 else {
4285 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4286 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
4287 dock->screen_ptr->flags.clip_balloon_mapped = 0;
4293 static void
4294 clipLeave(WDock *dock)
4296 XEvent event;
4297 WObjDescriptor *desc = NULL;
4299 if (!dock || dock->type!=WM_CLIP)
4300 return;
4302 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
4303 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
4304 (XPointer *)&desc)!=XCNOENT
4305 && desc && desc->parent_type==WCLASS_DOCK_ICON
4306 && ((WAppIcon*)desc->parent)->dock
4307 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
4308 /* We didn't left the Clip yet */
4309 XPutBackEvent(dpy, &event);
4310 return;
4313 XPutBackEvent(dpy, &event);
4314 } else {
4315 /* We entered a withdrawn window, so we're still in Clip */
4316 return;
4319 if (dock->auto_raise_magic) {
4320 WMDeleteTimerHandler(dock->auto_raise_magic);
4321 dock->auto_raise_magic = NULL;
4323 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
4324 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
4325 clipAutoLower,
4326 (void *)dock);
4329 if (dock->auto_expand_magic) {
4330 WMDeleteTimerHandler(dock->auto_expand_magic);
4331 dock->auto_expand_magic = NULL;
4333 if (dock->auto_collapse && !dock->auto_collapse_magic) {
4334 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
4335 clipAutoCollapse,
4336 (void *)dock);
4341 static void
4342 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
4344 WAppIcon *btn = (WAppIcon*)desc->parent;
4346 assert(event->type==LeaveNotify);
4348 if(desc->parent_type!=WCLASS_DOCK_ICON)
4349 return;
4351 clipLeave(btn->dock);
4355 static void
4356 clipAutoCollapse(void *cdata)
4358 WDock *dock = (WDock *)cdata;
4360 if (dock->type!=WM_CLIP)
4361 return;
4363 if (dock->auto_collapse) {
4364 dock->collapsed = 1;
4365 wDockHideIcons(dock);
4367 dock->auto_collapse_magic = NULL;
4371 static void
4372 clipAutoExpand(void *cdata)
4374 WDock *dock = (WDock *)cdata;
4376 if (dock->type!=WM_CLIP)
4377 return;
4379 if (dock->auto_collapse) {
4380 dock->collapsed = 0;
4381 wDockShowIcons(dock);
4383 dock->auto_expand_magic = NULL;
4387 static void
4388 clipAutoLower(void *cdata)
4390 WDock *dock = (WDock *)cdata;
4392 if (dock->type!=WM_CLIP)
4393 return;
4395 if (dock->auto_raise_lower)
4396 wDockLower(dock);
4398 dock->auto_lower_magic = NULL;
4402 static void
4403 clipAutoRaise(void *cdata)
4405 WDock *dock = (WDock *)cdata;
4407 if (dock->type!=WM_CLIP)
4408 return;
4410 if (dock->auto_raise_lower)
4411 wDockRaise(dock);
4413 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4414 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4417 dock->auto_raise_magic = NULL;
4421 static Bool
4422 iconCanBeOmnipresent(WAppIcon *aicon)
4424 WScreen *scr = aicon->icon->core->screen_ptr;
4425 WDock *clip;
4426 WAppIcon *btn;
4427 int i, j;
4429 for (i=0; i<scr->workspace_count; i++) {
4430 clip = scr->workspaces[i]->clip;
4432 if (clip == aicon->dock)
4433 continue;
4435 if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
4436 return False; /* Clip is full in some workspace */
4438 for (j=0; j<clip->max_icons; j++) {
4439 btn = clip->icon_array[j];
4440 if(btn && btn->xindex==aicon->xindex && btn->yindex==aicon->yindex)
4441 return False;
4445 return True;
4450 wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
4452 WScreen *scr = aicon->icon->core->screen_ptr;
4453 WAppIconChain *new_entry, *tmp, *tmp1;
4454 int status = WO_SUCCESS;
4456 if ((scr->dock && aicon->dock==scr->dock) || aicon==scr->clip_icon) {
4457 return WO_NOT_APPLICABLE;
4460 if (aicon->omnipresent == omnipresent)
4461 return WO_SUCCESS;
4463 if (omnipresent) {
4464 if (iconCanBeOmnipresent(aicon)) {
4465 aicon->omnipresent = 1;
4466 new_entry = wmalloc(sizeof(WAppIconChain));
4467 new_entry->aicon = aicon;
4468 new_entry->next = scr->global_icons;
4469 scr->global_icons = new_entry;
4470 scr->global_icon_count++;
4471 } else {
4472 aicon->omnipresent = 0;
4473 status = WO_FAILED;
4475 } else {
4476 aicon->omnipresent = 0;
4477 if (aicon == scr->global_icons->aicon) {
4478 tmp = scr->global_icons->next;
4479 wfree(scr->global_icons);
4480 scr->global_icons = tmp;
4481 scr->global_icon_count--;
4482 } else {
4483 tmp = scr->global_icons;
4484 while (tmp->next) {
4485 if (tmp->next->aicon == aicon) {
4486 tmp1 = tmp->next->next;
4487 wfree(tmp->next);
4488 tmp->next = tmp1;
4489 scr->global_icon_count--;
4490 break;
4492 tmp = tmp->next;
4497 wAppIconPaint(aicon);
4499 return status;