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, PanItem *pi, gint w, gint h)
628 work = pw->cache_list;
633 pc = static_cast<PanCacheData *>(work->data);
636 if (pc->cd && pc->cd->dimensions &&
637 pc->fd && pc->fd == pi->fd)
639 pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
640 pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
642 pw->cache_list = g_list_remove(pw->cache_list, pc);
643 pan_cache_data_free(pc);
649 PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
653 pi = g_new0(PanItem, 1);
654 pi->type = PAN_ITEM_IMAGE;
664 pi->color2.a = PAN_SHADOW_ALPHA / 2;
666 pan_item_image_find_size(pw, pi, w, h);
668 pw->list = g_list_prepend(pw->list, pi);
673 gint pan_item_image_draw(PanWindow *, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *, gint x, gint y, gint width, gint height)
680 if (util_clip_region(x, y, width, height,
681 pi->x, pi->y, pi->width, pi->height,
686 gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
687 static_cast<gdouble>(pi->x) - x,
688 static_cast<gdouble>(pi->y) - y,
689 1.0, 1.0, GDK_INTERP_NEAREST,
694 pixbuf_draw_rect_fill(pixbuf,
695 rx - x, ry - y, rw, rh,
696 pi->color2.r, pi->color2.g, pi->color2.b, pi->color2.a);
700 return (pi->pixbuf == nullptr);
705 *-----------------------------------------------------------------------------
707 *-----------------------------------------------------------------------------
710 PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
714 if (!key) return nullptr;
716 work = g_list_last(pw->list);
721 pi = static_cast<PanItem *>(work->data);
722 if ((pi->type == type || type == PAN_ITEM_NONE) &&
723 pi->key && strcmp(pi->key, key) == 0)
729 work = g_list_last(pw->list_static);
734 pi = static_cast<PanItem *>(work->data);
735 if ((pi->type == type || type == PAN_ITEM_NONE) &&
736 pi->key && strcmp(pi->key, key) == 0)
746 /* when ignore_case and partial are TRUE, path should be converted to lower case */
747 static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
748 PanItemType type, const gchar *path,
749 gboolean ignore_case, gboolean partial)
753 work = g_list_last(search_list);
758 pi = static_cast<PanItem *>(work->data);
759 if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
761 gboolean match = FALSE;
763 if (path[0] == G_DIR_SEPARATOR)
765 if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
767 else if (pi->fd->name)
775 haystack = g_utf8_strdown(pi->fd->name, -1);
776 match = (strstr(haystack, path) != nullptr);
781 if (strstr(pi->fd->name, path)) match = TRUE;
784 else if (ignore_case)
786 if (g_ascii_strcasecmp(path, pi->fd->name) == 0) match = TRUE;
790 if (strcmp(path, pi->fd->name) == 0) match = TRUE;
794 if (match) list = g_list_prepend(list, pi);
802 /* when ignore_case and partial are TRUE, path should be converted to lower case */
803 GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
804 gboolean ignore_case, gboolean partial)
806 GList *list = nullptr;
808 if (!path) return nullptr;
809 if (partial && path[0] == G_DIR_SEPARATOR) return nullptr;
811 list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
812 list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
814 return g_list_reverse(list);
817 GList *pan_item_find_by_fd(PanWindow *pw, PanItemType type, FileData *fd,
818 gboolean ignore_case, gboolean partial)
820 if (!fd) return nullptr;
821 return pan_item_find_by_path(pw, type, fd->path, ignore_case, partial);
825 static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
834 pi = static_cast<PanItem *>(work->data);
835 if ((pi->type == type || type == PAN_ITEM_NONE) &&
836 x >= pi->x && x < pi->x + pi->width &&
837 y >= pi->y && y < pi->y + pi->height &&
838 (!key || (pi->key && strcmp(pi->key, key) == 0)))
848 PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
849 gint x, gint y, const gchar *key)
853 pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
856 return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
861 *-----------------------------------------------------------------------------
863 *-----------------------------------------------------------------------------
866 PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
868 PanTextAlignment *ta;
870 ta = g_new0(PanTextAlignment, 1);
875 ta->key = g_strdup(key);
880 void pan_text_alignment_free(PanTextAlignment *ta)
884 g_list_free(ta->column1);
885 g_list_free(ta->column2);
890 PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
896 item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
897 PAN_TEXT_ATTR_BOLD, PAN_BORDER_NONE, PAN_POPUP_TEXT_COLOR);
898 pan_item_set_key(item, ta->key);
904 ta->column1 = g_list_append(ta->column1, item);
908 item = pan_item_text_new(ta->pw, ta->x, ta->y, text,
909 PAN_TEXT_ATTR_NONE, PAN_BORDER_NONE, PAN_POPUP_TEXT_COLOR);
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;
993 *-----------------------------------------------------------------------------
995 *-----------------------------------------------------------------------------
998 void pan_cache_data_free(PanCacheData *pc)
1002 cache_sim_data_free(pc->cd);
1003 file_data_unref(pc->fd);
1006 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */