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.
26 #include <glib-object.h>
28 #include <pango/pango.h>
33 #include "pan-types.h"
34 #include "pixbuf-util.h"
38 *-----------------------------------------------------------------------------
40 *-----------------------------------------------------------------------------
43 void pan_item_free(PanItem *pi)
47 if (pi->pixbuf) g_object_unref(pi->pixbuf);
48 if (pi->fd) file_data_unref(pi->fd);
56 void pan_item_set_key(PanItem *pi, const gchar *key)
63 pi->key = g_strdup(key);
67 void pan_item_added(PanWindow *pw, PanItem *pi)
70 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
73 void pan_item_remove(PanWindow *pw, PanItem *pi)
77 if (pw->click_pi == pi) pw->click_pi = nullptr;
78 if (pw->queue_pi == pi) pw->queue_pi = nullptr;
79 if (pw->search_pi == pi) pw->search_pi = nullptr;
80 pw->queue = g_list_remove(pw->queue, pi);
82 pw->list = g_list_remove(pw->list, pi);
83 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
87 void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
89 if (!pi || !child) return;
91 if (pi->x + pi->width < child->x + child->width + border)
92 pi->width = child->x + child->width + border - pi->x;
94 if (pi->y + pi->height < child->y + child->height + border)
95 pi->height = child->y + child->height + border - pi->y;
98 void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
102 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
103 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
108 *-----------------------------------------------------------------------------
110 *-----------------------------------------------------------------------------
113 PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
115 guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
116 guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
120 pi = g_new0(PanItem, 1);
121 pi->type = PAN_ITEM_BOX;
128 pi->color_r = base_r;
129 pi->color_g = base_g;
130 pi->color_b = base_b;
131 pi->color_a = base_a;
133 pi->color2_r = bord_r;
134 pi->color2_g = bord_g;
135 pi->color2_b = bord_b;
136 pi->color2_a = bord_a;
137 pi->border = border_size;
139 pw->list = g_list_prepend(pw->list, pi);
144 void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
148 if (!pi || pi->type != PAN_ITEM_BOX) return;
150 shadow = static_cast<gint *>(pi->data);
153 pi->width -= shadow[0];
154 pi->height -= shadow[0];
157 shadow = g_new0(gint, 2);
162 pi->height += offset;
168 gint pan_item_box_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *,
169 gint x, gint y, gint width, gint height)
182 shadow = static_cast<gint *>(pi->data);
188 if (pi->color_a > 254)
190 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
191 shadow[0], bh - shadow[0],
192 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
194 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
195 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
197 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
199 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
204 a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
205 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
207 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
209 PAN_SHADOW_COLOR, a);
213 if (util_clip_region(x, y, width, height,
214 pi->x, pi->y, bw, bh,
217 pixbuf_draw_rect_fill(pixbuf,
218 rx - x, ry - y, rw, rh,
219 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
221 if (util_clip_region(x, y, width, height,
222 pi->x, pi->y, bw, pi->border,
225 pixbuf_draw_rect_fill(pixbuf,
226 rx - x, ry - y, rw, rh,
227 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
229 if (util_clip_region(x, y, width, height,
230 pi->x, pi->y + pi->border, 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 + bw - pi->border, pi->y + pi->border,
239 pi->border, bh - pi->border * 2,
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);
246 if (util_clip_region(x, y, width, height,
247 pi->x, pi->y + bh - pi->border,
251 pixbuf_draw_rect_fill(pixbuf,
252 rx - x, ry - y, rw, rh,
253 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
261 *-----------------------------------------------------------------------------
263 *-----------------------------------------------------------------------------
266 PanItem *pan_item_tri_new(PanWindow *pw, FileData *, gint x, gint y, gint width, gint height,
267 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
268 guint8 r, guint8 g, guint8 b, guint8 a)
273 pi = g_new0(PanItem, 1);
274 pi->type = PAN_ITEM_TRIANGLE;
285 coord = g_new0(gint, 6);
295 pi->border = PAN_BORDER_NONE;
297 pw->list = g_list_prepend(pw->list, pi);
302 void pan_item_tri_border(PanItem *pi, gint borders,
303 guint8 r, guint8 g, guint8 b, guint8 a)
305 if (!pi || pi->type != PAN_ITEM_TRIANGLE) return;
307 pi->border = borders;
315 gint pan_item_tri_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
322 if (util_clip_region(x, y, width, height,
323 pi->x, pi->y, pi->width, pi->height,
324 &rx, &ry, &rw, &rh) && pi->data)
326 auto coord = static_cast<gint *>(pi->data);
327 pixbuf_draw_triangle(pixbuf,
328 rx - x, ry - y, rw, rh,
329 coord[0] - x, coord[1] - y,
330 coord[2] - x, coord[3] - y,
331 coord[4] - x, coord[5] - y,
332 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
334 if (pi->border & PAN_BORDER_1)
336 pixbuf_draw_line(pixbuf,
337 rx - x, ry - y, rw, rh,
338 coord[0] - x, coord[1] - y,
339 coord[2] - x, coord[3] - y,
340 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
342 if (pi->border & PAN_BORDER_2)
344 pixbuf_draw_line(pixbuf,
345 rx - x, ry - y, rw, rh,
346 coord[2] - x, coord[3] - y,
347 coord[4] - x, coord[5] - y,
348 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
350 if (pi->border & PAN_BORDER_3)
352 pixbuf_draw_line(pixbuf,
353 rx - x, ry - y, rw, rh,
354 coord[4] - x, coord[5] - y,
355 coord[0] - x, coord[1] - y,
356 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
365 *-----------------------------------------------------------------------------
367 *-----------------------------------------------------------------------------
370 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
374 layout = gtk_widget_create_pango_layout(widget, nullptr);
376 if (pi->text_attr & PAN_TEXT_ATTR_MARKUP)
378 pango_layout_set_markup(layout, pi->text, -1);
382 if (pi->text_attr & PAN_TEXT_ATTR_BOLD ||
383 pi->text_attr & PAN_TEXT_ATTR_HEADING)
388 pal = pango_attr_list_new();
389 if (pi->text_attr & PAN_TEXT_ATTR_BOLD)
391 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
393 pa->end_index = G_MAXINT;
394 pango_attr_list_insert(pal, pa);
396 if (pi->text_attr & PAN_TEXT_ATTR_HEADING)
398 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
400 pa->end_index = G_MAXINT;
401 pango_attr_list_insert(pal, pa);
403 pango_layout_set_attributes(layout, pal);
404 pango_attr_list_unref(pal);
407 pango_layout_set_text(layout, pi->text, -1);
411 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
415 if (!pi || !pi->text || !widget) return;
417 layout = pan_item_text_layout(pi, widget);
418 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
419 g_object_unref(G_OBJECT(layout));
421 pi->width += pi->border * 2;
422 pi->height += pi->border * 2;
425 PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
426 PanTextAttrType attr, PanBorderType border,
427 guint8 r, guint8 g, guint8 b, guint8 a)
431 pi = g_new0(PanItem, 1);
432 pi->type = PAN_ITEM_TEXT;
435 pi->text = g_strdup(text);
436 pi->text_attr = attr;
445 pan_item_text_compute_size(pi, pw->imd->pr);
447 pw->list = g_list_prepend(pw->list, pi);
452 gint pan_item_text_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr, gint x, gint y, gint, gint)
456 layout = pan_item_text_layout(pi, reinterpret_cast<GtkWidget *>(pr));
457 pixbuf_draw_layout(pixbuf, layout, reinterpret_cast<GtkWidget *>(pr),
458 pi->x - x + pi->border, pi->y - y + pi->border,
459 pi->color_r, pi->color_g, pi->color_b, pi->color_a);
460 g_object_unref(G_OBJECT(layout));
467 *-----------------------------------------------------------------------------
468 * item thumbnail type
469 *-----------------------------------------------------------------------------
472 PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y)
476 pi = g_new0(PanItem, 1);
478 pi->type = PAN_ITEM_THUMB;
482 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
483 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
485 pw->list = g_list_prepend(pw->list, pi);
490 gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
503 tw = gdk_pixbuf_get_width(pi->pixbuf);
504 th = gdk_pixbuf_get_height(pi->pixbuf);
506 tx = pi->x + (pi->width - tw) / 2;
507 ty = pi->y + (pi->height - th) / 2;
509 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
511 if (util_clip_region(x, y, width, height,
512 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
515 pixbuf_draw_shadow(pixbuf,
516 rx - x, ry - y, rw, rh,
517 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
519 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
524 if (util_clip_region(x, y, width, height,
525 tx + tw, ty + PAN_SHADOW_OFFSET,
526 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
529 pixbuf_draw_shadow(pixbuf,
530 rx - x, ry - y, rw, rh,
531 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
533 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
535 if (util_clip_region(x, y, width, height,
536 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
539 pixbuf_draw_shadow(pixbuf,
540 rx - x, ry - y, rw, rh,
541 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
543 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
547 if (util_clip_region(x, y, width, height,
551 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
552 static_cast<gdouble>(tx) - x,
553 static_cast<gdouble>(ty) - y,
554 1.0, 1.0, GDK_INTERP_NEAREST,
558 if (util_clip_region(x, y, width, height,
559 tx, ty, tw, PAN_OUTLINE_THICKNESS,
562 pixbuf_draw_rect_fill(pixbuf,
563 rx - x, ry - y, rw, rh,
564 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
566 if (util_clip_region(x, y, width, height,
567 tx, ty, PAN_OUTLINE_THICKNESS, th,
570 pixbuf_draw_rect_fill(pixbuf,
571 rx - x, ry - y, rw, rh,
572 PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
574 if (util_clip_region(x, y, width, height,
575 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
576 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
579 pixbuf_draw_rect_fill(pixbuf,
580 rx - x, ry - y, rw, rh,
581 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
583 if (util_clip_region(x, y, width, height,
584 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
585 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
588 pixbuf_draw_rect_fill(pixbuf,
589 rx - x, ry - y, rw, rh,
590 PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
595 tw = pi->width - PAN_SHADOW_OFFSET * 2;
596 th = pi->height - PAN_SHADOW_OFFSET * 2;
597 tx = pi->x + PAN_SHADOW_OFFSET;
598 ty = pi->y + PAN_SHADOW_OFFSET;
600 if (util_clip_region(x, y, width, height,
606 d = (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE) ? 2 : 8;
607 pixbuf_draw_rect_fill(pixbuf,
608 rx - x, ry - y, rw, rh,
610 PAN_SHADOW_ALPHA / d);
614 return (pi->pixbuf == nullptr);
619 *-----------------------------------------------------------------------------
621 *-----------------------------------------------------------------------------
624 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
633 work = pw->cache_list;
638 pc = static_cast<PanCacheData *>(work->data);
641 if (pc->cd && pc->cd->dimensions &&
642 pc->fd && pc->fd == pi->fd)
644 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
645 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
647 pw->cache_list = g_list_remove(pw->cache_list, pc);
648 cache_sim_data_free(pc->cd);
649 file_data_unref(pc->fd);
656 PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
660 pi = g_new0(PanItem, 1);
661 pi->type = PAN_ITEM_IMAGE;
671 pi->color2_a = PAN_SHADOW_ALPHA / 2;
673 pan_item_image_find_size(pw, pi, w, h);
675 pw->list = g_list_prepend(pw->list, pi);
680 gint pan_item_image_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
687 if (util_clip_region(x, y, width, height,
688 pi->x, pi->y, pi->width, pi->height,
693 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
694 static_cast<gdouble>(pi->x) - x,
695 static_cast<gdouble>(pi->y) - y,
696 1.0, 1.0, GDK_INTERP_NEAREST,
701 pixbuf_draw_rect_fill(pixbuf,
702 rx - x, ry - y, rw, rh,
703 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
707 return (pi->pixbuf == nullptr);
712 *-----------------------------------------------------------------------------
714 *-----------------------------------------------------------------------------
717 PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
721 if (!key) return nullptr;
723 work = g_list_last(pw->list);
728 pi = static_cast<PanItem *>(work->data);
729 if ((pi->type == type || type == PAN_ITEM_NONE) &&
730 pi->key && strcmp(pi->key, key) == 0)
736 work = g_list_last(pw->list_static);
741 pi = static_cast<PanItem *>(work->data);
742 if ((pi->type == type || type == PAN_ITEM_NONE) &&
743 pi->key && strcmp(pi->key, key) == 0)
753 /* when ignore_case and partial are TRUE, path should be converted to lower case */
754 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
755 PanItemType type, const gchar *path,
756 gboolean ignore_case, gboolean partial)
760 work = g_list_last(search_list);
765 pi = static_cast<PanItem *>(work->data);
766 if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
768 gboolean match = FALSE;
770 if (path[0] == G_DIR_SEPARATOR)
772 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
774 else if (pi->fd->name)
782 haystack = g_utf8_strdown(pi->fd->name, -1);
783 match = (strstr(haystack, path) != nullptr);
788 if (strstr(pi->fd->name, path)) match = TRUE;
791 else if (ignore_case)
793 if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
797 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
801 if (match) list = g_list_prepend(list, pi);
809 /* when ignore_case and partial are TRUE, path should be converted to lower case */
810 GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
811 gboolean ignore_case, gboolean partial)
813 GList *list = nullptr;
815 if (!path) return nullptr;
816 if (partial && path[0] == G_DIR_SEPARATOR) return nullptr;
818 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
819 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
821 return g_list_reverse(list);
824 GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
825 gboolean ignore_case, gboolean partial)
827 if (!fd) return nullptr;
828 return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
832 static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
841 pi = static_cast<PanItem *>(work->data);
842 if ((pi->type == type || type == PAN_ITEM_NONE) &&
843 x >= pi->x && x < pi->x + pi->width &&
844 y >= pi->y && y < pi->y + pi->height &&
845 (!key || (pi->key && strcmp(pi->key, key) == 0)))
855 PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
856 gint x, gint y, const gchar *key)
860 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
863 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
868 *-----------------------------------------------------------------------------
870 *-----------------------------------------------------------------------------
873 PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
875 PanTextAlignment *ta;
877 ta = g_new0(PanTextAlignment, 1);
882 ta->key = g_strdup(key);
887 void pan_text_alignment_free(PanTextAlignment *ta)
891 g_list_free(ta->column1);
892 g_list_free(ta->column2);
897 PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
903 item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
904 PAN_TEXT_ATTR_BOLD, PAN_BORDER_NONE,
905 PAN_POPUP_TEXT_COLOR, 255);
906 pan_item_set_key(item, ta->key);
912 ta->column1 = g_list_append(ta->column1, item);
916 item = pan_item_text_new(ta->pw, ta->x, ta->y, text,
917 PAN_TEXT_ATTR_NONE, PAN_BORDER_NONE,
918 PAN_POPUP_TEXT_COLOR, 255);
919 pan_item_set_key(item, ta->key);
925 ta->column2 = g_list_append(ta->column2, item);
930 void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
947 p = static_cast<PanItem *>(work1->data);
950 if (p && p->width > cw1) cw1 = p->width;
958 p = static_cast<PanItem *>(work2->data);
961 if (p && p->width > cw2) cw2 = p->width;
968 while (work1 && work2)
974 p1 = static_cast<PanItem *>(work1->data);
975 p2 = static_cast<PanItem *>(work2->data);
983 pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
988 p2->x = x + cw1 + PREF_PAD_SPACE;
990 pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
991 if (height < p2->height) height = p2->height;
994 if (!p1 && !p2) height = PREF_PAD_GROUP;
999 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */