1 /**
2 * @file icon.c
3 * @author Joe Wingbermuehle
4 * @date 2004-2006
5 *
6 * @brief Icon functions.
7 *
8 */
9
10 #include "jwm.h"
11 #include "icon.h"
12 #include "client.h"
13 #include "render.h"
14 #include "main.h"
15 #include "image.h"
16 #include "misc.h"
17 #include "hint.h"
18 #include "color.h"
19 #include "settings.h"
20 #include "border.h"
21
22 IconNode emptyIcon;
23
24 #ifdef USE_ICONS
25
26 /* Must be a power of two. */
27 #define HASH_SIZE 128
28
29 /** Linked list of icon paths. */
30 typedef struct IconPathNode {
31 char *path;
32 struct IconPathNode *next;
33 } IconPathNode;
34
35 /* These extensions are appended to icon names during search. */
36 const char *ICON_EXTENSIONS[] = {
37 "",
38 #ifdef USE_PNG
39 ".png",
40 ".PNG",
41 #endif
42 #if defined(USE_CAIRO) && defined(USE_RSVG)
43 ".svg",
44 ".SVG",
45 #endif
46 #ifdef USE_XPM
47 ".xpm",
48 ".XPM",
49 #endif
50 #ifdef USE_JPEG
51 ".jpg",
52 ".JPG",
53 ".jpeg",
54 ".JPEG",
55 #endif
56 #ifdef USE_XBM
57 ".xbm",
58 ".XBM",
59 #endif
60 };
61 static const unsigned EXTENSION_COUNT = ARRAY_LENGTH(ICON_EXTENSIONS);
62 static const unsigned MAX_EXTENSION_LENGTH = 5;
63
64 static IconNode **iconHash;
65 static IconPathNode *iconPaths;
66 static IconPathNode *iconPathsTail;
67 static GC iconGC;
68 static char iconSizeSet = 0;
69 static char *defaultIconName;
70
71 static void DoDestroyIcon(int index, IconNode *icon);
72 static IconNode *ReadNetWMIcon(Window win);
73 static IconNode *ReadWMHintIcon(Window win);
74 static IconNode *CreateIcon(const ImageNode *image);
75 static IconNode *CreateIconFromDrawable(Drawable d, Pixmap mask);
76 static IconNode *CreateIconFromBinary(const unsigned long *data,
77 unsigned int length);
78 static IconNode *LoadNamedIconHelper(const char *name, const char *path,
79 char save, char preserveAspect);
80
81 static ImageNode *GetBestImage(IconNode *icon, int rwidth, int rheight);
82 static ScaledIconNode *GetScaledIcon(IconNode *icon, long fg,
83 int rwidth, int rheight);
84
85 static void InsertIcon(IconNode *icon);
86 static IconNode *FindIcon(const char *name);
87 static unsigned int GetHash(const char *str);
88
89 /** Initialize icon data.
90 * This must be initialized before parsing the configuration.
91 */
InitializeIcons(void)92 void InitializeIcons(void)
93 {
94 unsigned int x;
95 iconPaths = NULL;
96 iconPathsTail = NULL;
97 iconHash = Allocate(sizeof(IconNode*) * HASH_SIZE);
98 for(x = 0; x < HASH_SIZE; x++) {
99 iconHash[x] = NULL;
100 }
101 memset(&emptyIcon, 0, sizeof(emptyIcon));
102 iconSizeSet = 0;
103 defaultIconName = NULL;
104 }
105
106 /** Startup icon support. */
StartupIcons(void)107 void StartupIcons(void)
108 {
109 XGCValues gcValues;
110 XIconSize iconSize;
111 unsigned long gcMask;
112 gcMask = GCGraphicsExposures;
113 gcValues.graphics_exposures = False;
114 iconGC = JXCreateGC(display, rootWindow, gcMask, &gcValues);
115
116 iconSize.min_width = GetBorderIconSize();
117 iconSize.min_height = GetBorderIconSize();
118 iconSize.max_width = iconSize.min_width;
119 iconSize.max_height = iconSize.min_height;
120 iconSize.width_inc = 1;
121 iconSize.height_inc = 1;
122 JXSetIconSizes(display, rootWindow, &iconSize, 1);
123 }
124
125 /** Shutdown icon support. */
ShutdownIcons(void)126 void ShutdownIcons(void)
127 {
128 unsigned int x;
129 for(x = 0; x < HASH_SIZE; x++) {
130 while(iconHash[x]) {
131 DoDestroyIcon(x, iconHash[x]);
132 }
133 }
134 JXFreeGC(display, iconGC);
135 }
136
137 /** Destroy icon data. */
DestroyIcons(void)138 void DestroyIcons(void)
139 {
140 IconPathNode *pn;
141 while(iconPaths) {
142 pn = iconPaths->next;
143 Release(iconPaths->path);
144 Release(iconPaths);
145 iconPaths = pn;
146 }
147 iconPathsTail = NULL;
148 if(iconHash) {
149 Release(iconHash);
150 iconHash = NULL;
151 }
152 if(defaultIconName) {
153 Release(defaultIconName);
154 defaultIconName = NULL;
155 }
156 }
157
158 /** Add an icon search path. */
AddIconPath(char * path)159 void AddIconPath(char *path)
160 {
161
162 IconPathNode *ip;
163 int length;
164 char addSep;
165
166 if(!path) {
167 return;
168 }
169
170 Trim(path);
171
172 length = strlen(path);
173 if(path[length - 1] != '/') {
174 addSep = 1;
175 } else {
176 addSep = 0;
177 }
178
179 ip = Allocate(sizeof(IconPathNode));
180 ip->path = Allocate(length + addSep + 1);
181 memcpy(ip->path, path, length + 1);
182 if(addSep) {
183 ip->path[length] = '/';
184 ip->path[length + 1] = 0;
185 }
186 ExpandPath(&ip->path);
187 ip->next = NULL;
188
189 if(iconPathsTail) {
190 iconPathsTail->next = ip;
191 } else {
192 iconPaths = ip;
193 }
194 iconPathsTail = ip;
195
196 }
197
198 /** Draw an icon. */
PutIcon(IconNode * icon,Drawable d,long fg,int x,int y,int width,int height)199 void PutIcon(IconNode *icon, Drawable d, long fg,
200 int x, int y, int width, int height)
201 {
202 ScaledIconNode *node;
203
204 Assert(icon);
205
206 if(icon == &emptyIcon) {
207 return;
208 }
209
210 /* Scale the icon. */
211 node = GetScaledIcon(icon, fg, width, height);
212 if(node) {
213
214 /* If we support xrender, use it. */
215 #ifdef USE_XRENDER
216 if(icon->render) {
217 PutScaledRenderIcon(icon, node, d, x, y, width, height);
218 return;
219 }
220 #endif
221
222 /* Draw the icon the old way. */
223 if(node->image != None) {
224
225 const int ix = x + (width - node->width) / 2;
226 const int iy = y + (height - node->height) / 2;
227
228 /* Set the clip mask. */
229 if(node->mask != None) {
230 JXSetClipOrigin(display, iconGC, ix, iy);
231 JXSetClipMask(display, iconGC, node->mask);
232 }
233
234 /* Draw the icon. */
235 JXCopyArea(display, node->image, d, iconGC, 0, 0,
236 node->width, node->height, ix, iy);
237
238 /* Reset the clip mask. */
239 if(node->mask != None) {
240 JXSetClipMask(display, iconGC, None);
241 JXSetClipOrigin(display, iconGC, 0, 0);
242 }
243
244 }
245
246 }
247
248 }
249
250 /** Load the icon for a client. */
LoadIcon(ClientNode * np)251 void LoadIcon(ClientNode *np)
252 {
253 /* If client already has an icon, destroy it first. */
254 DestroyIcon(np->icon);
255 np->icon = NULL;
256
257 /* Attempt to read _NET_WM_ICON for an icon. */
258 np->icon = ReadNetWMIcon(np->window);
259 if(np->icon) {
260 return;
261 }
262 if(np->owner != None) {
263 np->icon = ReadNetWMIcon(np->owner);
264 if(np->icon) {
265 return;
266 }
267 }
268
269 /* Attempt to read an icon from XWMHints. */
270 np->icon = ReadWMHintIcon(np->window);
271 if(np->icon) {
272 return;
273 }
274 if(np->owner != None) {
275 np->icon = ReadNetWMIcon(np->owner);
276 if(np->icon) {
277 return;
278 }
279 }
280
281 /* Attempt to read an icon based on the window name. */
282 if(np->instanceName) {
283 np->icon = LoadNamedIcon(np->instanceName, 1, 1);
284 if(np->icon) {
285 return;
286 }
287 }
288 }
289
290 /** Load an icon from a file. */
LoadNamedIcon(const char * name,char save,char preserveAspect)291 IconNode *LoadNamedIcon(const char *name, char save, char preserveAspect)
292 {
293
294 IconNode *icon;
295 IconPathNode *ip;
296
297 Assert(name);
298
299 /* If no icon is specified, return an empty icon. */
300 if(name[0] == 0) {
301 return &emptyIcon;
302 }
303
304 /* See if this icon has already been loaded. */
305 icon = FindIcon(name);
306 if(icon) {
307 return icon;
308 }
309
310 /* Check for an absolute file name. */
311 if(name[0] == '/') {
312 ImageNode *image = LoadImage(name, 0, 0, 1);
313 if(image) {
314 icon = CreateIcon(image);
315 icon->preserveAspect = preserveAspect;
316 icon->name = CopyString(name);
317 if(save) {
318 InsertIcon(icon);
319 }
320 DestroyImage(image);
321 return icon;
322 } else {
323 return &emptyIcon;
324 }
325 }
326
327 /* Try icon paths. */
328 for(ip = iconPaths; ip; ip = ip->next) {
329 icon = LoadNamedIconHelper(name, ip->path, save, preserveAspect);
330 if(icon) {
331 return icon;
332 }
333 }
334
335 /* The default icon. */
336 return NULL;
337 }
338
339 /** Helper for loading icons by name. */
LoadNamedIconHelper(const char * name,const char * path,char save,char preserveAspect)340 IconNode *LoadNamedIconHelper(const char *name, const char *path,
341 char save, char preserveAspect)
342 {
343 ImageNode *image;
344 char *temp;
345 const unsigned nameLength = strlen(name);
346 const unsigned pathLength = strlen(path);
347 unsigned i;
348 char hasExtension;
349
350 /* Full file name. */
351 temp = AllocateStack(nameLength + pathLength + MAX_EXTENSION_LENGTH + 1);
352 memcpy(&temp[0], path, pathLength);
353 memcpy(&temp[pathLength], name, nameLength + 1);
354
355 /* Determine if the extension is provided.
356 * We avoid extra file opens if so.
357 */
358 hasExtension = 0;
359 for(i = 1; i < EXTENSION_COUNT; i++) {
360 const unsigned offset = nameLength + pathLength;
361 const unsigned extLength = strlen(ICON_EXTENSIONS[i]);
362 if(JUNLIKELY(offset < extLength)) {
363 continue;
364 }
365 if(!strcmp(ICON_EXTENSIONS[i], &temp[offset])) {
366 hasExtension = 1;
367 break;
368 }
369 }
370
371 /* Attempt to load the image. */
372 image = NULL;
373 if(hasExtension) {
374 image = LoadImage(temp, 0, 0, 1);
375 } else {
376 for(i = 0; i < EXTENSION_COUNT; i++) {
377 const unsigned len = strlen(ICON_EXTENSIONS[i]);
378 memcpy(&temp[pathLength + nameLength], ICON_EXTENSIONS[i], len + 1);
379 image = LoadImage(temp, 0, 0, 1);
380 if(image) {
381 break;
382 }
383 }
384 }
385 ReleaseStack(temp);
386
387 /* Create the icon if we were able to load the image. */
388 if(image) {
389 IconNode *result = CreateIcon(image);
390 result->preserveAspect = preserveAspect;
391 result->name = CopyString(temp);
392 if(save) {
393 InsertIcon(result);
394 }
395 DestroyImage(image);
396 return result;
397 }
398
399 return NULL;
400 }
401
402 /** Read the icon property from a client. */
ReadNetWMIcon(Window win)403 IconNode *ReadNetWMIcon(Window win)
404 {
405 static const long MAX_LENGTH = 1 << 20;
406 IconNode *icon = NULL;
407 unsigned long count;
408 int status;
409 unsigned long extra;
410 Atom realType;
411 int realFormat;
412 unsigned char *data;
413 status = JXGetWindowProperty(display, win, atoms[ATOM_NET_WM_ICON],
414 0, MAX_LENGTH, False, XA_CARDINAL,
415 &realType, &realFormat, &count, &extra, &data);
416 if(status == Success && realFormat != 0 && data) {
417 icon = CreateIconFromBinary((unsigned long*)data, count);
418 JXFree(data);
419 }
420 return icon;
421 }
422
423 /** Read the icon WMHint property from a client. */
ReadWMHintIcon(Window win)424 IconNode *ReadWMHintIcon(Window win)
425 {
426 IconNode *icon = NULL;
427 XWMHints *hints = JXGetWMHints(display, win);
428 if(hints) {
429 Drawable d = None;
430 Pixmap mask = None;
431 if(hints->flags & IconMaskHint) {
432 mask = hints->icon_mask;
433 }
434 if(hints->flags & IconPixmapHint) {
435 d = hints->icon_pixmap;
436 }
437 if(d != None) {
438 icon = CreateIconFromDrawable(d, mask);
439 }
440 JXFree(hints);
441 }
442 return icon;
443 }
444
445 /** Create an icon from XPM image data. */
GetDefaultIcon(void)446 IconNode *GetDefaultIcon(void)
447 {
448 static const char * const name = "default";
449 const unsigned width = 8;
450 const unsigned height = 8;
451 const unsigned border = 1;
452 ImageNode *image;
453 IconNode *result;
454 unsigned bytes;
455 unsigned x, y;
456
457 /* Load the specified default, if configured. */
458 if(defaultIconName) {
459 result = LoadNamedIcon(defaultIconName, 1, 1);
460 return result ? result : &emptyIcon;
461 }
462
463 /* Check if this icon has already been loaded */
464 result = FindIcon(name);
465 if(result) {
466 return result;
467 }
468
469 /* Allocate image data. */
470 bytes = (width * height + 7) / 8;
471 image = CreateImage(width, height, 1);
472 memset(image->data, 0, bytes);
473 #ifdef USE_XRENDER
474 image->render = 0;
475 #endif
476
477 /* Allocate the icon node. */
478 result = CreateIcon(image);
479 result->name = CopyString(name);
480 result->images = image;
481 InsertIcon(result);
482
483 /* Draw the icon. */
484 for(y = border; y < height - border; y++) {
485 const unsigned pixel_left = y * width + border;
486 const unsigned pixel_right = y * width + width - 1 - border;
487 const unsigned offset_left = pixel_left / 8;
488 const unsigned mask_left = 1 << (pixel_left % 8);
489 const unsigned offset_right = pixel_right / 8;
490 const unsigned mask_right = 1 << (pixel_right % 8);
491 image->data[offset_left] |= mask_left;
492 image->data[offset_right] |= mask_right;
493 }
494 for(x = border; x < width - border; x++) {
495 const unsigned pixel_top = x + border * width;
496 const unsigned pixel_bottom = x + width * (height - 1 - border);
497 const unsigned offset_top = pixel_top / 8;
498 const unsigned mask_top = 1 << (pixel_top % 8);
499 const unsigned offset_bottom = pixel_bottom / 8;
500 const unsigned mask_bottom = 1 << (pixel_bottom % 8);
501 image->data[offset_top] |= mask_top;
502 image->data[offset_bottom] |= mask_bottom;
503 }
504
505 return result;
506 }
507
CreateIconFromDrawable(Drawable d,Pixmap mask)508 IconNode *CreateIconFromDrawable(Drawable d, Pixmap mask)
509 {
510 ImageNode *image;
511
512 image = LoadImageFromDrawable(d, mask);
513 if(image) {
514 IconNode *result = CreateIcon(image);
515 result->images = image;
516 return result;
517 } else {
518 return NULL;
519 }
520 }
521
522 /** Get the best image for the requested size. */
GetBestImage(IconNode * icon,int rwidth,int rheight)523 ImageNode *GetBestImage(IconNode *icon, int rwidth, int rheight)
524 {
525 ImageNode *best;
526 ImageNode *ip;
527
528 /* If we don't have an image loaded, load one. */
529 if(icon->images == NULL) {
530 return LoadImage(icon->name, rwidth, rheight, icon->preserveAspect);
531 }
532
533 /* Find the best image to use.
534 * Select the smallest image to completely cover the
535 * requested size. If no image completely covers the
536 * requested size, select the one that overlaps the most area.
537 * If no size is specified, use the largest. */
538 best = icon->images;
539 for(ip = icon->images->next; ip; ip = ip->next) {
540 const int best_area = best->width * best->height;
541 const int other_area = ip->width * ip->height;
542 int best_overlap;
543 int other_overlap;
544 if(rwidth == 0 && rheight == 0) {
545 best_overlap = 0;
546 other_overlap = 0;
547 } else if(rwidth == 0) {
548 best_overlap = Min(best->height, rheight);
549 other_overlap = Min(ip->height, rheight);
550 } else if(rheight == 0) {
551 best_overlap = Min(best->width, rwidth);
552 other_overlap = Min(ip->width, rwidth);
553 } else {
554 best_overlap = Min(best->width, rwidth)
555 * Min(best->height, rheight);
556 other_overlap = Min(ip->width, rwidth)
557 * Min(ip->height, rheight);
558 }
559 if(other_overlap > best_overlap) {
560 best = ip;
561 } else if(other_overlap == best_overlap) {
562 if(other_area < best_area) {
563 best = ip;
564 }
565 }
566 }
567 return best;
568 }
569
570 /** Get a scaled icon. */
GetScaledIcon(IconNode * icon,long fg,int rwidth,int rheight)571 ScaledIconNode *GetScaledIcon(IconNode *icon, long fg,
572 int rwidth, int rheight)
573 {
574
575 XColor color;
576 XImage *image;
577 XPoint *points;
578 ImageNode *imageNode;
579 ScaledIconNode *np;
580 GC maskGC;
581 int x, y;
582 int scalex, scaley; /* Fixed point. */
583 int srcx, srcy; /* Fixed point. */
584 int nwidth, nheight;
585 unsigned char *data;
586 unsigned perLine;
587
588 if(rwidth == 0) {
589 rwidth = icon->width;
590 }
591 if(rheight == 0) {
592 rheight = icon->height;
593 }
594
595 if(icon->preserveAspect) {
596 const int ratio = (icon->width << 16) / icon->height;
597 nwidth = Min(rwidth, (rheight * ratio) >> 16);
598 nheight = Min(rheight, (nwidth << 16) / ratio);
599 nwidth = (nheight * ratio) >> 16;
600 } else {
601 nheight = rheight;
602 nwidth = rwidth;
603 }
604 nwidth = Max(1, nwidth);
605 nheight = Max(1, nheight);
606
607 /* Check if this size already exists. */
608 for(np = icon->nodes; np; np = np->next) {
609 if(!icon->bitmap || np->fg == fg) {
610 #ifdef USE_XRENDER
611 /* If we are using xrender and only have one image size
612 * available, we can simply scale the existing icon. */
613 if(icon->render) {
614 if(icon->images == NULL || icon->images->next == NULL) {
615 return np;
616 }
617 }
618 #endif
619 if(np->width == nwidth && np->height == nheight) {
620 return np;
621 }
622 }
623 }
624
625 /* Need to load the image. */
626 imageNode = GetBestImage(icon, nwidth, nheight);
627 if(JUNLIKELY(!imageNode)) {
628 return NULL;
629 }
630
631 /* See if we can use XRender to create the icon. */
632 #ifdef USE_XRENDER
633 if(icon->render) {
634 np = CreateScaledRenderIcon(imageNode, fg);
635 np->next = icon->nodes;
636 icon->nodes = np;
637
638 /* Don't keep the image data around after creating the icon. */
639 if(icon->images == NULL) {
640 DestroyImage(imageNode);
641 }
642
643 return np;
644 }
645 #endif
646
647 /* Create a new ScaledIconNode the old-fashioned way. */
648 np = Allocate(sizeof(ScaledIconNode));
649 np->fg = fg;
650 np->width = nwidth;
651 np->height = nheight;
652 np->next = icon->nodes;
653 icon->nodes = np;
654
655 /* Create a mask. */
656 np->mask = JXCreatePixmap(display, rootWindow, nwidth, nheight, 1);
657 maskGC = JXCreateGC(display, np->mask, 0, NULL);
658 JXSetForeground(display, maskGC, 0);
659 JXFillRectangle(display, np->mask, maskGC, 0, 0, nwidth, nheight);
660 JXSetForeground(display, maskGC, 1);
661
662 /* Create a temporary XImage for scaling. */
663 image = JXCreateImage(display, rootVisual, rootDepth,
664 ZPixmap, 0, NULL, nwidth, nheight, 8, 0);
665 image->data = Allocate(sizeof(unsigned long) * nwidth * nheight);
666
667 /* Determine the scale factor. */
668 scalex = (imageNode->width << 16) / nwidth;
669 scaley = (imageNode->height << 16) / nheight;
670
671 points = Allocate(sizeof(XPoint) * nwidth);
672 data = imageNode->data;
673 if(imageNode->bitmap) {
674 perLine = (imageNode->width >> 3) + ((imageNode->width & 7) ? 1 : 0);
675 } else {
676 perLine = imageNode->width;
677 }
678 srcy = 0;
679 for(y = 0; y < nheight; y++) {
680 const int yindex = (srcy >> 16) * perLine;
681 int pindex = 0;
682 srcx = 0;
683 for(x = 0; x < nwidth; x++) {
684 if(imageNode->bitmap) {
685 const int tx = srcx >> 16;
686 const int offset = yindex + (tx >> 3);
687 const int mask = 1 << (tx & 7);
688 if(data[offset] & mask) {
689 points[pindex].x = x;
690 points[pindex].y = y;
691 XPutPixel(image, x, y, fg);
692 pindex += 1;
693 }
694 } else {
695 const int yindex = (srcy >> 16) * imageNode->width;
696 const int index = 4 * (yindex + (srcx >> 16));
697 color.red = data[index + 1];
698 color.red |= color.red << 8;
699 color.green = data[index + 2];
700 color.green |= color.green << 8;
701 color.blue = data[index + 3];
702 color.blue |= color.blue << 8;
703 GetColor(&color);
704 XPutPixel(image, x, y, color.pixel);
705 if(data[index] >= 128) {
706 points[pindex].x = x;
707 points[pindex].y = y;
708 pindex += 1;
709 }
710 }
711 srcx += scalex;
712 }
713 JXDrawPoints(display, np->mask, maskGC, points, pindex, CoordModeOrigin);
714 srcy += scaley;
715 }
716 Release(points);
717
718 /* Release the mask GC. */
719 JXFreeGC(display, maskGC);
720
721 /* Create the color data pixmap. */
722 np->image = JXCreatePixmap(display, rootWindow, nwidth, nheight,
723 rootDepth);
724
725 /* Render the image to the color data pixmap. */
726 JXPutImage(display, np->image, rootGC, image, 0, 0, 0, 0, nwidth, nheight);
727 /* Release the XImage. */
728 Release(image->data);
729 image->data = NULL;
730 JXDestroyImage(image);
731
732 if(icon->images == NULL) {
733 DestroyImage(imageNode);
734 }
735
736 return np;
737
738 }
739
740 /** Create an icon from binary data (as specified via window properties). */
CreateIconFromBinary(const unsigned long * input,unsigned int length)741 IconNode *CreateIconFromBinary(const unsigned long *input,
742 unsigned int length)
743 {
744 IconNode *result = NULL;
745 unsigned int offset = 0;
746
747 if(!input) {
748 return NULL;
749 }
750
751 while(offset < length) {
752
753 const unsigned width = input[offset + 0];
754 const unsigned height = input[offset + 1];
755 unsigned char *data;
756 ImageNode *image;
757 unsigned x;
758
759 if(JUNLIKELY(width * height + 2 > length - offset)) {
760 Debug("invalid image size: %d x %d + 2 > %d",
761 width, height, length - offset);
762 return result;
763 } else if(JUNLIKELY(width == 0 || height == 0)) {
764 Debug("invalid image size: %d x %d", width, height);
765 return result;
766 }
767
768 image = CreateImage(width, height, 0);
769 if(result == NULL) {
770 result = CreateIcon(image);
771 }
772 image->next = result->images;
773 result->images = image;
774 data = image->data;
775
776 /* Note: the data types here might be of different sizes. */
777 offset += 2;
778 for(x = 0; x < width * height; x++) {
779 *data++ = (input[offset] >> 24) & 0xFF;
780 *data++ = (input[offset] >> 16) & 0xFF;
781 *data++ = (input[offset] >> 8) & 0xFF;
782 *data++ = (input[offset] >> 0) & 0xFF;
783 offset += 1;
784 }
785
786 /* Don't insert this icon into the hash since it is transient. */
787
788 }
789
790 return result;
791 }
792
793 /** Create an empty icon node. */
CreateIcon(const ImageNode * image)794 IconNode *CreateIcon(const ImageNode *image)
795 {
796 IconNode *icon;
797 icon = Allocate(sizeof(IconNode));
798 icon->nodes = NULL;
799 icon->name = NULL;
800 icon->images = NULL;
801 icon->next = NULL;
802 icon->prev = NULL;
803 icon->width = image->width;
804 icon->height = image->height;
805 icon->bitmap = image->bitmap;
806 #ifdef USE_XRENDER
807 icon->render = image->render;
808 #endif
809 icon->preserveAspect = 1;
810 icon->transient = 1;
811 return icon;
812 }
813
814 /** Helper method for destroy icons. */
DoDestroyIcon(int index,IconNode * icon)815 void DoDestroyIcon(int index, IconNode *icon)
816 {
817 if(icon && icon != &emptyIcon) {
818 while(icon->nodes) {
819 ScaledIconNode *np = icon->nodes;
820 #ifdef USE_XRENDER
821 if(icon->render) {
822 if(np->image != None) {
823 JXRenderFreePicture(display, np->image);
824 }
825 if(np->mask != None) {
826 JXRenderFreePicture(display, np->mask);
827 }
828 #else
829 if(0) {
830 #endif
831 } else {
832 if(np->image != None) {
833 JXFreePixmap(display, np->image);
834 }
835 if(np->mask != None) {
836 JXFreePixmap(display, np->mask);
837 }
838 }
839 icon->nodes = np->next;
840 Release(np);
841 }
842 DestroyImage(icon->images);
843 if(icon->name) {
844 Release(icon->name);
845 }
846
847 if(icon->prev) {
848 icon->prev->next = icon->next;
849 } else {
850 iconHash[index] = icon->next;
851 }
852 if(icon->next) {
853 icon->next->prev = icon->prev;
854 }
855 Release(icon);
856 }
857 }
858
859 /** Destroy an icon. */
860 void DestroyIcon(IconNode *icon)
861 {
862 if(icon && icon->transient) {
863 const unsigned int index = GetHash(icon->name);
864 DoDestroyIcon(index, icon);
865 }
866 }
867
868 /** Insert an icon to the icon hash table. */
869 void InsertIcon(IconNode *icon)
870 {
871 unsigned int index;
872 Assert(icon);
873 Assert(icon->name);
874 index = GetHash(icon->name);
875 icon->prev = NULL;
876 if(iconHash[index]) {
877 iconHash[index]->prev = icon;
878 }
879 icon->next = iconHash[index];
880 icon->transient = 0;
881 iconHash[index] = icon;
882 }
883
884 /** Find a icon in the icon hash table. */
885 IconNode *FindIcon(const char *name)
886 {
887 const unsigned int index = GetHash(name);
888 IconNode *icon = iconHash[index];
889 while(icon) {
890 if(!strcmp(icon->name, name)) {
891 return icon;
892 }
893 icon = icon->next;
894 }
895
896 return NULL;
897 }
898
899 /** Get the hash for a string. */
900 unsigned int GetHash(const char *str)
901 {
902 unsigned int hash = 0;
903 if(str) {
904 unsigned int x;
905 for(x = 0; str[x]; x++) {
906 hash = (hash + (hash << 5)) ^ (unsigned int)str[x];
907 }
908 hash &= (HASH_SIZE - 1);
909 }
910 return hash;
911 }
912
913 /** Set the name of the default icon. */
914 void SetDefaultIcon(const char *name)
915 {
916 if(defaultIconName) {
917 Release(defaultIconName);
918 }
919 defaultIconName = CopyString(name);
920 }
921
922 #endif /* USE_ICONS */
923