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"
40 constexpr gint PAN_OUTLINE_THICKNESS = 1;
41 #define PAN_OUTLINE_ALPHA 180
42 #define PAN_OUTLINE_COLOR_1 255, 255, 255, PAN_OUTLINE_ALPHA
43 #define PAN_OUTLINE_COLOR_2 64, 64, 64, PAN_OUTLINE_ALPHA
46 constexpr PanColor PAN_POPUP_TEXT_COLOR{0, 0, 0, 225};
51 *-----------------------------------------------------------------------------
53 *-----------------------------------------------------------------------------
56 void pan_item_free(PanItem *pi)
60 if (pi->pixbuf) g_object_unref(pi->pixbuf);
61 if (pi->fd) file_data_unref(pi->fd);
69 void pan_item_set_key(PanItem *pi, const gchar *key)
76 pi->key = g_strdup(key);
80 void pan_item_added(PanWindow *pw, PanItem *pi)
83 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
86 void pan_item_remove(PanWindow *pw, PanItem *pi)
90 if (pw->click_pi == pi) pw->click_pi = nullptr;
91 if (pw->queue_pi == pi) pw->queue_pi = nullptr;
92 if (pw->search_pi == pi) pw->search_pi = nullptr;
93 pw->queue = g_list_remove(pw->queue, pi);
95 pw->list = g_list_remove(pw->list, pi);
96 image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
100 void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
102 if (!pi || !child) return;
104 if (pi->x + pi->width < child->x + child->width + border)
105 pi->width = child->x + child->width + border - pi->x;
107 if (pi->y + pi->height < child->y + child->height + border)
108 pi->height = child->y + child->height + border - pi->y;
111 void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
115 if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
116 if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
121 *-----------------------------------------------------------------------------
123 *-----------------------------------------------------------------------------
126 PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
127 gint border_size, const PanColor &base, const PanColor &bord)
131 pi = g_new0(PanItem, 1);
132 pi->type = PAN_ITEM_BOX;
142 pi->border = border_size;
144 pw->list = g_list_prepend(pw->list, pi);
149 void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
153 if (!pi || pi->type != PAN_ITEM_BOX) return;
155 shadow = static_cast<gint *>(pi->data);
158 pi->width -= shadow[0];
159 pi->height -= shadow[0];
162 shadow = g_new0(gint, 2);
167 pi->height += offset;
173 gint pan_item_box_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *,
174 gint x, gint y, gint width, gint height)
187 shadow = static_cast<gint *>(pi->data);
193 if (pi->color.a > 254)
195 pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
196 shadow[0], bh - shadow[0],
197 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
199 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
200 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
202 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
204 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
209 a = pi->color.a * PAN_SHADOW_ALPHA >> 8;
210 pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
212 pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
214 PAN_SHADOW_COLOR, a);
218 if (util_clip_region(x, y, width, height,
219 pi->x, pi->y, bw, bh,
222 pixbuf_draw_rect_fill(pixbuf,
223 rx - x, ry - y, rw, rh,
224 pi->color.r, pi->color.g, pi->color.b, pi->color.a);
226 if (util_clip_region(x, y, width, height,
227 pi->x, pi->y, bw, pi->border,
230 pixbuf_draw_rect_fill(pixbuf,
231 rx - x, ry - y, rw, rh,
232 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
234 if (util_clip_region(x, y, width, height,
235 pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
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);
242 if (util_clip_region(x, y, width, height,
243 pi->x + bw - pi->border, pi->y + pi->border,
244 pi->border, bh - pi->border * 2,
247 pixbuf_draw_rect_fill(pixbuf,
248 rx - x, ry - y, rw, rh,
249 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
251 if (util_clip_region(x, y, width, height,
252 pi->x, pi->y + bh - pi->border,
256 pixbuf_draw_rect_fill(pixbuf,
257 rx - x, ry - y, rw, rh,
258 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
266 *-----------------------------------------------------------------------------
268 *-----------------------------------------------------------------------------
271 PanItem *pan_item_tri_new(PanWindow *pw, FileData *, gint x, gint y, gint width, gint height,
272 gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
273 const PanColor &color)
278 pi = g_new0(PanItem, 1);
279 pi->type = PAN_ITEM_TRIANGLE;
287 coord = g_new0(gint, 6);
297 pi->border = PAN_BORDER_NONE;
299 pw->list = g_list_prepend(pw->list, pi);
304 void pan_item_tri_border(PanItem *pi, gint borders, const PanColor &color)
306 if (!pi || pi->type != PAN_ITEM_TRIANGLE) return;
308 pi->border = borders;
313 gint pan_item_tri_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
320 if (util_clip_region(x, y, width, height,
321 pi->x, pi->y, pi->width, pi->height,
322 &rx, &ry, &rw, &rh) && pi->data)
324 auto coord = static_cast<gint *>(pi->data);
325 pixbuf_draw_triangle(pixbuf,
326 rx - x, ry - y, rw, rh,
327 coord[0] - x, coord[1] - y,
328 coord[2] - x, coord[3] - y,
329 coord[4] - x, coord[5] - y,
330 pi->color.r, pi->color.g, pi->color.b, pi->color.a);
332 if (pi->border & PAN_BORDER_1)
334 pixbuf_draw_line(pixbuf,
335 rx - x, ry - y, rw, rh,
336 coord[0] - x, coord[1] - y,
337 coord[2] - x, coord[3] - y,
338 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
340 if (pi->border & PAN_BORDER_2)
342 pixbuf_draw_line(pixbuf,
343 rx - x, ry - y, rw, rh,
344 coord[2] - x, coord[3] - y,
345 coord[4] - x, coord[5] - y,
346 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
348 if (pi->border & PAN_BORDER_3)
350 pixbuf_draw_line(pixbuf,
351 rx - x, ry - y, rw, rh,
352 coord[4] - x, coord[5] - y,
353 coord[0] - x, coord[1] - y,
354 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
363 *-----------------------------------------------------------------------------
365 *-----------------------------------------------------------------------------
368 static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
372 layout = gtk_widget_create_pango_layout(widget, nullptr);
374 if (pi->text_attr & PAN_TEXT_ATTR_MARKUP)
376 pango_layout_set_markup(layout, pi->text, -1);
380 if (pi->text_attr & PAN_TEXT_ATTR_BOLD ||
381 pi->text_attr & PAN_TEXT_ATTR_HEADING)
386 pal = pango_attr_list_new();
387 if (pi->text_attr & PAN_TEXT_ATTR_BOLD)
389 pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
391 pa->end_index = G_MAXINT;
392 pango_attr_list_insert(pal, pa);
394 if (pi->text_attr & PAN_TEXT_ATTR_HEADING)
396 pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
398 pa->end_index = G_MAXINT;
399 pango_attr_list_insert(pal, pa);
401 pango_layout_set_attributes(layout, pal);
402 pango_attr_list_unref(pal);
405 pango_layout_set_text(layout, pi->text, -1);
409 static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
413 if (!pi || !pi->text || !widget) return;
415 layout = pan_item_text_layout(pi, widget);
416 pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
417 g_object_unref(G_OBJECT(layout));
419 pi->width += pi->border * 2;
420 pi->height += pi->border * 2;
423 PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
424 PanTextAttrType attr, PanBorderType border, const PanColor &color)
428 pi = g_new0(PanItem, 1);
429 pi->type = PAN_ITEM_TEXT;
432 pi->text = g_strdup(text);
433 pi->text_attr = attr;
439 pan_item_text_compute_size(pi, pw->imd->pr);
441 pw->list = g_list_prepend(pw->list, pi);
446 gint pan_item_text_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr, gint x, gint y, gint, gint)
450 layout = pan_item_text_layout(pi, reinterpret_cast<GtkWidget *>(pr));
451 pixbuf_draw_layout(pixbuf, layout, reinterpret_cast<GtkWidget *>(pr),
452 pi->x - x + pi->border, pi->y - y + pi->border,
453 pi->color.r, pi->color.g, pi->color.b, pi->color.a);
454 g_object_unref(G_OBJECT(layout));
461 *-----------------------------------------------------------------------------
462 * item thumbnail type
463 *-----------------------------------------------------------------------------
466 PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y)
470 pi = g_new0(PanItem, 1);
472 pi->type = PAN_ITEM_THUMB;
476 pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
477 pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
479 pw->list = g_list_prepend(pw->list, pi);
484 gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
497 tw = gdk_pixbuf_get_width(pi->pixbuf);
498 th = gdk_pixbuf_get_height(pi->pixbuf);
500 tx = pi->x + (pi->width - tw) / 2;
501 ty = pi->y + (pi->height - th) / 2;
503 if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
505 if (util_clip_region(x, y, width, height,
506 tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
509 pixbuf_draw_shadow(pixbuf,
510 rx - x, ry - y, rw, rh,
511 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
513 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
518 if (util_clip_region(x, y, width, height,
519 tx + tw, ty + PAN_SHADOW_OFFSET,
520 PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
523 pixbuf_draw_shadow(pixbuf,
524 rx - x, ry - y, rw, rh,
525 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
527 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
529 if (util_clip_region(x, y, width, height,
530 tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
533 pixbuf_draw_shadow(pixbuf,
534 rx - x, ry - y, rw, rh,
535 tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
537 PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
541 if (util_clip_region(x, y, width, height,
545 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
546 static_cast<gdouble>(tx) - x,
547 static_cast<gdouble>(ty) - y,
548 1.0, 1.0, GDK_INTERP_NEAREST,
552 if (util_clip_region(x, y, width, height,
553 tx, ty, tw, PAN_OUTLINE_THICKNESS,
556 pixbuf_draw_rect_fill(pixbuf,
557 rx - x, ry - y, rw, rh,
558 PAN_OUTLINE_COLOR_1);
560 if (util_clip_region(x, y, width, height,
561 tx, ty, PAN_OUTLINE_THICKNESS, th,
564 pixbuf_draw_rect_fill(pixbuf,
565 rx - x, ry - y, rw, rh,
566 PAN_OUTLINE_COLOR_1);
568 if (util_clip_region(x, y, width, height,
569 tx + tw - PAN_OUTLINE_THICKNESS, ty + PAN_OUTLINE_THICKNESS,
570 PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
573 pixbuf_draw_rect_fill(pixbuf,
574 rx - x, ry - y, rw, rh,
575 PAN_OUTLINE_COLOR_2);
577 if (util_clip_region(x, y, width, height,
578 tx + PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
579 tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
582 pixbuf_draw_rect_fill(pixbuf,
583 rx - x, ry - y, rw, rh,
584 PAN_OUTLINE_COLOR_2);
589 tw = pi->width - PAN_SHADOW_OFFSET * 2;
590 th = pi->height - PAN_SHADOW_OFFSET * 2;
591 tx = pi->x + PAN_SHADOW_OFFSET;
592 ty = pi->y + PAN_SHADOW_OFFSET;
594 if (util_clip_region(x, y, width, height,
600 d = (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE) ? 2 : 8;
601 pixbuf_draw_rect_fill(pixbuf,
602 rx - x, ry - y, rw, rh,
604 PAN_SHADOW_ALPHA / d);
608 return (pi->pixbuf == nullptr);
613 *-----------------------------------------------------------------------------
615 *-----------------------------------------------------------------------------
618 static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
627 work = pw->cache_list;
632 pc = static_cast<PanCacheData *>(work->data);
635 if (pc->cd && pc->cd->dimensions &&
636 pc->fd && pc->fd == pi->fd)
638 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
639 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
641 pw->cache_list = g_list_remove(pw->cache_list, pc);
642 cache_sim_data_free(pc->cd);
643 file_data_unref(pc->fd);
650 PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
654 pi = g_new0(PanItem, 1);
655 pi->type = PAN_ITEM_IMAGE;
665 pi->color2.a = PAN_SHADOW_ALPHA / 2;
667 pan_item_image_find_size(pw, pi, w, h);
669 pw->list = g_list_prepend(pw->list, pi);
674 gint pan_item_image_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
681 if (util_clip_region(x, y, width, height,
682 pi->x, pi->y, pi->width, pi->height,
687 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
688 static_cast<gdouble>(pi->x) - x,
689 static_cast<gdouble>(pi->y) - y,
690 1.0, 1.0, GDK_INTERP_NEAREST,
695 pixbuf_draw_rect_fill(pixbuf,
696 rx - x, ry - y, rw, rh,
697 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
701 return (pi->pixbuf == nullptr);
706 *-----------------------------------------------------------------------------
708 *-----------------------------------------------------------------------------
711 PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
715 if (!key) return nullptr;
717 work = g_list_last(pw->list);
722 pi = static_cast<PanItem *>(work->data);
723 if ((pi->type == type || type == PAN_ITEM_NONE) &&
724 pi->key && strcmp(pi->key, key) == 0)
730 work = g_list_last(pw->list_static);
735 pi = static_cast<PanItem *>(work->data);
736 if ((pi->type == type || type == PAN_ITEM_NONE) &&
737 pi->key && strcmp(pi->key, key) == 0)
747 /* when ignore_case and partial are TRUE, path should be converted to lower case */
748 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
749 PanItemType type, const gchar *path,
750 gboolean ignore_case, gboolean partial)
754 work = g_list_last(search_list);
759 pi = static_cast<PanItem *>(work->data);
760 if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
762 gboolean match = FALSE;
764 if (path[0] == G_DIR_SEPARATOR)
766 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
768 else if (pi->fd->name)
776 haystack = g_utf8_strdown(pi->fd->name, -1);
777 match = (strstr(haystack, path) != nullptr);
782 if (strstr(pi->fd->name, path)) match = TRUE;
785 else if (ignore_case)
787 if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
791 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
795 if (match) list = g_list_prepend(list, pi);
803 /* when ignore_case and partial are TRUE, path should be converted to lower case */
804 GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
805 gboolean ignore_case, gboolean partial)
807 GList *list = nullptr;
809 if (!path) return nullptr;
810 if (partial && path[0] == G_DIR_SEPARATOR) return nullptr;
812 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
813 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
815 return g_list_reverse(list);
818 GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
819 gboolean ignore_case, gboolean partial)
821 if (!fd) return nullptr;
822 return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
826 static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
835 pi = static_cast<PanItem *>(work->data);
836 if ((pi->type == type || type == PAN_ITEM_NONE) &&
837 x >= pi->x && x < pi->x + pi->width &&
838 y >= pi->y && y < pi->y + pi->height &&
839 (!key || (pi->key && strcmp(pi->key, key) == 0)))
849 PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
850 gint x, gint y, const gchar *key)
854 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
857 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
862 *-----------------------------------------------------------------------------
864 *-----------------------------------------------------------------------------
867 PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
869 PanTextAlignment *ta;
871 ta = g_new0(PanTextAlignment, 1);
876 ta->key = g_strdup(key);
881 void pan_text_alignment_free(PanTextAlignment *ta)
885 g_list_free(ta->column1);
886 g_list_free(ta->column2);
891 PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
897 item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
898 PAN_TEXT_ATTR_BOLD, PAN_BORDER_NONE, PAN_POPUP_TEXT_COLOR);
899 pan_item_set_key(item, ta->key);
905 ta->column1 = g_list_append(ta->column1, item);
909 item = pan_item_text_new(ta->pw, ta->x, ta->y, text,
910 PAN_TEXT_ATTR_NONE, PAN_BORDER_NONE, PAN_POPUP_TEXT_COLOR);
911 pan_item_set_key(item, ta->key);
917 ta->column2 = g_list_append(ta->column2, item);
922 void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
939 p = static_cast<PanItem *>(work1->data);
942 if (p && p->width > cw1) cw1 = p->width;
950 p = static_cast<PanItem *>(work2->data);
953 if (p && p->width > cw2) cw2 = p->width;
960 while (work1 && work2)
966 p1 = static_cast<PanItem *>(work1->data);
967 p2 = static_cast<PanItem *>(work2->data);
975 pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
980 p2->x = x + cw1 + PREF_PAD_SPACE;
982 pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
983 if (height < p2->height) height = p2->height;
986 if (!p1 && !p2) height = PREF_PAD_GROUP;
991 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */