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 }