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