Change to the linux kernel coding style
[wmaker-crm.git] / src / appicon.c
1 /* appicon.c- icon for applications (not mini-window)
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
31 #include "WindowMaker.h"
32 #include "wcore.h"
33 #include "window.h"
34 #include "icon.h"
35 #include "appicon.h"
36 #include "actions.h"
37 #include "stacking.h"
38 #include "dock.h"
39 #include "funcs.h"
40 #include "defaults.h"
41 #include "workspace.h"
42 #include "superfluous.h"
43 #include "menu.h"
44 #include "framewin.h"
45 #include "dialog.h"
46 #include "client.h"
47 #ifdef XDND
48 #include "xdnd.h"
49 #endif
50 #include "wsound.h"
51
52 /*
53  * icon_file for the dock is got from the preferences file by
54  * using the classname/instancename
55  */
56
57 /**** Global variables ****/
58 extern Cursor wCursor[WCUR_LAST];
59 extern WPreferences wPreferences;
60
61 #define MOD_MASK       wPreferences.modifier_mask
62
63 void appIconMouseDown(WObjDescriptor * desc, XEvent * event);
64 static void iconDblClick(WObjDescriptor * desc, XEvent * event);
65 static void iconExpose(WObjDescriptor * desc, XEvent * event);
66
67 WAppIcon *wAppIconCreateForDock(WScreen * scr, char *command, char *wm_instance, char *wm_class, int tile)
68 {
69         WAppIcon *dicon;
70         char *path;
71
72         dicon = wmalloc(sizeof(WAppIcon));
73         wretain(dicon);
74         memset(dicon, 0, sizeof(WAppIcon));
75         dicon->yindex = -1;
76         dicon->xindex = -1;
77
78         dicon->prev = NULL;
79         dicon->next = scr->app_icon_list;
80         if (scr->app_icon_list) {
81                 scr->app_icon_list->prev = dicon;
82         }
83         scr->app_icon_list = dicon;
84
85         if (command) {
86                 dicon->command = wstrdup(command);
87         }
88         if (wm_class)
89                 dicon->wm_class = wstrdup(wm_class);
90         if (wm_instance)
91                 dicon->wm_instance = wstrdup(wm_instance);
92
93         path = wDefaultGetIconFile(scr, wm_instance, wm_class, True);
94         if (!path && command) {
95                 wApplicationExtractDirPackIcon(scr, command, wm_instance, wm_class);
96
97                 path = wDefaultGetIconFile(scr, wm_instance, wm_class, False);
98         }
99
100         if (path)
101                 path = FindImage(wPreferences.icon_path, path);
102
103         dicon->icon = wIconCreateWithIconFile(scr, path, tile);
104         if (path)
105                 wfree(path);
106 #ifdef XDND
107         wXDNDMakeAwareness(dicon->icon->core->window);
108 #endif
109
110 #ifdef DEMATERIALIZE_ICON
111         {
112                 XSetWindowAttributes attribs;
113                 attribs.save_under = True;
114                 XChangeWindowAttributes(dpy, dicon->icon->core->window, CWSaveUnder, &attribs);
115         }
116 #endif
117
118         /* will be overriden by dock */
119         dicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
120         dicon->icon->core->descriptor.handle_expose = iconExpose;
121         dicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
122         dicon->icon->core->descriptor.parent = dicon;
123         AddToStackList(dicon->icon->core);
124
125         return dicon;
126 }
127
128 WAppIcon *wAppIconCreate(WWindow * leader_win)
129 {
130         WAppIcon *aicon;
131         WScreen *scr = leader_win->screen_ptr;
132
133         aicon = wmalloc(sizeof(WAppIcon));
134         wretain(aicon);
135         memset(aicon, 0, sizeof(WAppIcon));
136
137         aicon->yindex = -1;
138         aicon->xindex = -1;
139
140         aicon->prev = NULL;
141         aicon->next = scr->app_icon_list;
142         if (scr->app_icon_list) {
143                 scr->app_icon_list->prev = aicon;
144         }
145         scr->app_icon_list = aicon;
146
147         if (leader_win->wm_class)
148                 aicon->wm_class = wstrdup(leader_win->wm_class);
149         if (leader_win->wm_instance)
150                 aicon->wm_instance = wstrdup(leader_win->wm_instance);
151
152         aicon->icon = wIconCreate(leader_win);
153 #ifdef DEMATERIALIZE_ICON
154         {
155                 XSetWindowAttributes attribs;
156                 attribs.save_under = True;
157                 XChangeWindowAttributes(dpy, aicon->icon->core->window, CWSaveUnder, &attribs);
158         }
159 #endif
160 #ifdef XDND
161         wXDNDMakeAwareness(aicon->icon->core->window);
162 #endif
163
164         /* will be overriden if docked */
165         aicon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
166         aicon->icon->core->descriptor.handle_expose = iconExpose;
167         aicon->icon->core->descriptor.parent_type = WCLASS_APPICON;
168         aicon->icon->core->descriptor.parent = aicon;
169         AddToStackList(aicon->icon->core);
170         aicon->icon->show_title = 0;
171         wIconUpdate(aicon->icon);
172
173         return aicon;
174 }
175
176 void wAppIconDestroy(WAppIcon * aicon)
177 {
178         WScreen *scr = aicon->icon->core->screen_ptr;
179
180         RemoveFromStackList(aicon->icon->core);
181         wIconDestroy(aicon->icon);
182         if (aicon->command)
183                 wfree(aicon->command);
184 #ifdef XDND
185         if (aicon->dnd_command)
186                 wfree(aicon->dnd_command);
187 #endif
188         if (aicon->wm_instance)
189                 wfree(aicon->wm_instance);
190         if (aicon->wm_class)
191                 wfree(aicon->wm_class);
192
193         if (aicon == scr->app_icon_list) {
194                 if (aicon->next)
195                         aicon->next->prev = NULL;
196                 scr->app_icon_list = aicon->next;
197         } else {
198                 if (aicon->next)
199                         aicon->next->prev = aicon->prev;
200                 if (aicon->prev)
201                         aicon->prev->next = aicon->next;
202         }
203
204         aicon->destroyed = 1;
205         wrelease(aicon);
206 }
207
208 #ifdef NEWAPPICON
209 static void drawCorner(WIcon * icon, WWindow * wwin, int active)
210 {
211         WScreen *scr = wwin->screen_ptr;
212         XPoint points[3];
213         GC gc;
214
215         points[0].x = 2;
216         points[0].y = 2;
217         points[1].x = 12;
218         points[1].y = 2;
219         points[2].x = 2;
220         points[2].y = 12;
221         if (active) {
222                 gc = scr->focused_texture->any.gc;
223         } else {
224                 gc = scr->unfocused_texture->any.gc;
225         }
226         XFillPolygon(dpy, icon->core->window, gc, points, 3, Convex, CoordModeOrigin);
227 }
228 #endif                          /* NEWAPPICON */
229
230 static void drawCorner(WIcon * icon)
231 {
232         WScreen *scr = icon->core->screen_ptr;
233         XPoint points[3];
234
235         points[0].x = 1;
236         points[0].y = 1;
237         points[1].x = 12;
238         points[1].y = 1;
239         points[2].x = 1;
240         points[2].y = 12;
241         XFillPolygon(dpy, icon->core->window, scr->icon_title_texture->normal_gc,
242                      points, 3, Convex, CoordModeOrigin);
243         XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 0, 12);
244         XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 0, 0, 12, 0);
245         /* drawing the second line gives a weird concave look. -Dan */
246 #if 0
247         XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 1, 1, 1, 11);
248         XDrawLine(dpy, icon->core->window, scr->icon_title_texture->light_gc, 1, 1, 11, 1);
249 #endif
250 }
251
252 void wAppIconMove(WAppIcon * aicon, int x, int y)
253 {
254         XMoveWindow(dpy, aicon->icon->core->window, x, y);
255         aicon->x_pos = x;
256         aicon->y_pos = y;
257 }
258
259 #ifdef WS_INDICATOR
260 static void updateDockNumbers(WScreen * scr)
261 {
262         int length;
263         char *ws_numbers;
264         WAppIcon *dicon = scr->dock->icon_array[0];
265
266         ws_numbers = wmalloc(20);
267         snprintf(ws_numbers, 20, "%i [ %i ]", scr->current_workspace + 1, ((scr->current_workspace / 10) + 1));
268         length = strlen(ws_numbers);
269
270         XClearArea(dpy, dicon->icon->core->window, 2, 2, 50, WMFontHeight(scr->icon_title_font) + 1, False);
271
272         WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->black,
273                      scr->icon_title_font, 4, 3, ws_numbers, length);
274
275         WMDrawString(scr->wmscreen, dicon->icon->core->window, scr->white,
276                      scr->icon_title_font, 3, 2, ws_numbers, length);
277
278         wfree(ws_numbers);
279 }
280 #endif                          /* WS_INDICATOR */
281
282 void wAppIconPaint(WAppIcon * aicon)
283 {
284         WApplication *wapp;
285         WScreen *scr = aicon->icon->core->screen_ptr;
286
287         if (aicon->icon->owner)
288                 wapp = wApplicationOf(aicon->icon->owner->main_window);
289         else
290                 wapp = NULL;
291
292         wIconPaint(aicon->icon);
293
294 # ifdef WS_INDICATOR
295         if (aicon->docked && scr->dock && scr->dock == aicon->dock && aicon->yindex == 0)
296                 updateDockNumbers(scr);
297 # endif
298         if (scr->dock_dots && aicon->docked && !aicon->running && aicon->command != NULL) {
299                 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
300                 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
301                 XCopyArea(dpy, scr->dock_dots->image, aicon->icon->core->window,
302                           scr->copy_gc, 0, 0, scr->dock_dots->width, scr->dock_dots->height, 0, 0);
303         }
304 #ifdef HIDDENDOT
305         if (wapp && wapp->flags.hidden) {
306                 XSetClipMask(dpy, scr->copy_gc, scr->dock_dots->mask);
307                 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
308                 XCopyArea(dpy, scr->dock_dots->image,
309                           aicon->icon->core->window, scr->copy_gc, 0, 0, 7, scr->dock_dots->height, 0, 0);
310         }
311 #endif                          /* HIDDENDOT */
312
313         if (aicon->omnipresent)
314                 drawCorner(aicon->icon);
315
316         XSetClipMask(dpy, scr->copy_gc, None);
317         if (aicon->launching) {
318                 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
319                                0, 0, wPreferences.icon_size, wPreferences.icon_size);
320         }
321 }
322
323 #define canBeDocked(wwin)  ((wwin) && ((wwin)->wm_class||(wwin)->wm_instance))
324
325 static void hideCallback(WMenu * menu, WMenuEntry * entry)
326 {
327         WApplication *wapp = (WApplication *) entry->clientdata;
328
329         if (wapp->flags.hidden) {
330                 wWorkspaceChange(menu->menu->screen_ptr, wapp->last_workspace);
331                 wUnhideApplication(wapp, False, False);
332         } else {
333                 wHideApplication(wapp);
334         }
335 }
336
337 static void unhideHereCallback(WMenu * menu, WMenuEntry * entry)
338 {
339         WApplication *wapp = (WApplication *) entry->clientdata;
340
341         wUnhideApplication(wapp, False, True);
342 }
343
344 static void setIconCallback(WMenu * menu, WMenuEntry * entry)
345 {
346         WAppIcon *icon = ((WApplication *) entry->clientdata)->app_icon;
347         char *file = NULL;
348         WScreen *scr;
349         int result;
350
351         assert(icon != NULL);
352
353         if (icon->editing)
354                 return;
355         icon->editing = 1;
356         scr = icon->icon->core->screen_ptr;
357
358         wretain(icon);
359
360         result = wIconChooserDialog(scr, &file, icon->wm_instance, icon->wm_class);
361
362         if (result && !icon->destroyed) {
363                 if (file && *file == 0) {
364                         wfree(file);
365                         file = NULL;
366                 }
367                 if (!wIconChangeImageFile(icon->icon, file)) {
368                         wMessageDialog(scr, _("Error"),
369                                        _("Could not open specified icon file"), _("OK"), NULL, NULL);
370                 } else {
371                         wDefaultChangeIcon(scr, icon->wm_instance, icon->wm_class, file);
372                         wAppIconPaint(icon);
373                 }
374                 if (file)
375                         wfree(file);
376         }
377         icon->editing = 0;
378         wrelease(icon);
379 }
380
381 static void killCallback(WMenu * menu, WMenuEntry * entry)
382 {
383         WApplication *wapp = (WApplication *) entry->clientdata;
384         WFakeGroupLeader *fPtr;
385         char *buffer;
386
387         if (!WCHECK_STATE(WSTATE_NORMAL))
388                 return;
389
390         WCHANGE_STATE(WSTATE_MODAL);
391
392         assert(entry->clientdata != NULL);
393
394         buffer = wstrconcat(wapp->app_icon ? wapp->app_icon->wm_class : NULL,
395                             _(" will be forcibly closed.\n"
396                               "Any unsaved changes will be lost.\n" "Please confirm."));
397
398         fPtr = wapp->main_window_desc->fake_group;
399
400         wretain(wapp->main_window_desc);
401         if (wPreferences.dont_confirm_kill
402             || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
403                               buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
404                 if (fPtr != NULL) {
405                         WWindow *wwin, *twin;
406
407                         wwin = wapp->main_window_desc->screen_ptr->focused_window;
408                         while (wwin) {
409                                 twin = wwin->prev;
410                                 if (wwin->fake_group == fPtr) {
411                                         wClientKill(wwin);
412                                 }
413                                 wwin = twin;
414                         }
415                 } else if (!wapp->main_window_desc->flags.destroyed) {
416                         wClientKill(wapp->main_window_desc);
417                 }
418         }
419         wrelease(wapp->main_window_desc);
420
421         wfree(buffer);
422
423         WCHANGE_STATE(WSTATE_NORMAL);
424 }
425
426 static WMenu *createApplicationMenu(WScreen * scr)
427 {
428         WMenu *menu;
429
430         menu = wMenuCreate(scr, NULL, False);
431         wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
432         wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
433         wMenuAddCallback(menu, _("Set Icon..."), setIconCallback, NULL);
434         wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
435
436         return menu;
437 }
438
439 static void openApplicationMenu(WApplication * wapp, int x, int y)
440 {
441         WMenu *menu;
442         WScreen *scr = wapp->main_window_desc->screen_ptr;
443         int i;
444
445         if (!scr->icon_menu) {
446                 scr->icon_menu = createApplicationMenu(scr);
447                 wfree(scr->icon_menu->entries[1]->text);
448         }
449
450         menu = scr->icon_menu;
451
452         if (wapp->flags.hidden) {
453                 menu->entries[1]->text = _("Unhide");
454         } else {
455                 menu->entries[1]->text = _("Hide");
456         }
457
458         menu->flags.realized = 0;
459         wMenuRealize(menu);
460
461         x -= menu->frame->core->width / 2;
462         if (x + menu->frame->core->width > scr->scr_width)
463                 x = scr->scr_width - menu->frame->core->width;
464         if (x < 0)
465                 x = 0;
466
467         /* set client data */
468         for (i = 0; i < menu->entry_no; i++) {
469                 menu->entries[i]->clientdata = wapp;
470         }
471         wMenuMapAt(menu, x, y, False);
472 }
473
474 /******************************************************************/
475
476 static void iconExpose(WObjDescriptor * desc, XEvent * event)
477 {
478         wAppIconPaint(desc->parent);
479 }
480
481 static void iconDblClick(WObjDescriptor * desc, XEvent * event)
482 {
483         WAppIcon *aicon = desc->parent;
484         WApplication *wapp;
485         WScreen *scr = aicon->icon->core->screen_ptr;
486         int unhideHere;
487
488         assert(aicon->icon->owner != NULL);
489
490         wapp = wApplicationOf(aicon->icon->owner->main_window);
491 #ifdef DEBUG0
492         if (!wapp) {
493                 wwarning("could not find application descriptor for app icon!!");
494                 return;
495         }
496 #endif
497
498         unhideHere = (event->xbutton.state & ShiftMask);
499
500         /* go to the last workspace that the user worked on the app */
501         if (!unhideHere && wapp->last_workspace != scr->current_workspace)
502                 wWorkspaceChange(scr, wapp->last_workspace);
503
504         wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
505
506         if (event->xbutton.state & MOD_MASK) {
507                 wHideOtherApplications(aicon->icon->owner);
508         }
509 }
510
511 void appIconMouseDown(WObjDescriptor * desc, XEvent * event)
512 {
513         WAppIcon *aicon = desc->parent;
514         WIcon *icon = aicon->icon;
515         XEvent ev;
516         int x = aicon->x_pos, y = aicon->y_pos;
517         int dx = event->xbutton.x, dy = event->xbutton.y;
518         int grabbed = 0;
519         int done = 0;
520         int superfluous = wPreferences.superfluous;     /* we catch it to avoid problems */
521         WScreen *scr = icon->core->screen_ptr;
522         WWorkspace *workspace = scr->workspaces[scr->current_workspace];
523         int shad_x = 0, shad_y = 0, docking = 0, dockable, collapsed = 0;
524         int ix, iy;
525         int clickButton = event->xbutton.button;
526         Pixmap ghost = None;
527         Window wins[2];
528         Bool movingSingle = False;
529         int oldX = x;
530         int oldY = y;
531
532         if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
533                 return;
534
535         if (IsDoubleClick(scr, event)) {
536                 iconDblClick(desc, event);
537                 return;
538         }
539
540         if (event->xbutton.button == Button3) {
541                 WObjDescriptor *desc;
542                 WApplication *wapp = wApplicationOf(aicon->icon->owner->main_window);
543
544                 if (!wapp)
545                         return;
546
547                 if (event->xbutton.send_event &&
548                     XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
549                                  | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
550                                  GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
551                         wwarning("pointer grab failed for appicon menu");
552                         return;
553                 }
554
555                 openApplicationMenu(wapp, event->xbutton.x_root, event->xbutton.y_root);
556
557                 /* allow drag select of menu */
558                 desc = &scr->icon_menu->menu->descriptor;
559                 event->xbutton.send_event = True;
560                 (*desc->handle_mousedown) (desc, event);
561                 return;
562         }
563 #ifdef DEBUG
564         puts("Moving icon");
565 #endif
566         if (event->xbutton.state & MOD_MASK)
567                 wLowerFrame(icon->core);
568         else
569                 wRaiseFrame(icon->core);
570
571         if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
572                          | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
573                          GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
574                 wwarning("pointer grab failed for appicon move");
575         }
576
577         if (wPreferences.flags.nodock && wPreferences.flags.noclip)
578                 dockable = 0;
579         else
580                 dockable = canBeDocked(icon->owner);
581
582         wins[0] = icon->core->window;
583         wins[1] = scr->dock_shadow;
584         XRestackWindows(dpy, wins, 2);
585         if (superfluous) {
586                 if (icon->pixmap != None)
587                         ghost = MakeGhostIcon(scr, icon->pixmap);
588                 else
589                         ghost = MakeGhostIcon(scr, icon->core->window);
590                 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
591                 XClearWindow(dpy, scr->dock_shadow);
592         }
593
594         while (!done) {
595                 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
596                             | ButtonMotionMask | ExposureMask, &ev);
597                 switch (ev.type) {
598                 case Expose:
599                         WMHandleEvent(&ev);
600                         break;
601
602                 case MotionNotify:
603                         if (!grabbed) {
604                                 if (abs(dx - ev.xmotion.x) >= MOVE_THRESHOLD
605                                     || abs(dy - ev.xmotion.y) >= MOVE_THRESHOLD) {
606                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
607                                                                  | ButtonReleaseMask | ButtonPressMask,
608                                                                  wCursor[WCUR_MOVE], CurrentTime);
609                                         grabbed = 1;
610                                 } else {
611                                         break;
612                                 }
613                         }
614                         x = ev.xmotion.x_root - dx;
615                         y = ev.xmotion.y_root - dy;
616
617                         if (movingSingle) {
618                                 XMoveWindow(dpy, icon->core->window, x, y);
619                         } else {
620                                 wAppIconMove(aicon, x, y);
621                         }
622
623                         if (dockable) {
624                                 if (scr->dock && wDockSnapIcon(scr->dock, aicon, x, y, &ix, &iy, False)) {
625                                         shad_x = scr->dock->x_pos + ix * wPreferences.icon_size;
626                                         shad_y = scr->dock->y_pos + iy * wPreferences.icon_size;
627
628                                         if (scr->last_dock != scr->dock && collapsed) {
629                                                 scr->last_dock->collapsed = 1;
630                                                 wDockHideIcons(scr->last_dock);
631                                                 collapsed = 0;
632                                         }
633                                         if (!collapsed && (collapsed = scr->dock->collapsed)) {
634                                                 scr->dock->collapsed = 0;
635                                                 wDockShowIcons(scr->dock);
636                                         }
637
638                                         if (scr->dock->auto_raise_lower)
639                                                 wDockRaise(scr->dock);
640
641                                         scr->last_dock = scr->dock;
642
643                                         XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
644                                         if (!docking) {
645                                                 XMapWindow(dpy, scr->dock_shadow);
646                                         }
647                                         docking = 1;
648                                 } else if (workspace->clip &&
649                                            wDockSnapIcon(workspace->clip, aicon, x, y, &ix, &iy, False)) {
650                                         shad_x = workspace->clip->x_pos + ix * wPreferences.icon_size;
651                                         shad_y = workspace->clip->y_pos + iy * wPreferences.icon_size;
652
653                                         if (scr->last_dock != workspace->clip && collapsed) {
654                                                 scr->last_dock->collapsed = 1;
655                                                 wDockHideIcons(scr->last_dock);
656                                                 collapsed = 0;
657                                         }
658                                         if (!collapsed && (collapsed = workspace->clip->collapsed)) {
659                                                 workspace->clip->collapsed = 0;
660                                                 wDockShowIcons(workspace->clip);
661                                         }
662
663                                         if (workspace->clip->auto_raise_lower)
664                                                 wDockRaise(workspace->clip);
665
666                                         scr->last_dock = workspace->clip;
667
668                                         XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
669                                         if (!docking) {
670                                                 XMapWindow(dpy, scr->dock_shadow);
671                                         }
672                                         docking = 1;
673                                 } else if (docking) {
674                                         XUnmapWindow(dpy, scr->dock_shadow);
675                                         docking = 0;
676                                 }
677                         }
678
679                         break;
680
681                 case ButtonPress:
682                         break;
683
684                 case ButtonRelease:
685                         if (ev.xbutton.button != clickButton)
686                                 break;
687                         XUngrabPointer(dpy, CurrentTime);
688
689                         if (docking) {
690                                 Bool docked;
691
692                                 /* icon is trying to be docked */
693                                 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
694                                 XUnmapWindow(dpy, scr->dock_shadow);
695                                 docked = wDockAttachIcon(scr->last_dock, aicon, ix, iy);
696                                 if (scr->last_dock->auto_collapse) {
697                                         collapsed = 0;
698                                 }
699                                 if (workspace->clip &&
700                                     workspace->clip != scr->last_dock && workspace->clip->auto_raise_lower)
701                                         wDockLower(workspace->clip);
702
703                                 if (!docked) {
704                                         /* If icon could not be docked, slide it back to the old
705                                          * position */
706                                         SlideWindow(icon->core->window, x, y, oldX, oldY);
707                                 }
708
709                                 wSoundPlay(WSOUND_DOCK);
710                         } else {
711                                 if (movingSingle) {
712                                         /* move back to its place */
713                                         SlideWindow(icon->core->window, x, y, oldX, oldY);
714                                         wAppIconMove(aicon, oldX, oldY);
715                                 } else {
716                                         XMoveWindow(dpy, icon->core->window, x, y);
717                                         aicon->x_pos = x;
718                                         aicon->y_pos = y;
719                                 }
720                                 if (workspace->clip && workspace->clip->auto_raise_lower)
721                                         wDockLower(workspace->clip);
722                         }
723                         if (collapsed) {
724                                 scr->last_dock->collapsed = 1;
725                                 wDockHideIcons(scr->last_dock);
726                                 collapsed = 0;
727                         }
728                         if (superfluous) {
729                                 if (ghost != None)
730                                         XFreePixmap(dpy, ghost);
731                                 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
732                         }
733
734                         if (wPreferences.auto_arrange_icons)
735                                 wArrangeIcons(scr, True);
736
737                         done = 1;
738                         break;
739                 }
740         }
741 #ifdef DEBUG
742         puts("End icon move");
743 #endif
744
745 }