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