13b4dec0bff0fd84100671a1f527016b809dc1bb
[geeqie.git] / src / pan-view / pan-item.cc
1 /*
2  * Copyright (C) 2006 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
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 along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "pan-item.h"
23
24 #include <algorithm>
25 #include <cstring>
26
27 #include <glib-object.h>
28 #include <gtk/gtk.h>
29 #include <pango/pango.h>
30
31 #include "cache.h"
32 #include "filedata.h"
33 #include "image.h"
34 #include "pan-types.h"
35 #include "pixbuf-util.h"
36 #include "ui-misc.h"
37
38 namespace
39 {
40
41 constexpr gint PAN_OUTLINE_THICKNESS = 1;
42 #define PAN_OUTLINE_ALPHA 180
43 #define PAN_OUTLINE_COLOR_1 255, 255, 255, PAN_OUTLINE_ALPHA
44 #define PAN_OUTLINE_COLOR_2 64, 64, 64, PAN_OUTLINE_ALPHA
45
46 /* popup info box */
47 constexpr PanColor PAN_POPUP_TEXT_COLOR{0, 0, 0, 225};
48
49 } // namespace
50
51 /*
52  *-----------------------------------------------------------------------------
53  * item base functions
54  *-----------------------------------------------------------------------------
55  */
56
57 void pan_item_free(PanItem *pi)
58 {
59         if (!pi) return;
60
61         if (pi->pixbuf) g_object_unref(pi->pixbuf);
62         if (pi->fd) file_data_unref(pi->fd);
63         g_free(pi->text);
64         g_free(pi->key);
65         g_free(pi->data);
66
67         g_free(pi);
68 }
69
70 void pan_item_set_key(PanItem *pi, const gchar *key)
71 {
72         gchar *tmp;
73
74         if (!pi) return;
75
76         tmp = pi->key;
77         pi->key = g_strdup(key);
78         g_free(tmp);
79 }
80
81 void pan_item_added(PanWindow *pw, PanItem *pi)
82 {
83         if (!pi) return;
84         image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
85 }
86
87 void pan_item_remove(PanWindow *pw, PanItem *pi)
88 {
89         if (!pi) return;
90
91         if (pw->click_pi == pi) pw->click_pi = nullptr;
92         if (pw->queue_pi == pi) pw->queue_pi = nullptr;
93         if (pw->search_pi == pi) pw->search_pi = nullptr;
94         pw->queue = g_list_remove(pw->queue, pi);
95
96         pw->list = g_list_remove(pw->list, pi);
97         image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
98         pan_item_free(pi);
99 }
100
101 void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
102 {
103         if (!pi || !child) return;
104
105         if (pi->x + pi->width < child->x + child->width + border)
106                 pi->width = child->x + child->width + border - pi->x;
107
108         if (pi->y + pi->height < child->y + child->height + border)
109                 pi->height = child->y + child->height + border - pi->y;
110 }
111
112 void pan_item_size_coordinates(PanItem *pi, gint border, gint &w, gint &h)
113 {
114         if (!pi) return;
115
116         w = std::max(w, pi->x + pi->width + border);
117         h = std::max(h, pi->y + pi->height + border);
118 }
119
120
121 /*
122  *-----------------------------------------------------------------------------
123  * item box type
124  *-----------------------------------------------------------------------------
125  */
126
127 PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
128                           gint border_size, const PanColor &base, const PanColor &bord)
129 {
130         PanItem *pi;
131
132         pi = g_new0(PanItem, 1);
133         pi->type = PAN_ITEM_BOX;
134         pi->fd = fd;
135         pi->x = x;
136         pi->y = y;
137         pi->width = width;
138         pi->height = height;
139
140         pi->color = base;
141
142         pi->color2 = bord;
143         pi->border = border_size;
144
145         pw->list = g_list_prepend(pw->list, pi);
146
147         return pi;
148 }
149
150 void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
151 {
152         gint *shadow;
153
154         if (!pi || pi->type != PAN_ITEM_BOX) return;
155
156         shadow = static_cast<gint *>(pi->data);
157         if (shadow)
158                 {
159                 pi->width -= shadow[0];
160                 pi->height -= shadow[0];
161                 }
162
163         shadow = g_new0(gint, 2);
164         shadow[0] = offset;
165         shadow[1] = fade;
166
167         pi->width += offset;
168         pi->height += offset;
169
170         g_free(pi->data);
171         pi->data = shadow;
172 }
173
174 gint pan_item_box_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *,
175                        gint x, gint y, gint width, gint height)
176 {
177         gint bw;
178         gint bh;
179         gint *shadow;
180         gint rx;
181         gint ry;
182         gint rw;
183         gint rh;
184
185         bw = pi->width;
186         bh = pi->height;
187
188         shadow = static_cast<gint *>(pi->data);
189         if (shadow)
190                 {
191                 bw -= shadow[0];
192                 bh -= shadow[0];
193
194                 if (pi->color.a > 254)
195                         {
196                         pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
197                                            shadow[0], bh - shadow[0],
198                                            pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
199                                            shadow[1],
200                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
201                         pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
202                                            bw, shadow[0],
203                                            pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
204                                            shadow[1],
205                                            PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
206                         }
207                 else
208                         {
209                         gint a;
210                         a = pi->color.a * PAN_SHADOW_ALPHA >> 8;
211                         pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
212                                            bw, bh,
213                                            pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
214                                            shadow[1],
215                                            PAN_SHADOW_COLOR, a);
216                         }
217                 }
218
219         if (util_clip_region(x, y, width, height,
220                              pi->x, pi->y, bw, bh,
221                              &rx, &ry, &rw, &rh))
222                 {
223                 pixbuf_draw_rect_fill(pixbuf,
224                                       rx - x, ry - y, rw, rh,
225                                       pi->color.r, pi->color.g, pi->color.b, pi->color.a);
226                 }
227         if (util_clip_region(x, y, width, height,
228                              pi->x, pi->y, bw, pi->border,
229                              &rx, &ry, &rw, &rh))
230                 {
231                 pixbuf_draw_rect_fill(pixbuf,
232                                       rx - x, ry - y, rw, rh,
233                                       pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
234                 }
235         if (util_clip_region(x, y, width, height,
236                              pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
237                              &rx, &ry, &rw, &rh))
238                 {
239                 pixbuf_draw_rect_fill(pixbuf,
240                                       rx - x, ry - y, rw, rh,
241                                       pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
242                 }
243         if (util_clip_region(x, y, width, height,
244                              pi->x + bw - pi->border, pi->y + pi->border,
245                              pi->border, bh - pi->border * 2,
246                              &rx, &ry, &rw, &rh))
247                 {
248                 pixbuf_draw_rect_fill(pixbuf,
249                                       rx - x, ry - y, rw, rh,
250                                       pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
251                 }
252         if (util_clip_region(x, y, width, height,
253                              pi->x, pi->y + bh - pi->border,
254                              bw,  pi->border,
255                              &rx, &ry, &rw, &rh))
256                 {
257                 pixbuf_draw_rect_fill(pixbuf,
258                                       rx - x, ry - y, rw, rh,
259                                       pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
260                 }
261
262         return FALSE;
263 }
264
265
266 /*
267  *-----------------------------------------------------------------------------
268  * item triangle type
269  *-----------------------------------------------------------------------------
270  */
271
272 PanItem *pan_item_tri_new(PanWindow *pw, FileData *, gint x, gint y, gint width, gint height,
273                           gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
274                           const PanColor &color)
275 {
276         PanItem *pi;
277         gint *coord;
278
279         pi = g_new0(PanItem, 1);
280         pi->type = PAN_ITEM_TRIANGLE;
281         pi->x = x;
282         pi->y = y;
283         pi->width = width;
284         pi->height = height;
285
286         pi->color = color;
287
288         coord = g_new0(gint, 6);
289         coord[0] = x1;
290         coord[1] = y1;
291         coord[2] = x2;
292         coord[3] = y2;
293         coord[4] = x3;
294         coord[5] = y3;
295
296         pi->data = coord;
297
298         pi->border = PAN_BORDER_NONE;
299
300         pw->list = g_list_prepend(pw->list, pi);
301
302         return pi;
303 }
304
305 void pan_item_tri_border(PanItem *pi, gint borders, const PanColor &color)
306 {
307         if (!pi || pi->type != PAN_ITEM_TRIANGLE) return;
308
309         pi->border = borders;
310
311         pi->color2 = color;
312 }
313
314 gint pan_item_tri_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
315 {
316         gint rx;
317         gint ry;
318         gint rw;
319         gint rh;
320
321         if (util_clip_region(x, y, width, height,
322                              pi->x, pi->y, pi->width, pi->height,
323                              &rx, &ry, &rw, &rh) && pi->data)
324                 {
325                 auto coord = static_cast<gint *>(pi->data);
326                 pixbuf_draw_triangle(pixbuf,
327                                      rx - x, ry - y, rw, rh,
328                                      coord[0] - x, coord[1] - y,
329                                      coord[2] - x, coord[3] - y,
330                                      coord[4] - x, coord[5] - y,
331                                      pi->color.r, pi->color.g, pi->color.b, pi->color.a);
332
333                 if (pi->border & PAN_BORDER_1)
334                         {
335                         pixbuf_draw_line(pixbuf,
336                                          rx - x, ry - y, rw, rh,
337                                          coord[0] - x, coord[1] - y,
338                                          coord[2] - x, coord[3] - y,
339                                          pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
340                         }
341                 if (pi->border & PAN_BORDER_2)
342                         {
343                         pixbuf_draw_line(pixbuf,
344                                          rx - x, ry - y, rw, rh,
345                                          coord[2] - x, coord[3] - y,
346                                          coord[4] - x, coord[5] - y,
347                                          pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
348                         }
349                 if (pi->border & PAN_BORDER_3)
350                         {
351                         pixbuf_draw_line(pixbuf,
352                                          rx - x, ry - y, rw, rh,
353                                          coord[4] - x, coord[5] - y,
354                                          coord[0] - x, coord[1] - y,
355                                          pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
356                         }
357                 }
358
359         return FALSE;
360 }
361
362
363 /*
364  *-----------------------------------------------------------------------------
365  * item text type
366  *-----------------------------------------------------------------------------
367  */
368
369 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
370 {
371         PangoLayout *layout;
372
373         layout = gtk_widget_create_pango_layout(widget, nullptr);
374
375         if (pi->text_attr & PAN_TEXT_ATTR_MARKUP)
376                 {
377                 pango_layout_set_markup(layout, pi->text, -1);
378                 return layout;
379                 }
380
381         if (pi->text_attr & PAN_TEXT_ATTR_BOLD ||
382             pi->text_attr & PAN_TEXT_ATTR_HEADING)
383                 {
384                 PangoAttrList *pal;
385                 PangoAttribute *pa;
386
387                 pal = pango_attr_list_new();
388                 if (pi->text_attr & PAN_TEXT_ATTR_BOLD)
389                         {
390                         pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
391                         pa->start_index = 0;
392                         pa->end_index = G_MAXINT;
393                         pango_attr_list_insert(pal, pa);
394                         }
395                 if (pi->text_attr & PAN_TEXT_ATTR_HEADING)
396                         {
397                         pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
398                         pa->start_index = 0;
399                         pa->end_index = G_MAXINT;
400                         pango_attr_list_insert(pal, pa);
401                         }
402                 pango_layout_set_attributes(layout, pal);
403                 pango_attr_list_unref(pal);
404                 }
405
406         pango_layout_set_text(layout, pi->text, -1);
407         return layout;
408 }
409
410 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
411 {
412         PangoLayout *layout;
413
414         if (!pi || !pi->text || !widget) return;
415
416         layout = pan_item_text_layout(pi, widget);
417         pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
418         g_object_unref(G_OBJECT(layout));
419
420         pi->width += pi->border * 2;
421         pi->height += pi->border * 2;
422 }
423
424 PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
425                            PanTextAttrType attr, PanBorderType border, const PanColor &color)
426 {
427         PanItem *pi;
428
429         pi = g_new0(PanItem, 1);
430         pi->type = PAN_ITEM_TEXT;
431         pi->x = x;
432         pi->y = y;
433         pi->text = g_strdup(text);
434         pi->text_attr = attr;
435
436         pi->color = color;
437
438         pi->border = border;
439
440         pan_item_text_compute_size(pi, pw->imd->pr);
441
442         pw->list = g_list_prepend(pw->list, pi);
443
444         return pi;
445 }
446
447 gint pan_item_text_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr, gint x, gint y, gint, gint)
448 {
449         PangoLayout *layout;
450
451         layout = pan_item_text_layout(pi, reinterpret_cast<GtkWidget *>(pr));
452         pixbuf_draw_layout(pixbuf, layout, reinterpret_cast<GtkWidget *>(pr),
453                            pi->x - x + pi->border, pi->y - y + pi->border,
454                            pi->color.r, pi->color.g, pi->color.b, pi->color.a);
455         g_object_unref(G_OBJECT(layout));
456
457         return FALSE;
458 }
459
460
461 /*
462  *-----------------------------------------------------------------------------
463  * item thumbnail type
464  *-----------------------------------------------------------------------------
465  */
466
467 PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y)
468 {
469         PanItem *pi;
470
471         pi = g_new0(PanItem, 1);
472
473         pi->type = PAN_ITEM_THUMB;
474         pi->fd = fd;
475         pi->x = x;
476         pi->y = y;
477         pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
478         pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
479
480         pw->list = g_list_prepend(pw->list, pi);
481
482         return pi;
483 }
484
485 gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
486 {
487         gint tx;
488         gint ty;
489         gint tw;
490         gint th;
491         gint rx;
492         gint ry;
493         gint rw;
494         gint rh;
495
496         if (pi->pixbuf)
497                 {
498                 tw = gdk_pixbuf_get_width(pi->pixbuf);
499                 th = gdk_pixbuf_get_height(pi->pixbuf);
500
501                 tx = pi->x + (pi->width - tw) / 2;
502                 ty = pi->y + (pi->height - th) / 2;
503
504                 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
505                         {
506                         if (util_clip_region(x, y, width, height,
507                                              tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
508                                              &rx, &ry, &rw, &rh))
509                                 {
510                                 pixbuf_draw_shadow(pixbuf,
511                                                    rx - x, ry - y, rw, rh,
512                                                    tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
513                                                    PAN_SHADOW_FADE,
514                                                    PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
515                                 }
516                         }
517                 else
518                         {
519                         if (util_clip_region(x, y, width, height,
520                                              tx + tw, ty + PAN_SHADOW_OFFSET,
521                                              PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
522                                              &rx, &ry, &rw, &rh))
523                                 {
524                                 pixbuf_draw_shadow(pixbuf,
525                                                    rx - x, ry - y, rw, rh,
526                                                    tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
527                                                    PAN_SHADOW_FADE,
528                                                    PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
529                                 }
530                         if (util_clip_region(x, y, width, height,
531                                              tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
532                                              &rx, &ry, &rw, &rh))
533                                 {
534                                 pixbuf_draw_shadow(pixbuf,
535                                                    rx - x, ry - y, rw, rh,
536                                                    tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
537                                                    PAN_SHADOW_FADE,
538                                                    PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
539                                 }
540                         }
541
542                 if (util_clip_region(x, y, width, height,
543                                      tx, ty, tw, th,
544                                      &rx, &ry, &rw, &rh))
545                         {
546                         gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
547                                              static_cast<gdouble>(tx) - x,
548                                              static_cast<gdouble>(ty) - y,
549                                              1.0, 1.0, GDK_INTERP_NEAREST,
550                                              255);
551                         }
552
553                 if (util_clip_region(x, y, width, height,
554                                      tx, ty, tw, PAN_OUTLINE_THICKNESS,
555                                      &rx, &ry, &rw, &rh))
556                         {
557                         pixbuf_draw_rect_fill(pixbuf,
558                                               rx - x, ry - y, rw, rh,
559                                               PAN_OUTLINE_COLOR_1);
560                         }
561                 if (util_clip_region(x, y, width, height,
562                                      tx, ty, PAN_OUTLINE_THICKNESS, th,
563                                      &rx, &ry, &rw, &rh))
564                         {
565                         pixbuf_draw_rect_fill(pixbuf,
566                                               rx - x, ry - y, rw, rh,
567                                               PAN_OUTLINE_COLOR_1);
568                         }
569                 if (util_clip_region(x, y, width, height,
570                                      tx + tw - PAN_OUTLINE_THICKNESS, ty +  PAN_OUTLINE_THICKNESS,
571                                      PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
572                                      &rx, &ry, &rw, &rh))
573                         {
574                         pixbuf_draw_rect_fill(pixbuf,
575                                               rx - x, ry - y, rw, rh,
576                                               PAN_OUTLINE_COLOR_2);
577                         }
578                 if (util_clip_region(x, y, width, height,
579                                      tx +  PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
580                                      tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
581                                      &rx, &ry, &rw, &rh))
582                         {
583                         pixbuf_draw_rect_fill(pixbuf,
584                                               rx - x, ry - y, rw, rh,
585                                               PAN_OUTLINE_COLOR_2);
586                         }
587                 }
588         else
589                 {
590                 tw = pi->width - PAN_SHADOW_OFFSET * 2;
591                 th = pi->height - PAN_SHADOW_OFFSET * 2;
592                 tx = pi->x + PAN_SHADOW_OFFSET;
593                 ty = pi->y + PAN_SHADOW_OFFSET;
594
595                 if (util_clip_region(x, y, width, height,
596                                      tx, ty, tw, th,
597                                      &rx, &ry, &rw, &rh))
598                         {
599                         gint d;
600
601                         d = (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE) ? 2 : 8;
602                         pixbuf_draw_rect_fill(pixbuf,
603                                               rx - x, ry - y, rw, rh,
604                                               PAN_SHADOW_COLOR,
605                                               PAN_SHADOW_ALPHA / d);
606                         }
607                 }
608
609         return (pi->pixbuf == nullptr);
610 }
611
612
613 /*
614  *-----------------------------------------------------------------------------
615  * item image type
616  *-----------------------------------------------------------------------------
617  */
618
619 static void pan_item_image_find_size(PanWindow *pw, const FileData *fd, gint &w, gint &h)
620 {
621         GList *work;
622
623         if (!fd) return;
624
625         work = pw->cache_list;
626         while (work)
627                 {
628                 PanCacheData *pc;
629
630                 pc = static_cast<PanCacheData *>(work->data);
631                 work = work->next;
632
633                 if (pc->cd && pc->cd->dimensions &&
634                     pc->fd == fd)
635                         {
636                         w = MAX(1, pc->cd->width * pw->image_size / 100);
637                         h = MAX(1, pc->cd->height * pw->image_size / 100);
638
639                         pw->cache_list = g_list_remove(pw->cache_list, pc);
640                         pan_cache_data_free(pc);
641                         return;
642                         }
643                 }
644 }
645
646 PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
647 {
648         PanItem *pi;
649
650         pan_item_image_find_size(pw, fd, w, h);
651
652         pi = g_new0(PanItem, 1);
653         pi->type = PAN_ITEM_IMAGE;
654         pi->fd = fd;
655         pi->x = x;
656         pi->y = y;
657         pi->width = w;
658         pi->height = h;
659
660         pi->color.a = 255;
661
662         pi->color2.r = 0;
663         pi->color2.g = 0;
664         pi->color2.b = 0;
665         pi->color2.a = PAN_SHADOW_ALPHA / 2;
666
667         pw->list = g_list_prepend(pw->list, pi);
668
669         return pi;
670 }
671
672 gint pan_item_image_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
673 {
674         gint rx;
675         gint ry;
676         gint rw;
677         gint rh;
678
679         if (util_clip_region(x, y, width, height,
680                              pi->x, pi->y, pi->width, pi->height,
681                              &rx, &ry, &rw, &rh))
682                 {
683                 if (pi->pixbuf)
684                         {
685                         gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
686                                              static_cast<gdouble>(pi->x) - x,
687                                              static_cast<gdouble>(pi->y) - y,
688                                              1.0, 1.0, GDK_INTERP_NEAREST,
689                                              pi->color.a);
690                         }
691                 else
692                         {
693                         pixbuf_draw_rect_fill(pixbuf,
694                                               rx - x, ry - y, rw, rh,
695                                               pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
696                         }
697                 }
698
699         return (pi->pixbuf == nullptr);
700 }
701
702
703 /*
704  *-----------------------------------------------------------------------------
705  * item lookup/search
706  *-----------------------------------------------------------------------------
707  */
708
709 PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
710 {
711         GList *work;
712
713         if (!key) return nullptr;
714
715         work = g_list_last(pw->list);
716         while (work)
717                 {
718                 PanItem *pi;
719
720                 pi = static_cast<PanItem *>(work->data);
721                 if ((pi->type == type || type == PAN_ITEM_NONE) &&
722                      pi->key && strcmp(pi->key, key) == 0)
723                         {
724                         return pi;
725                         }
726                 work = work->prev;
727                 }
728         work = g_list_last(pw->list_static);
729         while (work)
730                 {
731                 PanItem *pi;
732
733                 pi = static_cast<PanItem *>(work->data);
734                 if ((pi->type == type || type == PAN_ITEM_NONE) &&
735                      pi->key && strcmp(pi->key, key) == 0)
736                         {
737                         return pi;
738                         }
739                 work = work->prev;
740                 }
741
742         return nullptr;
743 }
744
745 /* when ignore_case and partial are TRUE, path should be converted to lower case */
746 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
747                                       PanItemType type, const gchar *path,
748                                       gboolean ignore_case, gboolean partial)
749 {
750         GList *work;
751
752         work = g_list_last(search_list);
753         while (work)
754                 {
755                 PanItem *pi;
756
757                 pi = static_cast<PanItem *>(work->data);
758                 if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
759                         {
760                         gboolean match = FALSE;
761
762                         if (path[0] == G_DIR_SEPARATOR)
763                                 {
764                                 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
765                                 }
766                         else if (pi->fd->name)
767                                 {
768                                 if (partial)
769                                         {
770                                         if (ignore_case)
771                                                 {
772                                                 gchar *haystack;
773
774                                                 haystack = g_utf8_strdown(pi->fd->name, -1);
775                                                 match = (strstr(haystack, path) != nullptr);
776                                                 g_free(haystack);
777                                                 }
778                                         else
779                                                 {
780                                                 if (strstr(pi->fd->name, path)) match = TRUE;
781                                                 }
782                                         }
783                                 else if (ignore_case)
784                                         {
785                                         if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
786                                         }
787                                 else
788                                         {
789                                         if (strcmp(path, pi->fd->name) == 0) match = TRUE;
790                                         }
791                                 }
792
793                         if (match) list = g_list_prepend(list, pi);
794                         }
795                 work = work->prev;
796                 }
797
798         return list;
799 }
800
801 /* when ignore_case and partial are TRUE, path should be converted to lower case */
802 GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
803                              gboolean ignore_case, gboolean partial)
804 {
805         GList *list = nullptr;
806
807         if (!path) return nullptr;
808         if (partial && path[0] == G_DIR_SEPARATOR) return nullptr;
809
810         list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
811         list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
812
813         return g_list_reverse(list);
814 }
815
816 GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
817                            gboolean ignore_case, gboolean partial)
818 {
819         if (!fd) return nullptr;
820         return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
821 }
822
823
824 static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
825 {
826         GList *work;
827
828         work = list;
829         while (work)
830                 {
831                 PanItem *pi;
832
833                 pi = static_cast<PanItem *>(work->data);
834                 if ((pi->type == type || type == PAN_ITEM_NONE) &&
835                      x >= pi->x && x < pi->x + pi->width &&
836                      y >= pi->y && y < pi->y + pi->height &&
837                     (!key || (pi->key && strcmp(pi->key, key) == 0)))
838                         {
839                         return pi;
840                         }
841                 work = work->next;
842                 }
843
844         return nullptr;
845 }
846
847 PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
848                                 gint x, gint y, const gchar *key)
849 {
850         PanItem *pi;
851
852         pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
853         if (pi) return pi;
854
855         return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
856 }
857
858
859 /*
860  *-----------------------------------------------------------------------------
861  * text alignments
862  *-----------------------------------------------------------------------------
863  */
864
865 PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
866 {
867         PanTextAlignment *ta;
868
869         ta = g_new0(PanTextAlignment, 1);
870
871         ta->pw = pw;
872         ta->x = x;
873         ta->y = y;
874         ta->key = g_strdup(key);
875
876         return ta;
877 }
878
879 void pan_text_alignment_free(PanTextAlignment *ta)
880 {
881         if (!ta) return;
882
883         g_list_free(ta->column1);
884         g_list_free(ta->column2);
885         g_free(ta->key);
886         g_free(ta);
887 }
888
889 PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
890 {
891         PanItem *item;
892
893         if (label)
894                 {
895                 item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
896                                          PAN_TEXT_ATTR_BOLD, PAN_BORDER_NONE, PAN_POPUP_TEXT_COLOR);
897                 pan_item_set_key(item, ta->key);
898                 }
899         else
900                 {
901                 item = nullptr;
902                 }
903         ta->column1 = g_list_append(ta->column1, item);
904
905         if (text)
906                 {
907                 item = pan_item_text_new(ta->pw, ta->x, ta->y, text,
908                                          PAN_TEXT_ATTR_NONE, PAN_BORDER_NONE, PAN_POPUP_TEXT_COLOR);
909                 pan_item_set_key(item, ta->key);
910                 }
911         else
912                 {
913                 item = nullptr;
914                 }
915         ta->column2 = g_list_append(ta->column2, item);
916
917         return item;
918 }
919
920 void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
921 {
922         gint cw1;
923         gint cw2;
924         gint x;
925         gint y;
926         GList *work1;
927         GList *work2;
928
929         cw1 = 0;
930         cw2 = 0;
931
932         work1 = ta->column1;
933         while (work1)
934                 {
935                 PanItem *p;
936
937                 p = static_cast<PanItem *>(work1->data);
938                 work1 = work1->next;
939
940                 if (p && p->width > cw1) cw1 = p->width;
941                 }
942
943         work2 = ta->column2;
944         while (work2)
945                 {
946                 PanItem *p;
947
948                 p = static_cast<PanItem *>(work2->data);
949                 work2 = work2->next;
950
951                 if (p && p->width > cw2) cw2 = p->width;
952                 }
953
954         x = ta->x;
955         y = ta->y;
956         work1 = ta->column1;
957         work2 = ta->column2;
958         while (work1 && work2)
959                 {
960                 PanItem *p1;
961                 PanItem *p2;
962                 gint height = 0;
963
964                 p1 = static_cast<PanItem *>(work1->data);
965                 p2 = static_cast<PanItem *>(work2->data);
966                 work1 = work1->next;
967                 work2 = work2->next;
968
969                 if (p1)
970                         {
971                         p1->x = x;
972                         p1->y = y;
973                         pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
974                         height = p1->height;
975                         }
976                 if (p2)
977                         {
978                         p2->x = x + cw1 + PREF_PAD_SPACE;
979                         p2->y = y;
980                         pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
981                         if (height < p2->height) height = p2->height;
982                         }
983
984                 if (!p1 && !p2) height = PREF_PAD_GROUP;
985
986                 y += height;
987                 }
988 }
989
990
991 /*
992  *-----------------------------------------------------------------------------
993  * cache data
994  *-----------------------------------------------------------------------------
995  */
996
997 void pan_cache_data_free(PanCacheData *pc)
998 {
999         if (!pc) return;
1000
1001         cache_sim_data_free(pc->cd);
1002         file_data_unref(pc->fd);
1003         g_free(pc);
1004 }
1005 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */