test log scripts
[wmaker-crm.git] / src / dock.c
blobdaed30d2de05269b8a3267ac82f59b6aeccdad45
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 non-running dock icon? */
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];
363 color = scr->clip_title_color[CLIP_NORMAL];
365 XSetForeground(dpy, gc, WMColorPixel(color));
367 if (rpushed) {
368 p[0].x = tp+1;
369 p[0].y = 1;
370 p[1].x = ICON_SIZE-2;
371 p[1].y = 1;
372 p[2].x = ICON_SIZE-2;
373 p[2].y = pt-1;
374 } else if (lpushed) {
375 p[0].x = 1;
376 p[0].y = tp;
377 p[1].x = pt;
378 p[1].y = ICON_SIZE-2;
379 p[2].x = 1;
380 p[2].y = ICON_SIZE-2;
382 if (lpushed || rpushed) {
383 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
384 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
385 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
387 #ifdef GRADIENT_CLIP_ARROW
388 if (!collapsed) {
389 XSetFillStyle(dpy, scr->copy_gc, FillTiled);
390 XSetTile(dpy, scr->copy_gc, scr->clip_arrow_gradient);
391 XSetClipMask(dpy, scr->copy_gc, None);
392 gc = scr->copy_gc;
394 #endif /* GRADIENT_CLIP_ARROW */
396 /* top right arrow */
397 p[0].x = p[3].x = ICON_SIZE-5-as;
398 p[0].y = p[3].y = 5;
399 p[1].x = ICON_SIZE-6;
400 p[1].y = 5;
401 p[2].x = ICON_SIZE-6;
402 p[2].y = 4+as;
403 if (rpushed) {
404 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
405 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
406 } else {
407 #ifdef GRADIENT_CLIP_ARROW
408 if (!collapsed)
409 XSetTSOrigin(dpy, gc, ICON_SIZE-6-as, 5);
410 #endif
411 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
412 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
415 /* bottom left arrow */
416 p[0].x = p[3].x = 5;
417 p[0].y = p[3].y = ICON_SIZE-5-as;
418 p[1].x = 5;
419 p[1].y = ICON_SIZE-6;
420 p[2].x = 4+as;
421 p[2].y = ICON_SIZE-6;
422 if (lpushed) {
423 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
424 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
425 } else {
426 #ifdef GRADIENT_CLIP_ARROW
427 if (!collapsed)
428 XSetTSOrigin(dpy, gc, 5, ICON_SIZE-6-as);
429 #endif
430 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
431 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
433 #ifdef GRADIENT_CLIP_ARROW
434 if (!collapsed)
435 XSetFillStyle(dpy, scr->copy_gc, FillSolid);
436 #endif
440 RImage*
441 wClipMakeTile(WScreen *scr, RImage *normalTile)
443 RImage *tile = RCloneImage(normalTile);
444 RColor black;
445 RColor dark;
446 RColor light;
447 int pt, tp;
448 int as;
450 pt = CLIP_BUTTON_SIZE*wPreferences.icon_size/64;
451 tp = wPreferences.icon_size-1 - pt;
452 as = pt - 15;
454 black.alpha = 255;
455 black.red = black.green = black.blue = 0;
457 dark.alpha = 0;
458 dark.red = dark.green = dark.blue = 60;
460 light.alpha = 0;
461 light.red = light.green = light.blue = 80;
464 /* top right */
465 ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size-2,
466 pt-1, &dark);
467 RDrawLine(tile, tp-1, 0, wPreferences.icon_size-1, pt+1, &black);
468 ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size-3,
469 pt, &light);
471 /* arrow bevel */
472 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 7 - as, 4,
473 ICON_SIZE - 5, 4, &dark);
474 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 6 - as, 5,
475 ICON_SIZE - 5, 6 + as, &dark);
476 ROperateLine(tile, RAddOperation, ICON_SIZE - 5, 4, ICON_SIZE - 5, 6 + as,
477 &light);
479 /* bottom left */
480 ROperateLine(tile, RAddOperation, 2, tp+2, pt-2,
481 wPreferences.icon_size-3, &dark);
482 RDrawLine(tile, 0, tp-1, pt+1, wPreferences.icon_size-1, &black);
483 ROperateLine(tile, RSubtractOperation, 0, tp-2, pt+1,
484 wPreferences.icon_size-2, &light);
486 /* arrow bevel */
487 ROperateLine(tile, RSubtractOperation, 4, ICON_SIZE - 7 - as, 4,
488 ICON_SIZE - 5, &dark);
489 ROperateLine(tile, RSubtractOperation, 5, ICON_SIZE - 6 - as,
490 6 + as, ICON_SIZE - 5, &dark);
491 ROperateLine(tile, RAddOperation, 4, ICON_SIZE - 5, 6 + as, ICON_SIZE - 5,
492 &light);
494 return tile;
498 static void
499 omnipresentCallback(WMenu *menu, WMenuEntry *entry)
501 WAppIcon *clickedIcon = entry->clientdata;
502 WAppIcon *aicon;
503 WDock *dock;
504 WMArray *selectedIcons;
505 WMArrayIterator iter;
506 int failed;
508 assert(entry->clientdata!=NULL);
510 dock = clickedIcon->dock;
512 selectedIcons = getSelected(dock);
514 if (!WMGetArrayItemCount(selectedIcons))
515 WMAddToArray(selectedIcons, clickedIcon);
517 failed = 0;
518 WM_ITERATE_ARRAY(selectedIcons, aicon, iter) {
519 if (wClipMakeIconOmnipresent(aicon, !aicon->omnipresent) == WO_FAILED)
520 failed++;
521 else if (aicon->icon->selected)
522 wIconSelect(aicon->icon);
524 WMFreeArray(selectedIcons);
526 if (failed > 1) {
527 wMessageDialog(dock->screen_ptr, _("Warning"),
528 _("Some icons cannot be made omnipresent. "
529 "Please make sure that no other icon is "
530 "docked in the same positions on the other "
531 "workspaces and the Clip is not full in "
532 "some workspace."),
533 _("OK"), NULL, NULL);
534 } else if (failed == 1) {
535 wMessageDialog(dock->screen_ptr, _("Warning"),
536 _("Icon cannot be made omnipresent. "
537 "Please make sure that no other icon is "
538 "docked in the same position on the other "
539 "workspaces and the Clip is not full in "
540 "some workspace."),
541 _("OK"), NULL, NULL);
546 static void
547 removeIconsCallback(WMenu *menu, WMenuEntry *entry)
549 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
550 WDock *dock;
551 WAppIcon *aicon;
552 WMArray *selectedIcons;
553 int keepit;
554 WMArrayIterator it;
556 assert(clickedIcon!=NULL);
558 dock = clickedIcon->dock;
560 selectedIcons = getSelected(dock);
562 if (WMGetArrayItemCount(selectedIcons)) {
563 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
564 _("All selected icons will be removed!"),
565 _("OK"), _("Cancel"), NULL)!=WAPRDefault) {
566 WMFreeArray(selectedIcons);
567 return;
569 } else {
570 if (clickedIcon->xindex==0 && clickedIcon->yindex==0) {
571 WMFreeArray(selectedIcons);
572 return;
574 WMAddToArray(selectedIcons, clickedIcon);
577 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
578 keepit = aicon->running && wApplicationOf(aicon->main_window);
579 wDockDetach(dock, aicon);
580 if (keepit) {
581 /* XXX: can: aicon->icon == NULL ? */
582 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, wGetHeadForWindow(aicon->icon->owner));
583 XMoveWindow(dpy, aicon->icon->core->window,
584 aicon->x_pos, aicon->y_pos);
585 if (!dock->mapped || dock->collapsed)
586 XMapWindow(dpy, aicon->icon->core->window);
589 WMFreeArray(selectedIcons);
591 if (wPreferences.auto_arrange_icons)
592 wArrangeIcons(dock->screen_ptr, True);
596 static void
597 keepIconsCallback(WMenu *menu, WMenuEntry *entry)
599 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
600 WDock *dock;
601 WAppIcon *aicon;
602 WMArray *selectedIcons;
603 WMArrayIterator it;
605 assert(clickedIcon!=NULL);
606 dock = clickedIcon->dock;
608 selectedIcons = getSelected(dock);
610 if (!WMGetArrayItemCount(selectedIcons)
611 && clickedIcon!=dock->screen_ptr->clip_icon) {
612 char *command = NULL;
614 if (!clickedIcon->command && !clickedIcon->editing) {
615 clickedIcon->editing = 1;
616 if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
617 _("Type the command used to launch the application"),
618 &command)) {
619 if (command && (command[0]==0 ||
620 (command[0]=='-' && command[1]==0))) {
621 wfree(command);
622 command = NULL;
624 clickedIcon->command = command;
625 clickedIcon->editing = 0;
626 } else {
627 clickedIcon->editing = 0;
628 if (command)
629 wfree(command);
630 WMFreeArray(selectedIcons);
631 return;
635 WMAddToArray(selectedIcons, clickedIcon);
638 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
639 if (aicon->icon->selected)
640 wIconSelect(aicon->icon);
641 if (aicon && aicon->attracted && aicon->command) {
642 aicon->attracted = 0;
643 if (aicon->icon->shadowed) {
644 aicon->icon->shadowed = 0;
645 aicon->icon->force_paint = 1;
646 wAppIconPaint(aicon);
650 WMFreeArray(selectedIcons);
656 static void
657 toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
659 WDock *dock = (WDock*)entry->clientdata;
661 assert(entry->clientdata!=NULL);
663 dock->attract_icons = !dock->attract_icons;
664 /*if (!dock->attract_icons)
665 dock->keep_attracted = 0;*/
667 entry->flags.indicator_on = dock->attract_icons;
669 wMenuPaint(menu);
673 static void
674 selectCallback(WMenu *menu, WMenuEntry *entry)
676 WAppIcon *icon = (WAppIcon*)entry->clientdata;
678 assert(icon!=NULL);
680 wIconSelect(icon->icon);
682 wMenuPaint(menu);
686 static void
687 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
689 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
690 WDock *clip;
691 WAppIcon *aicon;
692 int x, y, x_pos, y_pos;
694 assert(entry->clientdata!=NULL);
695 clip = clickedIcon->dock;
697 aicon = clip->screen_ptr->app_icon_list;
699 while (aicon) {
700 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
701 x_pos = clip->x_pos + x*ICON_SIZE;
702 y_pos = clip->y_pos + y*ICON_SIZE;
703 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
704 #ifdef ANIMATIONS
705 if (wPreferences.no_animations) {
706 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
707 } else {
708 SlideWindow(aicon->icon->core->window,
709 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
711 #else
712 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
713 #endif /* ANIMATIONS */
715 aicon->attracted = 1;
716 if (!aicon->icon->shadowed) {
717 aicon->icon->shadowed = 1;
718 aicon->icon->force_paint = 1;
719 /* We don't do an wAppIconPaint() here because it's in
720 * wDockAttachIcon(). -Dan
723 wDockAttachIcon(clip, aicon, x, y);
724 if (clip->collapsed || !clip->mapped)
725 XUnmapWindow(dpy, aicon->icon->core->window);
727 aicon = aicon->next;
732 static void
733 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
735 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
736 WDock *dock;
737 WMArray *selectedIcons;
738 WMArrayIterator iter;
739 WAppIcon *btn;
740 int i;
742 assert(clickedIcon!=NULL);
743 dock = clickedIcon->dock;
745 selectedIcons = getSelected(dock);
747 if (!WMGetArrayItemCount(selectedIcons)) {
748 for (i=1; i<dock->max_icons; i++) {
749 btn = dock->icon_array[i];
750 if (btn && !btn->icon->selected) {
751 wIconSelect(btn->icon);
754 } else {
755 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
756 wIconSelect(btn->icon);
759 WMFreeArray(selectedIcons);
761 wMenuPaint(menu);
765 static void
766 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
768 assert(entry->clientdata!=NULL);
770 toggleCollapsed(entry->clientdata);
772 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
774 wMenuPaint(menu);
778 static void
779 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
781 WDock *dock;
782 assert(entry->clientdata!=NULL);
784 dock = (WDock*) entry->clientdata;
786 dock->auto_collapse = !dock->auto_collapse;
788 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
790 wMenuPaint(menu);
794 static void
795 toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
797 WDock *dock;
798 assert(entry->clientdata!=NULL);
800 dock = (WDock*) entry->clientdata;
802 dock->auto_raise_lower = !dock->auto_raise_lower;
804 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_raise_lower;
806 wMenuPaint(menu);
810 static void
811 launchCallback(WMenu *menu, WMenuEntry *entry)
813 WAppIcon *btn = (WAppIcon*)entry->clientdata;
815 launchDockedApplication(btn, False);
819 static void
820 settingsCallback(WMenu *menu, WMenuEntry *entry)
822 WAppIcon *btn = (WAppIcon*)entry->clientdata;
824 if (btn->editing)
825 return;
826 ShowDockAppSettingsPanel(btn);
830 static void
831 hideCallback(WMenu *menu, WMenuEntry *entry)
833 WApplication *wapp;
834 WAppIcon *btn = (WAppIcon*)entry->clientdata;
836 wapp = wApplicationOf(btn->icon->owner->main_window);
838 if (wapp->flags.hidden) {
839 wWorkspaceChange(btn->icon->core->screen_ptr, wapp->last_workspace);
840 wUnhideApplication(wapp, False, False);
841 } else {
842 wHideApplication(wapp);
847 static void
848 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
850 WApplication *wapp;
851 WAppIcon *btn = (WAppIcon*)entry->clientdata;
853 wapp = wApplicationOf(btn->icon->owner->main_window);
855 wUnhideApplication(wapp, False, True);
859 WAppIcon*
860 mainIconCreate(WScreen *scr, int type)
862 WAppIcon *btn;
863 int x_pos;
865 if (type == WM_CLIP) {
866 if (scr->clip_icon)
867 return scr->clip_icon;
868 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
869 btn->icon->core->descriptor.handle_expose = clipIconExpose;
870 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
871 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
872 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
873 x_pos = 0;
874 } else {
875 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
876 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
879 btn->xindex = 0;
880 btn->yindex = 0;
882 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
883 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
884 btn->icon->core->descriptor.parent = btn;
885 /*ChangeStackingLevel(btn->icon->core, WMDockLevel);*/
886 XMapWindow(dpy, btn->icon->core->window);
887 btn->x_pos = x_pos;
888 btn->y_pos = 0;
889 btn->docked = 1;
890 if (type == WM_CLIP)
891 scr->clip_icon = btn;
893 return btn;
897 static void
898 switchWSCommand(WMenu *menu, WMenuEntry *entry)
900 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
901 WScreen *scr = icon->icon->core->screen_ptr;
902 WDock *src, *dest;
903 WMArray *selectedIcons;
904 int x, y;
906 if (entry->order == scr->current_workspace)
907 return;
908 src = icon->dock;
909 dest = scr->workspaces[entry->order]->clip;
911 selectedIcons = getSelected(src);
913 if (WMGetArrayItemCount(selectedIcons)) {
914 WMArrayIterator iter;
916 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
917 if (wDockFindFreeSlot(dest, &x, &y)) {
918 moveIconBetweenDocks(src, dest, btn, x, y);
919 XUnmapWindow(dpy, btn->icon->core->window);
922 } else if (icon != scr->clip_icon) {
923 if (wDockFindFreeSlot(dest, &x, &y)) {
924 moveIconBetweenDocks(src, dest, icon, x, y);
925 XUnmapWindow(dpy, icon->icon->core->window);
928 WMFreeArray(selectedIcons);
933 static void
934 launchDockedApplication(WAppIcon *btn, Bool withSelection)
936 WScreen *scr = btn->icon->core->screen_ptr;
938 if (!btn->launching &&
939 ((!withSelection && btn->command!=NULL) ||
940 (withSelection && btn->paste_command!=NULL))) {
941 if (!btn->forced_dock) {
942 btn->relaunching = btn->running;
943 btn->running = 1;
945 if (btn->wm_instance || btn->wm_class) {
946 WWindowAttributes attr;
947 memset(&attr, 0, sizeof(WWindowAttributes));
948 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class,
949 &attr, NULL, True);
951 if (!attr.no_appicon && !btn->buggy_app)
952 btn->launching = 1;
953 else
954 btn->running = 0;
956 btn->drop_launch = 0;
957 btn->paste_launch = withSelection;
958 scr->last_dock = btn->dock;
959 btn->pid = execCommand(btn,
960 withSelection ? btn->paste_command : btn->command,
961 NULL);
962 if (btn->pid>0) {
963 if (btn->buggy_app) {
964 /* give feedback that the app was launched */
965 btn->launching = 1;
966 dockIconPaint(btn);
967 btn->launching = 0;
968 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
969 } else {
970 dockIconPaint(btn);
972 } else {
973 wwarning(_("could not launch application %s\n"), btn->command);
974 btn->launching = 0;
975 if (!btn->relaunching)
976 btn->running = 0;
983 static void
984 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
986 WScreen *scr = menu->frame->screen_ptr;
987 char title[MAX_WORKSPACENAME_WIDTH+1];
988 int i;
990 if (!menu || !icon)
991 return;
993 for (i=0; i<scr->workspace_count; i++) {
994 if (i < menu->entry_no) {
995 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
996 wfree(menu->entries[i]->text);
997 strcpy(title, scr->workspaces[i]->name);
998 menu->entries[i]->text = wstrdup(title);
999 menu->flags.realized = 0;
1001 menu->entries[i]->clientdata = (void*)icon;
1002 } else {
1003 strcpy(title, scr->workspaces[i]->name);
1005 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
1007 menu->flags.realized = 0;
1009 if (i == scr->current_workspace) {
1010 wMenuSetEnabled(menu, i, False);
1011 } else {
1012 wMenuSetEnabled(menu, i, True);
1016 if (!menu->flags.realized)
1017 wMenuRealize(menu);
1021 static WMenu*
1022 makeWorkspaceMenu(WScreen *scr)
1024 WMenu *menu;
1026 menu = wMenuCreate(scr, NULL, False);
1027 if (!menu)
1028 wwarning(_("could not create workspace submenu for Clip menu"));
1030 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
1032 menu->flags.realized = 0;
1033 wMenuRealize(menu);
1035 return menu;
1039 static void
1040 updateClipOptionsMenu(WMenu *menu, WDock *dock)
1042 WMenuEntry *entry;
1043 int index = 0;
1045 if (!menu || !dock)
1046 return;
1048 /* keep on top */
1049 entry = menu->entries[index];
1050 entry->flags.indicator_on = !dock->lowered;
1051 entry->clientdata = dock;
1053 /* collapsed */
1054 entry = menu->entries[++index];
1055 entry->flags.indicator_on = dock->collapsed;
1056 entry->clientdata = dock;
1058 /* auto-collapse */
1059 entry = menu->entries[++index];
1060 entry->flags.indicator_on = dock->auto_collapse;
1061 entry->clientdata = dock;
1063 /* auto-raise/lower */
1064 entry = menu->entries[++index];
1065 entry->flags.indicator_on = dock->auto_raise_lower;
1066 entry->clientdata = dock;
1067 wMenuSetEnabled(menu, index, dock->lowered);
1069 /* attract icons */
1070 entry = menu->entries[++index];
1071 entry->flags.indicator_on = dock->attract_icons;
1072 entry->clientdata = dock;
1074 menu->flags.realized = 0;
1075 wMenuRealize(menu);
1079 static WMenu*
1080 makeClipOptionsMenu(WScreen *scr)
1082 WMenu *menu;
1083 WMenuEntry *entry;
1085 menu = wMenuCreate(scr, NULL, False);
1086 if (!menu) {
1087 wwarning(_("could not create options submenu for Clip menu"));
1088 return NULL;
1091 entry = wMenuAddCallback(menu, _("Keep on Top"),
1092 toggleLoweredCallback, NULL);
1093 entry->flags.indicator = 1;
1094 entry->flags.indicator_on = 1;
1095 entry->flags.indicator_type = MI_CHECK;
1097 entry = wMenuAddCallback(menu, _("Collapsed"),
1098 toggleCollapsedCallback, NULL);
1099 entry->flags.indicator = 1;
1100 entry->flags.indicator_on = 1;
1101 entry->flags.indicator_type = MI_CHECK;
1103 entry = wMenuAddCallback(menu, _("Autocollapse"),
1104 toggleAutoCollapseCallback, NULL);
1105 entry->flags.indicator = 1;
1106 entry->flags.indicator_on = 1;
1107 entry->flags.indicator_type = MI_CHECK;
1109 entry = wMenuAddCallback(menu, _("Autoraise"),
1110 toggleAutoRaiseLowerCallback, NULL);
1111 entry->flags.indicator = 1;
1112 entry->flags.indicator_on = 1;
1113 entry->flags.indicator_type = MI_CHECK;
1115 entry = wMenuAddCallback(menu, _("Autoattract Icons"),
1116 toggleAutoAttractCallback, NULL);
1117 entry->flags.indicator = 1;
1118 entry->flags.indicator_on = 1;
1119 entry->flags.indicator_type = MI_CHECK;
1121 menu->flags.realized = 0;
1122 wMenuRealize(menu);
1124 return menu;
1128 static WMenu*
1129 dockMenuCreate(WScreen *scr, int type)
1131 WMenu *menu;
1132 WMenuEntry *entry;
1134 if (type == WM_CLIP && scr->clip_menu)
1135 return scr->clip_menu;
1137 menu = wMenuCreate(scr, NULL, False);
1138 if (type != WM_CLIP) {
1139 entry = wMenuAddCallback(menu, _("Keep on Top"),
1140 toggleLoweredCallback, NULL);
1141 entry->flags.indicator = 1;
1142 entry->flags.indicator_on = 1;
1143 entry->flags.indicator_type = MI_CHECK;
1144 } else {
1145 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1146 scr->clip_options = makeClipOptionsMenu(scr);
1147 if (scr->clip_options)
1148 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1150 entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback,
1151 NULL);
1152 wfree(entry->text);
1153 entry->text = _("Rename Workspace");
1155 entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
1156 entry->flags.indicator = 1;
1157 entry->flags.indicator_on = 1;
1158 entry->flags.indicator_type = MI_CHECK;
1160 entry = wMenuAddCallback(menu, _("Select All Icons"),
1161 selectIconsCallback, NULL);
1162 wfree(entry->text);
1163 entry->text = _("Select All Icons");
1165 entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
1166 wfree(entry->text);
1167 entry->text = _("Keep Icon");
1169 entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
1170 wfree(entry->text);
1171 entry->text = _("Move Icon To");
1172 scr->clip_submenu = makeWorkspaceMenu(scr);
1173 if (scr->clip_submenu)
1174 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1176 entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback,
1177 NULL);
1178 wfree(entry->text);
1179 entry->text = _("Remove Icon");
1181 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1184 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1186 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1188 entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1189 wfree(entry->text);
1190 entry->text = _("Hide");
1192 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1194 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1196 if (type == WM_CLIP)
1197 scr->clip_menu = menu;
1199 return menu;
1203 WDock*
1204 wDockCreate(WScreen *scr, int type)
1206 WDock *dock;
1207 WAppIcon *btn;
1208 int icon_count;
1210 make_keys();
1212 dock = wmalloc(sizeof(WDock));
1213 memset(dock, 0, sizeof(WDock));
1215 if (type == WM_CLIP)
1216 icon_count = CLIP_MAX_ICONS;
1217 else
1218 icon_count = scr->scr_height/wPreferences.icon_size;
1220 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1221 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1223 dock->max_icons = icon_count;
1225 btn = mainIconCreate(scr, type);
1227 btn->dock = dock;
1229 dock->x_pos = btn->x_pos;
1230 dock->y_pos = btn->y_pos;
1231 dock->screen_ptr = scr;
1232 dock->type = type;
1233 dock->icon_count = 1;
1234 dock->on_right_side = 1;
1235 dock->collapsed = 0;
1236 dock->auto_collapse = 0;
1237 dock->auto_collapse_magic = NULL;
1238 dock->auto_raise_lower = 0;
1239 dock->auto_lower_magic = NULL;
1240 dock->auto_raise_magic = NULL;
1241 dock->attract_icons = 0;
1242 dock->lowered = 1;
1243 dock->icon_array[0] = btn;
1244 wRaiseFrame(btn->icon->core);
1245 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1247 /* create dock menu */
1248 dock->menu = dockMenuCreate(scr, type);
1250 return dock;
1254 void
1255 wDockDestroy(WDock *dock)
1257 int i;
1258 WAppIcon *aicon;
1260 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1261 aicon = dock->icon_array[i];
1262 if (aicon) {
1263 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1264 wDockDetach(dock, aicon);
1265 if (keepit) {
1266 /* XXX: can: aicon->icon == NULL ? */
1267 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, wGetHeadForWindow(aicon->icon->owner));
1268 XMoveWindow(dpy, aicon->icon->core->window,
1269 aicon->x_pos, aicon->y_pos);
1270 if (!dock->mapped || dock->collapsed)
1271 XMapWindow(dpy, aicon->icon->core->window);
1275 if (wPreferences.auto_arrange_icons)
1276 wArrangeIcons(dock->screen_ptr, True);
1277 wfree(dock->icon_array);
1278 if (dock->menu && dock->type!=WM_CLIP)
1279 wMenuDestroy(dock->menu, True);
1280 if (dock->screen_ptr->last_dock == dock)
1281 dock->screen_ptr->last_dock = NULL;
1282 wfree(dock);
1286 void
1287 wClipIconPaint(WAppIcon *aicon)
1289 WScreen *scr = aicon->icon->core->screen_ptr;
1290 WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1291 WMColor *color;
1292 Window win = aicon->icon->core->window;
1293 int length, nlength;
1294 char *ws_name, ws_number[10];
1295 int ty, tx;
1297 wIconPaint(aicon->icon);
1299 length = strlen(workspace->name);
1300 ws_name = wmalloc(length + 1);
1301 snprintf(ws_name, length+1, "%s", workspace->name);
1302 snprintf(ws_number, sizeof(ws_number), "%i", scr->current_workspace + 1);
1303 nlength = strlen(ws_number);
1305 if (!workspace->clip->collapsed)
1306 color = scr->clip_title_color[CLIP_NORMAL];
1307 else
1308 color = scr->clip_title_color[CLIP_COLLAPSED];
1310 ty = ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
1312 tx = CLIP_BUTTON_SIZE*ICON_SIZE/64;
1314 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1315 ty, ws_name, length);
1316 /*WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, 4,
1317 2, ws_name, length);*/
1319 tx = (ICON_SIZE/2 - WMWidthOfString(scr->clip_title_font, ws_number,
1320 nlength))/2;
1322 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1323 2, ws_number, nlength);
1325 wfree(ws_name);
1327 if (aicon->launching) {
1328 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1329 0, 0, wPreferences.icon_size, wPreferences.icon_size);
1331 paintClipButtons(aicon, aicon->dock->lclip_button_pushed,
1332 aicon->dock->rclip_button_pushed);
1336 static void
1337 clipIconExpose(WObjDescriptor *desc, XEvent *event)
1339 wClipIconPaint(desc->parent);
1343 static void
1344 dockIconPaint(WAppIcon *btn)
1346 if (btn == btn->icon->core->screen_ptr->clip_icon)
1347 wClipIconPaint(btn);
1348 else
1349 wAppIconPaint(btn);
1353 static WMPropList*
1354 make_icon_state(WAppIcon *btn)
1356 WMPropList *node = NULL;
1357 WMPropList *command, *autolaunch, *lock, *name, *forced, *host;
1358 WMPropList *position, *buggy, *omnipresent;
1359 char *tmp;
1360 char buffer[64];
1362 if (btn) {
1363 if (!btn->command)
1364 command = WMCreatePLString("-");
1365 else
1366 command = WMCreatePLString(btn->command);
1368 autolaunch = btn->auto_launch ? dYes : dNo;
1370 lock = btn->lock ? dYes : dNo;
1372 tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
1374 name = WMCreatePLString(tmp);
1376 wfree(tmp);
1378 forced = btn->forced_dock ? dYes : dNo;
1380 buggy = btn->buggy_app ? dYes : dNo;
1382 if (btn == btn->icon->core->screen_ptr->clip_icon)
1383 snprintf(buffer, sizeof(buffer), "%i,%i", btn->x_pos, btn->y_pos);
1384 else
1385 snprintf(buffer, sizeof(buffer), "%hi,%hi", btn->xindex, btn->yindex);
1386 position = WMCreatePLString(buffer);
1388 node = WMCreatePLDictionary(dCommand, command,
1389 dName, name,
1390 dAutoLaunch, autolaunch,
1391 dLock, lock,
1392 dForced, forced,
1393 dBuggyApplication, buggy,
1394 dPosition, position,
1395 NULL);
1396 WMReleasePropList(command);
1397 WMReleasePropList(name);
1398 WMReleasePropList(position);
1400 omnipresent = btn->omnipresent ? dYes : dNo;
1401 if (btn->dock != btn->icon->core->screen_ptr->dock &&
1402 (btn->xindex != 0 || btn->yindex != 0))
1403 WMPutInPLDictionary(node, dOmnipresent, omnipresent);
1405 #ifdef OFFIX_DND
1406 if (btn->dnd_command) {
1407 command = WMCreatePLString(btn->dnd_command);
1408 WMPutInPLDictionary(node, dDropCommand, command);
1409 WMReleasePropList(command);
1411 #endif /* OFFIX_DND */
1413 if (btn->paste_command) {
1414 command = WMCreatePLString(btn->paste_command);
1415 WMPutInPLDictionary(node, dPasteCommand, command);
1416 WMReleasePropList(command);
1419 if (btn->client_machine && btn->remote_start) {
1420 host = WMCreatePLString(btn->client_machine);
1421 WMPutInPLDictionary(node, dHost, host);
1422 WMReleasePropList(host);
1426 return node;
1430 static WMPropList*
1431 dockSaveState(WDock *dock)
1433 int i;
1434 WMPropList *icon_info;
1435 WMPropList *list=NULL, *dock_state=NULL;
1436 WMPropList *value, *key;
1437 char buffer[256];
1439 list = WMCreatePLArray(NULL);
1441 for (i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
1442 WAppIcon *btn = dock->icon_array[i];
1444 if (!btn || btn->attracted)
1445 continue;
1447 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1448 WMAddToPLArray(list, icon_info);
1449 WMReleasePropList(icon_info);
1453 dock_state = WMCreatePLDictionary(dApplications, list,
1454 NULL);
1456 if (dock->type == WM_DOCK) {
1457 snprintf(buffer, sizeof(buffer), "Applications%i", dock->screen_ptr->scr_height);
1458 key = WMCreatePLString(buffer);
1459 WMPutInPLDictionary(dock_state, key, list);
1460 WMReleasePropList(key);
1463 snprintf(buffer, sizeof(buffer), "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0),
1464 dock->y_pos);
1465 value = WMCreatePLString(buffer);
1466 WMPutInPLDictionary(dock_state, dPosition, value);
1467 WMReleasePropList(value);
1469 WMReleasePropList(list);
1472 value = (dock->lowered ? dYes : dNo);
1473 WMPutInPLDictionary(dock_state, dLowered, value);
1475 if (dock->type == WM_CLIP) {
1476 value = (dock->collapsed ? dYes : dNo);
1477 WMPutInPLDictionary(dock_state, dCollapsed, value);
1479 value = (dock->auto_collapse ? dYes : dNo);
1480 WMPutInPLDictionary(dock_state, dAutoCollapse, value);
1482 value = (dock->auto_raise_lower ? dYes : dNo);
1483 WMPutInPLDictionary(dock_state, dAutoRaiseLower, value);
1485 value = (dock->attract_icons ? dYes : dNo);
1486 WMPutInPLDictionary(dock_state, dAutoAttractIcons, value);
1489 return dock_state;
1493 void
1494 wDockSaveState(WScreen *scr, WMPropList *old_state)
1496 WMPropList *dock_state;
1497 WMPropList *keys;
1499 dock_state = dockSaveState(scr->dock);
1502 * Copy saved states of docks with different sizes.
1504 if (old_state) {
1505 int i;
1506 WMPropList *tmp;
1508 keys = WMGetPLDictionaryKeys(old_state);
1509 for (i = 0; i < WMGetPropListItemCount(keys); i++) {
1510 tmp = WMGetFromPLArray(keys, i);
1512 if (strncasecmp(WMGetFromPLString(tmp), "applications", 12) == 0
1513 && !WMGetFromPLDictionary(dock_state, tmp)) {
1515 WMPutInPLDictionary(dock_state,
1516 tmp,
1517 WMGetFromPLDictionary(old_state, tmp));
1520 WMReleasePropList(keys);
1524 WMPutInPLDictionary(scr->session_state, dDock, dock_state);
1526 WMReleasePropList(dock_state);
1530 void
1531 wClipSaveState(WScreen *scr)
1533 WMPropList *clip_state;
1535 clip_state = make_icon_state(scr->clip_icon);
1537 WMPutInPLDictionary(scr->session_state, dClip, clip_state);
1539 WMReleasePropList(clip_state);
1543 WMPropList*
1544 wClipSaveWorkspaceState(WScreen *scr, int workspace)
1546 return dockSaveState(scr->workspaces[workspace]->clip);
1550 static Bool
1551 getBooleanDockValue(WMPropList *value, WMPropList *key)
1553 if (value) {
1554 if (WMIsPLString(value)) {
1555 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1556 return True;
1557 } else {
1558 wwarning(_("bad value in docked icon state info %s"),
1559 WMGetFromPLString(key));
1562 return False;
1566 static WAppIcon*
1567 restore_icon_state(WScreen *scr, WMPropList *info, int type, int index)
1569 WAppIcon *aicon;
1570 WMPropList *cmd, *value;
1573 cmd = WMGetFromPLDictionary(info, dCommand);
1574 if (!cmd || !WMIsPLString(cmd)) {
1575 return NULL;
1578 /* parse window name */
1579 value = WMGetFromPLDictionary(info, dName);
1580 if (!value)
1581 return NULL;
1584 char *wclass, *winstance;
1585 char *command;
1587 ParseWindowName(value, &winstance, &wclass, "dock");
1589 if (!winstance && !wclass) {
1590 return NULL;
1593 /* get commands */
1595 if (cmd)
1596 command = wstrdup(WMGetFromPLString(cmd));
1597 else
1598 command = NULL;
1600 if (!command || strcmp(command, "-")==0) {
1601 if (command)
1602 wfree(command);
1603 if (wclass)
1604 wfree(wclass);
1605 if (winstance)
1606 wfree(winstance);
1608 return NULL;
1611 aicon = wAppIconCreateForDock(scr, command, winstance, wclass,
1612 TILE_NORMAL);
1613 if (wclass)
1614 wfree(wclass);
1615 if (winstance)
1616 wfree(winstance);
1617 if (command)
1618 wfree(command);
1621 aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1622 if (type == WM_CLIP) {
1623 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1624 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1626 aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1627 aicon->icon->core->descriptor.parent = aicon;
1630 #ifdef OFFIX_DND
1631 cmd = WMGetFromPLDictionary(info, dDropCommand);
1632 if (cmd)
1633 aicon->dnd_command = wstrdup(WMGetFromPLString(cmd));
1634 #endif
1636 cmd = WMGetFromPLDictionary(info, dPasteCommand);
1637 if (cmd)
1638 aicon->paste_command = wstrdup(WMGetFromPLString(cmd));
1640 /* check auto launch */
1641 value = WMGetFromPLDictionary(info, dAutoLaunch);
1643 aicon->auto_launch = getBooleanDockValue(value, dAutoLaunch);
1645 /* check lock */
1646 value = WMGetFromPLDictionary(info, dLock);
1648 aicon->lock = getBooleanDockValue(value, dLock);
1650 /* check if it wasn't normally docked */
1651 value = WMGetFromPLDictionary(info, dForced);
1653 aicon->forced_dock = getBooleanDockValue(value, dForced);
1655 /* check if we can rely on the stuff in the app */
1656 value = WMGetFromPLDictionary(info, dBuggyApplication);
1658 aicon->buggy_app = getBooleanDockValue(value, dBuggyApplication);
1660 /* get position in the dock */
1661 value = WMGetFromPLDictionary(info, dPosition);
1662 if (value && WMIsPLString(value)) {
1663 if (sscanf(WMGetFromPLString(value), "%hi,%hi", &aicon->xindex,
1664 &aicon->yindex)!=2)
1665 wwarning(_("bad value in docked icon state info %s"),
1666 WMGetFromPLString(dPosition));
1668 /* check position sanity */
1669 /* incomplete section! */
1670 if (type == WM_DOCK) {
1671 aicon->xindex = 0;
1672 if (aicon->yindex < 0)
1673 wwarning(_("bad value in docked icon position %i,%i"),
1674 aicon->xindex, aicon->yindex);
1676 } else {
1677 aicon->yindex = index;
1678 aicon->xindex = 0;
1681 /* check if icon is omnipresent */
1682 value = WMGetFromPLDictionary(info, dOmnipresent);
1684 aicon->omnipresent = getBooleanDockValue(value, dOmnipresent);
1686 aicon->running = 0;
1687 aicon->docked = 1;
1689 return aicon;
1693 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1696 WAppIcon*
1697 wClipRestoreState(WScreen *scr, WMPropList *clip_state)
1699 WAppIcon *icon;
1700 WMPropList *value;
1703 icon = mainIconCreate(scr, WM_CLIP);
1705 if (!clip_state)
1706 return icon;
1707 else
1708 WMRetainPropList(clip_state);
1710 /* restore position */
1712 value = WMGetFromPLDictionary(clip_state, dPosition);
1714 if (value) {
1715 if (!WMIsPLString(value))
1716 COMPLAIN("Position");
1717 else {
1718 WMRect rect;
1719 int flags;
1721 if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos,
1722 &icon->y_pos)!=2)
1723 COMPLAIN("Position");
1725 /* check position sanity */
1726 rect.pos.x = icon->x_pos;
1727 rect.pos.y = icon->y_pos;
1728 rect.size.width = rect.size.height = ICON_SIZE;
1730 wGetRectPlacementInfo(scr, rect, &flags);
1731 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
1732 wScreenKeepInside(scr, &icon->x_pos, &icon->y_pos,
1733 ICON_SIZE, ICON_SIZE);
1737 #ifdef OFFIX_DND
1738 value = WMGetFromPLDictionary(clip_state, dDropCommand);
1739 if (value && WMIsPLString(value))
1740 icon->dnd_command = wstrdup(WMGetFromPLString(value));
1741 #endif
1743 value = WMGetFromPLDictionary(clip_state, dPasteCommand);
1744 if (value && WMIsPLString(value))
1745 icon->paste_command = wstrdup(WMGetFromPLString(value));
1747 WMReleasePropList(clip_state);
1749 return icon;
1753 WDock*
1754 wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type)
1756 WDock *dock;
1757 WMPropList *apps;
1758 WMPropList *value;
1759 WAppIcon *aicon, *old_top;
1760 int count, i;
1763 dock = wDockCreate(scr, type);
1765 if (!dock_state)
1766 return dock;
1768 if (dock_state)
1769 WMRetainPropList(dock_state);
1772 /* restore position */
1774 value = WMGetFromPLDictionary(dock_state, dPosition);
1776 if (value) {
1777 if (!WMIsPLString(value))
1778 COMPLAIN("Position");
1779 else {
1780 WMRect rect;
1781 int flags;
1783 if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos,
1784 &dock->y_pos)!=2)
1785 COMPLAIN("Position");
1787 /* check position sanity */
1788 rect.pos.x = dock->x_pos;
1789 rect.pos.y = dock->y_pos;
1790 rect.size.width = rect.size.height = ICON_SIZE;
1792 wGetRectPlacementInfo(scr, rect, &flags);
1793 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) {
1794 int x = dock->x_pos;
1795 wScreenKeepInside(scr, &x, &dock->y_pos, ICON_SIZE, ICON_SIZE);
1798 /* This is no more needed. ??? */
1799 if (type == WM_CLIP) {
1800 if (dock->x_pos < 0)
1801 dock->x_pos = 0;
1802 else if (dock->x_pos > scr->scr_width-ICON_SIZE)
1803 dock->x_pos = scr->scr_width-ICON_SIZE;
1804 } else {
1805 if (dock->x_pos >= 0) {
1806 dock->x_pos = DOCK_EXTRA_SPACE;
1807 dock->on_right_side = 0;
1808 } else {
1809 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1810 dock->on_right_side = 1;
1816 /* restore lowered/raised state */
1818 dock->lowered = 0;
1820 value = WMGetFromPLDictionary(dock_state, dLowered);
1822 if (value) {
1823 if (!WMIsPLString(value))
1824 COMPLAIN("Lowered");
1825 else {
1826 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1827 dock->lowered = 1;
1832 /* restore collapsed state */
1834 dock->collapsed = 0;
1836 value = WMGetFromPLDictionary(dock_state, dCollapsed);
1838 if (value) {
1839 if (!WMIsPLString(value))
1840 COMPLAIN("Collapsed");
1841 else {
1842 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1843 dock->collapsed = 1;
1848 /* restore auto-collapsed state */
1850 value = WMGetFromPLDictionary(dock_state, dAutoCollapse);
1852 if (value) {
1853 if (!WMIsPLString(value))
1854 COMPLAIN("AutoCollapse");
1855 else {
1856 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1857 dock->auto_collapse = 1;
1858 dock->collapsed = 1;
1864 /* restore auto-raise/lower state */
1866 value = WMGetFromPLDictionary(dock_state, dAutoRaiseLower);
1868 if (value) {
1869 if (!WMIsPLString(value))
1870 COMPLAIN("AutoRaiseLower");
1871 else {
1872 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1873 dock->auto_raise_lower = 1;
1878 /* restore attract icons state */
1880 dock->attract_icons = 0;
1882 value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons);
1884 if (value) {
1885 if (!WMIsPLString(value))
1886 COMPLAIN("AutoAttractIcons");
1887 else {
1888 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1889 dock->attract_icons = 1;
1894 /* application list */
1897 WMPropList *tmp;
1898 char buffer[64];
1901 * When saving, it saves the dock state in
1902 * Applications and Applicationsnnn
1904 * When loading, it will first try Applicationsnnn.
1905 * If it does not exist, use Applications as default.
1908 snprintf(buffer, sizeof(buffer), "Applications%i", scr->scr_height);
1910 tmp = WMCreatePLString(buffer);
1911 apps = WMGetFromPLDictionary(dock_state, tmp);
1912 WMReleasePropList(tmp);
1914 if (!apps) {
1915 apps = WMGetFromPLDictionary(dock_state, dApplications);
1919 if (!apps) {
1920 goto finish;
1923 count = WMGetPropListItemCount(apps);
1925 if (count==0)
1926 goto finish;
1928 old_top = dock->icon_array[0];
1930 /* dock->icon_count is set to 1 when dock is created.
1931 * Since Clip is already restored, we want to keep it so for clip,
1932 * but for dock we may change the default top tile, so we set it to 0.
1934 if (type == WM_DOCK)
1935 dock->icon_count = 0;
1937 for (i=0; i<count; i++) {
1938 if (dock->icon_count >= dock->max_icons) {
1939 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1940 break;
1943 value = WMGetFromPLArray(apps, i);
1944 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1946 dock->icon_array[dock->icon_count] = aicon;
1948 if (aicon) {
1949 aicon->dock = dock;
1950 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1951 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1953 if (dock->lowered)
1954 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1955 else
1956 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1958 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1959 0, 0);
1961 if (!dock->collapsed)
1962 XMapWindow(dpy, aicon->icon->core->window);
1963 wRaiseFrame(aicon->icon->core);
1965 dock->icon_count++;
1966 } else if (dock->icon_count==0 && type==WM_DOCK)
1967 dock->icon_count++;
1970 /* if the first icon is not defined, use the default */
1971 if (dock->icon_array[0]==NULL) {
1972 /* update default icon */
1973 old_top->x_pos = dock->x_pos;
1974 old_top->y_pos = dock->y_pos;
1975 if (dock->lowered)
1976 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1977 else
1978 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1979 dock->icon_array[0] = old_top;
1980 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1981 /* we don't need to increment dock->icon_count here because it was
1982 * incremented in the loop above.
1984 } else if (old_top!=dock->icon_array[0]) {
1985 if (old_top == scr->clip_icon)
1986 scr->clip_icon = dock->icon_array[0];
1987 wAppIconDestroy(old_top);
1990 finish:
1991 if (dock_state)
1992 WMReleasePropList(dock_state);
1994 return dock;
1999 void
2000 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
2002 if (btn && btn->command && !btn->running && !btn->launching) {
2004 btn->drop_launch = 0;
2005 btn->paste_launch = 0;
2007 btn->pid = execCommand(btn, btn->command, state);
2009 if (btn->pid>0) {
2010 if (!btn->forced_dock && !btn->buggy_app) {
2011 btn->launching = 1;
2012 dockIconPaint(btn);
2015 } else {
2016 wfree(state);
2021 void
2022 wDockDoAutoLaunch(WDock *dock, int workspace)
2024 WAppIcon *btn;
2025 WSavedState *state;
2026 int i;
2028 for (i = 0; i < dock->max_icons; i++) {
2029 btn = dock->icon_array[i];
2030 if (!btn || !btn->auto_launch)
2031 continue;
2033 state = wmalloc(sizeof(WSavedState));
2034 memset(state, 0, sizeof(WSavedState));
2035 state->workspace = workspace;
2036 /* TODO: this is klugy and is very difficult to understand
2037 * what's going on. Try to clean up */
2038 wDockLaunchWithState(dock, btn, state);
2043 #ifdef OFFIX_DND
2044 static WDock*
2045 findDock(WScreen *scr, XEvent *event, int *icon_pos)
2047 WDock *dock;
2048 int i;
2050 *icon_pos = -1;
2051 if ((dock = scr->dock)!=NULL) {
2052 for (i=0; i<dock->max_icons; i++) {
2053 if (dock->icon_array[i]
2054 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2055 *icon_pos = i;
2056 break;
2060 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
2061 for (i=0; i<dock->max_icons; i++) {
2062 if (dock->icon_array[i]
2063 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2064 *icon_pos = i;
2065 break;
2069 if(*icon_pos>=0)
2070 return dock;
2071 return NULL;
2076 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
2078 WDock *dock;
2079 WAppIcon *btn;
2080 int icon_pos;
2082 dock = findDock(scr, event, &icon_pos);
2083 if (!dock)
2084 return False;
2087 * Return True if the drop was on an application icon window.
2088 * In this case, let the ClientMessage handler redirect the
2089 * message to the app.
2091 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
2092 return True;
2094 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
2095 scr->flags.dnd_data_convertion_status = 0;
2097 btn = dock->icon_array[icon_pos];
2099 if (!btn->forced_dock) {
2100 btn->relaunching = btn->running;
2101 btn->running = 1;
2103 if (btn->wm_instance || btn->wm_class) {
2104 WWindowAttributes attr;
2105 memset(&attr, 0, sizeof(WWindowAttributes));
2106 wDefaultFillAttributes(btn->icon->core->screen_ptr,
2107 btn->wm_instance,
2108 btn->wm_class, &attr, NULL, True);
2110 if (!attr.no_appicon)
2111 btn->launching = 1;
2112 else
2113 btn->running = 0;
2116 btn->paste_launch = 0;
2117 btn->drop_launch = 1;
2118 scr->last_dock = dock;
2119 btn->pid = execCommand(btn, btn->dnd_command, NULL);
2120 if (btn->pid>0) {
2121 dockIconPaint(btn);
2122 } else {
2123 btn->launching = 0;
2124 if (!btn->relaunching) {
2125 btn->running = 0;
2129 return False;
2131 #endif /* OFFIX_DND */
2135 Bool
2136 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2138 WWindow *wwin;
2139 char **argv;
2140 int argc;
2141 int index;
2143 wwin = icon->icon->owner;
2144 if (icon->command==NULL) {
2145 icon->editing = 0;
2146 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2148 icon->command = wtokenjoin(argv, argc);
2149 XFreeStringList(argv);
2150 } else {
2151 char *command=NULL;
2153 /* icon->forced_dock = 1;*/
2154 if (dock->type!=WM_CLIP || !icon->attracted) {
2155 icon->editing = 1;
2156 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
2157 _("Type the command used to launch the application"),
2158 &command)) {
2159 if (command && (command[0]==0 ||
2160 (command[0]=='-' && command[1]==0))) {
2161 wfree(command);
2162 command = NULL;
2164 icon->command = command;
2165 icon->editing = 0;
2166 } else {
2167 icon->editing = 0;
2168 if (command)
2169 wfree(command);
2170 /* If the target is the dock, reject the icon. If
2171 * the target is the clip, make it an attracted icon
2173 if (dock->type==WM_CLIP) {
2174 icon->attracted = 1;
2175 if (!icon->icon->shadowed) {
2176 icon->icon->shadowed = 1;
2177 icon->icon->force_paint = 1;
2179 } else {
2180 return False;
2185 } else {
2186 icon->editing = 0;
2189 for (index=1; index<dock->max_icons; index++)
2190 if (dock->icon_array[index] == NULL)
2191 break;
2192 /* if (index == dock->max_icons)
2193 return; */
2195 assert(index < dock->max_icons);
2197 dock->icon_array[index] = icon;
2198 icon->yindex = y;
2199 icon->xindex = x;
2201 icon->omnipresent = 0;
2203 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2204 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2206 dock->icon_count++;
2208 icon->running = 1;
2209 icon->launching = 0;
2210 icon->docked = 1;
2211 icon->dock = dock;
2212 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2213 if (dock->type == WM_CLIP) {
2214 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2215 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2217 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2218 icon->icon->core->descriptor.parent = icon;
2220 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2221 icon->icon->core);
2222 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2223 wAppIconPaint(icon);
2225 if (wPreferences.auto_arrange_icons)
2226 wArrangeIcons(dock->screen_ptr, True);
2228 #ifdef OFFIX_DND
2229 if (icon->command && !icon->dnd_command) {
2230 int len = strlen(icon->command)+8;
2231 icon->dnd_command = wmalloc(len);
2232 snprintf(icon->dnd_command, len, "%s %%d", icon->command);
2234 #endif
2236 if (icon->command && !icon->paste_command) {
2237 int len = strlen(icon->command)+8;
2238 icon->paste_command = wmalloc(len);
2239 snprintf(icon->paste_command, len, "%s %%s", icon->command);
2242 return True;
2246 void
2247 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2249 int index;
2251 for(index=1; index<dock->max_icons; index++) {
2252 if(dock->icon_array[index] == icon)
2253 break;
2255 assert(index < dock->max_icons);
2257 icon->yindex = y;
2258 icon->xindex = x;
2260 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2261 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2265 Bool
2266 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2268 WWindow *wwin;
2269 char **argv;
2270 int argc;
2271 int index;
2273 if (src == dest)
2274 return True; /* No move needed, we're already there */
2276 if (dest == NULL)
2277 return False;
2279 wwin = icon->icon->owner;
2282 * For the moment we can't do this if we move icons in Clip from one
2283 * workspace to other, because if we move two or more icons without
2284 * command, the dialog box will not be able to tell us to which of the
2285 * moved icons it applies. -Dan
2287 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2288 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2290 icon->command = wtokenjoin(argv, argc);
2291 XFreeStringList(argv);
2292 } else {
2293 char *command=NULL;
2295 icon->editing = 1;
2296 /* icon->forced_dock = 1;*/
2297 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2298 _("Type the command used to launch the application"),
2299 &command)) {
2300 if (command && (command[0]==0 ||
2301 (command[0]=='-' && command[1]==0))) {
2302 wfree(command);
2303 command = NULL;
2305 icon->command = command;
2306 } else {
2307 icon->editing = 0;
2308 if (command)
2309 wfree(command);
2310 return False;
2312 icon->editing = 0;
2316 if (dest->type == WM_DOCK)
2317 wClipMakeIconOmnipresent(icon, False);
2319 for(index=1; index<src->max_icons; index++) {
2320 if(src->icon_array[index] == icon)
2321 break;
2323 assert(index < src->max_icons);
2325 src->icon_array[index] = NULL;
2326 src->icon_count--;
2328 for(index=1; index<dest->max_icons; index++) {
2329 if(dest->icon_array[index] == NULL)
2330 break;
2332 /* if (index == dest->max_icons)
2333 return; */
2335 assert(index < dest->max_icons);
2337 dest->icon_array[index] = icon;
2338 icon->dock = dest;
2340 /* deselect the icon */
2341 if (icon->icon->selected)
2342 wIconSelect(icon->icon);
2344 if (dest->type == WM_DOCK) {
2345 icon->icon->core->descriptor.handle_enternotify = NULL;
2346 icon->icon->core->descriptor.handle_leavenotify = NULL;
2347 } else {
2348 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2349 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2352 /* set it to be kept when moving to dock.
2353 * Unless the icon does not have a command set
2355 if (icon->command && dest->type==WM_DOCK) {
2356 icon->attracted = 0;
2357 if (icon->icon->shadowed) {
2358 icon->icon->shadowed = 0;
2359 icon->icon->force_paint = 1;
2363 if (src->auto_collapse || src->auto_raise_lower)
2364 clipLeave(src);
2366 icon->yindex = y;
2367 icon->xindex = x;
2369 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2370 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2372 dest->icon_count++;
2374 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2375 icon->icon->core);
2376 wAppIconPaint(icon);
2378 return True;
2382 void
2383 wDockDetach(WDock *dock, WAppIcon *icon)
2385 int index;
2387 /* make the settings panel be closed */
2388 if (icon->panel) {
2389 DestroyDockAppSettingsPanel(icon->panel);
2392 /* This must be called before icon->dock is set to NULL.
2393 * Don't move it. -Dan
2395 wClipMakeIconOmnipresent(icon, False);
2397 icon->docked = 0;
2398 icon->dock = NULL;
2399 icon->attracted = 0;
2400 icon->auto_launch = 0;
2401 if (icon->icon->shadowed) {
2402 icon->icon->shadowed = 0;
2403 icon->icon->force_paint = 1;
2406 /* deselect the icon */
2407 if (icon->icon->selected)
2408 wIconSelect(icon->icon);
2410 if (icon->command) {
2411 wfree(icon->command);
2412 icon->command = NULL;
2414 #ifdef OFFIX_DND
2415 if (icon->dnd_command) {
2416 wfree(icon->dnd_command);
2417 icon->dnd_command = NULL;
2419 #endif
2420 if (icon->paste_command) {
2421 wfree(icon->paste_command);
2422 icon->paste_command = NULL;
2425 for (index=1; index<dock->max_icons; index++)
2426 if (dock->icon_array[index] == icon)
2427 break;
2428 assert(index < dock->max_icons);
2429 dock->icon_array[index] = NULL;
2430 icon->yindex = -1;
2431 icon->xindex = -1;
2433 dock->icon_count--;
2435 /* if the dock is not attached to an application or
2436 * the the application did not set the approriate hints yet,
2437 * destroy the icon */
2438 if (!icon->running || !wApplicationOf(icon->main_window))
2439 wAppIconDestroy(icon);
2440 else {
2441 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2442 icon->icon->core->descriptor.handle_enternotify = NULL;
2443 icon->icon->core->descriptor.handle_leavenotify = NULL;
2444 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2445 icon->icon->core->descriptor.parent = icon;
2447 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2449 wAppIconPaint(icon);
2450 if (wPreferences.auto_arrange_icons) {
2451 wArrangeIcons(dock->screen_ptr, True);
2454 if (dock->auto_collapse || dock->auto_raise_lower)
2455 clipLeave(dock);
2460 * returns the closest Dock slot index for the passed
2461 * coordinates.
2463 * Returns False if icon can't be docked.
2465 * Note: this function should NEVER alter ret_x or ret_y, unless it will
2466 * return True. -Dan
2468 Bool
2469 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2470 int *ret_x, int *ret_y, int redocking)
2472 WScreen *scr = dock->screen_ptr;
2473 int dx, dy;
2474 int ex_x, ex_y;
2475 int i, offset = ICON_SIZE/2;
2476 WAppIcon *aicon = NULL;
2477 WAppIcon *nicon = NULL;
2478 int max_y_icons, max_x_icons;
2480 /* TODO: XINERAMA, for these */
2481 max_x_icons = scr->scr_width/ICON_SIZE;
2482 max_y_icons = scr->scr_height/ICON_SIZE-1;
2484 if (wPreferences.flags.noupdates)
2485 return False;
2487 dx = dock->x_pos;
2488 dy = dock->y_pos;
2490 /* if the dock is full */
2491 if (!redocking &&
2492 (dock->icon_count >= dock->max_icons)) {
2493 return False;
2496 /* exact position */
2497 if (req_y < dy)
2498 ex_y = (req_y - offset - dy)/ICON_SIZE;
2499 else
2500 ex_y = (req_y + offset - dy)/ICON_SIZE;
2502 if (req_x < dx)
2503 ex_x = (req_x - offset - dx)/ICON_SIZE;
2504 else
2505 ex_x = (req_x + offset - dx)/ICON_SIZE;
2507 /* check if the icon is outside the screen boundaries */
2509 WMRect rect;
2510 int flags;
2512 rect.pos.x = dx + ex_x*ICON_SIZE;
2513 rect.pos.y = dy + ex_y*ICON_SIZE;
2514 rect.size.width = rect.size.height = ICON_SIZE;
2516 wGetRectPlacementInfo(scr, rect, &flags);
2517 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
2518 return False;
2521 if (dock->type == WM_DOCK) {
2522 if (icon->dock != dock && ex_x != 0)
2523 return False;
2525 aicon = NULL;
2526 for (i=0; i<dock->max_icons; i++) {
2527 nicon = dock->icon_array[i];
2528 if (nicon && nicon->yindex == ex_y) {
2529 aicon = nicon;
2530 break;
2534 if (redocking) {
2535 int sig, done, closest;
2537 /* Possible cases when redocking:
2539 * icon dragged out of range of any slot -> false
2540 * icon dragged to range of free slot
2541 * icon dragged to range of same slot
2542 * icon dragged to range of different icon
2544 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2545 return False;
2547 if (ex_y>=0 && ex_y<=max_y_icons && (aicon==icon || !aicon)) {
2548 *ret_x = 0;
2549 *ret_y = ex_y;
2550 return True;
2553 /* start looking at the upper slot or lower? */
2554 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2555 sig = 1;
2556 else
2557 sig = -1;
2559 closest = -1;
2560 done = 0;
2561 /* look for closest free slot */
2562 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2563 int j;
2565 done = 1;
2566 closest = sig*(i/2) + ex_y;
2567 /* check if this slot is used */
2568 if (closest >= 0) {
2569 for (j = 0; j<dock->max_icons; j++) {
2570 if (dock->icon_array[j]
2571 && dock->icon_array[j]->yindex==closest) {
2572 /* slot is used by someone else */
2573 if (dock->icon_array[j]!=icon)
2574 done = 0;
2575 break;
2579 sig = -sig;
2581 if (done && closest >= 0 && closest <= max_y_icons &&
2582 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2584 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2585 *ret_x = 0;
2586 *ret_y = closest;
2587 return True;
2589 } else { /* !redocking */
2591 /* if slot is free and the icon is close enough, return it */
2592 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2593 *ret_x = 0;
2594 *ret_y = ex_y;
2595 return True;
2598 } else { /* CLIP */
2599 int neighbours = 0;
2600 int start, stop, k;
2602 start = icon->omnipresent ? 0 : scr->current_workspace;
2603 stop = icon->omnipresent ? scr->workspace_count : start+1;
2605 aicon = NULL;
2606 for (k=start; k<stop; k++) {
2607 WDock *tmp = scr->workspaces[k]->clip;
2608 if (!tmp)
2609 continue;
2610 for (i=0; i<tmp->max_icons; i++) {
2611 nicon = tmp->icon_array[i];
2612 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2613 aicon = nicon;
2614 break;
2617 if (aicon)
2618 break;
2620 for (k=start; k<stop; k++) {
2621 WDock *tmp = scr->workspaces[k]->clip;
2622 if (!tmp)
2623 continue;
2624 for (i=0; i<tmp->max_icons; i++) {
2625 nicon = tmp->icon_array[i];
2626 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2627 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2628 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2629 neighbours = 1;
2630 break;
2633 if (neighbours)
2634 break;
2637 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2638 *ret_x = ex_x;
2639 *ret_y = ex_y;
2640 return True;
2643 return False;
2647 static int
2648 onScreen(WScreen *scr, int x, int y, int sx, int ex, int sy, int ey)
2650 WMRect rect = wmkrect(x, y, ICON_SIZE, ICON_SIZE);
2651 int flags;
2653 wGetRectPlacementInfo(scr, rect, &flags);
2655 return !(flags & (XFLAG_DEAD | XFLAG_PARTIAL));
2660 * returns true if it can find a free slot in the dock,
2661 * in which case it changes x_pos and y_pos accordingly.
2662 * Else returns false.
2664 Bool
2665 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2667 WScreen *scr = dock->screen_ptr;
2668 WAppIcon *btn;
2669 WAppIconChain *chain;
2670 unsigned char *slot_map;
2671 int mwidth;
2672 int r;
2673 int x, y;
2674 int i, done = False;
2675 int corner;
2676 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2677 int extra_count=0;
2679 if (dock->type == WM_CLIP &&
2680 dock != scr->workspaces[scr->current_workspace]->clip)
2681 extra_count = scr->global_icon_count;
2683 /* if the dock is full */
2684 if (dock->icon_count+extra_count >= dock->max_icons) {
2685 return False;
2688 if (!wPreferences.flags.nodock && scr->dock) {
2689 if (scr->dock->on_right_side)
2690 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2691 else
2692 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2695 if (ex < dock->x_pos)
2696 ex = dock->x_pos;
2697 if (sx > dock->x_pos+ICON_SIZE)
2698 sx = dock->x_pos+ICON_SIZE;
2699 #define C_NONE 0
2700 #define C_NW 1
2701 #define C_NE 2
2702 #define C_SW 3
2703 #define C_SE 4
2705 /* check if clip is in a corner */
2706 if (dock->type==WM_CLIP) {
2707 if (dock->x_pos < 1 && dock->y_pos < 1)
2708 corner = C_NE;
2709 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2710 corner = C_SE;
2711 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2712 corner = C_SW;
2713 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2714 corner = C_NW;
2715 else
2716 corner = C_NONE;
2717 } else
2718 corner = C_NONE;
2720 /* If the clip is in the corner, use only slots that are in the border
2721 * of the screen */
2722 if (corner!=C_NONE) {
2723 char *hmap, *vmap;
2724 int hcount, vcount;
2726 hcount = WMIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2727 vcount = WMIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2728 hmap = wmalloc(hcount+1);
2729 memset(hmap, 0, hcount+1);
2730 vmap = wmalloc(vcount+1);
2731 memset(vmap, 0, vcount+1);
2733 /* mark used positions */
2734 switch (corner) {
2735 case C_NE:
2736 for (i=0; i<dock->max_icons; i++) {
2737 btn = dock->icon_array[i];
2738 if (!btn)
2739 continue;
2741 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2742 vmap[btn->yindex] = 1;
2743 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2744 hmap[btn->xindex] = 1;
2746 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2747 btn = chain->aicon;
2748 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2749 vmap[btn->yindex] = 1;
2750 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2751 hmap[btn->xindex] = 1;
2753 break;
2754 case C_NW:
2755 for (i=0; i<dock->max_icons; i++) {
2756 btn = dock->icon_array[i];
2757 if (!btn)
2758 continue;
2760 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2761 vmap[btn->yindex] = 1;
2762 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2763 hmap[-btn->xindex] = 1;
2765 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2766 btn = chain->aicon;
2767 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2768 vmap[btn->yindex] = 1;
2769 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2770 hmap[-btn->xindex] = 1;
2772 break;
2773 case C_SE:
2774 for (i=0; i<dock->max_icons; i++) {
2775 btn = dock->icon_array[i];
2776 if (!btn)
2777 continue;
2779 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2780 vmap[-btn->yindex] = 1;
2781 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2782 hmap[btn->xindex] = 1;
2784 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2785 btn = chain->aicon;
2786 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2787 vmap[-btn->yindex] = 1;
2788 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2789 hmap[btn->xindex] = 1;
2791 break;
2792 case C_SW:
2793 default:
2794 for (i=0; i<dock->max_icons; i++) {
2795 btn = dock->icon_array[i];
2796 if (!btn)
2797 continue;
2799 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2800 vmap[-btn->yindex] = 1;
2801 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2802 hmap[-btn->xindex] = 1;
2804 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2805 btn = chain->aicon;
2806 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2807 vmap[-btn->yindex] = 1;
2808 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2809 hmap[-btn->xindex] = 1;
2812 x=0; y=0;
2813 done = 0;
2814 /* search a vacant slot */
2815 for (i=1; i<WMAX(vcount, hcount); i++) {
2816 if (i < vcount && vmap[i]==0) {
2817 /* found a slot */
2818 x = 0;
2819 y = i;
2820 done = 1;
2821 break;
2822 } else if (i < hcount && hmap[i]==0) {
2823 /* found a slot */
2824 x = i;
2825 y = 0;
2826 done = 1;
2827 break;
2830 wfree(vmap);
2831 wfree(hmap);
2832 /* If found a slot, translate and return */
2833 if (done) {
2834 if (corner==C_NW || corner==C_NE) {
2835 *y_pos = y;
2836 } else {
2837 *y_pos = -y;
2839 if (corner==C_NE || corner==C_SE) {
2840 *x_pos = x;
2841 } else {
2842 *x_pos = -x;
2844 return True;
2846 /* else, try to find a slot somewhere else */
2849 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2850 * placed outside of screen */
2851 mwidth = (int)ceil(sqrt(dock->max_icons));
2853 /* In the worst case (the clip is in the corner of the screen),
2854 * the amount of icons that fit in the clip is smaller.
2855 * Double the map to get a safe value.
2857 mwidth += mwidth;
2859 r = (mwidth-1)/2;
2861 slot_map = wmalloc(mwidth*mwidth);
2862 memset(slot_map, 0, mwidth*mwidth);
2864 #define XY2OFS(x,y) (WMAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2866 /* mark used slots in the map. If the slot falls outside the map
2867 * (for example, when all icons are placed in line), ignore them. */
2868 for (i=0; i<dock->max_icons; i++) {
2869 btn = dock->icon_array[i];
2870 if (btn)
2871 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2873 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2874 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2876 /* Find closest slot from the center that is free by scanning the
2877 * map from the center to outward in circular passes.
2878 * This will not result in a neat layout, but will be optimal
2879 * in the sense that there will not be holes left.
2881 done = 0;
2882 for (i = 1; i <= r && !done; i++) {
2883 int tx, ty;
2885 /* top and bottom parts of the ring */
2886 for (x = -i; x <= i && !done; x++) {
2887 tx = dock->x_pos + x*ICON_SIZE;
2888 y = -i;
2889 ty = dock->y_pos + y*ICON_SIZE;
2890 if (slot_map[XY2OFS(x,y)]==0
2891 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2892 *x_pos = x;
2893 *y_pos = y;
2894 done = 1;
2895 break;
2897 y = i;
2898 ty = dock->y_pos + y*ICON_SIZE;
2899 if (slot_map[XY2OFS(x,y)]==0
2900 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2901 *x_pos = x;
2902 *y_pos = y;
2903 done = 1;
2904 break;
2907 /* left and right parts of the ring */
2908 for (y = -i+1; y <= i-1; y++) {
2909 ty = dock->y_pos + y*ICON_SIZE;
2910 x = -i;
2911 tx = dock->x_pos + x*ICON_SIZE;
2912 if (slot_map[XY2OFS(x,y)]==0
2913 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2914 *x_pos = x;
2915 *y_pos = y;
2916 done = 1;
2917 break;
2919 x = i;
2920 tx = dock->x_pos + x*ICON_SIZE;
2921 if (slot_map[XY2OFS(x,y)]==0
2922 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2923 *x_pos = x;
2924 *y_pos = y;
2925 done = 1;
2926 break;
2930 wfree(slot_map);
2931 #undef XY2OFS
2932 return done;
2936 static void
2937 moveDock(WDock *dock, int new_x, int new_y)
2939 WAppIcon *btn;
2940 int i;
2942 dock->x_pos = new_x;
2943 dock->y_pos = new_y;
2944 for (i=0; i<dock->max_icons; i++) {
2945 btn = dock->icon_array[i];
2946 if (btn) {
2947 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2948 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2949 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2955 static void
2956 swapDock(WDock *dock)
2958 WScreen *scr = dock->screen_ptr;
2959 WAppIcon *btn;
2960 int x, i;
2963 if (dock->on_right_side) {
2964 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2965 } else {
2966 x = dock->x_pos = DOCK_EXTRA_SPACE;
2969 for (i=0; i<dock->max_icons; i++) {
2970 btn = dock->icon_array[i];
2971 if (btn) {
2972 btn->x_pos = x;
2973 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2977 wScreenUpdateUsableArea(scr);
2981 static pid_t
2982 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2984 WScreen *scr = btn->icon->core->screen_ptr;
2985 pid_t pid;
2986 char **argv;
2987 int argc;
2988 char *cmdline;
2990 cmdline = ExpandOptions(scr, command);
2992 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2993 if (cmdline)
2994 wfree(cmdline);
2995 if (state)
2996 wfree(state);
2997 return 0;
3000 wtokensplit(cmdline, &argv, &argc);
3002 if (argv==NULL) {
3003 if (cmdline)
3004 wfree(cmdline);
3005 if (state)
3006 wfree(state);
3007 return 0;
3010 if ((pid=fork())==0) {
3011 char **args;
3012 int i;
3014 SetupEnvironment(scr);
3016 #ifdef HAVE_SETSID
3017 setsid();
3018 #endif
3020 args = malloc(sizeof(char*)*(argc+1));
3021 if (!args)
3022 exit(111);
3023 for (i=0; i<argc; i++) {
3024 args[i] = argv[i];
3026 args[argc] = NULL;
3027 execvp(argv[0], args);
3028 exit(111);
3030 wtokenfree(argv, argc);
3032 if (pid > 0) {
3033 if (!state) {
3034 state = wmalloc(sizeof(WSavedState));
3035 memset(state, 0, sizeof(WSavedState));
3036 state->hidden = -1;
3037 state->miniaturized = -1;
3038 state->shaded = -1;
3039 if (btn->dock==scr->dock || btn->omnipresent)
3040 state->workspace = -1;
3041 else
3042 state->workspace = scr->current_workspace;
3044 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
3045 state);
3046 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
3047 btn->dock);
3048 } else if (state) {
3049 wfree(state);
3051 wfree(cmdline);
3052 return pid;
3056 void
3057 wDockHideIcons(WDock *dock)
3059 int i;
3061 if (dock==NULL)
3062 return;
3064 for (i=1; i<dock->max_icons; i++) {
3065 if (dock->icon_array[i])
3066 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
3068 dock->mapped = 0;
3070 dockIconPaint(dock->icon_array[0]);
3074 void
3075 wDockShowIcons(WDock *dock)
3077 int i, newlevel;
3078 WAppIcon *btn;
3080 if (dock==NULL)
3081 return;
3083 btn = dock->icon_array[0];
3084 moveDock(dock, btn->x_pos, btn->y_pos);
3086 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
3087 ChangeStackingLevel(btn->icon->core, newlevel);
3089 for (i=1; i<dock->max_icons; i++) {
3090 if (dock->icon_array[i]) {
3091 MoveInStackListAbove(dock->icon_array[i]->icon->core,
3092 btn->icon->core);
3093 break;
3097 if (!dock->collapsed) {
3098 for (i=1; i<dock->max_icons; i++) {
3099 if (dock->icon_array[i]) {
3100 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
3104 dock->mapped = 1;
3106 dockIconPaint(btn);
3110 void
3111 wDockLower(WDock *dock)
3113 int i;
3115 for (i=0; i<dock->max_icons; i++) {
3116 if (dock->icon_array[i])
3117 wLowerFrame(dock->icon_array[i]->icon->core);
3122 void
3123 wDockRaise(WDock *dock)
3125 int i;
3127 for (i=dock->max_icons-1; i>=0; i--) {
3128 if (dock->icon_array[i])
3129 wRaiseFrame(dock->icon_array[i]->icon->core);
3134 void
3135 wDockRaiseLower(WDock *dock)
3137 if (!dock->icon_array[0]->icon->core->stacking->above
3138 ||(dock->icon_array[0]->icon->core->stacking->window_level
3139 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
3140 wDockLower(dock);
3141 else
3142 wDockRaise(dock);
3146 void
3147 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
3149 icon->launching = 0;
3150 icon->relaunching = 0;
3151 dockIconPaint(icon);
3155 WAppIcon*
3156 wDockFindIconForWindow(WDock *dock, Window window)
3158 WAppIcon *icon;
3159 int i;
3161 for (i=0; i<dock->max_icons; i++) {
3162 icon = dock->icon_array[i];
3163 if (icon && icon->main_window == window)
3164 return icon;
3166 return NULL;
3170 void
3171 wDockTrackWindowLaunch(WDock *dock, Window window)
3173 WAppIcon *icon;
3174 char *wm_class, *wm_instance;
3175 int i;
3176 Bool firstPass = True;
3177 Bool found = False;
3178 char *command = NULL;
3181 int argc;
3182 char **argv;
3184 if (XGetCommand(dpy, window, &argv, &argc)) {
3185 if (argc > 0 && argv != NULL)
3186 command = wtokenjoin(argv, argc);
3187 if (argv) {
3188 XFreeStringList(argv);
3193 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
3194 (!wm_class && !wm_instance))
3195 return;
3197 retry:
3198 for (i=0; i<dock->max_icons; i++) {
3199 icon = dock->icon_array[i];
3200 if (!icon)
3201 continue;
3203 /* app is already attached to icon */
3204 if (icon->main_window == window) {
3205 found = True;
3206 break;
3209 if ((icon->wm_instance || icon->wm_class)
3210 && (icon->launching || !icon->running)) {
3212 if (icon->wm_instance && wm_instance &&
3213 strcmp(icon->wm_instance, wm_instance)!=0) {
3214 continue;
3216 if (icon->wm_class && wm_class &&
3217 strcmp(icon->wm_class, wm_class)!=0) {
3218 continue;
3220 if (firstPass && command && strcmp(icon->command, command)!=0) {
3221 continue;
3224 if (!icon->relaunching) {
3225 WApplication *wapp;
3227 /* Possibly an application that was docked with dockit,
3228 * but the user did not update WMState to indicate that
3229 * it was docked by force */
3230 wapp = wApplicationOf(window);
3231 if (!wapp) {
3232 icon->forced_dock = 1;
3233 icon->running = 0;
3235 if (!icon->forced_dock)
3236 icon->main_window = window;
3239 found = True;
3240 if (!wPreferences.no_animations && !icon->launching &&
3241 !dock->screen_ptr->flags.startup && !dock->collapsed) {
3242 WAppIcon *aicon;
3243 int x0, y0;
3245 icon->launching = 1;
3246 dockIconPaint(icon);
3248 aicon = wAppIconCreateForDock(dock->screen_ptr, NULL,
3249 wm_instance, wm_class,
3250 TILE_NORMAL);
3251 /* XXX: can: aicon->icon == NULL ? */
3252 PlaceIcon(dock->screen_ptr, &x0, &y0, wGetHeadForWindow(aicon->icon->owner));
3253 wAppIconMove(aicon, x0, y0);
3254 /* Should this always be lowered? -Dan */
3255 if (dock->lowered)
3256 wLowerFrame(aicon->icon->core);
3257 XMapWindow(dpy, aicon->icon->core->window);
3258 aicon->launching = 1;
3259 wAppIconPaint(aicon);
3260 SlideWindow(aicon->icon->core->window, x0, y0,
3261 icon->x_pos, icon->y_pos);
3262 XUnmapWindow(dpy, aicon->icon->core->window);
3263 wAppIconDestroy(aicon);
3265 wDockFinishLaunch(dock, icon);
3266 break;
3270 if (firstPass && !found) {
3271 firstPass = False;
3272 goto retry;
3275 if (command)
3276 wfree(command);
3278 if (wm_class)
3279 XFree(wm_class);
3280 if (wm_instance)
3281 XFree(wm_instance);
3286 void
3287 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3289 if (!wPreferences.flags.noclip) {
3290 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3291 if (scr->current_workspace != workspace) {
3292 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3293 WAppIconChain *chain = scr->global_icons;
3295 while (chain) {
3296 moveIconBetweenDocks(chain->aicon->dock,
3297 scr->workspaces[workspace]->clip,
3298 chain->aicon, chain->aicon->xindex,
3299 chain->aicon->yindex);
3300 if (scr->workspaces[workspace]->clip->collapsed)
3301 XUnmapWindow(dpy, chain->aicon->icon->core->window);
3302 chain = chain->next;
3305 wDockHideIcons(old_clip);
3306 if (old_clip->auto_raise_lower) {
3307 if (old_clip->auto_raise_magic) {
3308 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3309 old_clip->auto_raise_magic = NULL;
3311 wDockLower(old_clip);
3313 if (old_clip->auto_collapse) {
3314 if (old_clip->auto_expand_magic) {
3315 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3316 old_clip->auto_expand_magic = NULL;
3318 old_clip->collapsed = 1;
3320 wDockShowIcons(scr->workspaces[workspace]->clip);
3322 if (scr->flags.clip_balloon_mapped)
3323 showClipBalloon(scr->clip_icon->dock, workspace);
3329 static void
3330 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3332 WAppIcon *icon;
3333 int i;
3335 for (i=0; i<dock->max_icons; i++) {
3336 icon = dock->icon_array[i];
3337 if (!icon)
3338 continue;
3340 if (icon->launching && icon->pid == pid) {
3341 if (!icon->relaunching) {
3342 icon->running = 0;
3343 icon->main_window = None;
3345 wDockFinishLaunch(dock, icon);
3346 icon->pid = 0;
3347 if (status==111) {
3348 char msg[PATH_MAX];
3349 char *cmd;
3351 if (icon->drop_launch)
3352 cmd = icon->dnd_command;
3353 else if (icon->paste_launch)
3354 cmd = icon->paste_command;
3355 else
3356 cmd = icon->command;
3358 snprintf(msg, sizeof(msg),
3359 _("Could not execute command \"%s\""), cmd);
3361 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3362 _("OK"), NULL, NULL);
3364 break;
3370 static void
3371 toggleLowered(WDock *dock)
3373 WAppIcon *tmp;
3374 int newlevel, i;
3376 /* lower/raise Dock */
3377 if (!dock->lowered) {
3378 newlevel = WMNormalLevel;
3379 dock->lowered = 1;
3380 } else {
3381 newlevel = WMDockLevel;
3382 dock->lowered = 0;
3385 for (i=0; i<dock->max_icons; i++) {
3386 tmp = dock->icon_array[i];
3387 if (!tmp)
3388 continue;
3390 ChangeStackingLevel(tmp->icon->core, newlevel);
3391 if (dock->lowered)
3392 wLowerFrame(tmp->icon->core);
3395 if (dock->type == WM_DOCK)
3396 wScreenUpdateUsableArea(dock->screen_ptr);
3400 static void
3401 toggleCollapsed(WDock *dock)
3403 if (dock->collapsed) {
3404 dock->collapsed = 0;
3405 wDockShowIcons(dock);
3407 else {
3408 dock->collapsed = 1;
3409 wDockHideIcons(dock);
3414 static void
3415 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3417 WScreen *scr = dock->screen_ptr;
3418 WObjDescriptor *desc;
3419 WMenuEntry *entry;
3420 WApplication *wapp = NULL;
3421 int index = 0;
3422 int x_pos;
3423 int n_selected;
3424 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3426 if (dock->type == WM_DOCK) {
3427 /* keep on top */
3428 entry = dock->menu->entries[index];
3429 entry->flags.indicator_on = !dock->lowered;
3430 entry->clientdata = dock;
3431 dock->menu->flags.realized = 0;
3432 } else {
3433 /* clip options */
3434 if (scr->clip_options)
3435 updateClipOptionsMenu(scr->clip_options, dock);
3437 n_selected = numberOfSelectedIcons(dock);
3439 /* Rename Workspace */
3440 entry = dock->menu->entries[++index];
3441 if (aicon == scr->clip_icon) {
3442 entry->callback = renameCallback;
3443 entry->clientdata = dock;
3444 entry->flags.indicator = 0;
3445 entry->text = _("Rename Workspace");
3446 } else {
3447 entry->callback = omnipresentCallback;
3448 entry->clientdata = aicon;
3449 if (n_selected > 0) {
3450 entry->flags.indicator = 0;
3451 entry->text = _("Toggle Omnipresent");
3452 } else {
3453 entry->flags.indicator = 1;
3454 entry->flags.indicator_on = aicon->omnipresent;
3455 entry->flags.indicator_type = MI_CHECK;
3456 entry->text = _("Omnipresent");
3460 /* select/unselect icon */
3461 entry = dock->menu->entries[++index];
3462 entry->clientdata = aicon;
3463 entry->flags.indicator_on = aicon->icon->selected;
3464 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3466 /* select/unselect all icons */
3467 entry = dock->menu->entries[++index];
3468 entry->clientdata = aicon;
3469 if (n_selected > 0)
3470 entry->text = _("Unselect All Icons");
3471 else
3472 entry->text = _("Select All Icons");
3473 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3475 /* keep icon(s) */
3476 entry = dock->menu->entries[++index];
3477 entry->clientdata = aicon;
3478 if (n_selected > 1)
3479 entry->text = _("Keep Icons");
3480 else
3481 entry->text = _("Keep Icon");
3482 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3484 /* this is the workspace submenu part */
3485 entry = dock->menu->entries[++index];
3486 if (n_selected > 1)
3487 entry->text = _("Move Icons To");
3488 else
3489 entry->text = _("Move Icon To");
3490 if (scr->clip_submenu)
3491 updateWorkspaceMenu(scr->clip_submenu, aicon);
3492 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3494 /* remove icon(s) */
3495 entry = dock->menu->entries[++index];
3496 entry->clientdata = aicon;
3497 if (n_selected > 1)
3498 entry->text = _("Remove Icons");
3499 else
3500 entry->text = _("Remove Icon");
3501 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3503 /* attract icon(s) */
3504 entry = dock->menu->entries[++index];
3505 entry->clientdata = aicon;
3507 dock->menu->flags.realized = 0;
3508 wMenuRealize(dock->menu);
3512 if (aicon->icon->owner) {
3513 wapp = wApplicationOf(aicon->icon->owner->main_window);
3514 } else {
3515 wapp = NULL;
3518 /* launch */
3519 entry = dock->menu->entries[++index];
3520 entry->clientdata = aicon;
3521 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3523 /* unhide here */
3524 entry = dock->menu->entries[++index];
3525 entry->clientdata = aicon;
3526 if (wapp && wapp->flags.hidden) {
3527 entry->text = _("Unhide Here");
3528 } else {
3529 entry->text = _("Bring Here");
3531 wMenuSetEnabled(dock->menu, index, appIsRunning);
3533 /* hide */
3534 entry = dock->menu->entries[++index];
3535 entry->clientdata = aicon;
3536 if (wapp && wapp->flags.hidden) {
3537 entry->text = _("Unhide");
3538 } else {
3539 entry->text = _("Hide");
3541 wMenuSetEnabled(dock->menu, index, appIsRunning);
3543 /* settings */
3544 entry = dock->menu->entries[++index];
3545 entry->clientdata = aicon;
3546 wMenuSetEnabled(dock->menu, index, !aicon->editing
3547 && !wPreferences.flags.noupdates);
3549 /* kill */
3550 entry = dock->menu->entries[++index];
3551 entry->clientdata = aicon;
3552 wMenuSetEnabled(dock->menu, index, appIsRunning);
3554 if (!dock->menu->flags.realized)
3555 wMenuRealize(dock->menu);
3557 if (dock->type == WM_CLIP) {
3558 /*x_pos = event->xbutton.x_root+2;*/
3559 x_pos = event->xbutton.x_root - dock->menu->frame->core->width/2 - 1;
3560 if (x_pos < 0) {
3561 x_pos = 0;
3562 } else if (x_pos + dock->menu->frame->core->width > scr->scr_width-2) {
3563 x_pos = scr->scr_width - dock->menu->frame->core->width - 4;
3565 } else {
3566 x_pos = dock->on_right_side ?
3567 scr->scr_width - dock->menu->frame->core->width - 3 : 0;
3570 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3572 /* allow drag select */
3573 event->xany.send_event = True;
3574 desc = &dock->menu->menu->descriptor;
3575 (*desc->handle_mousedown)(desc, event);
3579 /******************************************************************/
3580 static void
3581 iconDblClick(WObjDescriptor *desc, XEvent *event)
3583 WAppIcon *btn = desc->parent;
3584 WDock *dock = btn->dock;
3585 WApplication *wapp = NULL;
3586 int unhideHere = 0;
3588 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3589 wapp = wApplicationOf(btn->icon->owner->main_window);
3591 assert(wapp!=NULL);
3593 unhideHere = (event->xbutton.state & ShiftMask);
3595 /* go to the last workspace that the user worked on the app */
3596 if (wapp->last_workspace != dock->screen_ptr->current_workspace
3597 && !unhideHere) {
3598 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3601 wUnhideApplication(wapp, event->xbutton.button==Button2,
3602 unhideHere);
3604 if (event->xbutton.state & MOD_MASK) {
3605 wHideOtherApplications(btn->icon->owner);
3607 } else {
3608 if (event->xbutton.button==Button1) {
3610 if (event->xbutton.state & MOD_MASK) {
3611 /* raise/lower dock */
3612 toggleLowered(dock);
3613 } else if (btn == dock->screen_ptr->clip_icon) {
3614 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3615 toggleCollapsed(dock);
3616 else
3617 handleClipChangeWorkspace(dock->screen_ptr, event);
3618 } else if (btn->command) {
3619 if (!btn->launching &&
3620 (!btn->running || (event->xbutton.state & ControlMask))) {
3621 launchDockedApplication(btn, False);
3623 } else if (btn->xindex == 0 && btn->yindex == 0
3624 && btn->dock->type == WM_DOCK) {
3626 wShowGNUstepPanel(dock->screen_ptr);
3634 static void
3635 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3637 WScreen *scr = dock->screen_ptr;
3638 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3639 int x, y;
3640 XEvent ev;
3641 int grabbed = 0, swapped = 0, done;
3642 Pixmap ghost = None;
3643 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3645 #ifdef DEBUG
3646 puts("moving dock");
3647 #endif
3648 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3649 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3650 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3651 wwarning("pointer grab failed for dock move");
3653 y = 0;
3654 for (x=0; x<dock->max_icons; x++) {
3655 if (dock->icon_array[x]!=NULL &&
3656 dock->icon_array[x]->yindex > y)
3657 y = dock->icon_array[x]->yindex;
3659 y++;
3660 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3662 done = 0;
3663 while (!done) {
3664 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3665 |ButtonMotionMask|ExposureMask, &ev);
3666 switch (ev.type) {
3667 case Expose:
3668 WMHandleEvent(&ev);
3669 break;
3671 case MotionNotify:
3672 if (!grabbed) {
3673 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3674 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3675 XChangeActivePointerGrab(dpy, ButtonMotionMask
3676 |ButtonReleaseMask|ButtonPressMask,
3677 wCursor[WCUR_MOVE], CurrentTime);
3678 grabbed=1;
3680 break;
3682 if (dock->type == WM_CLIP) {
3683 x = ev.xmotion.x_root - ofs_x;
3684 y = ev.xmotion.y_root - ofs_y;
3685 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3687 moveDock(dock, x, y);
3688 } else {
3689 /* move vertically if pointer is inside the dock*/
3690 if ((dock->on_right_side &&
3691 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3692 || (!dock->on_right_side &&
3693 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3695 x = ev.xmotion.x_root - ofs_x;
3696 y = ev.xmotion.y_root - ofs_y;
3697 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3698 moveDock(dock, dock->x_pos, y);
3700 /* move horizontally to change sides */
3701 x = ev.xmotion.x_root - ofs_x;
3702 if (!dock->on_right_side) {
3704 /* is on left */
3706 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3707 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3708 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3709 if (superfluous && ghost==None) {
3710 ghost = MakeGhostDock(dock, dock->x_pos,
3711 scr->scr_width-ICON_SIZE
3712 -DOCK_EXTRA_SPACE-1,
3713 dock->y_pos);
3714 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3715 ghost);
3716 XClearWindow(dpy, scr->dock_shadow);
3718 XMapRaised(dpy, scr->dock_shadow);
3719 swapped = 1;
3720 } else {
3721 if (superfluous && ghost!=None) {
3722 XFreePixmap(dpy, ghost);
3723 ghost = None;
3725 XUnmapWindow(dpy, scr->dock_shadow);
3726 swapped = 0;
3728 } else {
3729 /* is on right */
3730 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3731 XMoveWindow(dpy, scr->dock_shadow,
3732 DOCK_EXTRA_SPACE, dock->y_pos);
3733 if (superfluous && ghost==None) {
3734 ghost = MakeGhostDock(dock, dock->x_pos,
3735 DOCK_EXTRA_SPACE, dock->y_pos);
3736 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3737 ghost);
3738 XClearWindow(dpy, scr->dock_shadow);
3740 XMapRaised(dpy, scr->dock_shadow);
3741 swapped = -1;
3742 } else {
3743 XUnmapWindow(dpy, scr->dock_shadow);
3744 swapped = 0;
3745 if (superfluous && ghost!=None) {
3746 XFreePixmap(dpy, ghost);
3747 ghost = None;
3752 break;
3754 case ButtonPress:
3755 break;
3757 case ButtonRelease:
3758 if (ev.xbutton.button != event->xbutton.button)
3759 break;
3760 XUngrabPointer(dpy, CurrentTime);
3761 XUnmapWindow(dpy, scr->dock_shadow);
3762 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3763 if (dock->type == WM_DOCK) {
3764 if (swapped!=0) {
3765 if (swapped>0)
3766 dock->on_right_side = 1;
3767 else
3768 dock->on_right_side = 0;
3769 swapDock(dock);
3770 wArrangeIcons(scr, False);
3773 done = 1;
3774 break;
3777 if (superfluous) {
3778 if (ghost!=None)
3779 XFreePixmap(dpy, ghost);
3780 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3782 #ifdef DEBUG
3783 puts("End dock move");
3784 #endif
3789 static void
3790 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3792 WScreen *scr = dock->screen_ptr;
3793 Window wins[2];
3794 WIcon *icon = aicon->icon;
3795 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3796 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3797 XEvent ev;
3798 int x = aicon->x_pos, y = aicon->y_pos;
3799 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3800 int shad_x = x, shad_y = y;
3801 int ix = aicon->xindex, iy = aicon->yindex;
3802 int tmp;
3803 Pixmap ghost = None;
3804 Bool docked;
3805 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3806 int omnipresent = aicon->omnipresent; /* this must be cached!!! */
3809 if (wPreferences.flags.noupdates)
3810 return;
3812 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3813 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3814 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3815 #ifdef DEBUG0
3816 wwarning("pointer grab failed for icon move");
3817 #endif
3820 if (!(event->xbutton.state & MOD_MASK))
3821 wRaiseFrame(icon->core);
3823 if (!wPreferences.flags.noclip)
3824 clip = scr->workspaces[scr->current_workspace]->clip;
3826 if (dock == scr->dock && !wPreferences.flags.noclip)
3827 dock2 = clip;
3828 else if (dock != scr->dock && !wPreferences.flags.nodock)
3829 dock2 = scr->dock;
3831 wins[0] = icon->core->window;
3832 wins[1] = scr->dock_shadow;
3833 XRestackWindows(dpy, wins, 2);
3834 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3835 ICON_SIZE, ICON_SIZE);
3836 if (superfluous) {
3837 if (icon->pixmap!=None)
3838 ghost = MakeGhostIcon(scr, icon->pixmap);
3839 else
3840 ghost = MakeGhostIcon(scr, icon->core->window);
3842 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3843 XClearWindow(dpy, scr->dock_shadow);
3845 XMapWindow(dpy, scr->dock_shadow);
3847 ondock = 1;
3850 while(1) {
3851 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3852 |ButtonMotionMask|ExposureMask, &ev);
3853 switch (ev.type) {
3854 case Expose:
3855 WMHandleEvent(&ev);
3856 break;
3858 case MotionNotify:
3859 if (!grabbed) {
3860 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3861 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3862 XChangeActivePointerGrab(dpy, ButtonMotionMask
3863 |ButtonReleaseMask|ButtonPressMask,
3864 wCursor[WCUR_MOVE], CurrentTime);
3865 grabbed=1;
3866 } else {
3867 break;
3871 if (omnipresent) {
3872 int i;
3873 for (i=0; i<scr->workspace_count; i++) {
3874 if (i == scr->current_workspace)
3875 continue;
3876 wDockShowIcons(scr->workspaces[i]->clip);
3880 x = ev.xmotion.x_root - ofs_x;
3881 y = ev.xmotion.y_root - ofs_y;
3882 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3883 if (tmp && dock2) {
3884 change_dock = 0;
3885 if (last_dock != dock && collapsed) {
3886 last_dock->collapsed = 1;
3887 wDockHideIcons(last_dock);
3888 collapsed = 0;
3890 if (!collapsed && (collapsed = dock->collapsed)) {
3891 dock->collapsed = 0;
3892 wDockShowIcons(dock);
3894 if (dock->auto_raise_lower)
3895 wDockRaise(dock);
3896 last_dock = dock;
3897 } else if (dock2) {
3898 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3899 if (tmp) {
3900 change_dock = 1;
3901 if (last_dock != dock2 && collapsed) {
3902 last_dock->collapsed = 1;
3903 wDockHideIcons(last_dock);
3904 collapsed = 0;
3906 if (!collapsed && (collapsed = dock2->collapsed)) {
3907 dock2->collapsed = 0;
3908 wDockShowIcons(dock2);
3910 if (dock2->auto_raise_lower)
3911 wDockRaise(dock2);
3912 last_dock = dock2;
3915 if (aicon->launching
3916 || aicon->lock
3917 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3918 || (!aicon->running && tmp)) {
3919 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3920 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3922 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3924 if (!ondock) {
3925 XMapWindow(dpy, scr->dock_shadow);
3927 ondock = 1;
3928 } else {
3929 if (ondock) {
3930 XUnmapWindow(dpy, scr->dock_shadow);
3932 ondock = 0;
3934 XMoveWindow(dpy, icon->core->window, x, y);
3935 break;
3937 case ButtonPress:
3938 break;
3940 case ButtonRelease:
3941 if (ev.xbutton.button != event->xbutton.button)
3942 break;
3943 XUngrabPointer(dpy, CurrentTime);
3944 if (ondock) {
3945 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3946 XUnmapWindow(dpy, scr->dock_shadow);
3947 if (!change_dock) {
3948 reattachIcon(dock, aicon, ix, iy);
3949 if (clip && dock!=clip && clip->auto_raise_lower)
3950 wDockLower(clip);
3951 } else {
3952 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3953 if (!docked) {
3954 /* Slide it back if dock rejected it */
3955 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3956 aicon->y_pos);
3957 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3959 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3960 collapsed = 0;
3963 } else {
3964 aicon->x_pos = x;
3965 aicon->y_pos = y;
3966 if (superfluous) {
3967 if (!aicon->running && !wPreferences.no_animations) {
3968 /* We need to deselect it, even if is deselected in
3969 * wDockDetach(), because else DoKaboom() will fail.
3971 if (aicon->icon->selected)
3972 wIconSelect(aicon->icon);
3974 wSoundPlay(WSOUND_KABOOM);
3975 DoKaboom(scr,aicon->icon->core->window, x, y);
3976 } else {
3977 wSoundPlay(WSOUND_UNDOCK);
3979 } else {
3980 wSoundPlay(WSOUND_UNDOCK);
3982 if (clip && clip->auto_raise_lower)
3983 wDockLower(clip);
3984 wDockDetach(dock, aicon);
3986 if (collapsed) {
3987 last_dock->collapsed = 1;
3988 wDockHideIcons(last_dock);
3989 collapsed = 0;
3991 if (superfluous) {
3992 if (ghost!=None)
3993 XFreePixmap(dpy, ghost);
3994 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3996 if (omnipresent) {
3997 int i;
3998 for (i=0; i<scr->workspace_count; i++) {
3999 if (i == scr->current_workspace)
4000 continue;
4001 wDockHideIcons(scr->workspaces[i]->clip);
4005 #ifdef DEBUG
4006 puts("End icon move");
4007 #endif
4008 return;
4014 static int
4015 getClipButton(int px, int py)
4017 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
4019 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
4020 return CLIP_IDLE;
4022 if (py <= pt-((int)ICON_SIZE-1-px))
4023 return CLIP_FORWARD;
4024 else if (px <= pt-((int)ICON_SIZE-1-py))
4025 return CLIP_REWIND;
4027 return CLIP_IDLE;
4031 static void
4032 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
4034 XEvent ev;
4035 int done, direction, new_ws;
4036 int new_dir;
4037 WDock *clip = scr->clip_icon->dock;
4039 direction = getClipButton(event->xbutton.x, event->xbutton.y);
4041 clip->lclip_button_pushed = direction==CLIP_REWIND;
4042 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4044 wClipIconPaint(scr->clip_icon);
4045 done = 0;
4046 while(!done) {
4047 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
4048 |ButtonPressMask, &ev);
4049 switch (ev.type) {
4050 case Expose:
4051 WMHandleEvent(&ev);
4052 break;
4054 case MotionNotify:
4055 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
4056 if (new_dir != direction) {
4057 direction = new_dir;
4058 clip->lclip_button_pushed = direction==CLIP_REWIND;
4059 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4060 wClipIconPaint(scr->clip_icon);
4062 break;
4064 case ButtonPress:
4065 break;
4067 case ButtonRelease:
4068 if (ev.xbutton.button == event->xbutton.button)
4069 done = 1;
4073 clip->lclip_button_pushed = 0;
4074 clip->rclip_button_pushed = 0;
4076 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
4078 if (direction == CLIP_FORWARD) {
4079 if (scr->current_workspace < scr->workspace_count-1)
4080 wWorkspaceChange(scr, scr->current_workspace+1);
4081 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
4082 wWorkspaceChange(scr, scr->current_workspace+1);
4083 else if (wPreferences.ws_cycle)
4084 wWorkspaceChange(scr, 0);
4086 else if (direction == CLIP_REWIND) {
4087 if (scr->current_workspace > 0)
4088 wWorkspaceChange(scr, scr->current_workspace-1);
4089 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
4090 wWorkspaceChange(scr, scr->workspace_count-1);
4093 wClipIconPaint(scr->clip_icon);
4097 static void
4098 iconMouseDown(WObjDescriptor *desc, XEvent *event)
4100 WAppIcon *aicon = desc->parent;
4101 WDock *dock = aicon->dock;
4102 WScreen *scr = aicon->icon->core->screen_ptr;
4104 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
4105 return;
4107 scr->last_dock = dock;
4109 if (dock->menu->flags.mapped)
4110 wMenuUnmap(dock->menu);
4112 if (IsDoubleClick(scr, event)) {
4113 /* double-click was not in the main clip icon */
4114 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
4115 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
4116 iconDblClick(desc, event);
4117 return;
4121 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
4122 XUnmapWindow(dpy, scr->clip_balloon);
4123 scr->flags.clip_balloon_mapped = 0;
4126 #ifdef DEBUG
4127 puts("handling dock");
4128 #endif
4129 if (event->xbutton.button == Button1) {
4130 if (event->xbutton.state & MOD_MASK)
4131 wDockLower(dock);
4132 else
4133 wDockRaise(dock);
4135 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
4136 dock->type!=WM_DOCK) {
4137 wIconSelect(aicon->icon);
4138 return;
4141 if (aicon->yindex==0 && aicon->xindex==0) {
4142 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
4143 && dock->type==WM_CLIP)
4144 handleClipChangeWorkspace(scr, event);
4145 else
4146 handleDockMove(dock, aicon, event);
4147 } else
4148 handleIconMove(dock, aicon, event);
4150 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4151 aicon==scr->clip_icon) {
4152 if (!scr->clip_ws_menu) {
4153 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
4155 if (scr->clip_ws_menu) {
4156 WMenu *wsMenu = scr->clip_ws_menu;
4157 int xpos;
4159 wWorkspaceMenuUpdate(scr, wsMenu);
4161 xpos = event->xbutton.x_root - wsMenu->frame->core->width/2 - 1;
4162 if (xpos < 0) {
4163 xpos = 0;
4164 } else if (xpos + wsMenu->frame->core->width > scr->scr_width-2) {
4165 xpos = scr->scr_width - wsMenu->frame->core->width - 4;
4167 wMenuMapAt(wsMenu, xpos, event->xbutton.y_root+2, False);
4169 desc = &wsMenu->menu->descriptor;
4170 event->xany.send_event = True;
4171 (*desc->handle_mousedown)(desc, event);
4173 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4174 (event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon) {
4175 wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
4176 } else if (event->xbutton.button == Button3) {
4177 if (event->xbutton.send_event &&
4178 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
4179 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
4180 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
4181 wwarning("pointer grab failed for dockicon menu");
4182 return;
4185 openDockMenu(dock, aicon, event);
4186 } else if (event->xbutton.button == Button2) {
4187 WAppIcon *btn = desc->parent;
4189 if (!btn->launching &&
4190 (!btn->running || (event->xbutton.state & ControlMask))) {
4191 launchDockedApplication(btn, True);
4197 static void
4198 showClipBalloon(WDock *dock, int workspace)
4200 int w, h;
4201 int x, y;
4202 WScreen *scr = dock->screen_ptr;
4203 char *text;
4204 Window stack[2];
4206 scr->flags.clip_balloon_mapped = 1;
4207 XMapWindow(dpy, scr->clip_balloon);
4209 text = scr->workspaces[workspace]->name;
4211 w = WMWidthOfString(scr->clip_title_font, text, strlen(text));
4213 h = WMFontHeight(scr->clip_title_font);
4214 XResizeWindow(dpy, scr->clip_balloon, w, h);
4216 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
4217 y = dock->y_pos + ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
4219 if (x+w > scr->scr_width) {
4220 x = scr->scr_width - w;
4221 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
4222 y = dock->y_pos - h - 1;
4223 else
4224 y = dock->y_pos + ICON_SIZE;
4225 XRaiseWindow(dpy, scr->clip_balloon);
4226 } else {
4227 stack[0] = scr->clip_icon->icon->core->window;
4228 stack[1] = scr->clip_balloon;
4229 XRestackWindows(dpy, stack, 2);
4231 XMoveWindow(dpy, scr->clip_balloon, x, y);
4232 XClearWindow(dpy, scr->clip_balloon);
4233 WMDrawString(scr->wmscreen, scr->clip_balloon,
4234 scr->clip_title_color[CLIP_NORMAL],
4235 scr->clip_title_font,
4236 0, 0, text, strlen(text));
4240 static void
4241 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
4243 WAppIcon *btn = (WAppIcon*)desc->parent;
4244 WDock *dock;
4245 WScreen *scr;
4247 assert(event->type==EnterNotify);
4249 if(desc->parent_type!=WCLASS_DOCK_ICON)
4250 return;
4252 scr = btn->icon->core->screen_ptr;
4253 if (!btn->omnipresent)
4254 dock = btn->dock;
4255 else
4256 dock = scr->workspaces[scr->current_workspace]->clip;
4258 if (!dock || dock->type!=WM_CLIP)
4259 return;
4261 /* The auto raise/lower code */
4262 if (dock->auto_lower_magic) {
4263 WMDeleteTimerHandler(dock->auto_lower_magic);
4264 dock->auto_lower_magic = NULL;
4266 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
4267 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
4268 clipAutoRaise,
4269 (void *)dock);
4272 /* The auto expand/collapse code */
4273 if (dock->auto_collapse_magic) {
4274 WMDeleteTimerHandler(dock->auto_collapse_magic);
4275 dock->auto_collapse_magic = NULL;
4277 if (dock->auto_collapse && !dock->auto_expand_magic) {
4278 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
4279 clipAutoExpand,
4280 (void *)dock);
4283 if (btn->xindex == 0 && btn->yindex == 0)
4284 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4285 else {
4286 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4287 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
4288 dock->screen_ptr->flags.clip_balloon_mapped = 0;
4294 static void
4295 clipLeave(WDock *dock)
4297 XEvent event;
4298 WObjDescriptor *desc = NULL;
4300 if (!dock || dock->type!=WM_CLIP)
4301 return;
4303 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
4304 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
4305 (XPointer *)&desc)!=XCNOENT
4306 && desc && desc->parent_type==WCLASS_DOCK_ICON
4307 && ((WAppIcon*)desc->parent)->dock
4308 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
4309 /* We didn't left the Clip yet */
4310 XPutBackEvent(dpy, &event);
4311 return;
4314 XPutBackEvent(dpy, &event);
4315 } else {
4316 /* We entered a withdrawn window, so we're still in Clip */
4317 return;
4320 if (dock->auto_raise_magic) {
4321 WMDeleteTimerHandler(dock->auto_raise_magic);
4322 dock->auto_raise_magic = NULL;
4324 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
4325 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
4326 clipAutoLower,
4327 (void *)dock);
4330 if (dock->auto_expand_magic) {
4331 WMDeleteTimerHandler(dock->auto_expand_magic);
4332 dock->auto_expand_magic = NULL;
4334 if (dock->auto_collapse && !dock->auto_collapse_magic) {
4335 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
4336 clipAutoCollapse,
4337 (void *)dock);
4342 static void
4343 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
4345 WAppIcon *btn = (WAppIcon*)desc->parent;
4347 assert(event->type==LeaveNotify);
4349 if(desc->parent_type!=WCLASS_DOCK_ICON)
4350 return;
4352 clipLeave(btn->dock);
4356 static void
4357 clipAutoCollapse(void *cdata)
4359 WDock *dock = (WDock *)cdata;
4361 if (dock->type!=WM_CLIP)
4362 return;
4364 if (dock->auto_collapse) {
4365 dock->collapsed = 1;
4366 wDockHideIcons(dock);
4368 dock->auto_collapse_magic = NULL;
4372 static void
4373 clipAutoExpand(void *cdata)
4375 WDock *dock = (WDock *)cdata;
4377 if (dock->type!=WM_CLIP)
4378 return;
4380 if (dock->auto_collapse) {
4381 dock->collapsed = 0;
4382 wDockShowIcons(dock);
4384 dock->auto_expand_magic = NULL;
4388 static void
4389 clipAutoLower(void *cdata)
4391 WDock *dock = (WDock *)cdata;
4393 if (dock->type!=WM_CLIP)
4394 return;
4396 if (dock->auto_raise_lower)
4397 wDockLower(dock);
4399 dock->auto_lower_magic = NULL;
4403 static void
4404 clipAutoRaise(void *cdata)
4406 WDock *dock = (WDock *)cdata;
4408 if (dock->type!=WM_CLIP)
4409 return;
4411 if (dock->auto_raise_lower)
4412 wDockRaise(dock);
4414 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4415 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4418 dock->auto_raise_magic = NULL;
4422 static Bool
4423 iconCanBeOmnipresent(WAppIcon *aicon)
4425 WScreen *scr = aicon->icon->core->screen_ptr;
4426 WDock *clip;
4427 WAppIcon *btn;
4428 int i, j;
4430 for (i=0; i<scr->workspace_count; i++) {
4431 clip = scr->workspaces[i]->clip;
4433 if (clip == aicon->dock)
4434 continue;
4436 if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
4437 return False; /* Clip is full in some workspace */
4439 for (j=0; j<clip->max_icons; j++) {
4440 btn = clip->icon_array[j];
4441 if(btn && btn->xindex==aicon->xindex && btn->yindex==aicon->yindex)
4442 return False;
4446 return True;
4451 wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
4453 WScreen *scr = aicon->icon->core->screen_ptr;
4454 WAppIconChain *new_entry, *tmp, *tmp1;
4455 int status = WO_SUCCESS;
4457 if ((scr->dock && aicon->dock==scr->dock) || aicon==scr->clip_icon) {
4458 return WO_NOT_APPLICABLE;
4461 if (aicon->omnipresent == omnipresent)
4462 return WO_SUCCESS;
4464 if (omnipresent) {
4465 if (iconCanBeOmnipresent(aicon)) {
4466 aicon->omnipresent = 1;
4467 new_entry = wmalloc(sizeof(WAppIconChain));
4468 new_entry->aicon = aicon;
4469 new_entry->next = scr->global_icons;
4470 scr->global_icons = new_entry;
4471 scr->global_icon_count++;
4472 } else {
4473 aicon->omnipresent = 0;
4474 status = WO_FAILED;
4476 } else {
4477 aicon->omnipresent = 0;
4478 if (aicon == scr->global_icons->aicon) {
4479 tmp = scr->global_icons->next;
4480 wfree(scr->global_icons);
4481 scr->global_icons = tmp;
4482 scr->global_icon_count--;
4483 } else {
4484 tmp = scr->global_icons;
4485 while (tmp->next) {
4486 if (tmp->next->aicon == aicon) {
4487 tmp1 = tmp->next->next;
4488 wfree(tmp->next);
4489 tmp->next = tmp1;
4490 scr->global_icon_count--;
4491 break;
4493 tmp = tmp->next;
4498 wAppIconPaint(aicon);
4500 return status;