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.
27 #include <glib-object.h>
29 #include <pango/pango.h>
34 #include "pan-types.h"
35 #include "pixbuf-util.h"
41 constexpr gint PAN_OUTLINE_THICKNESS = 1;
42 #define PAN_OUTLINE_ALPHA 180
43 #define PAN_OUTLINE_COLOR_1 255, 255, 255, PAN_OUTLINE_ALPHA
44 #define PAN_OUTLINE_COLOR_2 64, 64, 64, PAN_OUTLINE_ALPHA
47 constexpr PanColor PAN_POPUP_TEXT_COLOR{0, 0, 0, 225};
52 *-----------------------------------------------------------------------------
54 *-----------------------------------------------------------------------------
57 void pan_item_free(PanItem *pi)
61 if (pi->pixbuf) g_object_unref(pi->pixbuf);
62 if (pi->fd) file_data_unref(pi->fd);
70 void pan_item_set_key(PanItem *pi, const gchar *key)
77 pi->key = g_strdup(key);
81 void pan_item_added(PanWindow *pw, PanItem *pi)
84 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
87 void pan_item_remove(PanWindow *pw, PanItem *pi)
91 if (pw->click_pi == pi) pw->click_pi = nullptr;
92 if (pw->queue_pi == pi) pw->queue_pi = nullptr;
93 if (pw->search_pi == pi) pw->search_pi = nullptr;
94 pw->queue = g_list_remove(pw->queue, pi);
96 pw->list = g_list_remove(pw->list, pi);
97 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
101 void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
103 if (!pi || !child) return;
105 if (pi->x + pi->width < child->x + child->width + border)
106 pi->width = child->x + child->width + border - pi->x;
108 if (pi->y + pi->height < child->y + child->height + border)
109 pi->height = child->y + child->height + border - pi->y;
112 void pan_item_size_coordinates(PanItem *pi, gint border, gint &w, gint &h)
116 w = std::max(w, pi->x + pi->width + border);
117 h = std::max(h, pi->y + pi->height + border);
122 *-----------------------------------------------------------------------------
124 *-----------------------------------------------------------------------------
127 PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
128 gint border_size, const PanColor &base, const PanColor &bord)
132 pi = g_new0(PanItem, 1);
133 pi->type = PAN_ITEM_BOX;
143 pi->border = border_size;
145 pw->list = g_list_prepend(pw->list, pi);
150 void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
154 if (!pi || pi->type != PAN_ITEM_BOX) return;
156 shadow = static_cast<gint *>(pi->data);
159 pi->width -= shadow[0];
160 pi->height -= shadow[0];
163 shadow = g_new0(gint, 2);
168 pi->height += offset;
174 gint pan_item_box_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *,
175 gint x, gint y, gint width, gint height)
188 shadow = static_cast<gint *>(pi->data);
194 if (pi->color.a > 254)
196 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
197 shadow[0], bh - shadow[0],
198 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
200 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
201 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
203 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
205 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
210 a = pi->color.a * PAN_SHADOW_ALPHA >> 8;
211 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
213 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
215 PAN_SHADOW_COLOR, a);
219 if (util_clip_region(x, y, width, height,
220 pi->x, pi->y, bw, bh,
223 pixbuf_draw_rect_fill(pixbuf,
224 rx - x, ry - y, rw, rh,
225 pi->color.r, pi->color.g, pi->color.b, pi->color.a);
227 if (util_clip_region(x, y, width, height,
228 pi->x, pi->y, bw, pi->border,
231 pixbuf_draw_rect_fill(pixbuf,
232 rx - x, ry - y, rw, rh,
233 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
235 if (util_clip_region(x, y, width, height,
236 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
239 pixbuf_draw_rect_fill(pixbuf,
240 rx - x, ry - y, rw, rh,
241 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
243 if (util_clip_region(x, y, width, height,
244 pi->x + bw - pi->border, pi->y + pi->border,
245 pi->border, bh - pi->border * 2,
248 pixbuf_draw_rect_fill(pixbuf,
249 rx - x, ry - y, rw, rh,
250 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
252 if (util_clip_region(x, y, width, height,
253 pi->x, pi->y + bh - pi->border,
257 pixbuf_draw_rect_fill(pixbuf,
258 rx - x, ry - y, rw, rh,
259 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
267 *-----------------------------------------------------------------------------
269 *-----------------------------------------------------------------------------
272 PanItem *pan_item_tri_new(PanWindow *pw, FileData *, gint x, gint y, gint width, gint height,
273 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
274 const PanColor &color)
279 pi = g_new0(PanItem, 1);
280 pi->type = PAN_ITEM_TRIANGLE;
288 coord = g_new0(gint, 6);
298 pi->border = PAN_BORDER_NONE;
300 pw->list = g_list_prepend(pw->list, pi);
305 void pan_item_tri_border(PanItem *pi, gint borders, const PanColor &color)
307 if (!pi || pi->type != PAN_ITEM_TRIANGLE) return;
309 pi->border = borders;
314 gint pan_item_tri_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
321 if (util_clip_region(x, y, width, height,
322 pi->x, pi->y, pi->width, pi->height,
323 &rx, &ry, &rw, &rh) && pi->data)
325 auto coord = static_cast<gint *>(pi->data);
326 pixbuf_draw_triangle(pixbuf,
327 rx - x, ry - y, rw, rh,
328 coord[0] - x, coord[1] - y,
329 coord[2] - x, coord[3] - y,
330 coord[4] - x, coord[5] - y,
331 pi->color.r, pi->color.g, pi->color.b, pi->color.a);
333 if (pi->border & PAN_BORDER_1)
335 pixbuf_draw_line(pixbuf,
336 rx - x, ry - y, rw, rh,
337 coord[0] - x, coord[1] - y,
338 coord[2] - x, coord[3] - y,
339 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
341 if (pi->border & PAN_BORDER_2)
343 pixbuf_draw_line(pixbuf,
344 rx - x, ry - y, rw, rh,
345 coord[2] - x, coord[3] - y,
346 coord[4] - x, coord[5] - y,
347 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
349 if (pi->border & PAN_BORDER_3)
351 pixbuf_draw_line(pixbuf,
352 rx - x, ry - y, rw, rh,
353 coord[4] - x, coord[5] - y,
354 coord[0] - x, coord[1] - y,
355 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
364 *-----------------------------------------------------------------------------
366 *-----------------------------------------------------------------------------
369 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
373 layout = gtk_widget_create_pango_layout(widget, nullptr);
375 if (pi->text_attr & PAN_TEXT_ATTR_MARKUP)
377 pango_layout_set_markup(layout, pi->text, -1);
381 if (pi->text_attr & PAN_TEXT_ATTR_BOLD ||
382 pi->text_attr & PAN_TEXT_ATTR_HEADING)
387 pal = pango_attr_list_new();
388 if (pi->text_attr & PAN_TEXT_ATTR_BOLD)
390 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
392 pa->end_index = G_MAXINT;
393 pango_attr_list_insert(pal, pa);
395 if (pi->text_attr & PAN_TEXT_ATTR_HEADING)
397 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
399 pa->end_index = G_MAXINT;
400 pango_attr_list_insert(pal, pa);
402 pango_layout_set_attributes(layout, pal);
403 pango_attr_list_unref(pal);
406 pango_layout_set_text(layout, pi->text, -1);
410 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
414 if (!pi || !pi->text || !widget) return;
416 layout = pan_item_text_layout(pi, widget);
417 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
418 g_object_unref(G_OBJECT(layout));
420 pi->width += pi->border * 2;
421 pi->height += pi->border * 2;
424 PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
425 PanTextAttrType attr, PanBorderType border, const PanColor &color)
429 pi = g_new0(PanItem, 1);
430 pi->type = PAN_ITEM_TEXT;
433 pi->text = g_strdup(text);
434 pi->text_attr = attr;
440 pan_item_text_compute_size(pi, pw->imd->pr);
442 pw->list = g_list_prepend(pw->list, pi);
447 gint pan_item_text_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr, gint x, gint y, gint, gint)
451 layout = pan_item_text_layout(pi, reinterpret_cast<GtkWidget *>(pr));
452 pixbuf_draw_layout(pixbuf, layout, reinterpret_cast<GtkWidget *>(pr),
453 pi->x - x + pi->border, pi->y - y + pi->border,
454 pi->color.r, pi->color.g, pi->color.b, pi->color.a);
455 g_object_unref(G_OBJECT(layout));
462 *-----------------------------------------------------------------------------
463 * item thumbnail type
464 *-----------------------------------------------------------------------------
467 PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y)
471 pi = g_new0(PanItem, 1);
473 pi->type = PAN_ITEM_THUMB;
477 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
478 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
480 pw->list = g_list_prepend(pw->list, pi);
485 gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
498 tw = gdk_pixbuf_get_width(pi->pixbuf);
499 th = gdk_pixbuf_get_height(pi->pixbuf);
501 tx = pi->x + (pi->width - tw) / 2;
502 ty = pi->y + (pi->height - th) / 2;
504 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
506 if (util_clip_region(x, y, width, height,
507 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
510 pixbuf_draw_shadow(pixbuf,
511 rx - x, ry - y, rw, rh,
512 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
514 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
519 if (util_clip_region(x, y, width, height,
520 tx + tw, ty + PAN_SHADOW_OFFSET,
521 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
524 pixbuf_draw_shadow(pixbuf,
525 rx - x, ry - y, rw, rh,
526 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
528 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
530 if (util_clip_region(x, y, width, height,
531 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
534 pixbuf_draw_shadow(pixbuf,
535 rx - x, ry - y, rw, rh,
536 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
538 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
542 if (util_clip_region(x, y, width, height,
546 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
547 static_cast<gdouble>(tx) - x,
548 static_cast<gdouble>(ty) - y,
549 1.0, 1.0, GDK_INTERP_NEAREST,
553 if (util_clip_region(x, y, width, height,
554 tx, ty, tw, PAN_OUTLINE_THICKNESS,
557 pixbuf_draw_rect_fill(pixbuf,
558 rx - x, ry - y, rw, rh,
559 PAN_OUTLINE_COLOR_1);
561 if (util_clip_region(x, y, width, height,
562 tx, ty, PAN_OUTLINE_THICKNESS, th,
565 pixbuf_draw_rect_fill(pixbuf,
566 rx - x, ry - y, rw, rh,
567 PAN_OUTLINE_COLOR_1);
569 if (util_clip_region(x, y, width, height,
570 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
571 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
574 pixbuf_draw_rect_fill(pixbuf,
575 rx - x, ry - y, rw, rh,
576 PAN_OUTLINE_COLOR_2);
578 if (util_clip_region(x, y, width, height,
579 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
580 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
583 pixbuf_draw_rect_fill(pixbuf,
584 rx - x, ry - y, rw, rh,
585 PAN_OUTLINE_COLOR_2);
590 tw = pi->width - PAN_SHADOW_OFFSET * 2;
591 th = pi->height - PAN_SHADOW_OFFSET * 2;
592 tx = pi->x + PAN_SHADOW_OFFSET;
593 ty = pi->y + PAN_SHADOW_OFFSET;
595 if (util_clip_region(x, y, width, height,
601 d = (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE) ? 2 : 8;
602 pixbuf_draw_rect_fill(pixbuf,
603 rx - x, ry - y, rw, rh,
605 PAN_SHADOW_ALPHA / d);
609 return (pi->pixbuf == nullptr);
614 *-----------------------------------------------------------------------------
616 *-----------------------------------------------------------------------------
619 static void pan_item_image_find_size(PanWindow *pw, const FileData *fd, gint &w, gint &h)
625 work = pw->cache_list;
630 pc = static_cast<PanCacheData *>(work->data);
633 if (pc->cd && pc->cd->dimensions &&
636 w = MAX(1, pc->cd->width * pw->image_size / 100);
637 h = MAX(1, pc->cd->height * pw->image_size / 100);
639 pw->cache_list = g_list_remove(pw->cache_list, pc);
640 pan_cache_data_free(pc);
646 PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
650 pan_item_image_find_size(pw, fd, w, h);
652 pi = g_new0(PanItem, 1);
653 pi->type = PAN_ITEM_IMAGE;
665 pi->color2.a = PAN_SHADOW_ALPHA / 2;
667 pw->list = g_list_prepend(pw->list, pi);
672 gint pan_item_image_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
679 if (util_clip_region(x, y, width, height,
680 pi->x, pi->y, pi->width, pi->height,
685 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
686 static_cast<gdouble>(pi->x) - x,
687 static_cast<gdouble>(pi->y) - y,
688 1.0, 1.0, GDK_INTERP_NEAREST,
693 pixbuf_draw_rect_fill(pixbuf,
694 rx - x, ry - y, rw, rh,
695 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
699 return (pi->pixbuf == nullptr);
704 *-----------------------------------------------------------------------------
706 *-----------------------------------------------------------------------------
709 PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
713 if (!key) return nullptr;
715 work = g_list_last(pw->list);
720 pi = static_cast<PanItem *>(work->data);
721 if ((pi->type == type || type == PAN_ITEM_NONE) &&
722 pi->key && strcmp(pi->key, key) == 0)
728 work = g_list_last(pw->list_static);
733 pi = static_cast<PanItem *>(work->data);
734 if ((pi->type == type || type == PAN_ITEM_NONE) &&
735 pi->key && strcmp(pi->key, key) == 0)
745 /* when ignore_case and partial are TRUE, path should be converted to lower case */
746 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
747 PanItemType type, const gchar *path,
748 gboolean ignore_case, gboolean partial)
752 work = g_list_last(search_list);
757 pi = static_cast<PanItem *>(work->data);
758 if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
760 gboolean match = FALSE;
762 if (path[0] == G_DIR_SEPARATOR)
764 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
766 else if (pi->fd->name)
774 haystack = g_utf8_strdown(pi->fd->name, -1);
775 match = (strstr(haystack, path) != nullptr);
780 if (strstr(pi->fd->name, path)) match = TRUE;
783 else if (ignore_case)
785 if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
789 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
793 if (match) list = g_list_prepend(list, pi);
801 /* when ignore_case and partial are TRUE, path should be converted to lower case */
802 GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
803 gboolean ignore_case, gboolean partial)
805 GList *list = nullptr;
807 if (!path) return nullptr;
808 if (partial && path[0] == G_DIR_SEPARATOR) return nullptr;
810 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
811 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
813 return g_list_reverse(list);
816 GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
817 gboolean ignore_case, gboolean partial)
819 if (!fd) return nullptr;
820 return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
824 static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
833 pi = static_cast<PanItem *>(work->data);
834 if ((pi->type == type || type == PAN_ITEM_NONE) &&
835 x >= pi->x && x < pi->x + pi->width &&
836 y >= pi->y && y < pi->y + pi->height &&
837 (!key || (pi->key && strcmp(pi->key, key) == 0)))
847 PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
848 gint x, gint y, const gchar *key)
852 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
855 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
860 *-----------------------------------------------------------------------------
862 *-----------------------------------------------------------------------------
865 PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
867 PanTextAlignment *ta;
869 ta = g_new0(PanTextAlignment, 1);
874 ta->key = g_strdup(key);
879 void pan_text_alignment_free(PanTextAlignment *ta)
883 g_list_free(ta->column1);
884 g_list_free(ta->column2);
889 PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
895 item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
896 PAN_TEXT_ATTR_BOLD, PAN_BORDER_NONE, PAN_POPUP_TEXT_COLOR);
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, PAN_POPUP_TEXT_COLOR);
909 pan_item_set_key(item, ta->key);
915 ta->column2 = g_list_append(ta->column2, item);
920 void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
937 p = static_cast<PanItem *>(work1->data);
940 if (p && p->width > cw1) cw1 = p->width;
948 p = static_cast<PanItem *>(work2->data);
951 if (p && p->width > cw2) cw2 = p->width;
958 while (work1 && work2)
964 p1 = static_cast<PanItem *>(work1->data);
965 p2 = static_cast<PanItem *>(work2->data);
973 pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
978 p2->x = x + cw1 + PREF_PAD_SPACE;
980 pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
981 if (height < p2->height) height = p2->height;
984 if (!p1 && !p2) height = PREF_PAD_GROUP;
992 *-----------------------------------------------------------------------------
994 *-----------------------------------------------------------------------------
997 void pan_cache_data_free(PanCacheData *pc)
1001 cache_sim_data_free(pc->cd);
1002 file_data_unref(pc->fd);
1005 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */