Change to the linux kernel coding style
[wmaker-crm.git] / src / dock.c
1 /* dock.c- built-in Dock module for WindowMaker
2  *
3  *  Window Maker window manager
4  *
5  *  Copyright (c) 1997-2003 Alfredo K. Kojima
6  *  Copyright (c) 1998-2003 Dan Pascu
7  *
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.
12  *
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.
17  *
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.
22  */
23
24 #include "wconfig.h"
25
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <math.h>
32 #include <limits.h>
33
34 #ifndef PATH_MAX
35 #define PATH_MAX DEFAULT_PATH_MAX
36 #endif
37
38 #include "WindowMaker.h"
39 #include "wcore.h"
40 #include "window.h"
41 #include "icon.h"
42 #include "appicon.h"
43 #include "actions.h"
44 #include "stacking.h"
45 #include "dock.h"
46 #include "dialog.h"
47 #include "funcs.h"
48 #include "properties.h"
49 #include "menu.h"
50 #include "client.h"
51 #include "defaults.h"
52 #include "workspace.h"
53 #include "framewin.h"
54 #include "superfluous.h"
55 #include "wsound.h"
56 #include "xinerama.h"
57
58 /**** Local variables ****/
59 #define CLIP_REWIND       1
60 #define CLIP_IDLE         0
61 #define CLIP_FORWARD      2
62
63 /**** Global variables ****/
64
65 /* in dockedapp.c */
66 extern void DestroyDockAppSettingsPanel();
67
68 extern void ShowDockAppSettingsPanel(WAppIcon * aicon);
69
70 extern XContext wWinContext;
71
72 extern Cursor wCursor[WCUR_LAST];
73
74 extern WPreferences wPreferences;
75
76 extern XContext wWinContext;
77
78 #define MOD_MASK wPreferences.modifier_mask
79
80 extern void appIconMouseDown(WObjDescriptor * desc, XEvent * event);
81
82 #define ICON_SIZE wPreferences.icon_size
83
84 /***** Local variables ****/
85
86 static WMPropList *dCommand = NULL;
87 static WMPropList *dPasteCommand = NULL;
88 #ifdef XDND                     /* XXX was OFFIX */
89 static WMPropList *dDropCommand = NULL;
90 #endif
91 static WMPropList *dAutoLaunch, *dLock;
92 static WMPropList *dName, *dForced, *dBuggyApplication, *dYes, *dNo;
93 static WMPropList *dHost, *dDock, *dClip;
94 static WMPropList *dAutoAttractIcons;
95
96 static WMPropList *dPosition, *dApplications, *dLowered, *dCollapsed;
97
98 static WMPropList *dAutoCollapse, *dAutoRaiseLower, *dOmnipresent;
99
100 static void dockIconPaint(WAppIcon * btn);
101
102 static void iconMouseDown(WObjDescriptor * desc, XEvent * event);
103
104 static pid_t execCommand(WAppIcon * btn, char *command, WSavedState * state);
105
106 static void trackDeadProcess(pid_t pid, unsigned char status, WDock * dock);
107
108 static int getClipButton(int px, int py);
109
110 static void toggleLowered(WDock * dock);
111
112 static void toggleCollapsed(WDock * dock);
113
114 static void clipIconExpose(WObjDescriptor * desc, XEvent * event);
115
116 static void clipLeave(WDock * dock);
117
118 static void handleClipChangeWorkspace(WScreen * scr, XEvent * event);
119
120 Bool moveIconBetweenDocks(WDock * src, WDock * dest, WAppIcon * icon, int x, int y);
121
122 static void clipEnterNotify(WObjDescriptor * desc, XEvent * event);
123 static void clipLeaveNotify(WObjDescriptor * desc, XEvent * event);
124 static void clipAutoCollapse(void *cdata);
125 static void clipAutoExpand(void *cdata);
126 static void launchDockedApplication(WAppIcon * btn, Bool withSelection);
127
128 static void clipAutoLower(void *cdata);
129 static void clipAutoRaise(void *cdata);
130
131 static void showClipBalloon(WDock * dock, int workspace);
132
133 static void make_keys()
134 {
135         if (dCommand != NULL)
136                 return;
137
138         dCommand = WMRetainPropList(WMCreatePLString("Command"));
139         dPasteCommand = WMRetainPropList(WMCreatePLString("PasteCommand"));
140 #ifdef XDND
141         dDropCommand = WMRetainPropList(WMCreatePLString("DropCommand"));
142 #endif
143         dLock = WMRetainPropList(WMCreatePLString("Lock"));
144         dAutoLaunch = WMRetainPropList(WMCreatePLString("AutoLaunch"));
145         dName = WMRetainPropList(WMCreatePLString("Name"));
146         dForced = WMRetainPropList(WMCreatePLString("Forced"));
147         dBuggyApplication = WMRetainPropList(WMCreatePLString("BuggyApplication"));
148         dYes = WMRetainPropList(WMCreatePLString("Yes"));
149         dNo = WMRetainPropList(WMCreatePLString("No"));
150         dHost = WMRetainPropList(WMCreatePLString("Host"));
151
152         dPosition = WMCreatePLString("Position");
153         dApplications = WMCreatePLString("Applications");
154         dLowered = WMCreatePLString("Lowered");
155         dCollapsed = WMCreatePLString("Collapsed");
156         dAutoCollapse = WMCreatePLString("AutoCollapse");
157         dAutoRaiseLower = WMCreatePLString("AutoRaiseLower");
158         dAutoAttractIcons = WMCreatePLString("AutoAttractIcons");
159
160         dOmnipresent = WMCreatePLString("Omnipresent");
161
162         dDock = WMCreatePLString("Dock");
163         dClip = WMCreatePLString("Clip");
164 }
165
166 static void renameCallback(WMenu * menu, WMenuEntry * entry)
167 {
168         WDock *dock = entry->clientdata;
169         char buffer[128];
170         int wspace;
171         char *name;
172
173         assert(entry->clientdata != NULL);
174
175         wspace = dock->screen_ptr->current_workspace;
176
177         name = wstrdup(dock->screen_ptr->workspaces[wspace]->name);
178
179         snprintf(buffer, sizeof(buffer), _("Type the name for workspace %i:"), wspace + 1);
180         if (wInputDialog(dock->screen_ptr, _("Rename Workspace"), buffer, &name)) {
181                 wWorkspaceRename(dock->screen_ptr, wspace, name);
182         }
183         if (name) {
184                 wfree(name);
185         }
186 }
187
188 static void toggleLoweredCallback(WMenu * menu, WMenuEntry * entry)
189 {
190         assert(entry->clientdata != NULL);
191
192         toggleLowered(entry->clientdata);
193
194         entry->flags.indicator_on = !(((WDock *) entry->clientdata)->lowered);
195
196         wMenuPaint(menu);
197 }
198
199 static int matchWindow(void *item, void *cdata)
200 {
201         return (((WFakeGroupLeader *) item)->leader == (Window) cdata);
202 }
203
204 static void killCallback(WMenu * menu, WMenuEntry * entry)
205 {
206         WScreen *scr = menu->menu->screen_ptr;
207         WAppIcon *icon;
208         WFakeGroupLeader *fPtr;
209         char *buffer;
210
211         if (!WCHECK_STATE(WSTATE_NORMAL))
212                 return;
213
214         assert(entry->clientdata != NULL);
215
216         icon = (WAppIcon *) entry->clientdata;
217
218         icon->editing = 1;
219
220         WCHANGE_STATE(WSTATE_MODAL);
221
222         buffer = wstrconcat(icon->wm_class,
223                             _(" will be forcibly closed.\n"
224                               "Any unsaved changes will be lost.\n" "Please confirm."));
225
226         if (icon->icon && icon->icon->owner) {
227                 fPtr = icon->icon->owner->fake_group;
228         } else {
229                 /* is this really necessary? can we kill a non-running dock icon? */
230                 Window win = icon->main_window;
231                 int index;
232
233                 index = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void *)win);
234                 if (index != WANotFound)
235                         fPtr = WMGetFromArray(scr->fakeGroupLeaders, index);
236                 else
237                         fPtr = NULL;
238         }
239
240         if (wPreferences.dont_confirm_kill
241             || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
242                               buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
243                 if (fPtr != NULL) {
244                         WWindow *wwin, *twin;
245
246                         wwin = scr->focused_window;
247                         while (wwin) {
248                                 twin = wwin->prev;
249                                 if (wwin->fake_group == fPtr) {
250                                         wClientKill(wwin);
251                                 }
252                                 wwin = twin;
253                         }
254                 } else if (icon->icon && icon->icon->owner) {
255                         wClientKill(icon->icon->owner);
256                 }
257         }
258
259         wfree(buffer);
260
261         icon->editing = 0;
262
263         WCHANGE_STATE(WSTATE_NORMAL);
264 }
265
266 /* TODO: replace this function with a member of the dock struct */
267 static int numberOfSelectedIcons(WDock * dock)
268 {
269         WAppIcon *aicon;
270         int i, n;
271
272         n = 0;
273         for (i = 1; i < dock->max_icons; i++) {
274                 aicon = dock->icon_array[i];
275                 if (aicon && aicon->icon->selected) {
276                         n++;
277                 }
278         }
279
280         return n;
281 }
282
283 static WMArray *getSelected(WDock * dock)
284 {
285         WMArray *ret = WMCreateArray(8);
286         WAppIcon *btn;
287         int i;
288
289         for (i = 1; i < dock->max_icons; i++) {
290                 btn = dock->icon_array[i];
291                 if (btn && btn->icon->selected) {
292                         WMAddToArray(ret, btn);
293                 }
294         }
295
296         return ret;
297 }
298
299 static void paintClipButtons(WAppIcon * clipIcon, Bool lpushed, Bool rpushed)
300 {
301         Window win = clipIcon->icon->core->window;
302         WScreen *scr = clipIcon->icon->core->screen_ptr;
303         XPoint p[4];
304         int pt = CLIP_BUTTON_SIZE * ICON_SIZE / 64;
305         int tp = ICON_SIZE - pt;
306         int as = pt - 15;       /* 15 = 5+5+5 */
307         GC gc = scr->draw_gc;   /* maybe use WMColorGC() instead here? */
308         WMColor *color;
309 #ifdef GRADIENT_CLIP_ARROW
310         Bool collapsed = clipIcon->dock->collapsed;
311 #endif
312
313         /*if (!clipIcon->dock->collapsed)
314            color = scr->clip_title_color[CLIP_NORMAL];
315            else
316            color = scr->clip_title_color[CLIP_COLLAPSED]; */
317         color = scr->clip_title_color[CLIP_NORMAL];
318
319         XSetForeground(dpy, gc, WMColorPixel(color));
320
321         if (rpushed) {
322                 p[0].x = tp + 1;
323                 p[0].y = 1;
324                 p[1].x = ICON_SIZE - 2;
325                 p[1].y = 1;
326                 p[2].x = ICON_SIZE - 2;
327                 p[2].y = pt - 1;
328         } else if (lpushed) {
329                 p[0].x = 1;
330                 p[0].y = tp;
331                 p[1].x = pt;
332                 p[1].y = ICON_SIZE - 2;
333                 p[2].x = 1;
334                 p[2].y = ICON_SIZE - 2;
335         }
336         if (lpushed || rpushed) {
337                 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
338                 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
339                 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
340         }
341 #ifdef GRADIENT_CLIP_ARROW
342         if (!collapsed) {
343                 XSetFillStyle(dpy, scr->copy_gc, FillTiled);
344                 XSetTile(dpy, scr->copy_gc, scr->clip_arrow_gradient);
345                 XSetClipMask(dpy, scr->copy_gc, None);
346                 gc = scr->copy_gc;
347         }
348 #endif                          /* GRADIENT_CLIP_ARROW */
349
350         /* top right arrow */
351         p[0].x = p[3].x = ICON_SIZE - 5 - as;
352         p[0].y = p[3].y = 5;
353         p[1].x = ICON_SIZE - 6;
354         p[1].y = 5;
355         p[2].x = ICON_SIZE - 6;
356         p[2].y = 4 + as;
357         if (rpushed) {
358                 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
359                 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
360         } else {
361 #ifdef GRADIENT_CLIP_ARROW
362                 if (!collapsed)
363                         XSetTSOrigin(dpy, gc, ICON_SIZE - 6 - as, 5);
364 #endif
365                 XFillPolygon(dpy, win, gc, p, 3, Convex, CoordModeOrigin);
366                 XDrawLines(dpy, win, gc, p, 4, CoordModeOrigin);
367         }
368
369         /* bottom left arrow */
370         p[0].x = p[3].x = 5;
371         p[0].y = p[3].y = ICON_SIZE - 5 - as;
372         p[1].x = 5;
373         p[1].y = ICON_SIZE - 6;
374         p[2].x = 4 + as;
375         p[2].y = ICON_SIZE - 6;
376         if (lpushed) {
377                 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
378                 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
379         } else {
380 #ifdef GRADIENT_CLIP_ARROW
381                 if (!collapsed)
382                         XSetTSOrigin(dpy, gc, 5, ICON_SIZE - 6 - as);
383 #endif
384                 XFillPolygon(dpy, win, gc, p, 3, Convex, CoordModeOrigin);
385                 XDrawLines(dpy, win, gc, p, 4, CoordModeOrigin);
386         }
387 #ifdef GRADIENT_CLIP_ARROW
388         if (!collapsed)
389                 XSetFillStyle(dpy, scr->copy_gc, FillSolid);
390 #endif
391 }
392
393 RImage *wClipMakeTile(WScreen * scr, RImage * normalTile)
394 {
395         RImage *tile = RCloneImage(normalTile);
396         RColor black;
397         RColor dark;
398         RColor light;
399         int pt, tp;
400         int as;
401
402         pt = CLIP_BUTTON_SIZE * wPreferences.icon_size / 64;
403         tp = wPreferences.icon_size - 1 - pt;
404         as = pt - 15;
405
406         black.alpha = 255;
407         black.red = black.green = black.blue = 0;
408
409         dark.alpha = 0;
410         dark.red = dark.green = dark.blue = 60;
411
412         light.alpha = 0;
413         light.red = light.green = light.blue = 80;
414
415         /* top right */
416         ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size - 2, pt - 1, &dark);
417         RDrawLine(tile, tp - 1, 0, wPreferences.icon_size - 1, pt + 1, &black);
418         ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size - 3, pt, &light);
419
420         /* arrow bevel */
421         ROperateLine(tile, RSubtractOperation, ICON_SIZE - 7 - as, 4, ICON_SIZE - 5, 4, &dark);
422         ROperateLine(tile, RSubtractOperation, ICON_SIZE - 6 - as, 5, ICON_SIZE - 5, 6 + as, &dark);
423         ROperateLine(tile, RAddOperation, ICON_SIZE - 5, 4, ICON_SIZE - 5, 6 + as, &light);
424
425         /* bottom left */
426         ROperateLine(tile, RAddOperation, 2, tp + 2, pt - 2, wPreferences.icon_size - 3, &dark);
427         RDrawLine(tile, 0, tp - 1, pt + 1, wPreferences.icon_size - 1, &black);
428         ROperateLine(tile, RSubtractOperation, 0, tp - 2, pt + 1, wPreferences.icon_size - 2, &light);
429
430         /* arrow bevel */
431         ROperateLine(tile, RSubtractOperation, 4, ICON_SIZE - 7 - as, 4, ICON_SIZE - 5, &dark);
432         ROperateLine(tile, RSubtractOperation, 5, ICON_SIZE - 6 - as, 6 + as, ICON_SIZE - 5, &dark);
433         ROperateLine(tile, RAddOperation, 4, ICON_SIZE - 5, 6 + as, ICON_SIZE - 5, &light);
434
435         return tile;
436 }
437
438 static void omnipresentCallback(WMenu * menu, WMenuEntry * entry)
439 {
440         WAppIcon *clickedIcon = entry->clientdata;
441         WAppIcon *aicon;
442         WDock *dock;
443         WMArray *selectedIcons;
444         WMArrayIterator iter;
445         int failed;
446
447         assert(entry->clientdata != NULL);
448
449         dock = clickedIcon->dock;
450
451         selectedIcons = getSelected(dock);
452
453         if (!WMGetArrayItemCount(selectedIcons))
454                 WMAddToArray(selectedIcons, clickedIcon);
455
456         failed = 0;
457         WM_ITERATE_ARRAY(selectedIcons, aicon, iter) {
458                 if (wClipMakeIconOmnipresent(aicon, !aicon->omnipresent) == WO_FAILED)
459                         failed++;
460                 else if (aicon->icon->selected)
461                         wIconSelect(aicon->icon);
462         }
463         WMFreeArray(selectedIcons);
464
465         if (failed > 1) {
466                 wMessageDialog(dock->screen_ptr, _("Warning"),
467                                _("Some icons cannot be made omnipresent. "
468                                  "Please make sure that no other icon is "
469                                  "docked in the same positions on the other "
470                                  "workspaces and the Clip is not full in "
471                                  "some workspace."), _("OK"), NULL, NULL);
472         } else if (failed == 1) {
473                 wMessageDialog(dock->screen_ptr, _("Warning"),
474                                _("Icon cannot be made omnipresent. "
475                                  "Please make sure that no other icon is "
476                                  "docked in the same position on the other "
477                                  "workspaces and the Clip is not full in "
478                                  "some workspace."), _("OK"), NULL, NULL);
479         }
480 }
481
482 static void removeIconsCallback(WMenu * menu, WMenuEntry * entry)
483 {
484         WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata;
485         WDock *dock;
486         WAppIcon *aicon;
487         WMArray *selectedIcons;
488         int keepit;
489         WMArrayIterator it;
490
491         assert(clickedIcon != NULL);
492
493         dock = clickedIcon->dock;
494
495         selectedIcons = getSelected(dock);
496
497         if (WMGetArrayItemCount(selectedIcons)) {
498                 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
499                                    _("All selected icons will be removed!"),
500                                    _("OK"), _("Cancel"), NULL) != WAPRDefault) {
501                         WMFreeArray(selectedIcons);
502                         return;
503                 }
504         } else {
505                 if (clickedIcon->xindex == 0 && clickedIcon->yindex == 0) {
506                         WMFreeArray(selectedIcons);
507                         return;
508                 }
509                 WMAddToArray(selectedIcons, clickedIcon);
510         }
511
512         WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
513                 keepit = aicon->running && wApplicationOf(aicon->main_window);
514                 wDockDetach(dock, aicon);
515                 if (keepit) {
516                         /* XXX: can: aicon->icon == NULL ? */
517                         PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos,
518                                   wGetHeadForWindow(aicon->icon->owner));
519                         XMoveWindow(dpy, aicon->icon->core->window, aicon->x_pos, aicon->y_pos);
520                         if (!dock->mapped || dock->collapsed)
521                                 XMapWindow(dpy, aicon->icon->core->window);
522                 }
523         }
524         WMFreeArray(selectedIcons);
525
526         if (wPreferences.auto_arrange_icons)
527                 wArrangeIcons(dock->screen_ptr, True);
528 }
529
530 static void keepIconsCallback(WMenu * menu, WMenuEntry * entry)
531 {
532         WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata;
533         WDock *dock;
534         WAppIcon *aicon;
535         WMArray *selectedIcons;
536         WMArrayIterator it;
537
538         assert(clickedIcon != NULL);
539         dock = clickedIcon->dock;
540
541         selectedIcons = getSelected(dock);
542
543         if (!WMGetArrayItemCount(selectedIcons)
544             && clickedIcon != dock->screen_ptr->clip_icon) {
545                 char *command = NULL;
546
547                 if (!clickedIcon->command && !clickedIcon->editing) {
548                         clickedIcon->editing = 1;
549                         if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
550                                          _("Type the command used to launch the application"), &command)) {
551                                 if (command && (command[0] == 0 || (command[0] == '-' && command[1] == 0))) {
552                                         wfree(command);
553                                         command = NULL;
554                                 }
555                                 clickedIcon->command = command;
556                                 clickedIcon->editing = 0;
557                         } else {
558                                 clickedIcon->editing = 0;
559                                 if (command)
560                                         wfree(command);
561                                 WMFreeArray(selectedIcons);
562                                 return;
563                         }
564                 }
565
566                 WMAddToArray(selectedIcons, clickedIcon);
567         }
568
569         WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
570                 if (aicon->icon->selected)
571                         wIconSelect(aicon->icon);
572                 if (aicon && aicon->attracted && aicon->command) {
573                         aicon->attracted = 0;
574                         if (aicon->icon->shadowed) {
575                                 aicon->icon->shadowed = 0;
576                                 aicon->icon->force_paint = 1;
577                                 wAppIconPaint(aicon);
578                         }
579                 }
580         }
581         WMFreeArray(selectedIcons);
582 }
583
584 static void toggleAutoAttractCallback(WMenu * menu, WMenuEntry * entry)
585 {
586         WDock *dock = (WDock *) entry->clientdata;
587
588         assert(entry->clientdata != NULL);
589
590         dock->attract_icons = !dock->attract_icons;
591         /*if (!dock->attract_icons)
592            dock->keep_attracted = 0; */
593
594         entry->flags.indicator_on = dock->attract_icons;
595
596         wMenuPaint(menu);
597 }
598
599 static void selectCallback(WMenu * menu, WMenuEntry * entry)
600 {
601         WAppIcon *icon = (WAppIcon *) entry->clientdata;
602
603         assert(icon != NULL);
604
605         wIconSelect(icon->icon);
606
607         wMenuPaint(menu);
608 }
609
610 static void colectIconsCallback(WMenu * menu, WMenuEntry * entry)
611 {
612         WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata;
613         WDock *clip;
614         WAppIcon *aicon;
615         int x, y, x_pos, y_pos;
616
617         assert(entry->clientdata != NULL);
618         clip = clickedIcon->dock;
619
620         aicon = clip->screen_ptr->app_icon_list;
621
622         while (aicon) {
623                 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
624                         x_pos = clip->x_pos + x * ICON_SIZE;
625                         y_pos = clip->y_pos + y * ICON_SIZE;
626                         if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
627 #ifdef ANIMATIONS
628                                 if (wPreferences.no_animations) {
629                                         XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
630                                 } else {
631                                         SlideWindow(aicon->icon->core->window,
632                                                     aicon->x_pos, aicon->y_pos, x_pos, y_pos);
633                                 }
634 #else
635                                 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
636 #endif                          /* ANIMATIONS */
637                         }
638                         aicon->attracted = 1;
639                         if (!aicon->icon->shadowed) {
640                                 aicon->icon->shadowed = 1;
641                                 aicon->icon->force_paint = 1;
642                                 /* We don't do an wAppIconPaint() here because it's in
643                                  * wDockAttachIcon(). -Dan
644                                  */
645                         }
646                         wDockAttachIcon(clip, aicon, x, y);
647                         if (clip->collapsed || !clip->mapped)
648                                 XUnmapWindow(dpy, aicon->icon->core->window);
649                 }
650                 aicon = aicon->next;
651         }
652 }
653
654 static void selectIconsCallback(WMenu * menu, WMenuEntry * entry)
655 {
656         WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata;
657         WDock *dock;
658         WMArray *selectedIcons;
659         WMArrayIterator iter;
660         WAppIcon *btn;
661         int i;
662
663         assert(clickedIcon != NULL);
664         dock = clickedIcon->dock;
665
666         selectedIcons = getSelected(dock);
667
668         if (!WMGetArrayItemCount(selectedIcons)) {
669                 for (i = 1; i < dock->max_icons; i++) {
670                         btn = dock->icon_array[i];
671                         if (btn && !btn->icon->selected) {
672                                 wIconSelect(btn->icon);
673                         }
674                 }
675         } else {
676                 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
677                         wIconSelect(btn->icon);
678                 }
679         }
680         WMFreeArray(selectedIcons);
681
682         wMenuPaint(menu);
683 }
684
685 static void toggleCollapsedCallback(WMenu * menu, WMenuEntry * entry)
686 {
687         assert(entry->clientdata != NULL);
688
689         toggleCollapsed(entry->clientdata);
690
691         entry->flags.indicator_on = ((WDock *) entry->clientdata)->collapsed;
692
693         wMenuPaint(menu);
694 }
695
696 static void toggleAutoCollapseCallback(WMenu * menu, WMenuEntry * entry)
697 {
698         WDock *dock;
699         assert(entry->clientdata != NULL);
700
701         dock = (WDock *) entry->clientdata;
702
703         dock->auto_collapse = !dock->auto_collapse;
704
705         entry->flags.indicator_on = ((WDock *) entry->clientdata)->auto_collapse;
706
707         wMenuPaint(menu);
708 }
709
710 static void toggleAutoRaiseLowerCallback(WMenu * menu, WMenuEntry * entry)
711 {
712         WDock *dock;
713         assert(entry->clientdata != NULL);
714
715         dock = (WDock *) entry->clientdata;
716
717         dock->auto_raise_lower = !dock->auto_raise_lower;
718
719         entry->flags.indicator_on = ((WDock *) entry->clientdata)->auto_raise_lower;
720
721         wMenuPaint(menu);
722 }
723
724 static void launchCallback(WMenu * menu, WMenuEntry * entry)
725 {
726         WAppIcon *btn = (WAppIcon *) entry->clientdata;
727
728         launchDockedApplication(btn, False);
729 }
730
731 static void settingsCallback(WMenu * menu, WMenuEntry * entry)
732 {
733         WAppIcon *btn = (WAppIcon *) entry->clientdata;
734
735         if (btn->editing)
736                 return;
737         ShowDockAppSettingsPanel(btn);
738 }
739
740 static void hideCallback(WMenu * menu, WMenuEntry * entry)
741 {
742         WApplication *wapp;
743         WAppIcon *btn = (WAppIcon *) entry->clientdata;
744
745         wapp = wApplicationOf(btn->icon->owner->main_window);
746
747         if (wapp->flags.hidden) {
748                 wWorkspaceChange(btn->icon->core->screen_ptr, wapp->last_workspace);
749                 wUnhideApplication(wapp, False, False);
750         } else {
751                 wHideApplication(wapp);
752         }
753 }
754
755 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
756 {
757         WApplication *wapp;
758         WAppIcon *btn = (WAppIcon *) entry->clientdata;
759
760         wapp = wApplicationOf(btn->icon->owner->main_window);
761
762         wUnhideApplication(wapp, False, True);
763 }
764
765 WAppIcon *mainIconCreate(WScreen * scr, int type)
766 {
767         WAppIcon *btn;
768         int x_pos;
769
770         if (type == WM_CLIP) {
771                 if (scr->clip_icon)
772                         return scr->clip_icon;
773                 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
774                 btn->icon->core->descriptor.handle_expose = clipIconExpose;
775                 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
776                 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
777                 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE; */
778                 x_pos = 0;
779         } else {
780                 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
781                 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
782         }
783
784         btn->xindex = 0;
785         btn->yindex = 0;
786
787         btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
788         btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
789         btn->icon->core->descriptor.parent = btn;
790         /*ChangeStackingLevel(btn->icon->core, WMDockLevel); */
791         XMapWindow(dpy, btn->icon->core->window);
792         btn->x_pos = x_pos;
793         btn->y_pos = 0;
794         btn->docked = 1;
795         if (type == WM_CLIP)
796                 scr->clip_icon = btn;
797
798         return btn;
799 }
800
801 static void switchWSCommand(WMenu * menu, WMenuEntry * entry)
802 {
803         WAppIcon *btn, *icon = (WAppIcon *) entry->clientdata;
804         WScreen *scr = icon->icon->core->screen_ptr;
805         WDock *src, *dest;
806         WMArray *selectedIcons;
807         int x, y;
808
809         if (entry->order == scr->current_workspace)
810                 return;
811         src = icon->dock;
812         dest = scr->workspaces[entry->order]->clip;
813
814         selectedIcons = getSelected(src);
815
816         if (WMGetArrayItemCount(selectedIcons)) {
817                 WMArrayIterator iter;
818
819                 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
820                         if (wDockFindFreeSlot(dest, &x, &y)) {
821                                 moveIconBetweenDocks(src, dest, btn, x, y);
822                                 XUnmapWindow(dpy, btn->icon->core->window);
823                         }
824                 }
825         } else if (icon != scr->clip_icon) {
826                 if (wDockFindFreeSlot(dest, &x, &y)) {
827                         moveIconBetweenDocks(src, dest, icon, x, y);
828                         XUnmapWindow(dpy, icon->icon->core->window);
829                 }
830         }
831         WMFreeArray(selectedIcons);
832 }
833
834 static void launchDockedApplication(WAppIcon * btn, Bool withSelection)
835 {
836         WScreen *scr = btn->icon->core->screen_ptr;
837
838         if (!btn->launching &&
839             ((!withSelection && btn->command != NULL) || (withSelection && btn->paste_command != NULL))) {
840                 if (!btn->forced_dock) {
841                         btn->relaunching = btn->running;
842                         btn->running = 1;
843                 }
844                 if (btn->wm_instance || btn->wm_class) {
845                         WWindowAttributes attr;
846                         memset(&attr, 0, sizeof(WWindowAttributes));
847                         wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class, &attr, NULL, True);
848
849                         if (!attr.no_appicon && !btn->buggy_app)
850                                 btn->launching = 1;
851                         else
852                                 btn->running = 0;
853                 }
854                 btn->drop_launch = 0;
855                 btn->paste_launch = withSelection;
856                 scr->last_dock = btn->dock;
857                 btn->pid = execCommand(btn, (withSelection ? btn->paste_command : btn->command), NULL);
858                 if (btn->pid > 0) {
859                         if (btn->buggy_app) {
860                                 /* give feedback that the app was launched */
861                                 btn->launching = 1;
862                                 dockIconPaint(btn);
863                                 btn->launching = 0;
864                                 WMAddTimerHandler(200, (WMCallback *) dockIconPaint, btn);
865                         } else {
866                                 dockIconPaint(btn);
867                         }
868                 } else {
869                         wwarning(_("could not launch application %s\n"), btn->command);
870                         btn->launching = 0;
871                         if (!btn->relaunching) {
872                                 btn->running = 0;
873                         }
874                 }
875         }
876 }
877
878 static void updateWorkspaceMenu(WMenu * menu, WAppIcon * icon)
879 {
880         WScreen *scr = menu->frame->screen_ptr;
881         char title[MAX_WORKSPACENAME_WIDTH + 1];
882         int i;
883
884         if (!menu || !icon)
885                 return;
886
887         for (i = 0; i < scr->workspace_count; i++) {
888                 if (i < menu->entry_no) {
889                         if (strcmp(menu->entries[i]->text, scr->workspaces[i]->name) != 0) {
890                                 wfree(menu->entries[i]->text);
891                                 strcpy(title, scr->workspaces[i]->name);
892                                 menu->entries[i]->text = wstrdup(title);
893                                 menu->flags.realized = 0;
894                         }
895                         menu->entries[i]->clientdata = (void *)icon;
896                 } else {
897                         strcpy(title, scr->workspaces[i]->name);
898
899                         wMenuAddCallback(menu, title, switchWSCommand, (void *)icon);
900
901                         menu->flags.realized = 0;
902                 }
903                 if (i == scr->current_workspace) {
904                         wMenuSetEnabled(menu, i, False);
905                 } else {
906                         wMenuSetEnabled(menu, i, True);
907                 }
908         }
909
910         if (!menu->flags.realized)
911                 wMenuRealize(menu);
912 }
913
914 static WMenu *makeWorkspaceMenu(WScreen * scr)
915 {
916         WMenu *menu;
917
918         menu = wMenuCreate(scr, NULL, False);
919         if (!menu)
920                 wwarning(_("could not create workspace submenu for Clip menu"));
921
922         wMenuAddCallback(menu, "", switchWSCommand, (void *)scr->clip_icon);
923
924         menu->flags.realized = 0;
925         wMenuRealize(menu);
926
927         return menu;
928 }
929
930 static void updateClipOptionsMenu(WMenu * menu, WDock * dock)
931 {
932         WMenuEntry *entry;
933         int index = 0;
934
935         if (!menu || !dock)
936                 return;
937
938         /* keep on top */
939         entry = menu->entries[index];
940         entry->flags.indicator_on = !dock->lowered;
941         entry->clientdata = dock;
942
943         /* collapsed */
944         entry = menu->entries[++index];
945         entry->flags.indicator_on = dock->collapsed;
946         entry->clientdata = dock;
947
948         /* auto-collapse */
949         entry = menu->entries[++index];
950         entry->flags.indicator_on = dock->auto_collapse;
951         entry->clientdata = dock;
952
953         /* auto-raise/lower */
954         entry = menu->entries[++index];
955         entry->flags.indicator_on = dock->auto_raise_lower;
956         entry->clientdata = dock;
957         wMenuSetEnabled(menu, index, dock->lowered);
958
959         /* attract icons */
960         entry = menu->entries[++index];
961         entry->flags.indicator_on = dock->attract_icons;
962         entry->clientdata = dock;
963
964         menu->flags.realized = 0;
965         wMenuRealize(menu);
966 }
967
968 static WMenu *makeClipOptionsMenu(WScreen * scr)
969 {
970         WMenu *menu;
971         WMenuEntry *entry;
972
973         menu = wMenuCreate(scr, NULL, False);
974         if (!menu) {
975                 wwarning(_("could not create options submenu for Clip menu"));
976                 return NULL;
977         }
978
979         entry = wMenuAddCallback(menu, _("Keep on Top"), toggleLoweredCallback, NULL);
980         entry->flags.indicator = 1;
981         entry->flags.indicator_on = 1;
982         entry->flags.indicator_type = MI_CHECK;
983
984         entry = wMenuAddCallback(menu, _("Collapsed"), toggleCollapsedCallback, NULL);
985         entry->flags.indicator = 1;
986         entry->flags.indicator_on = 1;
987         entry->flags.indicator_type = MI_CHECK;
988
989         entry = wMenuAddCallback(menu, _("Autocollapse"), toggleAutoCollapseCallback, NULL);
990         entry->flags.indicator = 1;
991         entry->flags.indicator_on = 1;
992         entry->flags.indicator_type = MI_CHECK;
993
994         entry = wMenuAddCallback(menu, _("Autoraise"), toggleAutoRaiseLowerCallback, NULL);
995         entry->flags.indicator = 1;
996         entry->flags.indicator_on = 1;
997         entry->flags.indicator_type = MI_CHECK;
998
999         entry = wMenuAddCallback(menu, _("Autoattract Icons"), toggleAutoAttractCallback, NULL);
1000         entry->flags.indicator = 1;
1001         entry->flags.indicator_on = 1;
1002         entry->flags.indicator_type = MI_CHECK;
1003
1004         menu->flags.realized = 0;
1005         wMenuRealize(menu);
1006
1007         return menu;
1008 }
1009
1010 static WMenu *dockMenuCreate(WScreen * scr, int type)
1011 {
1012         WMenu *menu;
1013         WMenuEntry *entry;
1014
1015         if (type == WM_CLIP && scr->clip_menu)
1016                 return scr->clip_menu;
1017
1018         menu = wMenuCreate(scr, NULL, False);
1019         if (type != WM_CLIP) {
1020                 entry = wMenuAddCallback(menu, _("Keep on Top"), toggleLoweredCallback, NULL);
1021                 entry->flags.indicator = 1;
1022                 entry->flags.indicator_on = 1;
1023                 entry->flags.indicator_type = MI_CHECK;
1024         } else {
1025                 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1026                 scr->clip_options = makeClipOptionsMenu(scr);
1027                 if (scr->clip_options)
1028                         wMenuEntrySetCascade(menu, entry, scr->clip_options);
1029
1030                 entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback, NULL);
1031                 wfree(entry->text);
1032                 entry->text = _("Rename Workspace");
1033
1034                 entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
1035                 entry->flags.indicator = 1;
1036                 entry->flags.indicator_on = 1;
1037                 entry->flags.indicator_type = MI_CHECK;
1038
1039                 entry = wMenuAddCallback(menu, _("Select All Icons"), selectIconsCallback, NULL);
1040                 wfree(entry->text);
1041                 entry->text = _("Select All Icons");
1042
1043                 entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
1044                 wfree(entry->text);
1045                 entry->text = _("Keep Icon");
1046
1047                 entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
1048                 wfree(entry->text);
1049                 entry->text = _("Move Icon To");
1050                 scr->clip_submenu = makeWorkspaceMenu(scr);
1051                 if (scr->clip_submenu)
1052                         wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1053
1054                 entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback, NULL);
1055                 wfree(entry->text);
1056                 entry->text = _("Remove Icon");
1057
1058                 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1059         }
1060
1061         wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1062
1063         wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1064
1065         entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1066         wfree(entry->text);
1067         entry->text = _("Hide");
1068
1069         wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1070
1071         wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1072
1073         if (type == WM_CLIP)
1074                 scr->clip_menu = menu;
1075
1076         return menu;
1077 }
1078
1079 WDock *wDockCreate(WScreen * scr, int type)
1080 {
1081         WDock *dock;
1082         WAppIcon *btn;
1083         int icon_count;
1084
1085         make_keys();
1086
1087         dock = wmalloc(sizeof(WDock));
1088         memset(dock, 0, sizeof(WDock));
1089
1090         if (type == WM_CLIP)
1091                 icon_count = CLIP_MAX_ICONS;
1092         else
1093                 icon_count = scr->scr_height / wPreferences.icon_size;
1094
1095         dock->icon_array = wmalloc(sizeof(WAppIcon *) * icon_count);
1096         memset(dock->icon_array, 0, sizeof(WAppIcon *) * icon_count);
1097
1098         dock->max_icons = icon_count;
1099
1100         btn = mainIconCreate(scr, type);
1101
1102         btn->dock = dock;
1103
1104         dock->x_pos = btn->x_pos;
1105         dock->y_pos = btn->y_pos;
1106         dock->screen_ptr = scr;
1107         dock->type = type;
1108         dock->icon_count = 1;
1109         dock->on_right_side = 1;
1110         dock->collapsed = 0;
1111         dock->auto_collapse = 0;
1112         dock->auto_collapse_magic = NULL;
1113         dock->auto_raise_lower = 0;
1114         dock->auto_lower_magic = NULL;
1115         dock->auto_raise_magic = NULL;
1116         dock->attract_icons = 0;
1117         dock->lowered = 1;
1118         dock->icon_array[0] = btn;
1119         wRaiseFrame(btn->icon->core);
1120         XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1121
1122         /* create dock menu */
1123         dock->menu = dockMenuCreate(scr, type);
1124
1125         return dock;
1126 }
1127
1128 void wDockDestroy(WDock * dock)
1129 {
1130         int i;
1131         WAppIcon *aicon;
1132
1133         for (i = (dock->type == WM_CLIP) ? 1 : 0; i < dock->max_icons; i++) {
1134                 aicon = dock->icon_array[i];
1135                 if (aicon) {
1136                         int keepit = aicon->running && wApplicationOf(aicon->main_window);
1137                         wDockDetach(dock, aicon);
1138                         if (keepit) {
1139                                 /* XXX: can: aicon->icon == NULL ? */
1140                                 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos,
1141                                           wGetHeadForWindow(aicon->icon->owner));
1142                                 XMoveWindow(dpy, aicon->icon->core->window, aicon->x_pos, aicon->y_pos);
1143                                 if (!dock->mapped || dock->collapsed)
1144                                         XMapWindow(dpy, aicon->icon->core->window);
1145                         }
1146                 }
1147         }
1148         if (wPreferences.auto_arrange_icons)
1149                 wArrangeIcons(dock->screen_ptr, True);
1150         wfree(dock->icon_array);
1151         if (dock->menu && dock->type != WM_CLIP)
1152                 wMenuDestroy(dock->menu, True);
1153         if (dock->screen_ptr->last_dock == dock)
1154                 dock->screen_ptr->last_dock = NULL;
1155         wfree(dock);
1156 }
1157
1158 void wClipIconPaint(WAppIcon * aicon)
1159 {
1160         WScreen *scr = aicon->icon->core->screen_ptr;
1161         WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1162         WMColor *color;
1163         Window win = aicon->icon->core->window;
1164         int length, nlength;
1165         char *ws_name, ws_number[10];
1166         int ty, tx;
1167
1168         wIconPaint(aicon->icon);
1169
1170         length = strlen(workspace->name);
1171         ws_name = wmalloc(length + 1);
1172         snprintf(ws_name, length + 1, "%s", workspace->name);
1173         snprintf(ws_number, sizeof(ws_number), "%i", scr->current_workspace + 1);
1174         nlength = strlen(ws_number);
1175
1176         if (!workspace->clip->collapsed)
1177                 color = scr->clip_title_color[CLIP_NORMAL];
1178         else
1179                 color = scr->clip_title_color[CLIP_COLLAPSED];
1180
1181         ty = ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
1182
1183         tx = CLIP_BUTTON_SIZE * ICON_SIZE / 64;
1184
1185         WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx, ty, ws_name, length);
1186         /*WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, 4,
1187            2, ws_name, length); */
1188
1189         tx = (ICON_SIZE / 2 - WMWidthOfString(scr->clip_title_font, ws_number, nlength)) / 2;
1190
1191         WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx, 2, ws_number, nlength);
1192
1193         wfree(ws_name);
1194
1195         if (aicon->launching) {
1196                 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1197                                0, 0, wPreferences.icon_size, wPreferences.icon_size);
1198         }
1199         paintClipButtons(aicon, aicon->dock->lclip_button_pushed, aicon->dock->rclip_button_pushed);
1200 }
1201
1202 static void clipIconExpose(WObjDescriptor * desc, XEvent * event)
1203 {
1204         wClipIconPaint(desc->parent);
1205 }
1206
1207 static void dockIconPaint(WAppIcon * btn)
1208 {
1209         if (btn == btn->icon->core->screen_ptr->clip_icon)
1210                 wClipIconPaint(btn);
1211         else
1212                 wAppIconPaint(btn);
1213 }
1214
1215 static WMPropList *make_icon_state(WAppIcon * btn)
1216 {
1217         WMPropList *node = NULL;
1218         WMPropList *command, *autolaunch, *lock, *name, *forced, *host;
1219         WMPropList *position, *buggy, *omnipresent;
1220         char *tmp;
1221         char buffer[64];
1222
1223         if (btn) {
1224                 if (!btn->command)
1225                         command = WMCreatePLString("-");
1226                 else
1227                         command = WMCreatePLString(btn->command);
1228
1229                 autolaunch = btn->auto_launch ? dYes : dNo;
1230
1231                 lock = btn->lock ? dYes : dNo;
1232
1233                 tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
1234
1235                 name = WMCreatePLString(tmp);
1236
1237                 wfree(tmp);
1238
1239                 forced = btn->forced_dock ? dYes : dNo;
1240
1241                 buggy = btn->buggy_app ? dYes : dNo;
1242
1243                 if (btn == btn->icon->core->screen_ptr->clip_icon)
1244                         snprintf(buffer, sizeof(buffer), "%i,%i", btn->x_pos, btn->y_pos);
1245                 else
1246                         snprintf(buffer, sizeof(buffer), "%hi,%hi", btn->xindex, btn->yindex);
1247                 position = WMCreatePLString(buffer);
1248
1249                 node = WMCreatePLDictionary(dCommand, command,
1250                                             dName, name,
1251                                             dAutoLaunch, autolaunch,
1252                                             dLock, lock,
1253                                             dForced, forced, dBuggyApplication, buggy, dPosition, position, NULL);
1254                 WMReleasePropList(command);
1255                 WMReleasePropList(name);
1256                 WMReleasePropList(position);
1257
1258                 omnipresent = btn->omnipresent ? dYes : dNo;
1259                 if (btn->dock != btn->icon->core->screen_ptr->dock && (btn->xindex != 0 || btn->yindex != 0))
1260                         WMPutInPLDictionary(node, dOmnipresent, omnipresent);
1261
1262 #ifdef XDND                     /* was OFFIX */
1263                 if (btn->dnd_command) {
1264                         command = WMCreatePLString(btn->dnd_command);
1265                         WMPutInPLDictionary(node, dDropCommand, command);
1266                         WMReleasePropList(command);
1267                 }
1268 #endif                          /* XDND */
1269
1270                 if (btn->paste_command) {
1271                         command = WMCreatePLString(btn->paste_command);
1272                         WMPutInPLDictionary(node, dPasteCommand, command);
1273                         WMReleasePropList(command);
1274                 }
1275
1276                 if (btn->client_machine && btn->remote_start) {
1277                         host = WMCreatePLString(btn->client_machine);
1278                         WMPutInPLDictionary(node, dHost, host);
1279                         WMReleasePropList(host);
1280                 }
1281         }
1282
1283         return node;
1284 }
1285
1286 static WMPropList *dockSaveState(WDock * dock)
1287 {
1288         int i;
1289         WMPropList *icon_info;
1290         WMPropList *list = NULL, *dock_state = NULL;
1291         WMPropList *value, *key;
1292         char buffer[256];
1293
1294         list = WMCreatePLArray(NULL);
1295
1296         for (i = (dock->type == WM_DOCK ? 0 : 1); i < dock->max_icons; i++) {
1297                 WAppIcon *btn = dock->icon_array[i];
1298
1299                 if (!btn || btn->attracted)
1300                         continue;
1301
1302                 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1303                         WMAddToPLArray(list, icon_info);
1304                         WMReleasePropList(icon_info);
1305                 }
1306         }
1307
1308         dock_state = WMCreatePLDictionary(dApplications, list, NULL);
1309
1310         if (dock->type == WM_DOCK) {
1311                 snprintf(buffer, sizeof(buffer), "Applications%i", dock->screen_ptr->scr_height);
1312                 key = WMCreatePLString(buffer);
1313                 WMPutInPLDictionary(dock_state, key, list);
1314                 WMReleasePropList(key);
1315
1316                 snprintf(buffer, sizeof(buffer), "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0), dock->y_pos);
1317                 value = WMCreatePLString(buffer);
1318                 WMPutInPLDictionary(dock_state, dPosition, value);
1319                 WMReleasePropList(value);
1320         }
1321         WMReleasePropList(list);
1322
1323         value = (dock->lowered ? dYes : dNo);
1324         WMPutInPLDictionary(dock_state, dLowered, value);
1325
1326         if (dock->type == WM_CLIP) {
1327                 value = (dock->collapsed ? dYes : dNo);
1328                 WMPutInPLDictionary(dock_state, dCollapsed, value);
1329
1330                 value = (dock->auto_collapse ? dYes : dNo);
1331                 WMPutInPLDictionary(dock_state, dAutoCollapse, value);
1332
1333                 value = (dock->auto_raise_lower ? dYes : dNo);
1334                 WMPutInPLDictionary(dock_state, dAutoRaiseLower, value);
1335
1336                 value = (dock->attract_icons ? dYes : dNo);
1337                 WMPutInPLDictionary(dock_state, dAutoAttractIcons, value);
1338         }
1339
1340         return dock_state;
1341 }
1342
1343 void wDockSaveState(WScreen * scr, WMPropList * old_state)
1344 {
1345         WMPropList *dock_state;
1346         WMPropList *keys;
1347
1348         dock_state = dockSaveState(scr->dock);
1349
1350         /*
1351          * Copy saved states of docks with different sizes.
1352          */
1353         if (old_state) {
1354                 int i;
1355                 WMPropList *tmp;
1356
1357                 keys = WMGetPLDictionaryKeys(old_state);
1358                 for (i = 0; i < WMGetPropListItemCount(keys); i++) {
1359                         tmp = WMGetFromPLArray(keys, i);
1360
1361                         if (strncasecmp(WMGetFromPLString(tmp), "applications", 12) == 0
1362                             && !WMGetFromPLDictionary(dock_state, tmp)) {
1363
1364                                 WMPutInPLDictionary(dock_state, tmp, WMGetFromPLDictionary(old_state, tmp));
1365                         }
1366                 }
1367                 WMReleasePropList(keys);
1368         }
1369
1370         WMPutInPLDictionary(scr->session_state, dDock, dock_state);
1371
1372         WMReleasePropList(dock_state);
1373 }
1374
1375 void wClipSaveState(WScreen * scr)
1376 {
1377         WMPropList *clip_state;
1378
1379         clip_state = make_icon_state(scr->clip_icon);
1380
1381         WMPutInPLDictionary(scr->session_state, dClip, clip_state);
1382
1383         WMReleasePropList(clip_state);
1384 }
1385
1386 WMPropList *wClipSaveWorkspaceState(WScreen * scr, int workspace)
1387 {
1388         return dockSaveState(scr->workspaces[workspace]->clip);
1389 }
1390
1391 static Bool getBooleanDockValue(WMPropList * value, WMPropList * key)
1392 {
1393         if (value) {
1394                 if (WMIsPLString(value)) {
1395                         if (strcasecmp(WMGetFromPLString(value), "YES") == 0)
1396                                 return True;
1397                 } else {
1398                         wwarning(_("bad value in docked icon state info %s"), WMGetFromPLString(key));
1399                 }
1400         }
1401         return False;
1402 }
1403
1404 static WAppIcon *restore_icon_state(WScreen * scr, WMPropList * info, int type, int index)
1405 {
1406         WAppIcon *aicon;
1407         WMPropList *cmd, *value;
1408
1409         cmd = WMGetFromPLDictionary(info, dCommand);
1410         if (!cmd || !WMIsPLString(cmd)) {
1411                 return NULL;
1412         }
1413
1414         /* parse window name */
1415         value = WMGetFromPLDictionary(info, dName);
1416         if (!value)
1417                 return NULL;
1418
1419         {
1420                 char *wclass, *winstance;
1421                 char *command;
1422
1423                 ParseWindowName(value, &winstance, &wclass, "dock");
1424
1425                 if (!winstance && !wclass) {
1426                         return NULL;
1427                 }
1428
1429                 /* get commands */
1430
1431                 if (cmd)
1432                         command = wstrdup(WMGetFromPLString(cmd));
1433                 else
1434                         command = NULL;
1435
1436                 if (!command || strcmp(command, "-") == 0) {
1437                         if (command)
1438                                 wfree(command);
1439                         if (wclass)
1440                                 wfree(wclass);
1441                         if (winstance)
1442                                 wfree(winstance);
1443
1444                         return NULL;
1445                 }
1446
1447                 aicon = wAppIconCreateForDock(scr, command, winstance, wclass, TILE_NORMAL);
1448                 if (wclass)
1449                         wfree(wclass);
1450                 if (winstance)
1451                         wfree(winstance);
1452                 if (command)
1453                         wfree(command);
1454         }
1455
1456         aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1457         if (type == WM_CLIP) {
1458                 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1459                 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1460         }
1461         aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1462         aicon->icon->core->descriptor.parent = aicon;
1463
1464 #ifdef XDND                     /* was OFFIX */
1465         cmd = WMGetFromPLDictionary(info, dDropCommand);
1466         if (cmd)
1467                 aicon->dnd_command = wstrdup(WMGetFromPLString(cmd));
1468 #endif
1469
1470         cmd = WMGetFromPLDictionary(info, dPasteCommand);
1471         if (cmd)
1472                 aicon->paste_command = wstrdup(WMGetFromPLString(cmd));
1473
1474         /* check auto launch */
1475         value = WMGetFromPLDictionary(info, dAutoLaunch);
1476
1477         aicon->auto_launch = getBooleanDockValue(value, dAutoLaunch);
1478
1479         /* check lock */
1480         value = WMGetFromPLDictionary(info, dLock);
1481
1482         aicon->lock = getBooleanDockValue(value, dLock);
1483
1484         /* check if it wasn't normally docked */
1485         value = WMGetFromPLDictionary(info, dForced);
1486
1487         aicon->forced_dock = getBooleanDockValue(value, dForced);
1488
1489         /* check if we can rely on the stuff in the app */
1490         value = WMGetFromPLDictionary(info, dBuggyApplication);
1491
1492         aicon->buggy_app = getBooleanDockValue(value, dBuggyApplication);
1493
1494         /* get position in the dock */
1495         value = WMGetFromPLDictionary(info, dPosition);
1496         if (value && WMIsPLString(value)) {
1497                 if (sscanf(WMGetFromPLString(value), "%hi,%hi", &aicon->xindex, &aicon->yindex) != 2)
1498                         wwarning(_("bad value in docked icon state info %s"), WMGetFromPLString(dPosition));
1499
1500                 /* check position sanity */
1501                 /* incomplete section! */
1502                 if (type == WM_DOCK) {
1503                         aicon->xindex = 0;
1504                         if (aicon->yindex < 0)
1505                                 wwarning(_("bad value in docked icon position %i,%i"),
1506                                          aicon->xindex, aicon->yindex);
1507                 }
1508         } else {
1509                 aicon->yindex = index;
1510                 aicon->xindex = 0;
1511         }
1512
1513         /* check if icon is omnipresent */
1514         value = WMGetFromPLDictionary(info, dOmnipresent);
1515
1516         aicon->omnipresent = getBooleanDockValue(value, dOmnipresent);
1517
1518         aicon->running = 0;
1519         aicon->docked = 1;
1520
1521         return aicon;
1522 }
1523
1524 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1525
1526 WAppIcon *wClipRestoreState(WScreen * scr, WMPropList * clip_state)
1527 {
1528         WAppIcon *icon;
1529         WMPropList *value;
1530
1531         icon = mainIconCreate(scr, WM_CLIP);
1532
1533         if (!clip_state)
1534                 return icon;
1535         else
1536                 WMRetainPropList(clip_state);
1537
1538         /* restore position */
1539
1540         value = WMGetFromPLDictionary(clip_state, dPosition);
1541
1542         if (value) {
1543                 if (!WMIsPLString(value))
1544                         COMPLAIN("Position");
1545                 else {
1546                         WMRect rect;
1547                         int flags;
1548
1549                         if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos, &icon->y_pos) != 2)
1550                                 COMPLAIN("Position");
1551
1552                         /* check position sanity */
1553                         rect.pos.x = icon->x_pos;
1554                         rect.pos.y = icon->y_pos;
1555                         rect.size.width = rect.size.height = ICON_SIZE;
1556
1557                         wGetRectPlacementInfo(scr, rect, &flags);
1558                         if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
1559                                 wScreenKeepInside(scr, &icon->x_pos, &icon->y_pos, ICON_SIZE, ICON_SIZE);
1560                 }
1561         }
1562 #ifdef XDND                     /* was OFFIX */
1563         value = WMGetFromPLDictionary(clip_state, dDropCommand);
1564         if (value && WMIsPLString(value))
1565                 icon->dnd_command = wstrdup(WMGetFromPLString(value));
1566 #endif
1567
1568         value = WMGetFromPLDictionary(clip_state, dPasteCommand);
1569         if (value && WMIsPLString(value))
1570                 icon->paste_command = wstrdup(WMGetFromPLString(value));
1571
1572         WMReleasePropList(clip_state);
1573
1574         return icon;
1575 }
1576
1577 WDock *wDockRestoreState(WScreen * scr, WMPropList * dock_state, int type)
1578 {
1579         WDock *dock;
1580         WMPropList *apps;
1581         WMPropList *value;
1582         WAppIcon *aicon, *old_top;
1583         int count, i;
1584
1585         dock = wDockCreate(scr, type);
1586
1587         if (!dock_state)
1588                 return dock;
1589
1590         if (dock_state)
1591                 WMRetainPropList(dock_state);
1592
1593         /* restore position */
1594
1595         value = WMGetFromPLDictionary(dock_state, dPosition);
1596
1597         if (value) {
1598                 if (!WMIsPLString(value)) {
1599                         COMPLAIN("Position");
1600                 } else {
1601                         WMRect rect;
1602                         int flags;
1603
1604                         if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos, &dock->y_pos) != 2)
1605                                 COMPLAIN("Position");
1606
1607                         /* check position sanity */
1608                         rect.pos.x = dock->x_pos;
1609                         rect.pos.y = dock->y_pos;
1610                         rect.size.width = rect.size.height = ICON_SIZE;
1611
1612                         wGetRectPlacementInfo(scr, rect, &flags);
1613                         if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) {
1614                                 int x = dock->x_pos;
1615                                 wScreenKeepInside(scr, &x, &dock->y_pos, ICON_SIZE, ICON_SIZE);
1616                         }
1617
1618                         /* Is this needed any more? */
1619                         if (type == WM_CLIP) {
1620                                 if (dock->x_pos < 0) {
1621                                         dock->x_pos = 0;
1622                                 } else if (dock->x_pos > scr->scr_width - ICON_SIZE) {
1623                                         dock->x_pos = scr->scr_width - ICON_SIZE;
1624                                 }
1625                         } else {
1626                                 if (dock->x_pos >= 0) {
1627                                         dock->x_pos = DOCK_EXTRA_SPACE;
1628                                         dock->on_right_side = 0;
1629                                 } else {
1630                                         dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1631                                         dock->on_right_side = 1;
1632                                 }
1633                         }
1634                 }
1635         }
1636
1637         /* restore lowered/raised state */
1638
1639         dock->lowered = 0;
1640
1641         value = WMGetFromPLDictionary(dock_state, dLowered);
1642
1643         if (value) {
1644                 if (!WMIsPLString(value)) {
1645                         COMPLAIN("Lowered");
1646                 } else {
1647                         if (strcasecmp(WMGetFromPLString(value), "YES") == 0) {
1648                                 dock->lowered = 1;
1649                         }
1650                 }
1651         }
1652
1653         /* restore collapsed state */
1654
1655         dock->collapsed = 0;
1656
1657         value = WMGetFromPLDictionary(dock_state, dCollapsed);
1658
1659         if (value) {
1660                 if (!WMIsPLString(value)) {
1661                         COMPLAIN("Collapsed");
1662                 } else {
1663                         if (strcasecmp(WMGetFromPLString(value), "YES") == 0) {
1664                                 dock->collapsed = 1;
1665                         }
1666                 }
1667         }
1668
1669         /* restore auto-collapsed state */
1670
1671         value = WMGetFromPLDictionary(dock_state, dAutoCollapse);
1672
1673         if (value) {
1674                 if (!WMIsPLString(value)) {
1675                         COMPLAIN("AutoCollapse");
1676                 } else {
1677                         if (strcasecmp(WMGetFromPLString(value), "YES") == 0) {
1678                                 dock->auto_collapse = 1;
1679                                 dock->collapsed = 1;
1680                         }
1681                 }
1682         }
1683
1684         /* restore auto-raise/lower state */
1685
1686         value = WMGetFromPLDictionary(dock_state, dAutoRaiseLower);
1687
1688         if (value) {
1689                 if (!WMIsPLString(value)) {
1690                         COMPLAIN("AutoRaiseLower");
1691                 } else {
1692                         if (strcasecmp(WMGetFromPLString(value), "YES") == 0) {
1693                                 dock->auto_raise_lower = 1;
1694                         }
1695                 }
1696         }
1697
1698         /* restore attract icons state */
1699
1700         dock->attract_icons = 0;
1701
1702         value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons);
1703
1704         if (value) {
1705                 if (!WMIsPLString(value)) {
1706                         COMPLAIN("AutoAttractIcons");
1707                 } else {
1708                         if (strcasecmp(WMGetFromPLString(value), "YES") == 0) {
1709                                 dock->attract_icons = 1;
1710                         }
1711                 }
1712         }
1713
1714         /* application list */
1715
1716         {
1717                 WMPropList *tmp;
1718                 char buffer[64];
1719
1720                 /*
1721                  * When saving, it saves the dock state in
1722                  * Applications and Applicationsnnn
1723                  *
1724                  * When loading, it will first try Applicationsnnn.
1725                  * If it does not exist, use Applications as default.
1726                  */
1727
1728                 snprintf(buffer, sizeof(buffer), "Applications%i", scr->scr_height);
1729
1730                 tmp = WMCreatePLString(buffer);
1731                 apps = WMGetFromPLDictionary(dock_state, tmp);
1732                 WMReleasePropList(tmp);
1733
1734                 if (!apps) {
1735                         apps = WMGetFromPLDictionary(dock_state, dApplications);
1736                 }
1737         }
1738
1739         if (!apps) {
1740                 goto finish;
1741         }
1742
1743         count = WMGetPropListItemCount(apps);
1744
1745         if (count == 0)
1746                 goto finish;
1747
1748         old_top = dock->icon_array[0];
1749
1750         /* dock->icon_count is set to 1 when dock is created.
1751          * Since Clip is already restored, we want to keep it so for clip,
1752          * but for dock we may change the default top tile, so we set it to 0.
1753          */
1754         if (type == WM_DOCK)
1755                 dock->icon_count = 0;
1756
1757         for (i = 0; i < count; i++) {
1758                 if (dock->icon_count >= dock->max_icons) {
1759                         wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1760                         break;
1761                 }
1762
1763                 value = WMGetFromPLArray(apps, i);
1764                 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1765
1766                 dock->icon_array[dock->icon_count] = aicon;
1767
1768                 if (aicon) {
1769                         aicon->dock = dock;
1770                         aicon->x_pos = dock->x_pos + (aicon->xindex * ICON_SIZE);
1771                         aicon->y_pos = dock->y_pos + (aicon->yindex * ICON_SIZE);
1772
1773                         if (dock->lowered)
1774                                 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1775                         else
1776                                 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1777
1778                         wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos, 0, 0);
1779
1780                         if (!dock->collapsed)
1781                                 XMapWindow(dpy, aicon->icon->core->window);
1782                         wRaiseFrame(aicon->icon->core);
1783
1784                         dock->icon_count++;
1785                 } else if (dock->icon_count == 0 && type == WM_DOCK)
1786                         dock->icon_count++;
1787         }
1788
1789         /* if the first icon is not defined, use the default */
1790         if (dock->icon_array[0] == NULL) {
1791                 /* update default icon */
1792                 old_top->x_pos = dock->x_pos;
1793                 old_top->y_pos = dock->y_pos;
1794                 if (dock->lowered)
1795                         ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1796                 else
1797                         ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1798                 dock->icon_array[0] = old_top;
1799                 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1800                 /* we don't need to increment dock->icon_count here because it was
1801                  * incremented in the loop above.
1802                  */
1803         } else if (old_top != dock->icon_array[0]) {
1804                 if (old_top == scr->clip_icon)
1805                         scr->clip_icon = dock->icon_array[0];
1806                 wAppIconDestroy(old_top);
1807         }
1808
1809  finish:
1810         if (dock_state)
1811                 WMReleasePropList(dock_state);
1812
1813         return dock;
1814 }
1815
1816 void wDockLaunchWithState(WDock * dock, WAppIcon * btn, WSavedState * state)
1817 {
1818         if (btn && btn->command && !btn->running && !btn->launching) {
1819
1820                 btn->drop_launch = 0;
1821                 btn->paste_launch = 0;
1822
1823                 btn->pid = execCommand(btn, btn->command, state);
1824
1825                 if (btn->pid > 0) {
1826                         if (!btn->forced_dock && !btn->buggy_app) {
1827                                 btn->launching = 1;
1828                                 dockIconPaint(btn);
1829                         }
1830                 }
1831         } else {
1832                 wfree(state);
1833         }
1834 }
1835
1836 void wDockDoAutoLaunch(WDock * dock, int workspace)
1837 {
1838         WAppIcon *btn;
1839         WSavedState *state;
1840         int i;
1841
1842         for (i = 0; i < dock->max_icons; i++) {
1843                 btn = dock->icon_array[i];
1844                 if (!btn || !btn->auto_launch)
1845                         continue;
1846
1847                 state = wmalloc(sizeof(WSavedState));
1848                 memset(state, 0, sizeof(WSavedState));
1849                 state->workspace = workspace;
1850                 /* TODO: this is klugy and is very difficult to understand
1851                  * what's going on. Try to clean up */
1852                 wDockLaunchWithState(dock, btn, state);
1853         }
1854 }
1855
1856 #ifdef XDND                     /* was OFFIX */
1857 static WDock *findDock(WScreen * scr, XEvent * event, int *icon_pos)
1858 {
1859         WDock *dock;
1860         int i;
1861
1862         *icon_pos = -1;
1863         if ((dock = scr->dock) != NULL) {
1864                 for (i = 0; i < dock->max_icons; i++) {
1865                         if (dock->icon_array[i]
1866                             && dock->icon_array[i]->icon->core->window == event->xclient.window) {
1867                                 *icon_pos = i;
1868                                 break;
1869                         }
1870                 }
1871         }
1872         if (*icon_pos < 0 && (dock = scr->workspaces[scr->current_workspace]->clip) != NULL) {
1873                 for (i = 0; i < dock->max_icons; i++) {
1874                         if (dock->icon_array[i]
1875                             && dock->icon_array[i]->icon->core->window == event->xclient.window) {
1876                                 *icon_pos = i;
1877                                 break;
1878                         }
1879                 }
1880         }
1881         if (*icon_pos >= 0)
1882                 return dock;
1883         return NULL;
1884 }
1885
1886 int wDockReceiveDNDDrop(WScreen * scr, XEvent * event)
1887 {
1888         WDock *dock;
1889         WAppIcon *btn;
1890         int icon_pos;
1891
1892         dock = findDock(scr, event, &icon_pos);
1893         if (!dock)
1894                 return False;
1895
1896         /*
1897          * Return True if the drop was on an application icon window.
1898          * In this case, let the ClientMessage handler redirect the
1899          * message to the app.
1900          */
1901         if (dock->icon_array[icon_pos]->icon->icon_win != None)
1902                 return True;
1903
1904         if (dock->icon_array[icon_pos]->dnd_command != NULL) {
1905                 scr->flags.dnd_data_convertion_status = 0;
1906
1907                 btn = dock->icon_array[icon_pos];
1908
1909                 if (!btn->forced_dock) {
1910                         btn->relaunching = btn->running;
1911                         btn->running = 1;
1912                 }
1913                 if (btn->wm_instance || btn->wm_class) {
1914                         WWindowAttributes attr;
1915                         memset(&attr, 0, sizeof(WWindowAttributes));
1916                         wDefaultFillAttributes(btn->icon->core->screen_ptr,
1917                                                btn->wm_instance, btn->wm_class, &attr, NULL, True);
1918
1919                         if (!attr.no_appicon)
1920                                 btn->launching = 1;
1921                         else
1922                                 btn->running = 0;
1923                 }
1924
1925                 btn->paste_launch = 0;
1926                 btn->drop_launch = 1;
1927                 scr->last_dock = dock;
1928                 btn->pid = execCommand(btn, btn->dnd_command, NULL);
1929                 if (btn->pid > 0) {
1930                         dockIconPaint(btn);
1931                 } else {
1932                         btn->launching = 0;
1933                         if (!btn->relaunching) {
1934                                 btn->running = 0;
1935                         }
1936                 }
1937         }
1938         return False;
1939 }
1940 #endif                          /* XDND */
1941
1942 Bool wDockAttachIcon(WDock * dock, WAppIcon * icon, int x, int y)
1943 {
1944         WWindow *wwin;
1945         int index;
1946
1947         wwin = icon->icon->owner;
1948         if (icon->command == NULL) {
1949                 char *command;
1950
1951                 icon->editing = 0;
1952
1953                 command = GetCommandForWindow(wwin->client_win);
1954                 if (command) {
1955                         icon->command = command;
1956                 } else {
1957                         /* icon->forced_dock = 1; */
1958                         if (dock->type != WM_CLIP || !icon->attracted) {
1959                                 icon->editing = 1;
1960                                 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
1961                                                  _("Type the command used to launch the application"), &command)) {
1962                                         if (command && (command[0] == 0 || (command[0] == '-' && command[1] == 0))) {
1963                                                 wfree(command);
1964                                                 command = NULL;
1965                                         }
1966                                         icon->command = command;
1967                                         icon->editing = 0;
1968                                 } else {
1969                                         icon->editing = 0;
1970                                         if (command)
1971                                                 wfree(command);
1972                                         /* If the target is the dock, reject the icon. If
1973                                          * the target is the clip, make it an attracted icon
1974                                          */
1975                                         if (dock->type == WM_CLIP) {
1976                                                 icon->attracted = 1;
1977                                                 if (!icon->icon->shadowed) {
1978                                                         icon->icon->shadowed = 1;
1979                                                         icon->icon->force_paint = 1;
1980                                                 }
1981                                         } else {
1982                                                 return False;
1983                                         }
1984                                 }
1985                         }
1986                 }
1987         } else {
1988                 icon->editing = 0;
1989         }
1990
1991         for (index = 1; index < dock->max_icons; index++)
1992                 if (dock->icon_array[index] == NULL)
1993                         break;
1994         /* if (index == dock->max_icons)
1995            return; */
1996
1997         assert(index < dock->max_icons);
1998
1999         dock->icon_array[index] = icon;
2000         icon->yindex = y;
2001         icon->xindex = x;
2002
2003         icon->omnipresent = 0;
2004
2005         icon->x_pos = dock->x_pos + x * ICON_SIZE;
2006         icon->y_pos = dock->y_pos + y * ICON_SIZE;
2007
2008         dock->icon_count++;
2009
2010         icon->running = 1;
2011         icon->launching = 0;
2012         icon->docked = 1;
2013         icon->dock = dock;
2014         icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2015         if (dock->type == WM_CLIP) {
2016                 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2017                 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2018         }
2019         icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2020         icon->icon->core->descriptor.parent = icon;
2021
2022         MoveInStackListUnder(dock->icon_array[index - 1]->icon->core, icon->icon->core);
2023         wAppIconMove(icon, icon->x_pos, icon->y_pos);
2024         wAppIconPaint(icon);
2025
2026         if (wPreferences.auto_arrange_icons)
2027                 wArrangeIcons(dock->screen_ptr, True);
2028
2029 #ifdef XDND                     /* was OFFIX */
2030         if (icon->command && !icon->dnd_command) {
2031                 int len = strlen(icon->command) + 8;
2032                 icon->dnd_command = wmalloc(len);
2033                 snprintf(icon->dnd_command, len, "%s %%d", icon->command);
2034         }
2035 #endif
2036
2037         if (icon->command && !icon->paste_command) {
2038                 int len = strlen(icon->command) + 8;
2039                 icon->paste_command = wmalloc(len);
2040                 snprintf(icon->paste_command, len, "%s %%s", icon->command);
2041         }
2042
2043         return True;
2044 }
2045
2046 void reattachIcon(WDock * dock, WAppIcon * icon, int x, int y)
2047 {
2048         int index;
2049
2050         for (index = 1; index < dock->max_icons; index++) {
2051                 if (dock->icon_array[index] == icon)
2052                         break;
2053         }
2054         assert(index < dock->max_icons);
2055
2056         icon->yindex = y;
2057         icon->xindex = x;
2058
2059         icon->x_pos = dock->x_pos + x * ICON_SIZE;
2060         icon->y_pos = dock->y_pos + y * ICON_SIZE;
2061 }
2062
2063 Bool moveIconBetweenDocks(WDock * src, WDock * dest, WAppIcon * icon, int x, int y)
2064 {
2065         WWindow *wwin;
2066         char *command;
2067         int index;
2068
2069         if (src == dest)
2070                 return True;    /* No move needed, we're already there */
2071
2072         if (dest == NULL)
2073                 return False;
2074
2075         wwin = icon->icon->owner;
2076
2077         /*
2078          * For the moment we can't do this if we move icons in Clip from one
2079          * workspace to other, because if we move two or more icons without
2080          * command, the dialog box will not be able to tell us to which of the
2081          * moved icons it applies. -Dan
2082          */
2083         if ((dest->type == WM_DOCK /*|| dest->keep_attracted */ ) && icon->command == NULL) {
2084                 command = GetCommandForWindow(wwin->client_win);
2085                 if (command) {
2086                         icon->command = command;
2087                 } else {
2088                         icon->editing = 1;
2089                         /* icon->forced_dock = 1; */
2090                         if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2091                                          _("Type the command used to launch the application"), &command)) {
2092                                 if (command && (command[0] == 0 || (command[0] == '-' && command[1] == 0))) {
2093                                         wfree(command);
2094                                         command = NULL;
2095                                 }
2096                                 icon->command = command;
2097                         } else {
2098                                 icon->editing = 0;
2099                                 if (command)
2100                                         wfree(command);
2101                                 return False;
2102                         }
2103                         icon->editing = 0;
2104                 }
2105         }
2106
2107         if (dest->type == WM_DOCK)
2108                 wClipMakeIconOmnipresent(icon, False);
2109
2110         for (index = 1; index < src->max_icons; index++) {
2111                 if (src->icon_array[index] == icon)
2112                         break;
2113         }
2114         assert(index < src->max_icons);
2115
2116         src->icon_array[index] = NULL;
2117         src->icon_count--;
2118
2119         for (index = 1; index < dest->max_icons; index++) {
2120                 if (dest->icon_array[index] == NULL)
2121                         break;
2122         }
2123         /* if (index == dest->max_icons)
2124            return; */
2125
2126         assert(index < dest->max_icons);
2127
2128         dest->icon_array[index] = icon;
2129         icon->dock = dest;
2130
2131         /* deselect the icon */
2132         if (icon->icon->selected)
2133                 wIconSelect(icon->icon);
2134
2135         if (dest->type == WM_DOCK) {
2136                 icon->icon->core->descriptor.handle_enternotify = NULL;
2137                 icon->icon->core->descriptor.handle_leavenotify = NULL;
2138         } else {
2139                 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2140                 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2141         }
2142
2143         /* set it to be kept when moving to dock.
2144          * Unless the icon does not have a command set
2145          */
2146         if (icon->command && dest->type == WM_DOCK) {
2147                 icon->attracted = 0;
2148                 if (icon->icon->shadowed) {
2149                         icon->icon->shadowed = 0;
2150                         icon->icon->force_paint = 1;
2151                 }
2152         }
2153
2154         if (src->auto_collapse || src->auto_raise_lower)
2155                 clipLeave(src);
2156
2157         icon->yindex = y;
2158         icon->xindex = x;
2159
2160         icon->x_pos = dest->x_pos + x * ICON_SIZE;
2161         icon->y_pos = dest->y_pos + y * ICON_SIZE;
2162
2163         dest->icon_count++;
2164
2165         MoveInStackListUnder(dest->icon_array[index - 1]->icon->core, icon->icon->core);
2166         wAppIconPaint(icon);
2167
2168         return True;
2169 }
2170
2171 void wDockDetach(WDock * dock, WAppIcon * icon)
2172 {
2173         int index;
2174
2175         /* make the settings panel be closed */
2176         if (icon->panel) {
2177                 DestroyDockAppSettingsPanel(icon->panel);
2178         }
2179
2180         /* This must be called before icon->dock is set to NULL.
2181          * Don't move it. -Dan
2182          */
2183         wClipMakeIconOmnipresent(icon, False);
2184
2185         icon->docked = 0;
2186         icon->dock = NULL;
2187         icon->attracted = 0;
2188         icon->auto_launch = 0;
2189         if (icon->icon->shadowed) {
2190                 icon->icon->shadowed = 0;
2191                 icon->icon->force_paint = 1;
2192         }
2193
2194         /* deselect the icon */
2195         if (icon->icon->selected)
2196                 wIconSelect(icon->icon);
2197
2198         if (icon->command) {
2199                 wfree(icon->command);
2200                 icon->command = NULL;
2201         }
2202 #ifdef XDND                     /* was OFFIX */
2203         if (icon->dnd_command) {
2204                 wfree(icon->dnd_command);
2205                 icon->dnd_command = NULL;
2206         }
2207 #endif
2208         if (icon->paste_command) {
2209                 wfree(icon->paste_command);
2210                 icon->paste_command = NULL;
2211         }
2212
2213         for (index = 1; index < dock->max_icons; index++)
2214                 if (dock->icon_array[index] == icon)
2215                         break;
2216         assert(index < dock->max_icons);
2217         dock->icon_array[index] = NULL;
2218         icon->yindex = -1;
2219         icon->xindex = -1;
2220
2221         dock->icon_count--;
2222
2223         /* if the dock is not attached to an application or
2224          * the the application did not set the approriate hints yet,
2225          * destroy the icon */
2226         if (!icon->running || !wApplicationOf(icon->main_window))
2227                 wAppIconDestroy(icon);
2228         else {
2229                 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2230                 icon->icon->core->descriptor.handle_enternotify = NULL;
2231                 icon->icon->core->descriptor.handle_leavenotify = NULL;
2232                 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2233                 icon->icon->core->descriptor.parent = icon;
2234
2235                 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2236
2237                 wAppIconPaint(icon);
2238                 if (wPreferences.auto_arrange_icons) {
2239                         wArrangeIcons(dock->screen_ptr, True);
2240                 }
2241         }
2242         if (dock->auto_collapse || dock->auto_raise_lower)
2243                 clipLeave(dock);
2244 }
2245
2246 /*
2247  * returns the closest Dock slot index for the passed
2248  * coordinates.
2249  *
2250  * Returns False if icon can't be docked.
2251  *
2252  * Note: this function should NEVER alter ret_x or ret_y, unless it will
2253  * return True. -Dan
2254  */
2255 Bool wDockSnapIcon(WDock * dock, WAppIcon * icon, int req_x, int req_y, int *ret_x, int *ret_y, int redocking)
2256 {
2257         WScreen *scr = dock->screen_ptr;
2258         int dx, dy;
2259         int ex_x, ex_y;
2260         int i, offset = ICON_SIZE / 2;
2261         WAppIcon *aicon = NULL;
2262         WAppIcon *nicon = NULL;
2263         int max_y_icons, max_x_icons;
2264
2265         /* TODO: XINERAMA, for these */
2266         max_x_icons = scr->scr_width / ICON_SIZE;
2267         max_y_icons = scr->scr_height / ICON_SIZE - 1;
2268
2269         if (wPreferences.flags.noupdates)
2270                 return False;
2271
2272         dx = dock->x_pos;
2273         dy = dock->y_pos;
2274
2275         /* if the dock is full */
2276         if (!redocking && (dock->icon_count >= dock->max_icons)) {
2277                 return False;
2278         }
2279
2280         /* exact position */
2281         if (req_y < dy)
2282                 ex_y = (req_y - offset - dy) / ICON_SIZE;
2283         else
2284                 ex_y = (req_y + offset - dy) / ICON_SIZE;
2285
2286         if (req_x < dx)
2287                 ex_x = (req_x - offset - dx) / ICON_SIZE;
2288         else
2289                 ex_x = (req_x + offset - dx) / ICON_SIZE;
2290
2291         /* check if the icon is outside the screen boundaries */
2292         {
2293                 WMRect rect;
2294                 int flags;
2295
2296                 rect.pos.x = dx + ex_x * ICON_SIZE;
2297                 rect.pos.y = dy + ex_y * ICON_SIZE;
2298                 rect.size.width = rect.size.height = ICON_SIZE;
2299
2300                 wGetRectPlacementInfo(scr, rect, &flags);
2301                 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
2302                         return False;
2303         }
2304
2305         if (dock->type == WM_DOCK) {
2306                 if (icon->dock != dock && ex_x != 0)
2307                         return False;
2308
2309                 aicon = NULL;
2310                 for (i = 0; i < dock->max_icons; i++) {
2311                         nicon = dock->icon_array[i];
2312                         if (nicon && nicon->yindex == ex_y) {
2313                                 aicon = nicon;
2314                                 break;
2315                         }
2316                 }
2317
2318                 if (redocking) {
2319                         int sig, done, closest;
2320
2321                         /* Possible cases when redocking:
2322                          *
2323                          * icon dragged out of range of any slot -> false
2324                          * icon dragged to range of free slot
2325                          * icon dragged to range of same slot
2326                          * icon dragged to range of different icon
2327                          */
2328                         if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2329                                 return False;
2330
2331                         if (ex_y >= 0 && ex_y <= max_y_icons && (aicon == icon || !aicon)) {
2332                                 *ret_x = 0;
2333                                 *ret_y = ex_y;
2334                                 return True;
2335                         }
2336
2337                         /* start looking at the upper slot or lower? */
2338                         if (ex_y * ICON_SIZE < (req_y + offset - dy))
2339                                 sig = 1;
2340                         else
2341                                 sig = -1;
2342
2343                         closest = -1;
2344                         done = 0;
2345                         /* look for closest free slot */
2346                         for (i = 0; i < (DOCK_DETTACH_THRESHOLD + 1) * 2 && !done; i++) {
2347                                 int j;
2348
2349                                 done = 1;
2350                                 closest = sig * (i / 2) + ex_y;
2351                                 /* check if this slot is used */
2352                                 if (closest >= 0) {
2353                                         for (j = 0; j < dock->max_icons; j++) {
2354                                                 if (dock->icon_array[j]
2355                                                     && dock->icon_array[j]->yindex == closest) {
2356                                                         /* slot is used by someone else */
2357                                                         if (dock->icon_array[j] != icon)
2358                                                                 done = 0;
2359                                                         break;
2360                                                 }
2361                                         }
2362                                 }
2363                                 sig = -sig;
2364                         }
2365                         if (done && closest >= 0 && closest <= max_y_icons &&
2366                             ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD + 1)
2367                              || (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD + 1))) {
2368                                 *ret_x = 0;
2369                                 *ret_y = closest;
2370                                 return True;
2371                         }
2372                 } else {        /* !redocking */
2373
2374                         /* if slot is free and the icon is close enough, return it */
2375                         if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2376                                 *ret_x = 0;
2377                                 *ret_y = ex_y;
2378                                 return True;
2379                         }
2380                 }
2381         } else {                /* CLIP */
2382                 int neighbours = 0;
2383                 int start, stop, k;
2384
2385                 start = icon->omnipresent ? 0 : scr->current_workspace;
2386                 stop = icon->omnipresent ? scr->workspace_count : start + 1;
2387
2388                 aicon = NULL;
2389                 for (k = start; k < stop; k++) {
2390                         WDock *tmp = scr->workspaces[k]->clip;
2391                         if (!tmp)
2392                                 continue;
2393                         for (i = 0; i < tmp->max_icons; i++) {
2394                                 nicon = tmp->icon_array[i];
2395                                 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2396                                         aicon = nicon;
2397                                         break;
2398                                 }
2399                         }
2400                         if (aicon)
2401                                 break;
2402                 }
2403                 for (k = start; k < stop; k++) {
2404                         WDock *tmp = scr->workspaces[k]->clip;
2405                         if (!tmp)
2406                                 continue;
2407                         for (i = 0; i < tmp->max_icons; i++) {
2408                                 nicon = tmp->icon_array[i];
2409                                 if (nicon && nicon != icon &&   /* Icon can't be it's own neighbour */
2410                                     (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2411                                      abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2412                                         neighbours = 1;
2413                                         break;
2414                                 }
2415                         }
2416                         if (neighbours)
2417                                 break;
2418                 }
2419
2420                 if (neighbours && (aicon == NULL || (redocking && aicon == icon))) {
2421                         *ret_x = ex_x;
2422                         *ret_y = ex_y;
2423                         return True;
2424                 }
2425         }
2426         return False;
2427 }
2428
2429 static int onScreen(WScreen * scr, int x, int y, int sx, int ex, int sy, int ey)
2430 {
2431         WMRect rect = wmkrect(x, y, ICON_SIZE, ICON_SIZE);
2432         int flags;
2433
2434         wGetRectPlacementInfo(scr, rect, &flags);
2435
2436         return !(flags & (XFLAG_DEAD | XFLAG_PARTIAL));
2437 }
2438
2439 /*
2440  * returns true if it can find a free slot in the dock,
2441  * in which case it changes x_pos and y_pos accordingly.
2442  * Else returns false.
2443  */
2444 Bool wDockFindFreeSlot(WDock * dock, int *x_pos, int *y_pos)
2445 {
2446         WScreen *scr = dock->screen_ptr;
2447         WAppIcon *btn;
2448         WAppIconChain *chain;
2449         unsigned char *slot_map;
2450         int mwidth;
2451         int r;
2452         int x, y;
2453         int i, done = False;
2454         int corner;
2455         int sx = 0, sy = 0, ex = scr->scr_width, ey = scr->scr_height;
2456         int extra_count = 0;
2457
2458         if (dock->type == WM_CLIP && dock != scr->workspaces[scr->current_workspace]->clip)
2459                 extra_count = scr->global_icon_count;
2460
2461         /* if the dock is full */
2462         if (dock->icon_count + extra_count >= dock->max_icons) {
2463                 return False;
2464         }
2465
2466         if (!wPreferences.flags.nodock && scr->dock) {
2467                 if (scr->dock->on_right_side)
2468                         ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2469                 else
2470                         sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2471         }
2472
2473         if (ex < dock->x_pos)
2474                 ex = dock->x_pos;
2475         if (sx > dock->x_pos + ICON_SIZE)
2476                 sx = dock->x_pos + ICON_SIZE;
2477 #define C_NONE 0
2478 #define C_NW 1
2479 #define C_NE 2
2480 #define C_SW 3
2481 #define C_SE 4
2482
2483         /* check if clip is in a corner */
2484         if (dock->type == WM_CLIP) {
2485                 if (dock->x_pos < 1 && dock->y_pos < 1)
2486                         corner = C_NE;
2487                 else if (dock->x_pos < 1 && dock->y_pos >= (ey - ICON_SIZE))
2488                         corner = C_SE;
2489                 else if (dock->x_pos >= (ex - ICON_SIZE) && dock->y_pos >= (ey - ICON_SIZE))
2490                         corner = C_SW;
2491                 else if (dock->x_pos >= (ex - ICON_SIZE) && dock->y_pos < 1)
2492                         corner = C_NW;
2493                 else
2494                         corner = C_NONE;
2495         } else
2496                 corner = C_NONE;
2497
2498         /* If the clip is in the corner, use only slots that are in the border
2499          * of the screen */
2500         if (corner != C_NONE) {
2501                 char *hmap, *vmap;
2502                 int hcount, vcount;
2503
2504                 hcount = WMIN(dock->max_icons, scr->scr_width / ICON_SIZE);
2505                 vcount = WMIN(dock->max_icons, scr->scr_height / ICON_SIZE);
2506                 hmap = wmalloc(hcount + 1);
2507                 memset(hmap, 0, hcount + 1);
2508                 vmap = wmalloc(vcount + 1);
2509                 memset(vmap, 0, vcount + 1);
2510
2511                 /* mark used positions */
2512                 switch (corner) {
2513                 case C_NE:
2514                         for (i = 0; i < dock->max_icons; i++) {
2515                                 btn = dock->icon_array[i];
2516                                 if (!btn)
2517                                         continue;
2518
2519                                 if (btn->xindex == 0 && btn->yindex > 0 && btn->yindex < vcount)
2520                                         vmap[btn->yindex] = 1;
2521                                 else if (btn->yindex == 0 && btn->xindex > 0 && btn->xindex < hcount)
2522                                         hmap[btn->xindex] = 1;
2523                         }
2524                         for (chain = scr->global_icons; chain != NULL; chain = chain->next) {
2525                                 btn = chain->aicon;
2526                                 if (btn->xindex == 0 && btn->yindex > 0 && btn->yindex < vcount)
2527                                         vmap[btn->yindex] = 1;
2528                                 else if (btn->yindex == 0 && btn->xindex > 0 && btn->xindex < hcount)
2529                                         hmap[btn->xindex] = 1;
2530                         }
2531                         break;
2532                 case C_NW:
2533                         for (i = 0; i < dock->max_icons; i++) {
2534                                 btn = dock->icon_array[i];
2535                                 if (!btn)
2536                                         continue;
2537
2538                                 if (btn->xindex == 0 && btn->yindex > 0 && btn->yindex < vcount)
2539                                         vmap[btn->yindex] = 1;
2540                                 else if (btn->yindex == 0 && btn->xindex < 0 && btn->xindex > -hcount)
2541                                         hmap[-btn->xindex] = 1;
2542                         }
2543                         for (chain = scr->global_icons; chain != NULL; chain = chain->next) {
2544                                 btn = chain->aicon;
2545                                 if (btn->xindex == 0 && btn->yindex > 0 && btn->yindex < vcount)
2546                                         vmap[btn->yindex] = 1;
2547                                 else if (btn->yindex == 0 && btn->xindex < 0 && btn->xindex > -hcount)
2548                                         hmap[-btn->xindex] = 1;
2549                         }
2550                         break;
2551                 case C_SE:
2552                         for (i = 0; i < dock->max_icons; i++) {
2553                                 btn = dock->icon_array[i];
2554                                 if (!btn)
2555                                         continue;
2556
2557                                 if (btn->xindex == 0 && btn->yindex < 0 && btn->yindex > -vcount)
2558                                         vmap[-btn->yindex] = 1;
2559                                 else if (btn->yindex == 0 && btn->xindex > 0 && btn->xindex < hcount)
2560                                         hmap[btn->xindex] = 1;
2561                         }
2562                         for (chain = scr->global_icons; chain != NULL; chain = chain->next) {
2563                                 btn = chain->aicon;
2564                                 if (btn->xindex == 0 && btn->yindex < 0 && btn->yindex > -vcount)
2565                                         vmap[-btn->yindex] = 1;
2566                                 else if (btn->yindex == 0 && btn->xindex > 0 && btn->xindex < hcount)
2567                                         hmap[btn->xindex] = 1;
2568                         }
2569                         break;
2570                 case C_SW:
2571                 default:
2572                         for (i = 0; i < dock->max_icons; i++) {
2573                                 btn = dock->icon_array[i];
2574                                 if (!btn)
2575                                         continue;
2576
2577                                 if (btn->xindex == 0 && btn->yindex < 0 && btn->yindex > -vcount)
2578                                         vmap[-btn->yindex] = 1;
2579                                 else if (btn->yindex == 0 && btn->xindex < 0 && btn->xindex > -hcount)
2580                                         hmap[-btn->xindex] = 1;
2581                         }
2582                         for (chain = scr->global_icons; chain != NULL; chain = chain->next) {
2583                                 btn = chain->aicon;
2584                                 if (btn->xindex == 0 && btn->yindex < 0 && btn->yindex > -vcount)
2585                                         vmap[-btn->yindex] = 1;
2586                                 else if (btn->yindex == 0 && btn->xindex < 0 && btn->xindex > -hcount)
2587                                         hmap[-btn->xindex] = 1;
2588                         }
2589                 }
2590                 x = 0;
2591                 y = 0;
2592                 done = 0;
2593                 /* search a vacant slot */
2594                 for (i = 1; i < WMAX(vcount, hcount); i++) {
2595                         if (i < vcount && vmap[i] == 0) {
2596                                 /* found a slot */
2597                                 x = 0;
2598                                 y = i;
2599                                 done = 1;
2600                                 break;
2601                         } else if (i < hcount && hmap[i] == 0) {
2602                                 /* found a slot */
2603                                 x = i;
2604                                 y = 0;
2605                                 done = 1;
2606                                 break;
2607                         }
2608                 }
2609                 wfree(vmap);
2610                 wfree(hmap);
2611                 /* If found a slot, translate and return */
2612                 if (done) {
2613                         if (corner == C_NW || corner == C_NE) {
2614                                 *y_pos = y;
2615                         } else {
2616                                 *y_pos = -y;
2617                         }
2618                         if (corner == C_NE || corner == C_SE) {
2619                                 *x_pos = x;
2620                         } else {
2621                                 *x_pos = -x;
2622                         }
2623                         return True;
2624                 }
2625                 /* else, try to find a slot somewhere else */
2626         }
2627
2628         /* a map of mwidth x mwidth would be enough if we allowed icons to be
2629          * placed outside of screen */
2630         mwidth = (int)ceil(sqrt(dock->max_icons));
2631
2632         /* In the worst case (the clip is in the corner of the screen),
2633          * the amount of icons that fit in the clip is smaller.
2634          * Double the map to get a safe value.
2635          */
2636         mwidth += mwidth;
2637
2638         r = (mwidth - 1) / 2;
2639
2640         slot_map = wmalloc(mwidth * mwidth);
2641         memset(slot_map, 0, mwidth * mwidth);
2642
2643 #define XY2OFS(x,y) (WMAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2644
2645         /* mark used slots in the map. If the slot falls outside the map
2646          * (for example, when all icons are placed in line), ignore them. */
2647         for (i = 0; i < dock->max_icons; i++) {
2648                 btn = dock->icon_array[i];
2649                 if (btn)
2650                         slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2651         }
2652         for (chain = scr->global_icons; chain != NULL; chain = chain->next) {
2653                 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2654         }
2655         /* Find closest slot from the center that is free by scanning the
2656          * map from the center to outward in circular passes.
2657          * This will not result in a neat layout, but will be optimal
2658          * in the sense that there will not be holes left.
2659          */
2660         done = 0;
2661         for (i = 1; i <= r && !done; i++) {
2662                 int tx, ty;
2663
2664                 /* top and bottom parts of the ring */
2665                 for (x = -i; x <= i && !done; x++) {
2666                         tx = dock->x_pos + x * ICON_SIZE;
2667                         y = -i;
2668                         ty = dock->y_pos + y * ICON_SIZE;
2669                         if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2670                                 *x_pos = x;
2671                                 *y_pos = y;
2672                                 done = 1;
2673                                 break;
2674                         }
2675                         y = i;
2676                         ty = dock->y_pos + y * ICON_SIZE;
2677                         if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2678                                 *x_pos = x;
2679                                 *y_pos = y;
2680                                 done = 1;
2681                                 break;
2682                         }
2683                 }
2684                 /* left and right parts of the ring */
2685                 for (y = -i + 1; y <= i - 1; y++) {
2686                         ty = dock->y_pos + y * ICON_SIZE;
2687                         x = -i;
2688                         tx = dock->x_pos + x * ICON_SIZE;
2689                         if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2690                                 *x_pos = x;
2691                                 *y_pos = y;
2692                                 done = 1;
2693                                 break;
2694                         }
2695                         x = i;
2696                         tx = dock->x_pos + x * ICON_SIZE;
2697                         if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2698                                 *x_pos = x;
2699                                 *y_pos = y;
2700                                 done = 1;
2701                                 break;
2702                         }
2703                 }
2704         }
2705         wfree(slot_map);
2706 #undef XY2OFS
2707         return done;
2708 }
2709
2710 static void moveDock(WDock * dock, int new_x, int new_y)
2711 {
2712         WAppIcon *btn;
2713         int i;
2714
2715         dock->x_pos = new_x;
2716         dock->y_pos = new_y;
2717         for (i = 0; i < dock->max_icons; i++) {
2718                 btn = dock->icon_array[i];
2719                 if (btn) {
2720                         btn->x_pos = new_x + btn->xindex * ICON_SIZE;
2721                         btn->y_pos = new_y + btn->yindex * ICON_SIZE;
2722                         XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2723                 }
2724         }
2725 }
2726
2727 static void swapDock(WDock * dock)
2728 {
2729         WScreen *scr = dock->screen_ptr;
2730         WAppIcon *btn;
2731         int x, i;
2732
2733         if (dock->on_right_side) {
2734                 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2735         } else {
2736                 x = dock->x_pos = DOCK_EXTRA_SPACE;
2737         }
2738
2739         for (i = 0; i < dock->max_icons; i++) {
2740                 btn = dock->icon_array[i];
2741                 if (btn) {
2742                         btn->x_pos = x;
2743                         XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2744                 }
2745         }
2746
2747         wScreenUpdateUsableArea(scr);
2748 }
2749
2750 static pid_t execCommand(WAppIcon * btn, char *command, WSavedState * state)
2751 {
2752         WScreen *scr = btn->icon->core->screen_ptr;
2753         pid_t pid;
2754         char **argv;
2755         int argc;
2756         char *cmdline;
2757
2758         cmdline = ExpandOptions(scr, command);
2759
2760         if (scr->flags.dnd_data_convertion_status || !cmdline) {
2761                 if (cmdline)
2762                         wfree(cmdline);
2763                 if (state)
2764                         wfree(state);
2765                 return 0;
2766         }
2767
2768         wtokensplit(cmdline, &argv, &argc);
2769
2770         if (!argc) {
2771                 if (cmdline)
2772                         wfree(cmdline);
2773                 if (state)
2774                         wfree(state);
2775                 return 0;
2776         }
2777
2778         if ((pid = fork()) == 0) {
2779                 char **args;
2780                 int i;
2781
2782                 SetupEnvironment(scr);
2783
2784 #ifdef HAVE_SETSID
2785                 setsid();
2786 #endif
2787
2788                 args = malloc(sizeof(char *) * (argc + 1));
2789                 if (!args)
2790                         exit(111);
2791                 for (i = 0; i < argc; i++) {
2792                         args[i] = argv[i];
2793                 }
2794                 args[argc] = NULL;
2795                 execvp(argv[0], args);
2796                 exit(111);
2797         }
2798         wtokenfree(argv, argc);
2799
2800         if (pid > 0) {
2801                 if (!state) {
2802                         state = wmalloc(sizeof(WSavedState));
2803                         memset(state, 0, sizeof(WSavedState));
2804                         state->hidden = -1;
2805                         state->miniaturized = -1;
2806                         state->shaded = -1;
2807                         if (btn->dock == scr->dock || btn->omnipresent)
2808                                 state->workspace = -1;
2809                         else
2810                                 state->workspace = scr->current_workspace;
2811                 }
2812                 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid, state);
2813                 wAddDeathHandler(pid, (WDeathHandler *) trackDeadProcess, btn->dock);
2814         } else if (state) {
2815                 wfree(state);
2816         }
2817         wfree(cmdline);
2818         return pid;
2819 }
2820
2821 void wDockHideIcons(WDock * dock)
2822 {
2823         int i;
2824
2825         if (dock == NULL)
2826                 return;
2827
2828         for (i = 1; i < dock->max_icons; i++) {
2829                 if (dock->icon_array[i])
2830                         XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
2831         }
2832         dock->mapped = 0;
2833
2834         dockIconPaint(dock->icon_array[0]);
2835 }
2836
2837 void wDockShowIcons(WDock * dock)
2838 {
2839         int i, newlevel;
2840         WAppIcon *btn;
2841
2842         if (dock == NULL)
2843                 return;
2844
2845         btn = dock->icon_array[0];
2846         moveDock(dock, btn->x_pos, btn->y_pos);
2847
2848         newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
2849         ChangeStackingLevel(btn->icon->core, newlevel);
2850
2851         for (i = 1; i < dock->max_icons; i++) {
2852                 if (dock->icon_array[i]) {
2853                         MoveInStackListAbove(dock->icon_array[i]->icon->core, btn->icon->core);
2854                         break;
2855                 }
2856         }
2857
2858         if (!dock->collapsed) {
2859                 for (i = 1; i < dock->max_icons; i++) {
2860                         if (dock->icon_array[i]) {
2861                                 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
2862                         }
2863                 }
2864         }
2865         dock->mapped = 1;
2866
2867         dockIconPaint(btn);
2868 }
2869
2870 void wDockLower(WDock * dock)
2871 {
2872         int i;
2873
2874         for (i = 0; i < dock->max_icons; i++) {
2875                 if (dock->icon_array[i])
2876                         wLowerFrame(dock->icon_array[i]->icon->core);
2877         }
2878 }
2879
2880 void wDockRaise(WDock * dock)
2881 {
2882         int i;
2883
2884         for (i = dock->max_icons - 1; i >= 0; i--) {
2885                 if (dock->icon_array[i])
2886                         wRaiseFrame(dock->icon_array[i]->icon->core);
2887         }
2888 }
2889
2890 void wDockRaiseLower(WDock * dock)
2891 {
2892         if (!dock->icon_array[0]->icon->core->stacking->above
2893             || (dock->icon_array[0]->icon->core->stacking->window_level
2894                 != dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
2895                 wDockLower(dock);
2896         else
2897                 wDockRaise(dock);
2898 }
2899
2900 void wDockFinishLaunch(WDock * dock, WAppIcon * icon)
2901 {
2902         icon->launching = 0;
2903         icon->relaunching = 0;
2904         dockIconPaint(icon);
2905 }
2906
2907 WAppIcon *wDockFindIconForWindow(WDock * dock, Window window)
2908 {
2909         WAppIcon *icon;
2910         int i;
2911
2912         for (i = 0; i < dock->max_icons; i++) {
2913                 icon = dock->icon_array[i];
2914                 if (icon && icon->main_window == window)
2915                         return icon;
2916         }
2917         return NULL;
2918 }
2919
2920 void wDockTrackWindowLaunch(WDock * dock, Window window)
2921 {
2922         WAppIcon *icon;
2923         char *wm_class, *wm_instance;
2924         int i;
2925         Bool firstPass = True;
2926         Bool found = False;
2927         char *command = NULL;
2928
2929         command = GetCommandForWindow(window);
2930
2931         if (!PropGetWMClass(window, &wm_class, &wm_instance) || (!wm_class && !wm_instance)) {
2932
2933                 if (command)
2934                         wfree(command);
2935                 return;
2936         }
2937
2938  retry:
2939         for (i = 0; i < dock->max_icons; i++) {
2940                 icon = dock->icon_array[i];
2941                 if (!icon)
2942                         continue;
2943
2944                 /* app is already attached to icon */
2945                 if (icon->main_window == window) {
2946                         found = True;
2947                         break;
2948                 }
2949
2950                 if ((icon->wm_instance || icon->wm_class)
2951                     && (icon->launching || !icon->running)) {
2952
2953                         if (icon->wm_instance && wm_instance && strcmp(icon->wm_instance, wm_instance) != 0) {
2954                                 continue;
2955                         }
2956                         if (icon->wm_class && wm_class && strcmp(icon->wm_class, wm_class) != 0) {
2957                                 continue;
2958                         }
2959                         if (firstPass && command && strcmp(icon->command, command) != 0) {
2960                                 continue;
2961                         }
2962
2963                         if (!icon->relaunching) {
2964                                 WApplication *wapp;
2965
2966                                 /* Possibly an application that was docked with dockit,
2967                                  * but the user did not update WMState to indicate that
2968                                  * it was docked by force */
2969                                 wapp = wApplicationOf(window);
2970                                 if (!wapp) {
2971                                         icon->forced_dock = 1;
2972                                         icon->running = 0;
2973                                 }
2974                                 if (!icon->forced_dock) {
2975                                         icon->main_window = window;
2976                                 }
2977                         }
2978                         found = True;
2979                         if (!wPreferences.no_animations && !icon->launching &&
2980                             !dock->screen_ptr->flags.startup && !dock->collapsed) {
2981                                 WAppIcon *aicon;
2982                                 int x0, y0;
2983
2984                                 icon->launching = 1;
2985                                 dockIconPaint(icon);
2986
2987                                 aicon = wAppIconCreateForDock(dock->screen_ptr, NULL,
2988                                                               wm_instance, wm_class, TILE_NORMAL);
2989                                 /* XXX: can: aicon->icon == NULL ? */
2990                                 PlaceIcon(dock->screen_ptr, &x0, &y0, wGetHeadForWindow(aicon->icon->owner));
2991                                 wAppIconMove(aicon, x0, y0);
2992                                 /* Should this always be lowered? -Dan */
2993                                 if (dock->lowered)
2994                                         wLowerFrame(aicon->icon->core);
2995                                 XMapWindow(dpy, aicon->icon->core->window);
2996                                 aicon->launching = 1;
2997                                 wAppIconPaint(aicon);
2998                                 SlideWindow(aicon->icon->core->window, x0, y0, icon->x_pos, icon->y_pos);
2999                                 XUnmapWindow(dpy, aicon->icon->core->window);
3000                                 wAppIconDestroy(aicon);
3001                         }
3002                         wDockFinishLaunch(dock, icon);
3003                         break;
3004                 }
3005         }
3006
3007         if (firstPass && !found) {
3008                 firstPass = False;
3009                 goto retry;
3010         }
3011
3012         if (command)
3013                 wfree(command);
3014
3015         if (wm_class)
3016                 XFree(wm_class);
3017         if (wm_instance)
3018                 XFree(wm_instance);
3019 }
3020
3021 void wClipUpdateForWorkspaceChange(WScreen * scr, int workspace)
3022 {
3023         if (!wPreferences.flags.noclip) {
3024                 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3025                 if (scr->current_workspace != workspace) {
3026                         WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3027                         WAppIconChain *chain = scr->global_icons;
3028
3029                         while (chain) {
3030                                 moveIconBetweenDocks(chain->aicon->dock,
3031                                                      scr->workspaces[workspace]->clip,
3032                                                      chain->aicon, chain->aicon->xindex, chain->aicon->yindex);
3033                                 if (scr->workspaces[workspace]->clip->collapsed)
3034                                         XUnmapWindow(dpy, chain->aicon->icon->core->window);
3035                                 chain = chain->next;
3036                         }
3037
3038                         wDockHideIcons(old_clip);
3039                         if (old_clip->auto_raise_lower) {
3040                                 if (old_clip->auto_raise_magic) {
3041                                         WMDeleteTimerHandler(old_clip->auto_raise_magic);
3042                                         old_clip->auto_raise_magic = NULL;
3043                                 }
3044                                 wDockLower(old_clip);
3045                         }
3046                         if (old_clip->auto_collapse) {
3047                                 if (old_clip->auto_expand_magic) {
3048                                         WMDeleteTimerHandler(old_clip->auto_expand_magic);
3049                                         old_clip->auto_expand_magic = NULL;
3050                                 }
3051                                 old_clip->collapsed = 1;
3052                         }
3053                         wDockShowIcons(scr->workspaces[workspace]->clip);
3054                 }
3055                 if (scr->flags.clip_balloon_mapped)
3056                         showClipBalloon(scr->clip_icon->dock, workspace);
3057         }
3058 }
3059
3060 static void trackDeadProcess(pid_t pid, unsigned char status, WDock * dock)
3061 {
3062         WAppIcon *icon;
3063         int i;
3064
3065         for (i = 0; i < dock->max_icons; i++) {
3066                 icon = dock->icon_array[i];
3067                 if (!icon)
3068                         continue;
3069
3070                 if (icon->launching && icon->pid == pid) {
3071                         if (!icon->relaunching) {
3072                                 icon->running = 0;
3073                                 icon->main_window = None;
3074                         }
3075                         wDockFinishLaunch(dock, icon);
3076                         icon->pid = 0;
3077                         if (status == 111) {
3078                                 char msg[PATH_MAX];
3079                                 char *cmd;
3080
3081 #ifdef XDND
3082                                 if (icon->drop_launch)
3083                                         cmd = icon->dnd_command;
3084                                 else
3085 #endif
3086                                 if (icon->paste_launch)
3087                                         cmd = icon->paste_command;
3088                                 else
3089                                         cmd = icon->command;
3090
3091                                 snprintf(msg, sizeof(msg), _("Could not execute command \"%s\""), cmd);
3092
3093                                 wMessageDialog(dock->screen_ptr, _("Error"), msg, _("OK"), NULL, NULL);
3094                         }
3095                         break;
3096                 }
3097         }
3098 }
3099
3100 static void toggleLowered(WDock * dock)
3101 {
3102         WAppIcon *tmp;
3103         int newlevel, i;
3104
3105         /* lower/raise Dock */
3106         if (!dock->lowered) {
3107                 newlevel = WMNormalLevel;
3108                 dock->lowered = 1;
3109         } else {
3110                 newlevel = WMDockLevel;
3111                 dock->lowered = 0;
3112         }
3113
3114         for (i = 0; i < dock->max_icons; i++) {
3115                 tmp = dock->icon_array[i];
3116                 if (!tmp)
3117                         continue;
3118
3119                 ChangeStackingLevel(tmp->icon->core, newlevel);
3120                 if (dock->lowered)
3121                         wLowerFrame(tmp->icon->core);
3122         }
3123
3124         if (dock->type == WM_DOCK)
3125                 wScreenUpdateUsableArea(dock->screen_ptr);
3126 }
3127
3128 static void toggleCollapsed(WDock * dock)
3129 {
3130         if (dock->collapsed) {
3131                 dock->collapsed = 0;
3132                 wDockShowIcons(dock);
3133         } else {
3134                 dock->collapsed = 1;
3135                 wDockHideIcons(dock);
3136         }
3137 }
3138
3139 static void openDockMenu(WDock * dock, WAppIcon * aicon, XEvent * event)
3140 {
3141         WScreen *scr = dock->screen_ptr;
3142         WObjDescriptor *desc;
3143         WMenuEntry *entry;
3144         WApplication *wapp = NULL;
3145         int index = 0;
3146         int x_pos;
3147         int n_selected;
3148         int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3149
3150         if (dock->type == WM_DOCK) {
3151                 /* keep on top */
3152                 entry = dock->menu->entries[index];
3153                 entry->flags.indicator_on = !dock->lowered;
3154                 entry->clientdata = dock;
3155                 dock->menu->flags.realized = 0;
3156         } else {
3157                 /* clip options */
3158                 if (scr->clip_options)
3159                         updateClipOptionsMenu(scr->clip_options, dock);
3160
3161                 n_selected = numberOfSelectedIcons(dock);
3162
3163                 /* Rename Workspace */
3164                 entry = dock->menu->entries[++index];
3165                 if (aicon == scr->clip_icon) {
3166                         entry->callback = renameCallback;
3167                         entry->clientdata = dock;
3168                         entry->flags.indicator = 0;
3169                         entry->text = _("Rename Workspace");
3170                 } else {
3171                         entry->callback = omnipresentCallback;
3172                         entry->clientdata = aicon;
3173                         if (n_selected > 0) {
3174                                 entry->flags.indicator = 0;
3175                                 entry->text = _("Toggle Omnipresent");
3176                         } else {
3177                                 entry->flags.indicator = 1;
3178                                 entry->flags.indicator_on = aicon->omnipresent;
3179                                 entry->flags.indicator_type = MI_CHECK;
3180                                 entry->text = _("Omnipresent");
3181                         }
3182                 }
3183
3184                 /* select/unselect icon */
3185                 entry = dock->menu->entries[++index];
3186                 entry->clientdata = aicon;
3187                 entry->flags.indicator_on = aicon->icon->selected;
3188                 wMenuSetEnabled(dock->menu, index, aicon != scr->clip_icon);
3189
3190                 /* select/unselect all icons */
3191                 entry = dock->menu->entries[++index];
3192                 entry->clientdata = aicon;
3193                 if (n_selected > 0)
3194                         entry->text = _("Unselect All Icons");
3195                 else
3196                         entry->text = _("Select All Icons");
3197                 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3198
3199                 /* keep icon(s) */
3200                 entry = dock->menu->entries[++index];
3201                 entry->clientdata = aicon;
3202                 if (n_selected > 1)
3203                         entry->text = _("Keep Icons");
3204                 else
3205                         entry->text = _("Keep Icon");
3206                 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3207
3208                 /* this is the workspace submenu part */
3209                 entry = dock->menu->entries[++index];
3210                 if (n_selected > 1)
3211                         entry->text = _("Move Icons To");
3212                 else
3213                         entry->text = _("Move Icon To");
3214                 if (scr->clip_submenu)
3215                         updateWorkspaceMenu(scr->clip_submenu, aicon);
3216                 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3217
3218                 /* remove icon(s) */
3219                 entry = dock->menu->entries[++index];
3220                 entry->clientdata = aicon;
3221                 if (n_selected > 1)
3222                         entry->text = _("Remove Icons");
3223                 else
3224                         entry->text = _("Remove Icon");
3225                 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3226
3227                 /* attract icon(s) */
3228                 entry = dock->menu->entries[++index];
3229                 entry->clientdata = aicon;
3230
3231                 dock->menu->flags.realized = 0;
3232                 wMenuRealize(dock->menu);
3233         }
3234
3235         if (aicon->icon->owner) {
3236                 wapp = wApplicationOf(aicon->icon->owner->main_window);
3237         } else {
3238                 wapp = NULL;
3239         }
3240
3241         /* launch */
3242         entry = dock->menu->entries[++index];
3243         entry->clientdata = aicon;
3244         wMenuSetEnabled(dock->menu, index, aicon->command != NULL);
3245
3246         /* unhide here */
3247         entry = dock->menu->entries[++index];
3248         entry->clientdata = aicon;
3249         if (wapp && wapp->flags.hidden) {
3250                 entry->text = _("Unhide Here");
3251         } else {
3252                 entry->text = _("Bring Here");
3253         }
3254         wMenuSetEnabled(dock->menu, index, appIsRunning);
3255
3256         /* hide */
3257         entry = dock->menu->entries[++index];
3258         entry->clientdata = aicon;
3259         if (wapp && wapp->flags.hidden) {
3260                 entry->text = _("Unhide");
3261         } else {
3262                 entry->text = _("Hide");
3263         }
3264         wMenuSetEnabled(dock->menu, index, appIsRunning);
3265
3266         /* settings */
3267         entry = dock->menu->entries[++index];
3268         entry->clientdata = aicon;
3269         wMenuSetEnabled(dock->menu, index, !aicon->editing && !wPreferences.flags.noupdates);
3270
3271         /* kill */
3272         entry = dock->menu->entries[++index];
3273         entry->clientdata = aicon;
3274         wMenuSetEnabled(dock->menu, index, appIsRunning);
3275
3276         if (!dock->menu->flags.realized)
3277                 wMenuRealize(dock->menu);
3278
3279         if (dock->type == WM_CLIP) {
3280                 /*x_pos = event->xbutton.x_root+2; */
3281                 x_pos = event->xbutton.x_root - dock->menu->frame->core->width / 2 - 1;
3282                 if (x_pos < 0) {
3283                         x_pos = 0;
3284                 } else if (x_pos + dock->menu->frame->core->width > scr->scr_width - 2) {
3285                         x_pos = scr->scr_width - dock->menu->frame->core->width - 4;
3286                 }
3287         } else {
3288                 x_pos = dock->on_right_side ? scr->scr_width - dock->menu->frame->core->width - 3 : 0;
3289         }
3290
3291         wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root + 2, False);
3292
3293         /* allow drag select */
3294         event->xany.send_event = True;
3295         desc = &dock->menu->menu->descriptor;
3296         (*desc->handle_mousedown) (desc, event);
3297 }
3298
3299 /******************************************************************/
3300 static void iconDblClick(WObjDescriptor * desc, XEvent * event)
3301 {
3302         WAppIcon *btn = desc->parent;
3303         WDock *dock = btn->dock;
3304         WApplication *wapp = NULL;
3305         int unhideHere = 0;
3306
3307         if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3308                 wapp = wApplicationOf(btn->icon->owner->main_window);
3309
3310                 assert(wapp != NULL);
3311
3312                 unhideHere = (event->xbutton.state & ShiftMask);
3313
3314                 /* go to the last workspace that the user worked on the app */
3315                 if (wapp->last_workspace != dock->screen_ptr->current_workspace && !unhideHere) {
3316                         wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3317                 }
3318
3319                 wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
3320
3321                 if (event->xbutton.state & MOD_MASK) {
3322                         wHideOtherApplications(btn->icon->owner);
3323                 }
3324         } else {
3325                 if (event->xbutton.button == Button1) {
3326
3327                         if (event->xbutton.state & MOD_MASK) {
3328                                 /* raise/lower dock */
3329                                 toggleLowered(dock);
3330                         } else if (btn == dock->screen_ptr->clip_icon) {
3331                                 if (getClipButton(event->xbutton.x, event->xbutton.y) == CLIP_IDLE)
3332                                         toggleCollapsed(dock);
3333                                 else
3334                                         handleClipChangeWorkspace(dock->screen_ptr, event);
3335                         } else if (btn->command) {
3336                                 if (!btn->launching && (!btn->running || (event->xbutton.state & ControlMask))) {
3337                                         launchDockedApplication(btn, False);
3338                                 }
3339                         } else if (btn->xindex == 0 && btn->yindex == 0 && btn->dock->type == WM_DOCK) {
3340                                 wShowGNUstepPanel(dock->screen_ptr);
3341                         }
3342                 }
3343         }
3344 }
3345
3346 static void handleDockMove(WDock * dock, WAppIcon * aicon, XEvent * event)
3347 {
3348         WScreen *scr = dock->screen_ptr;
3349         int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3350         int x, y;
3351         XEvent ev;
3352         int grabbed = 0, swapped = 0, done;
3353         Pixmap ghost = None;
3354         int superfluous = wPreferences.superfluous;     /* we catch it to avoid problems */
3355
3356 #ifdef DEBUG
3357         puts("moving dock");
3358 #endif
3359         if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3360                          | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
3361                          GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
3362                 wwarning("pointer grab failed for dock move");
3363         }
3364         y = 0;
3365         for (x = 0; x < dock->max_icons; x++) {
3366                 if (dock->icon_array[x] != NULL && dock->icon_array[x]->yindex > y)
3367                         y = dock->icon_array[x]->yindex;
3368         }
3369         y++;
3370         XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE * y);
3371
3372         done = 0;
3373         while (!done) {
3374                 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
3375                             | ButtonMotionMask | ExposureMask, &ev);
3376                 switch (ev.type) {
3377                 case Expose:
3378                         WMHandleEvent(&ev);
3379                         break;
3380
3381                 case MotionNotify:
3382                         if (!grabbed) {
3383                                 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
3384                                     || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
3385                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
3386                                                                  | ButtonReleaseMask | ButtonPressMask,
3387                                                                  wCursor[WCUR_MOVE], CurrentTime);
3388                                         grabbed = 1;
3389                                 }
3390                                 break;
3391                         }
3392                         if (dock->type == WM_CLIP) {
3393                                 x = ev.xmotion.x_root - ofs_x;
3394                                 y = ev.xmotion.y_root - ofs_y;
3395                                 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3396
3397                                 moveDock(dock, x, y);
3398                         } else {
3399                                 /* move vertically if pointer is inside the dock */
3400                                 if ((dock->on_right_side && ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3401                                     || (!dock->on_right_side && ev.xmotion.x_root <= dock->x_pos + ICON_SIZE * 2)) {
3402
3403                                         x = ev.xmotion.x_root - ofs_x;
3404                                         y = ev.xmotion.y_root - ofs_y;
3405                                         wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3406                                         moveDock(dock, dock->x_pos, y);
3407                                 }
3408                                 /* move horizontally to change sides */
3409                                 x = ev.xmotion.x_root - ofs_x;
3410                                 if (!dock->on_right_side) {
3411
3412                                         /* is on left */
3413
3414                                         if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE * 2) {
3415                                                 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width - ICON_SIZE
3416                                                             - DOCK_EXTRA_SPACE - 1, dock->y_pos);
3417                                                 if (superfluous && ghost == None) {
3418                                                         ghost = MakeGhostDock(dock, dock->x_pos,
3419                                                                               scr->scr_width - ICON_SIZE
3420                                                                               - DOCK_EXTRA_SPACE - 1, dock->y_pos);
3421                                                         XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3422                                                         XClearWindow(dpy, scr->dock_shadow);
3423                                                 }
3424                                                 XMapRaised(dpy, scr->dock_shadow);
3425                                                 swapped = 1;
3426                                         } else {
3427                                                 if (superfluous && ghost != None) {
3428                                                         XFreePixmap(dpy, ghost);
3429                                                         ghost = None;
3430                                                 }
3431                                                 XUnmapWindow(dpy, scr->dock_shadow);
3432                                                 swapped = 0;
3433                                         }
3434                                 } else {
3435                                         /* is on right */
3436                                         if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3437                                                 XMoveWindow(dpy, scr->dock_shadow, DOCK_EXTRA_SPACE, dock->y_pos);
3438                                                 if (superfluous && ghost == None) {
3439                                                         ghost = MakeGhostDock(dock, dock->x_pos,
3440                                                                               DOCK_EXTRA_SPACE, dock->y_pos);
3441                                                         XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3442                                                         XClearWindow(dpy, scr->dock_shadow);
3443                                                 }
3444                                                 XMapRaised(dpy, scr->dock_shadow);
3445                                                 swapped = -1;
3446                                         } else {
3447                                                 XUnmapWindow(dpy, scr->dock_shadow);
3448                                                 swapped = 0;
3449                                                 if (superfluous && ghost != None) {
3450                                                         XFreePixmap(dpy, ghost);
3451                                                         ghost = None;
3452                                                 }
3453                                         }
3454                                 }
3455                         }
3456                         break;
3457
3458                 case ButtonPress:
3459                         break;
3460
3461                 case ButtonRelease:
3462                         if (ev.xbutton.button != event->xbutton.button)
3463                                 break;
3464                         XUngrabPointer(dpy, CurrentTime);
3465                         XUnmapWindow(dpy, scr->dock_shadow);
3466                         XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3467                         if (dock->type == WM_DOCK) {
3468                                 if (swapped != 0) {
3469                                         if (swapped > 0)
3470                                                 dock->on_right_side = 1;
3471                                         else
3472                                                 dock->on_right_side = 0;
3473                                         swapDock(dock);
3474                                         wArrangeIcons(scr, False);
3475                                 }
3476                         }
3477                         done = 1;
3478                         break;
3479                 }
3480         }
3481         if (superfluous) {
3482                 if (ghost != None)
3483                         XFreePixmap(dpy, ghost);
3484                 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3485         }
3486 #ifdef DEBUG
3487         puts("End dock move");
3488 #endif
3489 }
3490
3491 static void handleIconMove(WDock * dock, WAppIcon * aicon, XEvent * event)
3492 {
3493         WScreen *scr = dock->screen_ptr;
3494         Window wins[2];
3495         WIcon *icon = aicon->icon;
3496         WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3497         int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3498         XEvent ev;
3499         int x = aicon->x_pos, y = aicon->y_pos;
3500         int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3501         int shad_x = x, shad_y = y;
3502         int ix = aicon->xindex, iy = aicon->yindex;
3503         int tmp;
3504         Pixmap ghost = None;
3505         Bool docked;
3506         int superfluous = wPreferences.superfluous;     /* we catch it to avoid problems */
3507         int omnipresent = aicon->omnipresent;   /* this must be cached!!! */
3508
3509         if (wPreferences.flags.noupdates)
3510                 return;
3511
3512         if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3513                          | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
3514                          GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
3515 #ifdef DEBUG0
3516                 wwarning("pointer grab failed for icon move");
3517 #endif
3518         }
3519
3520         if (!(event->xbutton.state & MOD_MASK))
3521                 wRaiseFrame(icon->core);
3522
3523         if (!wPreferences.flags.noclip)
3524                 clip = scr->workspaces[scr->current_workspace]->clip;
3525
3526         if (dock == scr->dock && !wPreferences.flags.noclip)
3527                 dock2 = clip;
3528         else if (dock != scr->dock && !wPreferences.flags.nodock)
3529                 dock2 = scr->dock;
3530
3531         wins[0] = icon->core->window;
3532         wins[1] = scr->dock_shadow;
3533         XRestackWindows(dpy, wins, 2);
3534         XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
3535         if (superfluous) {
3536                 if (icon->pixmap != None)
3537                         ghost = MakeGhostIcon(scr, icon->pixmap);
3538                 else
3539                         ghost = MakeGhostIcon(scr, icon->core->window);
3540
3541                 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3542                 XClearWindow(dpy, scr->dock_shadow);
3543         }
3544         XMapWindow(dpy, scr->dock_shadow);
3545
3546         ondock = 1;
3547
3548         while (1) {
3549                 XMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
3550                            | ButtonMotionMask | ExposureMask, &ev);
3551                 switch (ev.type) {
3552                 case Expose:
3553                         WMHandleEvent(&ev);
3554                         break;
3555
3556                 case MotionNotify:
3557                         if (!grabbed) {
3558                                 if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
3559                                     || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
3560                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
3561                                                                  | ButtonReleaseMask | ButtonPressMask,
3562                                                                  wCursor[WCUR_MOVE], CurrentTime);
3563                                         grabbed = 1;
3564                                 } else {
3565                                         break;
3566                                 }
3567                         }
3568
3569                         if (omnipresent) {
3570                                 int i;
3571                                 for (i = 0; i < scr->workspace_count; i++) {
3572                                         if (i == scr->current_workspace)
3573                                                 continue;
3574                                         wDockShowIcons(scr->workspaces[i]->clip);
3575                                 }
3576                         }
3577
3578                         x = ev.xmotion.x_root - ofs_x;
3579                         y = ev.xmotion.y_root - ofs_y;
3580                         tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3581                         if (tmp && dock2) {
3582                                 change_dock = 0;
3583                                 if (last_dock != dock && collapsed) {
3584                                         last_dock->collapsed = 1;
3585                                         wDockHideIcons(last_dock);
3586                                         collapsed = 0;
3587                                 }
3588                                 if (!collapsed && (collapsed = dock->collapsed)) {
3589                                         dock->collapsed = 0;
3590                                         wDockShowIcons(dock);
3591                                 }
3592                                 if (dock->auto_raise_lower)
3593                                         wDockRaise(dock);
3594                                 last_dock = dock;
3595                         } else if (dock2) {
3596                                 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3597                                 if (tmp) {
3598                                         change_dock = 1;
3599                                         if (last_dock != dock2 && collapsed) {
3600                                                 last_dock->collapsed = 1;
3601                                                 wDockHideIcons(last_dock);
3602                                                 collapsed = 0;
3603                                         }
3604                                         if (!collapsed && (collapsed = dock2->collapsed)) {
3605                                                 dock2->collapsed = 0;
3606                                                 wDockShowIcons(dock2);
3607                                         }
3608                                         if (dock2->auto_raise_lower)
3609                                                 wDockRaise(dock2);
3610                                         last_dock = dock2;
3611                                 }
3612                         }
3613                         if (aicon->launching || aicon->lock || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3614                             || (!aicon->running && tmp)) {
3615                                 shad_x = last_dock->x_pos + ix * wPreferences.icon_size;
3616                                 shad_y = last_dock->y_pos + iy * wPreferences.icon_size;
3617
3618                                 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3619
3620                                 if (!ondock) {
3621                                         XMapWindow(dpy, scr->dock_shadow);
3622                                 }
3623                                 ondock = 1;
3624                         } else {
3625                                 if (ondock) {
3626                                         XUnmapWindow(dpy, scr->dock_shadow);
3627                                 }
3628                                 ondock = 0;
3629                         }
3630                         XMoveWindow(dpy, icon->core->window, x, y);
3631                         break;
3632
3633                 case ButtonPress:
3634                         break;
3635
3636                 case ButtonRelease:
3637                         if (ev.xbutton.button != event->xbutton.button)
3638                                 break;
3639                         XUngrabPointer(dpy, CurrentTime);
3640                         if (ondock) {
3641                                 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3642                                 XUnmapWindow(dpy, scr->dock_shadow);
3643                                 if (!change_dock) {
3644                                         reattachIcon(dock, aicon, ix, iy);
3645                                         if (clip && dock != clip && clip->auto_raise_lower)
3646                                                 wDockLower(clip);
3647                                 } else {
3648                                         docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3649                                         if (!docked) {
3650                                                 /* Slide it back if dock rejected it */
3651                                                 SlideWindow(icon->core->window, x, y, aicon->x_pos, aicon->y_pos);
3652                                                 reattachIcon(dock, aicon, aicon->xindex, aicon->yindex);
3653                                         }
3654                                         if (last_dock->type == WM_CLIP && last_dock->auto_collapse) {
3655                                                 collapsed = 0;
3656                                         }
3657                                 }
3658                         } else {
3659                                 aicon->x_pos = x;
3660                                 aicon->y_pos = y;
3661                                 if (superfluous) {
3662                                         if (!aicon->running && !wPreferences.no_animations) {
3663                                                 /* We need to deselect it, even if is deselected in
3664                                                  * wDockDetach(), because else DoKaboom() will fail.
3665                                                  */
3666                                                 if (aicon->icon->selected)
3667                                                         wIconSelect(aicon->icon);
3668
3669                                                 wSoundPlay(WSOUND_KABOOM);