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)
173 shadow = static_cast<gint *>(pi->data);
179 if (pi->color_a > 254)
181 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
182 shadow[0], bh - shadow[0],
183 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
185 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
186 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
188 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
190 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
195 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
196 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
198 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
200 PAN_SHADOW_COLOR, a);
204 if (util_clip_region(x, y, width, height,
205 pi->x, pi->y, bw, bh,
208 pixbuf_draw_rect_fill(pixbuf,
209 rx - x, ry - y, rw, rh,
210 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
212 if (util_clip_region(x, y, width, height,
213 pi->x, pi->y, bw, pi->border,
216 pixbuf_draw_rect_fill(pixbuf,
217 rx - x, ry - y, rw, rh,
218 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
220 if (util_clip_region(x, y, width, height,
221 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
224 pixbuf_draw_rect_fill(pixbuf,
225 rx - x, ry - y, rw, rh,
226 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
228 if (util_clip_region(x, y, width, height,
229 pi->x + bw - pi->border, pi->y + pi->border,
230 pi->border, bh - pi->border * 2,
233 pixbuf_draw_rect_fill(pixbuf,
234 rx - x, ry - y, rw, rh,
235 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
237 if (util_clip_region(x, y, width, height,
238 pi->x, pi->y + bh - pi->border,
242 pixbuf_draw_rect_fill(pixbuf,
243 rx - x, ry - y, rw, rh,
244 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
252 *-----------------------------------------------------------------------------
254 *-----------------------------------------------------------------------------
257 PanItem *pan_item_tri_new(PanWindow *pw, FileData *, gint x, gint y, gint width, gint height,
258 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
259 guint8 r, guint8 g, guint8 b, guint8 a)
264 pi = g_new0(PanItem, 1);
265 pi->type = PAN_ITEM_TRIANGLE;
276 coord = g_new0(gint, 6);
286 pi->border = PAN_BORDER_NONE;
288 pw->list = g_list_prepend(pw->list, pi);
293 void pan_item_tri_border(PanItem *pi, gint borders,
294 guint8 r, guint8 g, guint8 b, guint8 a)
296 if (!pi || pi->type != PAN_ITEM_TRIANGLE) return;
298 pi->border = borders;
306 gint pan_item_tri_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
313 if (util_clip_region(x, y, width, height,
314 pi->x, pi->y, pi->width, pi->height,
315 &rx, &ry, &rw, &rh) && pi->data)
317 auto coord = static_cast<gint *>(pi->data);
318 pixbuf_draw_triangle(pixbuf,
319 rx - x, ry - y, rw, rh,
320 coord[0] - x, coord[1] - y,
321 coord[2] - x, coord[3] - y,
322 coord[4] - x, coord[5] - y,
323 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
325 if (pi->border & PAN_BORDER_1)
327 pixbuf_draw_line(pixbuf,
328 rx - x, ry - y, rw, rh,
329 coord[0] - x, coord[1] - y,
330 coord[2] - x, coord[3] - y,
331 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
333 if (pi->border & PAN_BORDER_2)
335 pixbuf_draw_line(pixbuf,
336 rx - x, ry - y, rw, rh,
337 coord[2] - x, coord[3] - y,
338 coord[4] - x, coord[5] - y,
339 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
341 if (pi->border & PAN_BORDER_3)
343 pixbuf_draw_line(pixbuf,
344 rx - x, ry - y, rw, rh,
345 coord[4] - x, coord[5] - y,
346 coord[0] - x, coord[1] - y,
347 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
356 *-----------------------------------------------------------------------------
358 *-----------------------------------------------------------------------------
361 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
365 layout = gtk_widget_create_pango_layout(widget, nullptr);
367 if (pi->text_attr & PAN_TEXT_ATTR_MARKUP)
369 pango_layout_set_markup(layout, pi->text, -1);
373 if (pi->text_attr & PAN_TEXT_ATTR_BOLD ||
374 pi->text_attr & PAN_TEXT_ATTR_HEADING)
379 pal = pango_attr_list_new();
380 if (pi->text_attr & PAN_TEXT_ATTR_BOLD)
382 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
384 pa->end_index = G_MAXINT;
385 pango_attr_list_insert(pal, pa);
387 if (pi->text_attr & PAN_TEXT_ATTR_HEADING)
389 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
391 pa->end_index = G_MAXINT;
392 pango_attr_list_insert(pal, pa);
394 pango_layout_set_attributes(layout, pal);
395 pango_attr_list_unref(pal);
398 pango_layout_set_text(layout, pi->text, -1);
402 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
406 if (!pi || !pi->text || !widget) return;
408 layout = pan_item_text_layout(pi, widget);
409 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
410 g_object_unref(G_OBJECT(layout));
412 pi->width += pi->border * 2;
413 pi->height += pi->border * 2;
416 PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
417 PanTextAttrType attr, PanBorderType border,
418 guint8 r, guint8 g, guint8 b, guint8 a)
422 pi = g_new0(PanItem, 1);
423 pi->type = PAN_ITEM_TEXT;
426 pi->text = g_strdup(text);
427 pi->text_attr = attr;
436 pan_item_text_compute_size(pi, pw->imd->pr);
438 pw->list = g_list_prepend(pw->list, pi);
443 gint pan_item_text_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr, gint x, gint y, gint, gint)
447 layout = pan_item_text_layout(pi, reinterpret_cast<GtkWidget *>(pr));
448 pixbuf_draw_layout(pixbuf, layout, reinterpret_cast<GtkWidget *>(pr),
449 pi->x - x + pi->border, pi->y - y + pi->border,
450 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
451 g_object_unref(G_OBJECT(layout));
458 *-----------------------------------------------------------------------------
459 * item thumbnail type
460 *-----------------------------------------------------------------------------
463 PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y)
467 pi = g_new0(PanItem, 1);
469 pi->type = PAN_ITEM_THUMB;
473 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
474 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
476 pw->list = g_list_prepend(pw->list, pi);
481 gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
494 tw = gdk_pixbuf_get_width(pi->pixbuf);
495 th = gdk_pixbuf_get_height(pi->pixbuf);
497 tx = pi->x + (pi->width - tw) / 2;
498 ty = pi->y + (pi->height - th) / 2;
500 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
502 if (util_clip_region(x, y, width, height,
503 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
506 pixbuf_draw_shadow(pixbuf,
507 rx - x, ry - y, rw, rh,
508 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
510 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
515 if (util_clip_region(x, y, width, height,
516 tx + tw, ty + PAN_SHADOW_OFFSET,
517 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
520 pixbuf_draw_shadow(pixbuf,
521 rx - x, ry - y, rw, rh,
522 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
524 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
526 if (util_clip_region(x, y, width, height,
527 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
530 pixbuf_draw_shadow(pixbuf,
531 rx - x, ry - y, rw, rh,
532 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
534 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
538 if (util_clip_region(x, y, width, height,
542 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
543 static_cast<gdouble>(tx) - x,
544 static_cast<gdouble>(ty) - y,
545 1.0, 1.0, GDK_INTERP_NEAREST,
549 if (util_clip_region(x, y, width, height,
550 tx, ty, tw, PAN_OUTLINE_THICKNESS,
553 pixbuf_draw_rect_fill(pixbuf,
554 rx - x, ry - y, rw, rh,
555 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
557 if (util_clip_region(x, y, width, height,
558 tx, ty, PAN_OUTLINE_THICKNESS, th,
561 pixbuf_draw_rect_fill(pixbuf,
562 rx - x, ry - y, rw, rh,
563 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
565 if (util_clip_region(x, y, width, height,
566 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
567 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
570 pixbuf_draw_rect_fill(pixbuf,
571 rx - x, ry - y, rw, rh,
572 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
574 if (util_clip_region(x, y, width, height,
575 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
576 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
579 pixbuf_draw_rect_fill(pixbuf,
580 rx - x, ry - y, rw, rh,
581 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
586 tw = pi->width - PAN_SHADOW_OFFSET * 2;
587 th = pi->height - PAN_SHADOW_OFFSET * 2;
588 tx = pi->x + PAN_SHADOW_OFFSET;
589 ty = pi->y + PAN_SHADOW_OFFSET;
591 if (util_clip_region(x, y, width, height,
597 d = (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE) ? 2 : 8;
598 pixbuf_draw_rect_fill(pixbuf,
599 rx - x, ry - y, rw, rh,
601 PAN_SHADOW_ALPHA / d);
605 return (pi->pixbuf == nullptr);
610 *-----------------------------------------------------------------------------
612 *-----------------------------------------------------------------------------
615 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
624 work = pw->cache_list;
629 pc = static_cast<PanCacheData *>(work->data);
632 if (pc->cd && pc->cd->dimensions &&
633 pc->fd && pc->fd == pi->fd)
635 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
636 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
638 pw->cache_list = g_list_remove(pw->cache_list, pc);
639 cache_sim_data_free(pc->cd);
640 file_data_unref(pc->fd);
647 PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
651 pi = g_new0(PanItem, 1);
652 pi->type = PAN_ITEM_IMAGE;
662 pi->color2_a = PAN_SHADOW_ALPHA / 2;
664 pan_item_image_find_size(pw, pi, w, h);
666 pw->list = g_list_prepend(pw->list, pi);
671 gint pan_item_image_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
678 if (util_clip_region(x, y, width, height,
679 pi->x, pi->y, pi->width, pi->height,
684 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
685 static_cast<gdouble>(pi->x) - x,
686 static_cast<gdouble>(pi->y) - y,
687 1.0, 1.0, GDK_INTERP_NEAREST,
692 pixbuf_draw_rect_fill(pixbuf,
693 rx - x, ry - y, rw, rh,
694 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
698 return (pi->pixbuf == nullptr);
703 *-----------------------------------------------------------------------------
705 *-----------------------------------------------------------------------------
708 PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
712 if (!key) return nullptr;
714 work = g_list_last(pw->list);
719 pi = static_cast<PanItem *>(work->data);
720 if ((pi->type == type || type == PAN_ITEM_NONE) &&
721 pi->key && strcmp(pi->key, key) == 0)
727 work = g_list_last(pw->list_static);
732 pi = static_cast<PanItem *>(work->data);
733 if ((pi->type == type || type == PAN_ITEM_NONE) &&
734 pi->key && strcmp(pi->key, key) == 0)
744 /* when ignore_case and partial are TRUE, path should be converted to lower case */
745 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
746 PanItemType type, const gchar *path,
747 gboolean ignore_case, gboolean partial)
751 work = g_list_last(search_list);
756 pi = static_cast<PanItem *>(work->data);
757 if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
759 gboolean match = FALSE;
761 if (path[0] == G_DIR_SEPARATOR)
763 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
765 else if (pi->fd->name)
773 haystack = g_utf8_strdown(pi->fd->name, -1);
774 match = (strstr(haystack, path) != nullptr);
779 if (strstr(pi->fd->name, path)) match = TRUE;
782 else if (ignore_case)
784 if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
788 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
792 if (match) list = g_list_prepend(list, pi);
800 /* when ignore_case and partial are TRUE, path should be converted to lower case */
801 GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
802 gboolean ignore_case, gboolean partial)
804 GList *list = nullptr;
806 if (!path) return nullptr;
807 if (partial && path[0] == G_DIR_SEPARATOR) return nullptr;
809 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
810 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
812 return g_list_reverse(list);
815 GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
816 gboolean ignore_case, gboolean partial)
818 if (!fd) return nullptr;
819 return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
823 static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
832 pi = static_cast<PanItem *>(work->data);
833 if ((pi->type == type || type == PAN_ITEM_NONE) &&
834 x >= pi->x && x < pi->x + pi->width &&
835 y >= pi->y && y < pi->y + pi->height &&
836 (!key || (pi->key && strcmp(pi->key, key) == 0)))
846 PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
847 gint x, gint y, const gchar *key)
851 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
854 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
859 *-----------------------------------------------------------------------------
861 *-----------------------------------------------------------------------------
864 PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
866 PanTextAlignment *ta;
868 ta = g_new0(PanTextAlignment, 1);
873 ta->key = g_strdup(key);
878 void pan_text_alignment_free(PanTextAlignment *ta)
882 g_list_free(ta->column1);
883 g_list_free(ta->column2);
888 PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
894 item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
895 PAN_TEXT_ATTR_BOLD, PAN_BORDER_NONE,
896 PAN_POPUP_TEXT_COLOR, 255);
897 pan_item_set_key(item, ta->key);
903 ta->column1 = g_list_append(ta->column1, item);
907 item = pan_item_text_new(ta->pw, ta->x, ta->y, text,
908 PAN_TEXT_ATTR_NONE, PAN_BORDER_NONE,
909 PAN_POPUP_TEXT_COLOR, 255);
910 pan_item_set_key(item, ta->key);
916 ta->column2 = g_list_append(ta->column2, item);
921 void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
938 p = static_cast<PanItem *>(work1->data);
941 if (p && p->width > cw1) cw1 = p->width;
949 p = static_cast<PanItem *>(work2->data);
952 if (p && p->width > cw2) cw2 = p->width;
959 while (work1 && work2)
965 p1 = static_cast<PanItem *>(work1->data);
966 p2 = static_cast<PanItem *>(work2->data);
974 pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
979 p2->x = x + cw1 + PREF_PAD_SPACE;
981 pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
982 if (height < p2->height) height = p2->height;
985 if (!p1 && !p2) height = PREF_PAD_GROUP;
990 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */