2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
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.
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.
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.
25 #include "pixbuf-util.h"
29 *-----------------------------------------------------------------------------
31 *-----------------------------------------------------------------------------
34 void pan_item_free(PanItem *pi)
38 if (pi->pixbuf) g_object_unref(pi->pixbuf);
39 if (pi->fd) file_data_unref(pi->fd);
47 void pan_item_set_key(PanItem *pi, const gchar *key)
54 pi->key = g_strdup(key);
58 void pan_item_added(PanWindow *pw, PanItem *pi)
61 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
64 void pan_item_remove(PanWindow *pw, PanItem *pi)
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);
73 pw->list = g_list_remove(pw->list, pi);
74 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
78 void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
80 if (!pi || !child) return;
82 if (pi->x + pi->width < child->x + child->width + border)
83 pi->width = child->x + child->width + border - pi->x;
85 if (pi->y + pi->height < child->y + child->height + border)
86 pi->height = child->y + child->height + border - pi->y;
89 void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
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;
99 *-----------------------------------------------------------------------------
101 *-----------------------------------------------------------------------------
104 PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
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)
111 pi = g_new0(PanItem, 1);
112 pi->type = PAN_ITEM_BOX;
119 pi->color_r = base_r;
120 pi->color_g = base_g;
121 pi->color_b = base_b;
122 pi->color_a = base_a;
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;
130 pw->list = g_list_prepend(pw->list, pi);
135 void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
139 if (!pi || pi->type != PAN_ITEM_BOX) return;
141 shadow = static_cast<gint *>(pi->data);
144 pi->width -= shadow[0];
145 pi->height -= shadow[0];
148 shadow = g_new0(gint, 2);
153 pi->height += offset;
159 gint pan_item_box_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *,
160 gint x, gint y, gint width, gint height)
169 shadow = static_cast<gint *>(pi->data);
175 if (pi->color_a > 254)
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,
181 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
182 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
184 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
186 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
191 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
192 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
194 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
196 PAN_SHADOW_COLOR, a);
200 if (util_clip_region(x, y, width, height,
201 pi->x, pi->y, bw, bh,
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);
208 if (util_clip_region(x, y, width, height,
209 pi->x, pi->y, bw, pi->border,
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);
216 if (util_clip_region(x, y, width, height,
217 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
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);
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,
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);
233 if (util_clip_region(x, y, width, height,
234 pi->x, pi->y + bh - pi->border,
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);
248 *-----------------------------------------------------------------------------
250 *-----------------------------------------------------------------------------
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)
260 pi = g_new0(PanItem, 1);
261 pi->type = PAN_ITEM_TRIANGLE;
272 coord = g_new0(gint, 6);
282 pi->border = PAN_BORDER_NONE;
284 pw->list = g_list_prepend(pw->list, pi);
289 void pan_item_tri_border(PanItem *pi, gint borders,
290 guint8 r, guint8 g, guint8 b, guint8 a)
292 if (!pi || pi->type != PAN_ITEM_TRIANGLE) return;
294 pi->border = borders;
302 gint pan_item_tri_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
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)
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);
318 if (pi->border & PAN_BORDER_1)
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);
326 if (pi->border & PAN_BORDER_2)
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);
334 if (pi->border & PAN_BORDER_3)
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);
349 *-----------------------------------------------------------------------------
351 *-----------------------------------------------------------------------------
354 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
358 layout = gtk_widget_create_pango_layout(widget, nullptr);
360 if (pi->text_attr & PAN_TEXT_ATTR_MARKUP)
362 pango_layout_set_markup(layout, pi->text, -1);
366 if (pi->text_attr & PAN_TEXT_ATTR_BOLD ||
367 pi->text_attr & PAN_TEXT_ATTR_HEADING)
372 pal = pango_attr_list_new();
373 if (pi->text_attr & PAN_TEXT_ATTR_BOLD)
375 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
377 pa->end_index = G_MAXINT;
378 pango_attr_list_insert(pal, pa);
380 if (pi->text_attr & PAN_TEXT_ATTR_HEADING)
382 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
384 pa->end_index = G_MAXINT;
385 pango_attr_list_insert(pal, pa);
387 pango_layout_set_attributes(layout, pal);
388 pango_attr_list_unref(pal);
391 pango_layout_set_text(layout, pi->text, -1);
395 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
399 if (!pi || !pi->text || !widget) return;
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));
405 pi->width += pi->border * 2;
406 pi->height += pi->border * 2;
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)
415 pi = g_new0(PanItem, 1);
416 pi->type = PAN_ITEM_TEXT;
419 pi->text = g_strdup(text);
420 pi->text_attr = attr;
429 pan_item_text_compute_size(pi, pw->imd->pr);
431 pw->list = g_list_prepend(pw->list, pi);
436 gint pan_item_text_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr, gint x, gint y, gint, gint)
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));
451 *-----------------------------------------------------------------------------
452 * item thumbnail type
453 *-----------------------------------------------------------------------------
456 PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y)
460 pi = g_new0(PanItem, 1);
462 pi->type = PAN_ITEM_THUMB;
466 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
467 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
469 pw->list = g_list_prepend(pw->list, pi);
474 gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
481 tw = gdk_pixbuf_get_width(pi->pixbuf);
482 th = gdk_pixbuf_get_height(pi->pixbuf);
484 tx = pi->x + (pi->width - tw) / 2;
485 ty = pi->y + (pi->height - th) / 2;
487 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
489 if (util_clip_region(x, y, width, height,
490 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
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,
497 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
502 if (util_clip_region(x, y, width, height,
503 tx + tw, ty + PAN_SHADOW_OFFSET,
504 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
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,
511 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
513 if (util_clip_region(x, y, width, height,
514 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
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,
521 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
525 if (util_clip_region(x, y, width, height,
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,
536 if (util_clip_region(x, y, width, height,
537 tx, ty, tw, PAN_OUTLINE_THICKNESS,
540 pixbuf_draw_rect_fill(pixbuf,
541 rx - x, ry - y, rw, rh,
542 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
544 if (util_clip_region(x, y, width, height,
545 tx, ty, PAN_OUTLINE_THICKNESS, th,
548 pixbuf_draw_rect_fill(pixbuf,
549 rx - x, ry - y, rw, rh,
550 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
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,
557 pixbuf_draw_rect_fill(pixbuf,
558 rx - x, ry - y, rw, rh,
559 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
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,
566 pixbuf_draw_rect_fill(pixbuf,
567 rx - x, ry - y, rw, rh,
568 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
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;
578 if (util_clip_region(x, y, width, height,
584 d = (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE) ? 2 : 8;
585 pixbuf_draw_rect_fill(pixbuf,
586 rx - x, ry - y, rw, rh,
588 PAN_SHADOW_ALPHA / d);
592 return (pi->pixbuf == nullptr);
597 *-----------------------------------------------------------------------------
599 *-----------------------------------------------------------------------------
602 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
611 work = pw->cache_list;
616 pc = static_cast<PanCacheData *>(work->data);
619 if (pc->cd && pc->cd->dimensions &&
620 pc->fd && pc->fd == pi->fd)
622 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
623 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
625 pw->cache_list = g_list_remove(pw->cache_list, pc);
626 cache_sim_data_free(pc->cd);
627 file_data_unref(pc->fd);
634 PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
638 pi = g_new0(PanItem, 1);
639 pi->type = PAN_ITEM_IMAGE;
649 pi->color2_a = PAN_SHADOW_ALPHA / 2;
651 pan_item_image_find_size(pw, pi, w, h);
653 pw->list = g_list_prepend(pw->list, pi);
658 gint pan_item_image_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
662 if (util_clip_region(x, y, width, height,
663 pi->x, pi->y, pi->width, pi->height,
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,
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);
682 return (pi->pixbuf == nullptr);
687 *-----------------------------------------------------------------------------
689 *-----------------------------------------------------------------------------
692 PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
696 if (!key) return nullptr;
698 work = g_list_last(pw->list);
703 pi = static_cast<PanItem *>(work->data);
704 if ((pi->type == type || type == PAN_ITEM_NONE) &&
705 pi->key && strcmp(pi->key, key) == 0)
711 work = g_list_last(pw->list_static);
716 pi = static_cast<PanItem *>(work->data);
717 if ((pi->type == type || type == PAN_ITEM_NONE) &&
718 pi->key && strcmp(pi->key, key) == 0)
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)
735 work = g_list_last(search_list);
740 pi = static_cast<PanItem *>(work->data);
741 if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
743 gboolean match = FALSE;
745 if (path[0] == G_DIR_SEPARATOR)
747 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
749 else if (pi->fd->name)
757 haystack = g_utf8_strdown(pi->fd->name, -1);
758 match = (strstr(haystack, path) != nullptr);
763 if (strstr(pi->fd->name, path)) match = TRUE;
766 else if (ignore_case)
768 if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
772 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
776 if (match) list = g_list_prepend(list, pi);
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)
788 GList *list = nullptr;
790 if (!path) return nullptr;
791 if (partial && path[0] == G_DIR_SEPARATOR) return nullptr;
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);
796 return g_list_reverse(list);
799 GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
800 gboolean ignore_case, gboolean partial)
802 if (!fd) return nullptr;
803 return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
807 static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
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)))
830 PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
831 gint x, gint y, const gchar *key)
835 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
838 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
843 *-----------------------------------------------------------------------------
845 *-----------------------------------------------------------------------------
848 PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
850 PanTextAlignment *ta;
852 ta = g_new0(PanTextAlignment, 1);
857 ta->key = g_strdup(key);
862 void pan_text_alignment_free(PanTextAlignment *ta)
866 g_list_free(ta->column1);
867 g_list_free(ta->column2);
872 PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
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);
887 ta->column1 = g_list_append(ta->column1, item);
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);
900 ta->column2 = g_list_append(ta->column2, item);
905 void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
920 p = static_cast<PanItem *>(work1->data);
923 if (p && p->width > cw1) cw1 = p->width;
931 p = static_cast<PanItem *>(work2->data);
934 if (p && p->width > cw2) cw2 = p->width;
941 while (work1 && work2)
947 p1 = static_cast<PanItem *>(work1->data);
948 p2 = static_cast<PanItem *>(work2->data);
956 pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
961 p2->x = x + cw1 + PREF_PAD_SPACE;
963 pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
964 if (height < p2->height) height = p2->height;
967 if (!p1 && !p2) height = PREF_PAD_GROUP;
972 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */