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