Deduplicate PanCacheData destroying
[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, PanItem *pi, gint w, gint h)
620 {
621         GList *work;
622
623         pi->width = w;
624         pi->height = h;
625
626         if (!pi->fd) return;
627
628         work = pw->cache_list;
629         while (work)
630                 {
631                 PanCacheData *pc;
632
633                 pc = static_cast<PanCacheData *>(work->data);
634                 work = work->next;
635
636                 if (pc->cd && pc->cd->dimensions &&
637                     pc->fd && pc->fd == pi->fd)
638                         {
639                         pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
640                         pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
641
642                         pw->cache_list = g_list_remove(pw->cache_list, pc);
643                         pan_cache_data_free(pc);
644                         return;
645                         }
646                 }
647 }
648
649 PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
650 {
651         PanItem *pi;
652
653         pi = g_new0(PanItem, 1);
654         pi->type = PAN_ITEM_IMAGE;
655         pi->fd = fd;
656         pi->x = x;
657         pi->y = y;
658
659         pi->color.a = 255;
660
661         pi->color2.r = 0;
662         pi->color2.g = 0;
663         pi->color2.b = 0;
664         pi->color2.a = PAN_SHADOW_ALPHA / 2;
665
666         pan_item_image_find_size(pw, pi, w, h);
667
668         pw->list = g_list_prepend(pw->list, pi);
669
670         return pi;
671 }
672
673 gint pan_item_image_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
674 {
675         gint rx;
676         gint ry;
677         gint rw;
678         gint rh;
679
680         if (util_clip_region(x, y, width, height,
681                              pi->x, pi->y, pi->width, pi->height,
682                              &rx, &ry, &rw, &rh))
683                 {
684                 if (pi->pixbuf)
685                         {
686                         gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
687                                              static_cast<gdouble>(pi->x) - x,
688                                              static_cast<gdouble>(pi->y) - y,
689                                              1.0, 1.0, GDK_INTERP_NEAREST,
690                                              pi->color.a);
691                         }
692                 else
693                         {
694                         pixbuf_draw_rect_fill(pixbuf,
695                                               rx - x, ry - y, rw, rh,
696                                               pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
697                         }
698                 }
699
700         return (pi->pixbuf == nullptr);
701 }
702
703
704 /*
705  *-----------------------------------------------------------------------------
706  * item lookup/search
707  *-----------------------------------------------------------------------------
708  */
709
710 PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
711 {
712         GList *work;
713
714         if (!key) return nullptr;
715
716         work = g_list_last(pw->list);
717         while (work)
718                 {
719                 PanItem *pi;
720
721                 pi = static_cast<PanItem *>(work->data);
722                 if ((pi->type == type || type == PAN_ITEM_NONE) &&
723                      pi->key && strcmp(pi->key, key) == 0)
724                         {
725                         return pi;
726                         }
727                 work = work->prev;
728                 }
729         work = g_list_last(pw->list_static);
730         while (work)
731                 {
732                 PanItem *pi;
733
734                 pi = static_cast<PanItem *>(work->data);
735                 if ((pi->type == type || type == PAN_ITEM_NONE) &&
736                      pi->key && strcmp(pi->key, key) == 0)
737                         {
738                         return pi;
739                         }
740                 work = work->prev;
741                 }
742
743         return nullptr;
744 }
745
746 /* when ignore_case and partial are TRUE, path should be converted to lower case */
747 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
748                                       PanItemType type, const gchar *path,
749                                       gboolean ignore_case, gboolean partial)
750 {
751         GList *work;
752
753         work = g_list_last(search_list);
754         while (work)
755                 {
756                 PanItem *pi;
757
758                 pi = static_cast<PanItem *>(work->data);
759                 if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
760                         {
761                         gboolean match = FALSE;
762
763                         if (path[0] == G_DIR_SEPARATOR)
764                                 {
765                                 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
766                                 }
767                         else if (pi->fd->name)
768                                 {
769                                 if (partial)
770                                         {
771                                         if (ignore_case)
772                                                 {
773                                                 gchar *haystack;
774
775                                                 haystack = g_utf8_strdown(pi->fd->name, -1);
776                                                 match = (strstr(haystack, path) != nullptr);
777                                                 g_free(haystack);
778                                                 }
779                                         else
780                                                 {
781                                                 if (strstr(pi->fd->name, path)) match = TRUE;
782                                                 }
783                                         }
784                                 else if (ignore_case)
785                                         {
786                                         if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
787                                         }
788                                 else
789                                         {
790                                         if (strcmp(path, pi->fd->name) == 0) match = TRUE;
791                                         }
792                                 }
793
794                         if (match) list = g_list_prepend(list, pi);
795                         }
796                 work = work->prev;
797                 }
798
799         return list;
800 }
801
802 /* when ignore_case and partial are TRUE, path should be converted to lower case */
803 GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
804                              gboolean ignore_case, gboolean partial)
805 {
806         GList *list = nullptr;
807
808         if (!path) return nullptr;
809         if (partial && path[0] == G_DIR_SEPARATOR) return nullptr;
810
811         list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
812         list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
813
814         return g_list_reverse(list);
815 }
816
817 GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
818                            gboolean ignore_case, gboolean partial)
819 {
820         if (!fd) return nullptr;
821         return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
822 }
823
824
825 static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
826 {
827         GList *work;
828
829         work = list;
830         while (work)
831                 {
832                 PanItem *pi;
833
834                 pi = static_cast<PanItem *>(work->data);
835                 if ((pi->type == type || type == PAN_ITEM_NONE) &&
836                      x >= pi->x && x < pi->x + pi->width &&
837                      y >= pi->y && y < pi->y + pi->height &&
838                     (!key || (pi->key && strcmp(pi->key, key) == 0)))
839                         {
840                         return pi;
841                         }
842                 work = work->next;
843                 }
844
845         return nullptr;
846 }
847
848 PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
849                                 gint x, gint y, const gchar *key)
850 {
851         PanItem *pi;
852
853         pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
854         if (pi) return pi;
855
856         return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
857 }
858
859
860 /*
861  *-----------------------------------------------------------------------------
862  * text alignments
863  *-----------------------------------------------------------------------------
864  */
865
866 PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
867 {
868         PanTextAlignment *ta;
869
870         ta = g_new0(PanTextAlignment, 1);
871
872         ta->pw = pw;
873         ta->x = x;
874         ta->y = y;
875         ta->key = g_strdup(key);
876
877         return ta;
878 }
879
880 void pan_text_alignment_free(PanTextAlignment *ta)
881 {
882         if (!ta) return;
883
884         g_list_free(ta->column1);
885         g_list_free(ta->column2);
886         g_free(ta->key);
887         g_free(ta);
888 }
889
890 PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
891 {
892         PanItem *item;
893
894         if (label)
895                 {
896                 item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
897                                          PAN_TEXT_ATTR_BOLD, PAN_BORDER_NONE, PAN_POPUP_TEXT_COLOR);
898                 pan_item_set_key(item, ta->key);
899                 }
900         else
901                 {
902                 item = nullptr;
903                 }
904         ta->column1 = g_list_append(ta->column1, item);
905
906         if (text)
907                 {
908                 item = pan_item_text_new(ta->pw, ta->x, ta->y, text,
909                                          PAN_TEXT_ATTR_NONE, PAN_BORDER_NONE, PAN_POPUP_TEXT_COLOR);
910                 pan_item_set_key(item, ta->key);
911                 }
912         else
913                 {
914                 item = nullptr;
915                 }
916         ta->column2 = g_list_append(ta->column2, item);
917
918         return item;
919 }
920
921 void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
922 {
923         gint cw1;
924         gint cw2;
925         gint x;
926         gint y;
927         GList *work1;
928         GList *work2;
929
930         cw1 = 0;
931         cw2 = 0;
932
933         work1 = ta->column1;
934         while (work1)
935                 {
936                 PanItem *p;
937
938                 p = static_cast<PanItem *>(work1->data);
939                 work1 = work1->next;
940
941                 if (p && p->width > cw1) cw1 = p->width;
942                 }
943
944         work2 = ta->column2;
945         while (work2)
946                 {
947                 PanItem *p;
948
949                 p = static_cast<PanItem *>(work2->data);
950                 work2 = work2->next;
951
952                 if (p && p->width > cw2) cw2 = p->width;
953                 }
954
955         x = ta->x;
956         y = ta->y;
957         work1 = ta->column1;
958         work2 = ta->column2;
959         while (work1 && work2)
960                 {
961                 PanItem *p1;
962                 PanItem *p2;
963                 gint height = 0;
964
965                 p1 = static_cast<PanItem *>(work1->data);
966                 p2 = static_cast<PanItem *>(work2->data);
967                 work1 = work1->next;
968                 work2 = work2->next;
969
970                 if (p1)
971                         {
972                         p1->x = x;
973                         p1->y = y;
974                         pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
975                         height = p1->height;
976                         }
977                 if (p2)
978                         {
979                         p2->x = x + cw1 + PREF_PAD_SPACE;
980                         p2->y = y;
981                         pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
982                         if (height < p2->height) height = p2->height;
983                         }
984
985                 if (!p1 && !p2) height = PREF_PAD_GROUP;
986
987                 y += height;
988                 }
989 }
990
991
992 /*
993  *-----------------------------------------------------------------------------
994  * cache data
995  *-----------------------------------------------------------------------------
996  */
997
998 void pan_cache_data_free(PanCacheData *pc)
999 {
1000         if (!pc) return;
1001
1002         cache_sim_data_free(pc->cd);
1003         file_data_unref(pc->fd);
1004         g_free(pc);
1005 }
1006 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */