Change to the linux kernel coding style
[wmaker-crm.git] / src / icon.c
1 /* icon.c - window icon and dock and appicon parent
2  *
3  *  Window Maker window manager
4  *
5  *  Copyright (c) 1997-2003 Alfredo K. Kojima
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20  *  USA.
21  */
22
23 #include "wconfig.h"
24
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <wraster.h>
34 #include <sys/stat.h>
35
36 #include "WindowMaker.h"
37 #include "wcore.h"
38 #include "texture.h"
39 #include "window.h"
40 #include "icon.h"
41 #include "actions.h"
42 #include "funcs.h"
43 #include "stacking.h"
44 #include "application.h"
45 #include "defaults.h"
46 #include "appicon.h"
47 #include "wmspec.h"
48
49 /**** Global varianebles ****/
50 extern WPreferences wPreferences;
51
52 #define MOD_MASK wPreferences.modifier_mask
53
54 extern Cursor wCursor[WCUR_LAST];
55
56 static void miniwindowExpose(WObjDescriptor * desc, XEvent * event);
57 static void miniwindowMouseDown(WObjDescriptor * desc, XEvent * event);
58 static void miniwindowDblClick(WObjDescriptor * desc, XEvent * event);
59
60 /****** Notification Observers ******/
61
62 static void appearanceObserver(void *self, WMNotification * notif)
63 {
64         WIcon *icon = (WIcon *) self;
65         int flags = (int)(uintptr_t) WMGetNotificationClientData(notif);
66
67         if (flags & WTextureSettings) {
68                 icon->force_paint = 1;
69         }
70         if (flags & WFontSettings) {
71                 icon->force_paint = 1;
72         }
73         /*
74            if (flags & WColorSettings) {
75            }
76          */
77
78         wIconPaint(icon);
79
80         /* so that the appicon expose handlers will paint the appicon specific
81          * stuff */
82         XClearArea(dpy, icon->core->window, 0, 0, icon->core->width, icon->core->height, True);
83 }
84
85 static void tileObserver(void *self, WMNotification * notif)
86 {
87         WIcon *icon = (WIcon *) self;
88
89         icon->force_paint = 1;
90         wIconPaint(icon);
91
92         XClearArea(dpy, icon->core->window, 0, 0, 1, 1, True);
93 }
94
95 /************************************/
96
97 INLINE static void getSize(Drawable d, unsigned int *w, unsigned int *h, unsigned int *dep)
98 {
99         Window rjunk;
100         int xjunk, yjunk;
101         unsigned int bjunk;
102
103         XGetGeometry(dpy, d, &rjunk, &xjunk, &yjunk, w, h, &bjunk, dep);
104 }
105
106 WIcon *wIconCreate(WWindow * wwin)
107 {
108         WScreen *scr = wwin->screen_ptr;
109         WIcon *icon;
110         char *file;
111         unsigned long vmask = 0;
112         XSetWindowAttributes attribs;
113
114         icon = wmalloc(sizeof(WIcon));
115         memset(icon, 0, sizeof(WIcon));
116         icon->core = wCoreCreateTopLevel(scr, wwin->icon_x, wwin->icon_y,
117                                          wPreferences.icon_size, wPreferences.icon_size, 0);
118
119         if (wPreferences.use_saveunders) {
120                 vmask |= CWSaveUnder;
121                 attribs.save_under = True;
122         }
123         /* a white border for selecting it */
124         vmask |= CWBorderPixel;
125         attribs.border_pixel = scr->white_pixel;
126
127         XChangeWindowAttributes(dpy, icon->core->window, vmask, &attribs);
128
129         /* will be overriden if this is an application icon */
130         icon->core->descriptor.handle_mousedown = miniwindowMouseDown;
131         icon->core->descriptor.handle_expose = miniwindowExpose;
132         icon->core->descriptor.parent_type = WCLASS_MINIWINDOW;
133         icon->core->descriptor.parent = icon;
134
135         icon->core->stacking = wmalloc(sizeof(WStacking));
136         icon->core->stacking->above = NULL;
137         icon->core->stacking->under = NULL;
138         icon->core->stacking->window_level = NORMAL_ICON_LEVEL;
139         icon->core->stacking->child_of = NULL;
140
141         icon->owner = wwin;
142         if (wwin->wm_hints && (wwin->wm_hints->flags & IconWindowHint)) {
143                 if (wwin->client_win == wwin->main_window) {
144                         WApplication *wapp;
145                         /* do not let miniwindow steal app-icon's icon window */
146                         wapp = wApplicationOf(wwin->client_win);
147                         if (!wapp || wapp->app_icon == NULL)
148                                 icon->icon_win = wwin->wm_hints->icon_window;
149                 } else {
150                         icon->icon_win = wwin->wm_hints->icon_window;
151                 }
152         }
153 #ifdef NO_MINIWINDOW_TITLES
154         icon->show_title = 0;
155 #else
156         icon->show_title = 1;
157 #endif
158 #ifdef NETWM_HINTS
159         if (!icon->image && !WFLAGP(wwin, always_user_icon))
160                 icon->image = RRetainImage(wwin->net_icon_image);
161         if (!icon->image)
162 #endif
163                 icon->image = wDefaultGetImage(scr, wwin->wm_instance, wwin->wm_class);
164
165         file = wDefaultGetIconFile(scr, wwin->wm_instance, wwin->wm_class, False);
166         if (file) {
167                 icon->file = wstrdup(file);
168         }
169
170         icon->icon_name = wNETWMGetIconName(wwin->client_win);
171         if (icon->icon_name)
172                 wwin->flags.net_has_icon_title = 1;
173         else
174                 wGetIconName(dpy, wwin->client_win, &icon->icon_name);
175
176         icon->tile_type = TILE_NORMAL;
177
178         wIconUpdate(icon);
179
180         XFlush(dpy);
181
182         WMAddNotificationObserver(appearanceObserver, icon, WNIconAppearanceSettingsChanged, icon);
183         WMAddNotificationObserver(tileObserver, icon, WNIconTileSettingsChanged, icon);
184         return icon;
185 }
186
187 WIcon *wIconCreateWithIconFile(WScreen * scr, char *iconfile, int tile)
188 {
189         WIcon *icon;
190         unsigned long vmask = 0;
191         XSetWindowAttributes attribs;
192
193         icon = wmalloc(sizeof(WIcon));
194         memset(icon, 0, sizeof(WIcon));
195         icon->core = wCoreCreateTopLevel(scr, 0, 0, wPreferences.icon_size, wPreferences.icon_size, 0);
196         if (wPreferences.use_saveunders) {
197                 vmask = CWSaveUnder;
198                 attribs.save_under = True;
199         }
200         /* a white border for selecting it */
201         vmask |= CWBorderPixel;
202         attribs.border_pixel = scr->white_pixel;
203
204         XChangeWindowAttributes(dpy, icon->core->window, vmask, &attribs);
205
206         /* will be overriden if this is a application icon */
207         icon->core->descriptor.handle_mousedown = miniwindowMouseDown;
208         icon->core->descriptor.handle_expose = miniwindowExpose;
209         icon->core->descriptor.parent_type = WCLASS_MINIWINDOW;
210         icon->core->descriptor.parent = icon;
211
212         icon->core->stacking = wmalloc(sizeof(WStacking));
213         icon->core->stacking->above = NULL;
214         icon->core->stacking->under = NULL;
215         icon->core->stacking->window_level = NORMAL_ICON_LEVEL;
216         icon->core->stacking->child_of = NULL;
217
218         if (iconfile) {
219                 icon->image = RLoadImage(scr->rcontext, iconfile, 0);
220                 if (!icon->image) {
221                         wwarning(_("error loading image file \"%s\""), iconfile, RMessageForError(RErrorCode));
222                 }
223
224                 icon->image = wIconValidateIconSize(scr, icon->image);
225
226                 icon->file = wstrdup(iconfile);
227         }
228
229         icon->tile_type = tile;
230
231         wIconUpdate(icon);
232
233         WMAddNotificationObserver(appearanceObserver, icon, WNIconAppearanceSettingsChanged, icon);
234         WMAddNotificationObserver(tileObserver, icon, WNIconTileSettingsChanged, icon);
235
236         return icon;
237 }
238
239 void wIconDestroy(WIcon * icon)
240 {
241         WCoreWindow *core = icon->core;
242         WScreen *scr = core->screen_ptr;
243
244         WMRemoveNotificationObserver(icon);
245
246         if (icon->handlerID)
247                 WMDeleteTimerHandler(icon->handlerID);
248
249         if (icon->icon_win) {
250                 int x = 0, y = 0;
251
252                 if (icon->owner) {
253                         x = icon->owner->icon_x;
254                         y = icon->owner->icon_y;
255                 }
256                 XUnmapWindow(dpy, icon->icon_win);
257                 XReparentWindow(dpy, icon->icon_win, scr->root_win, x, y);
258         }
259         if (icon->icon_name)
260                 XFree(icon->icon_name);
261
262         if (icon->pixmap)
263                 XFreePixmap(dpy, icon->pixmap);
264
265         if (icon->file)
266                 wfree(icon->file);
267
268         if (icon->image != NULL)
269                 RReleaseImage(icon->image);
270
271         wCoreDestroy(icon->core);
272         wfree(icon);
273 }
274
275 static void drawIconTitle(WScreen * scr, Pixmap pixmap, int height)
276 {
277         XFillRectangle(dpy, pixmap, scr->icon_title_texture->normal_gc, 0, 0, wPreferences.icon_size, height + 1);
278         XDrawLine(dpy, pixmap, scr->icon_title_texture->light_gc, 0, 0, wPreferences.icon_size, 0);
279         XDrawLine(dpy, pixmap, scr->icon_title_texture->light_gc, 0, 0, 0, height + 1);
280         XDrawLine(dpy, pixmap, scr->icon_title_texture->dim_gc,
281                   wPreferences.icon_size - 1, 0, wPreferences.icon_size - 1, height + 1);
282 }
283
284 static Pixmap makeIcon(WScreen * scr, RImage * icon, int titled, int shadowed, int tileType)
285 {
286         RImage *tile;
287         Pixmap pixmap;
288         int x, y, sx, sy;
289         unsigned w, h;
290         int theight = WMFontHeight(scr->icon_title_font);
291
292         if (tileType == TILE_NORMAL)
293                 tile = RCloneImage(scr->icon_tile);
294         else {
295                 assert(scr->clip_tile);
296                 tile = RCloneImage(scr->clip_tile);
297         }
298         if (icon) {
299                 w = (icon->width > wPreferences.icon_size)
300                     ? wPreferences.icon_size : icon->width;
301                 x = (wPreferences.icon_size - w) / 2;
302                 sx = (icon->width - w) / 2;
303
304                 if (!titled) {
305                         h = (icon->height > wPreferences.icon_size)
306                             ? wPreferences.icon_size : icon->height;
307                         y = (wPreferences.icon_size - h) / 2;
308                         sy = (icon->height - h) / 2;
309                 } else {
310                         h = (icon->height + theight > wPreferences.icon_size
311                              ? wPreferences.icon_size - theight : icon->height);
312                         y = theight + ((int)wPreferences.icon_size - theight - h) / 2;
313                         sy = (icon->height - h) / 2;
314                 }
315                 RCombineArea(tile, icon, sx, sy, w, h, x, y);
316         }
317
318         if (shadowed) {
319                 RColor color;
320
321                 color.red = scr->icon_back_texture->light.red >> 8;
322                 color.green = scr->icon_back_texture->light.green >> 8;
323                 color.blue = scr->icon_back_texture->light.blue >> 8;
324                 color.alpha = 150;      /* about 60% */
325                 RClearImage(tile, &color);
326         }
327
328         if (!RConvertImage(scr->rcontext, tile, &pixmap)) {
329                 wwarning(_("error rendering image:%s"), RMessageForError(RErrorCode));
330         }
331         RReleaseImage(tile);
332
333         if (titled)
334                 drawIconTitle(scr, pixmap, theight);
335
336         return pixmap;
337 }
338
339 void wIconChangeTitle(WIcon * icon, char *new_title)
340 {
341         int changed;
342
343         changed = (new_title == NULL && icon->icon_name != NULL)
344             || (new_title != NULL && icon->icon_name == NULL);
345
346         if (icon->icon_name != NULL)
347                 XFree(icon->icon_name);
348
349         icon->icon_name = new_title;
350
351         if (changed)
352                 icon->force_paint = 1;
353         wIconPaint(icon);
354 }
355
356 void wIconChangeImage(WIcon * icon, RImage * new_image)
357 {
358         assert(icon != NULL);
359
360         if (icon->image)
361                 RReleaseImage(icon->image);
362
363         icon->image = new_image;
364
365         wIconUpdate(icon);
366 }
367
368 RImage *wIconValidateIconSize(WScreen * scr, RImage * icon)
369 {
370         RImage *tmp;
371         int w, h;
372
373         if (!icon)
374                 return NULL;
375 #ifndef DONT_SCALE_ICONS
376         if (wPreferences.icon_size != 64) {
377                 w = wPreferences.icon_size * icon->width / 64;
378                 h = wPreferences.icon_size * icon->height / 64;
379
380                 tmp = RScaleImage(icon, w, h);
381                 RReleaseImage(icon);
382                 icon = tmp;
383         }
384 #endif
385 #if 0
386         if (icon->width > wPreferences.icon_size || icon->height > wPreferences.icon_size) {
387                 if (icon->width > icon->height) {
388                         w = wPreferences.icon_size - 4;
389                         h = w * icon->height / icon->width;
390                 } else {
391                         h = wPreferences.icon_size - 4;
392                         w = h * icon->width / icon->height;
393                 }
394                 tmp = RScaleImage(icon, w, h);
395                 RReleaseImage(icon);
396                 icon = tmp;
397         }
398 #endif
399
400         return icon;
401 }
402
403 Bool wIconChangeImageFile(WIcon * icon, char *file)
404 {
405         WScreen *scr = icon->core->screen_ptr;
406         RImage *image;
407         char *path;
408         int error = 0;
409
410         if (!file) {
411                 wIconChangeImage(icon, NULL);
412                 return True;
413         }
414
415         path = FindImage(wPreferences.icon_path, file);
416
417         if (path && (image = RLoadImage(scr->rcontext, path, 0))) {
418                 image = wIconValidateIconSize(icon->core->screen_ptr, image);
419
420                 wIconChangeImage(icon, image);
421         } else {
422                 error = 1;
423         }
424
425         if (path)
426                 wfree(path);
427
428         return !error;
429 }
430
431 static char *getnameforicon(WWindow * wwin)
432 {
433         char *prefix, *suffix;
434         char *path;
435         int len;
436
437         if (wwin->wm_class && wwin->wm_instance) {
438                 int len = strlen(wwin->wm_class) + strlen(wwin->wm_instance) + 2;
439                 suffix = wmalloc(len);
440                 snprintf(suffix, len, "%s.%s", wwin->wm_instance, wwin->wm_class);
441         } else if (wwin->wm_class) {
442                 int len = strlen(wwin->wm_class) + 1;
443                 suffix = wmalloc(len);
444                 snprintf(suffix, len, "%s", wwin->wm_class);
445         } else if (wwin->wm_instance) {
446                 int len = strlen(wwin->wm_instance) + 1;
447                 suffix = wmalloc(len);
448                 snprintf(suffix, len, "%s", wwin->wm_instance);
449         } else {
450                 return NULL;
451         }
452
453         prefix = wusergnusteppath();
454         len = strlen(prefix) + 64 + strlen(suffix);
455         path = wmalloc(len + 1);
456         snprintf(path, len, "%s/Library/WindowMaker", prefix);
457
458         if (access(path, F_OK) != 0) {
459                 if (mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR)) {
460                         wsyserror(_("could not create directory %s"), path);
461                         wfree(path);
462                         wfree(suffix);
463                         return NULL;
464                 }
465         }
466         strcat(path, "/CachedPixmaps");
467         if (access(path, F_OK) != 0) {
468                 if (mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
469                         wsyserror(_("could not create directory %s"), path);
470                         wfree(path);
471                         wfree(suffix);
472                         return NULL;
473                 }
474         }
475
476         strcat(path, "/");
477         strcat(path, suffix);
478         strcat(path, ".xpm");
479         wfree(suffix);
480
481         return path;
482 }
483
484 /*
485  * wIconStore--
486  *      Stores the client supplied icon at ~/GNUstep/Library/WindowMaker/CachedPixmaps
487  * and returns the path for that icon. Returns NULL if there is no
488  * client supplied icon or on failure.
489  *
490  * Side effects:
491  *      New directories might be created.
492  */
493 char *wIconStore(WIcon * icon)
494 {
495         char *path;
496         RImage *image;
497         WWindow *wwin = icon->owner;
498
499         if (!wwin || !wwin->wm_hints || !(wwin->wm_hints->flags & IconPixmapHint)
500             || wwin->wm_hints->icon_pixmap == None)
501                 return NULL;
502
503         path = getnameforicon(wwin);
504         if (!path)
505                 return NULL;
506
507         image = RCreateImageFromDrawable(icon->core->screen_ptr->rcontext,
508                                          wwin->wm_hints->icon_pixmap, (wwin->wm_hints->flags & IconMaskHint)
509                                          ? wwin->wm_hints->icon_mask : None);
510         if (!image) {
511                 wfree(path);
512                 return NULL;
513         }
514
515         if (!RSaveImage(image, path, "XPM")) {
516                 wfree(path);
517                 path = NULL;
518         }
519         RReleaseImage(image);
520
521         return path;
522 }
523
524 /*
525 void wIconChangeIconWindow(WIcon *icon, Window new_window);
526 */
527
528 static void cycleColor(void *data)
529 {
530         WIcon *icon = (WIcon *) data;
531         WScreen *scr = icon->core->screen_ptr;
532         XGCValues gcv;
533
534         icon->step--;
535         gcv.dash_offset = icon->step;
536         XChangeGC(dpy, scr->icon_select_gc, GCDashOffset, &gcv);
537
538         XDrawRectangle(dpy, icon->core->window, scr->icon_select_gc, 0, 0,
539                        icon->core->width - 1, icon->core->height - 1);
540         icon->handlerID = WMAddTimerHandler(COLOR_CYCLE_DELAY, cycleColor, icon);
541 }
542
543 void wIconSetHighlited(WIcon * icon, Bool flag)
544 {
545         if (icon->highlighted == flag) {
546                 return;
547         }
548
549         icon->highlighted = flag;
550         wIconPaint(icon);
551 }
552
553 void wIconSelect(WIcon * icon)
554 {
555         WScreen *scr = icon->core->screen_ptr;
556         icon->selected = !icon->selected;
557
558         if (icon->selected) {
559                 icon->step = 0;
560                 if (!wPreferences.dont_blink)
561                         icon->handlerID = WMAddTimerHandler(10, cycleColor, icon);
562                 else
563                         XDrawRectangle(dpy, icon->core->window, scr->icon_select_gc, 0, 0,
564                                        icon->core->width - 1, icon->core->height - 1);
565         } else {
566                 if (icon->handlerID) {
567                         WMDeleteTimerHandler(icon->handlerID);
568                         icon->handlerID = NULL;
569                 }
570                 XClearArea(dpy, icon->core->window, 0, 0, icon->core->width, icon->core->height, True);
571         }
572 }
573
574 void wIconUpdate(WIcon * icon)
575 {
576         WScreen *scr = icon->core->screen_ptr;
577         int title_height = WMFontHeight(scr->icon_title_font);
578         WWindow *wwin = icon->owner;
579
580         assert(scr->icon_tile != NULL);
581
582         if (icon->pixmap != None)
583                 XFreePixmap(dpy, icon->pixmap);
584         icon->pixmap = None;
585
586         if (wwin && (WFLAGP(wwin, always_user_icon)
587 #ifdef NETWM_HINTS
588                      || wwin->net_icon_image
589 #endif
590             ))
591                 goto user_icon;
592
593         /* use client specified icon window */
594         if (icon->icon_win != None) {
595                 XWindowAttributes attr;
596                 int resize = 0;
597                 unsigned int width, height, depth;
598                 int theight;
599                 Pixmap pixmap;
600
601                 getSize(icon->icon_win, &width, &height, &depth);
602
603                 if (width > wPreferences.icon_size) {
604                         resize = 1;
605                         width = wPreferences.icon_size;
606                 }
607                 if (height > wPreferences.icon_size) {
608                         resize = 1;
609                         height = wPreferences.icon_size;
610                 }
611                 if (icon->show_title && (height + title_height < wPreferences.icon_size)) {
612                         pixmap = XCreatePixmap(dpy, scr->w_win, wPreferences.icon_size,
613                                                wPreferences.icon_size, scr->w_depth);
614                         XSetClipMask(dpy, scr->copy_gc, None);
615                         XCopyArea(dpy, scr->icon_tile_pixmap, pixmap, scr->copy_gc, 0, 0,
616                                   wPreferences.icon_size, wPreferences.icon_size, 0, 0);
617                         drawIconTitle(scr, pixmap, title_height);
618                         theight = title_height;
619                 } else {
620                         pixmap = None;
621                         theight = 0;
622                         XSetWindowBackgroundPixmap(dpy, icon->core->window, scr->icon_tile_pixmap);
623                 }
624
625                 XSetWindowBorderWidth(dpy, icon->icon_win, 0);
626                 XReparentWindow(dpy, icon->icon_win, icon->core->window,
627                                 (wPreferences.icon_size - width) / 2,
628                                 theight + (wPreferences.icon_size - height - theight) / 2);
629                 if (resize)
630                         XResizeWindow(dpy, icon->icon_win, width, height);
631
632                 XMapWindow(dpy, icon->icon_win);
633
634                 XAddToSaveSet(dpy, icon->icon_win);
635
636                 icon->pixmap = pixmap;
637
638                 if (XGetWindowAttributes(dpy, icon->icon_win, &attr)) {
639                         if (attr.all_event_masks & ButtonPressMask) {
640                                 wHackedGrabButton(Button1, MOD_MASK, icon->core->window, True,
641                                                   ButtonPressMask, GrabModeSync, GrabModeAsync,
642                                                   None, wCursor[WCUR_ARROW]);
643                         }
644                 }
645         } else if (wwin && wwin->wm_hints && (wwin->wm_hints->flags & IconPixmapHint)) {
646                 int x, y;
647                 unsigned int w, h;
648                 Window jw;
649                 int ji, dotitle;
650                 unsigned int ju, d;
651                 Pixmap pixmap;
652
653                 if (!XGetGeometry(dpy, wwin->wm_hints->icon_pixmap, &jw, &ji, &ji, &w, &h, &ju, &d)) {
654                         icon->owner->wm_hints->flags &= ~IconPixmapHint;
655                         goto user_icon;
656                 }
657
658                 pixmap = XCreatePixmap(dpy, icon->core->window, wPreferences.icon_size,
659                                        wPreferences.icon_size, scr->w_depth);
660                 XSetClipMask(dpy, scr->copy_gc, None);
661                 XCopyArea(dpy, scr->icon_tile_pixmap, pixmap, scr->copy_gc, 0, 0,
662                           wPreferences.icon_size, wPreferences.icon_size, 0, 0);
663
664                 if (w > wPreferences.icon_size)
665                         w = wPreferences.icon_size;
666                 x = (wPreferences.icon_size - w) / 2;
667
668                 if (icon->show_title && (title_height < wPreferences.icon_size)) {
669                         drawIconTitle(scr, pixmap, title_height);
670                         dotitle = 1;
671
672                         if (h > wPreferences.icon_size - title_height - 2) {
673                                 h = wPreferences.icon_size - title_height - 2;
674                                 y = title_height + 1;
675                         } else {
676                                 y = (wPreferences.icon_size - h - title_height) / 2 + title_height + 1;
677                         }
678                 } else {
679                         dotitle = 0;
680                         if (w > wPreferences.icon_size)
681                                 w = wPreferences.icon_size;
682                         y = (wPreferences.icon_size - h) / 2;
683                 }
684
685                 if (wwin->wm_hints->flags & IconMaskHint)
686                         XSetClipMask(dpy, scr->copy_gc, wwin->wm_hints->icon_mask);
687
688                 XSetClipOrigin(dpy, scr->copy_gc, x, y);
689
690                 if (d != scr->w_depth) {
691                         XSetForeground(dpy, scr->copy_gc, scr->black_pixel);
692                         XSetBackground(dpy, scr->copy_gc, scr->white_pixel);
693                         XCopyPlane(dpy, wwin->wm_hints->icon_pixmap, pixmap, scr->copy_gc, 0, 0, w, h, x, y, 1);
694                 } else {
695                         XCopyArea(dpy, wwin->wm_hints->icon_pixmap, pixmap, scr->copy_gc, 0, 0, w, h, x, y);
696                 }
697
698                 XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
699
700                 icon->pixmap = pixmap;
701         } else {
702  user_icon:
703
704                 if (icon->image) {
705                         icon->pixmap = makeIcon(scr, icon->image, icon->show_title,
706                                                 icon->shadowed, icon->tile_type);
707                 } else {
708                         /* make default icons */
709
710                         if (!scr->def_icon_pixmap) {
711                                 RImage *image = NULL;
712                                 char *path;
713                                 char *file;
714
715                                 file = wDefaultGetIconFile(scr, NULL, NULL, False);
716                                 if (file) {
717                                         path = FindImage(wPreferences.icon_path, file);
718                                         if (!path) {
719                                                 wwarning(_("could not find default icon \"%s\""), file);
720                                                 goto make_icons;
721                                         }
722
723                                         image = RLoadImage(scr->rcontext, path, 0);
724                                         if (!image) {
725                                                 wwarning(_("could not load default icon \"%s\":%s"),
726                                                          file, RMessageForError(RErrorCode));
727                                         }
728                                         wfree(path);
729                                 }
730  make_icons:
731
732                                 image = wIconValidateIconSize(scr, image);
733                                 scr->def_icon_pixmap = makeIcon(scr, image, False, False, icon->tile_type);
734                                 scr->def_ticon_pixmap = makeIcon(scr, image, True, False, icon->tile_type);
735                                 if (image)
736                                         RReleaseImage(image);
737                         }
738
739                         if (icon->show_title) {
740                                 XSetWindowBackgroundPixmap(dpy, icon->core->window, scr->def_ticon_pixmap);
741                         } else {
742                                 XSetWindowBackgroundPixmap(dpy, icon->core->window, scr->def_icon_pixmap);
743                         }
744                         icon->pixmap = None;
745                 }
746         }
747         if (icon->pixmap != None) {
748                 XSetWindowBackgroundPixmap(dpy, icon->core->window, icon->pixmap);
749         }
750         XClearWindow(dpy, icon->core->window);
751
752         wIconPaint(icon);
753 }
754
755 void wIconPaint(WIcon * icon)
756 {
757         WScreen *scr = icon->core->screen_ptr;
758         int x;
759         char *tmp;
760
761         if (icon->force_paint) {
762                 icon->force_paint = 0;
763                 wIconUpdate(icon);
764                 return;
765         }
766
767         XClearWindow(dpy, icon->core->window);
768
769         /* draw the icon title */
770         if (icon->show_title && icon->icon_name != NULL) {
771                 int l;
772                 int w;
773
774                 tmp = ShrinkString(scr->icon_title_font, icon->icon_name, wPreferences.icon_size - 4);
775                 w = WMWidthOfString(scr->icon_title_font, tmp, l = strlen(tmp));
776
777                 if (w > icon->core->width - 4)
778                         x = (icon->core->width - 4) - w;
779                 else
780                         x = (icon->core->width - w) / 2;
781
782                 WMDrawString(scr->wmscreen, icon->core->window, scr->icon_title_color,
783                              scr->icon_title_font, x, 1, tmp, l);
784                 wfree(tmp);
785         }
786
787         if (icon->selected)
788                 XDrawRectangle(dpy, icon->core->window, scr->icon_select_gc, 0, 0,
789                                icon->core->width - 1, icon->core->height - 1);
790 }
791
792 /******************************************************************/
793
794 static void miniwindowExpose(WObjDescriptor * desc, XEvent * event)
795 {
796         wIconPaint(desc->parent);
797 }
798
799 static void miniwindowDblClick(WObjDescriptor * desc, XEvent * event)
800 {
801         WIcon *icon = desc->parent;
802
803         assert(icon->owner != NULL);
804
805         wDeiconifyWindow(icon->owner);
806 }
807
808 static void miniwindowMouseDown(WObjDescriptor * desc, XEvent * event)
809 {
810         WIcon *icon = desc->parent;
811         WWindow *wwin = icon->owner;
812         XEvent ev;
813         int x = wwin->icon_x, y = wwin->icon_y;
814         int dx = event->xbutton.x, dy = event->xbutton.y;
815         int grabbed = 0;
816         int clickButton = event->xbutton.button;
817
818         if (WCHECK_STATE(WSTATE_MODAL))
819                 return;
820
821         if (IsDoubleClick(icon->core->screen_ptr, event)) {
822                 miniwindowDblClick(desc, event);
823                 return;
824         }
825 #ifdef DEBUG
826         puts("Moving miniwindow");
827 #endif
828         if (event->xbutton.button == Button1) {
829                 if (event->xbutton.state & MOD_MASK)
830                         wLowerFrame(icon->core);
831                 else
832                         wRaiseFrame(icon->core);
833                 if (event->xbutton.state & ShiftMask) {
834                         wIconSelect(icon);
835                         wSelectWindow(icon->owner, !wwin->flags.selected);
836                 }
837         } else if (event->xbutton.button == Button3) {
838                 WObjDescriptor *desc;
839
840                 OpenMiniwindowMenu(wwin, event->xbutton.x_root, event->xbutton.y_root);
841
842                 /* allow drag select of menu */
843                 desc = &wwin->screen_ptr->window_menu->menu->descriptor;
844                 event->xbutton.send_event = True;
845                 (*desc->handle_mousedown) (desc, event);
846
847                 return;
848         }
849
850         if (XGrabPointer(dpy, icon->core->window, False, ButtonMotionMask
851                          | ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
852                          GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
853 #ifdef DEBUG0
854                 wwarning("pointer grab failed for icon move");
855 #endif
856         }
857         while (1) {
858                 WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
859                             | ButtonMotionMask | ExposureMask, &ev);
860                 switch (ev.type) {
861                 case Expose:
862                         WMHandleEvent(&ev);
863                         break;
864
865                 case MotionNotify:
866                         if (!grabbed) {
867                                 if (abs(dx - ev.xmotion.x) >= MOVE_THRESHOLD
868                                     || abs(dy - ev.xmotion.y) >= MOVE_THRESHOLD) {
869                                         XChangeActivePointerGrab(dpy, ButtonMotionMask
870                                                                  | ButtonReleaseMask | ButtonPressMask,
871                                                                  wCursor[WCUR_MOVE], CurrentTime);
872                                         grabbed = 1;
873                                 } else {
874                                         break;
875                                 }
876                         }
877                         x = ev.xmotion.x_root - dx;
878                         y = ev.xmotion.y_root - dy;
879                         XMoveWindow(dpy, icon->core->window, x, y);
880                         break;
881
882                 case ButtonPress:
883                         break;
884
885                 case ButtonRelease:
886                         if (ev.xbutton.button != clickButton)
887                                 break;
888
889                         if (wwin->icon_x != x || wwin->icon_y != y)
890                                 wwin->flags.icon_moved = 1;
891
892                         XMoveWindow(dpy, icon->core->window, x, y);
893
894                         wwin->icon_x = x;
895                         wwin->icon_y = y;
896 #ifdef DEBUG
897                         puts("End miniwindow move");
898 #endif
899                         XUngrabPointer(dpy, CurrentTime);
900
901                         if (wPreferences.auto_arrange_icons)
902                                 wArrangeIcons(wwin->screen_ptr, True);
903                         return;
904
905                 }
906         }
907 }