diff src/pan-item.c @ 105:de64a683d5d0

Thu Nov 9 17:43:06 2006 John Ellis <johne@verizon.net> * pan-view.c: Break this monstrosity of code into smaller files. * pan-calendar.c, pan-folder.c, pan-grid.c, pan-timeline.c: Move the layout types into their own files (for now flower is with folder tree). * pan-item.c: PanItem creation, drawing, actions, and maintenance. * pan-types.h: Defines, data types, function prototypes. * pan-util.c: Various utilities. * src/Makefile.am: Add new files above.
author gqview
date Thu, 09 Nov 2006 22:53:11 +0000
parents
children 71e1ebee420e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pan-item.c	Thu Nov 09 22:53:11 2006 +0000
@@ -0,0 +1,965 @@
+/*
+ * GQview
+ * (C) 2006 John Ellis
+ *
+ * Author: John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+
+#include "gqview.h"
+#include "pan-types.h"
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item base functions
+ *-----------------------------------------------------------------------------
+ */
+
+void pan_item_free(PanItem *pi)
+{
+	if (!pi) return;
+
+	if (pi->pixbuf) g_object_unref(pi->pixbuf);
+	if (pi->fd) file_data_free(pi->fd);
+	g_free(pi->text);
+	g_free(pi->key);
+	g_free(pi->data);
+
+	g_free(pi);
+}
+
+void pan_item_set_key(PanItem *pi, const gchar *key)
+{
+	gchar *tmp;
+
+	if (!pi) return;
+
+	tmp = pi->key;
+	pi->key = g_strdup(key);
+	g_free(tmp);
+}
+
+void pan_item_added(PanWindow *pw, PanItem *pi)
+{
+	if (!pi) return;
+	image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
+}
+
+void pan_item_remove(PanWindow *pw, PanItem *pi)
+{
+	if (!pi) return;
+
+	if (pw->click_pi == pi) pw->click_pi = NULL;
+	if (pw->queue_pi == pi)	pw->queue_pi = NULL;
+	if (pw->search_pi == pi) pw->search_pi = NULL;
+	pw->queue = g_list_remove(pw->queue, pi);
+
+	pw->list = g_list_remove(pw->list, pi);
+	image_area_changed(pw->imd, pi->x, pi->y, pi->width, pi->height);
+	pan_item_free(pi);
+}
+
+void pan_item_size_by_item(PanItem *pi, PanItem *child, gint border)
+{
+	if (!pi || !child) return;
+
+	if (pi->x + pi->width < child->x + child->width + border)
+		pi->width = child->x + child->width + border - pi->x;
+
+	if (pi->y + pi->height < child->y + child->height + border)
+		pi->height = child->y + child->height + border - pi->y;
+}
+
+void pan_item_size_coordinates(PanItem *pi, gint border, gint *w, gint *h)
+{
+	if (!pi) return;
+
+	if (*w < pi->x + pi->width + border) *w = pi->x + pi->width + border;
+	if (*h < pi->y + pi->height + border) *h = pi->y + pi->height + border;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item box type
+ *-----------------------------------------------------------------------------
+ */
+
+PanItem *pan_item_box_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
+			  gint border_size,
+			  guint8 base_r, guint8 base_g, guint8 base_b, guint8 base_a,
+			  guint8 bord_r, guint8 bord_g, guint8 bord_b, guint8 bord_a)
+{
+	PanItem *pi;
+
+	pi = g_new0(PanItem, 1);
+	pi->type = PAN_ITEM_BOX;
+	pi->fd = fd;
+	pi->x = x;
+	pi->y = y;
+	pi->width = width;
+	pi->height = height;
+
+	pi->color_r = base_r;
+	pi->color_g = base_g;
+	pi->color_b = base_b;
+	pi->color_a = base_a;
+
+	pi->color2_r = bord_r;
+	pi->color2_g = bord_g;
+	pi->color2_b = bord_b;
+	pi->color2_a = bord_a;
+	pi->border = border_size;
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+void pan_item_box_shadow(PanItem *pi, gint offset, gint fade)
+{
+	gint *shadow;
+
+	if (!pi || pi->type != PAN_ITEM_BOX) return;
+
+	shadow = pi->data;
+	if (shadow)
+		{
+		pi->width -= shadow[0];
+		pi->height -= shadow[0];
+		}
+
+	shadow = g_new0(gint, 2);
+	shadow[0] = offset;
+	shadow[1] = fade;
+
+	pi->width += offset;
+	pi->height += offset;
+
+	g_free(pi->data);
+	pi->data = shadow;
+}
+
+gint pan_item_box_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+		       gint x, gint y, gint width, gint height)
+{
+	gint bw, bh;
+	gint *shadow;
+	gint rx, ry, rw, rh;
+
+	bw = pi->width;
+	bh = pi->height;
+
+	shadow = pi->data;
+	if (shadow)
+		{
+		bw -= shadow[0];
+		bh -= shadow[0];
+
+		if (pi->color_a > 254)
+			{
+			pixbuf_draw_shadow(pixbuf, pi->x - x + bw, pi->y - y + shadow[0],
+					   shadow[0], bh - shadow[0],
+					   pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
+					   shadow[1],
+					   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+			pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + bh,
+					   bw, shadow[0],
+					   pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
+					   shadow[1],
+					   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+			}
+		else
+			{
+			gint a;
+			a = pi->color_a * PAN_SHADOW_ALPHA >> 8;
+			pixbuf_draw_shadow(pixbuf, pi->x - x + shadow[0], pi->y - y + shadow[0],
+					   bw, bh,
+					   pi->x - x + shadow[0], pi->y - y + shadow[0], bw, bh,
+					   shadow[1],
+					   PAN_SHADOW_COLOR, a);
+			}
+		}
+
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y, bw, bh,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color_r, pi->color_g, pi->color_b, pi->color_a);
+		}
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y, bw, pi->border,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+		}
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y + pi->border, pi->border, bh - pi->border * 2,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+		}
+	if (util_clip_region(x, y, width, height,
+			     pi->x + bw - pi->border, pi->y + pi->border,
+			     pi->border, bh - pi->border * 2,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+		}
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y + bh - pi->border,
+			     bw,  pi->border,
+			     &rx, &ry, &rw, &rh))
+		{
+		pixbuf_draw_rect_fill(pixbuf,
+				      rx - x, ry - y, rw, rh,
+				      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+		}
+
+	return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item triangle type
+ *-----------------------------------------------------------------------------
+ */
+
+PanItem *pan_item_tri_new(PanWindow *pw, FileData *fd, gint x, gint y, gint width, gint height,
+			  gint x1, gint y1, gint x2, gint y2, gint x3, gint y3,
+			  guint8 r, guint8 g, guint8 b, guint8 a)
+{
+	PanItem *pi;
+	gint *coord;
+
+	pi = g_new0(PanItem, 1);
+	pi->type = PAN_ITEM_TRIANGLE;
+	pi->x = x;
+	pi->y = y;
+	pi->width = width;
+	pi->height = height;
+
+	pi->color_r = r;
+	pi->color_g = g;
+	pi->color_b = b;
+	pi->color_a = a;
+
+	coord = g_new0(gint, 6);
+	coord[0] = x1;
+	coord[1] = y1;
+	coord[2] = x2;
+	coord[3] = y2;
+	coord[4] = x3;
+	coord[5] = y3;
+
+	pi->data = coord;
+
+	pi->border = PAN_BORDER_NONE;
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+void pan_item_tri_border(PanItem *pi, gint borders,
+			 guint8 r, guint8 g, guint8 b, guint8 a)
+{
+	if (!pi || pi->type != PAN_ITEM_TRIANGLE) return;
+
+	pi->border = borders;
+
+	pi->color2_r = r;
+	pi->color2_g = g;
+	pi->color2_b = b;
+	pi->color2_a = a;
+}
+
+gint pan_item_tri_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+		       gint x, gint y, gint width, gint height)
+{
+	gint rx, ry, rw, rh;
+
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y, pi->width, pi->height,
+			     &rx, &ry, &rw, &rh) && pi->data)
+		{
+		gint *coord = pi->data;
+		pixbuf_draw_triangle(pixbuf,
+				     rx - x, ry - y, rw, rh,
+				     coord[0] - x, coord[1] - y,
+				     coord[2] - x, coord[3] - y,
+				     coord[4] - x, coord[5] - y,
+				     pi->color_r, pi->color_g, pi->color_b, pi->color_a);
+
+		if (pi->border & PAN_BORDER_1)
+			{
+			pixbuf_draw_line(pixbuf,
+					 rx - x, ry - y, rw, rh,
+					 coord[0] - x, coord[1] - y,
+					 coord[2] - x, coord[3] - y,
+					 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+			}
+		if (pi->border & PAN_BORDER_2)
+			{
+			pixbuf_draw_line(pixbuf,
+					 rx - x, ry - y, rw, rh,
+					 coord[2] - x, coord[3] - y,
+					 coord[4] - x, coord[5] - y,
+					 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+			}
+		if (pi->border & PAN_BORDER_3)
+			{
+			pixbuf_draw_line(pixbuf,
+					 rx - x, ry - y, rw, rh,
+					 coord[4] - x, coord[5] - y,
+					 coord[0] - x, coord[1] - y,
+					 pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+			}
+		}
+
+	return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item text type
+ *-----------------------------------------------------------------------------
+ */
+
+static PangoLayout *pan_item_text_layout(PanItem *pi, GtkWidget *widget)
+{
+	PangoLayout *layout;
+
+	layout = gtk_widget_create_pango_layout(widget, NULL);
+
+	if (pi->text_attr & PAN_TEXT_ATTR_MARKUP)
+		{
+		pango_layout_set_markup(layout, pi->text, -1);
+		return layout;
+		}
+
+	if (pi->text_attr & PAN_TEXT_ATTR_BOLD ||
+	    pi->text_attr & PAN_TEXT_ATTR_HEADING)
+		{
+		PangoAttrList *pal;
+		PangoAttribute *pa;
+		
+		pal = pango_attr_list_new();
+		if (pi->text_attr & PAN_TEXT_ATTR_BOLD)
+			{
+			pa = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+			pa->start_index = 0;
+			pa->end_index = G_MAXINT;
+			pango_attr_list_insert(pal, pa);
+			}
+		if (pi->text_attr & PAN_TEXT_ATTR_HEADING)
+			{
+			pa = pango_attr_scale_new(PANGO_SCALE_LARGE);
+			pa->start_index = 0;
+			pa->end_index = G_MAXINT;
+			pango_attr_list_insert(pal, pa);
+			}
+		pango_layout_set_attributes(layout, pal);
+		pango_attr_list_unref(pal);
+		}
+
+	pango_layout_set_text(layout, pi->text, -1);
+	return layout;
+}
+
+static void pan_item_text_compute_size(PanItem *pi, GtkWidget *widget)
+{
+	PangoLayout *layout;
+
+	if (!pi || !pi->text || !widget) return;
+
+	layout = pan_item_text_layout(pi, widget);
+	pango_layout_get_pixel_size(layout, &pi->width, &pi->height);
+	g_object_unref(G_OBJECT(layout));
+
+	pi->width += pi->border * 2;
+	pi->height += pi->border * 2;
+}
+
+PanItem *pan_item_text_new(PanWindow *pw, gint x, gint y, const gchar *text,
+			   PanTextAttrType attr, PanBorderType border,
+			   guint8 r, guint8 g, guint8 b, guint8 a)
+{
+	PanItem *pi;
+
+	pi = g_new0(PanItem, 1);
+	pi->type = PAN_ITEM_TEXT;
+	pi->x = x;
+	pi->y = y;
+	pi->text = g_strdup(text);
+	pi->text_attr = attr;
+
+	pi->color_r = r;
+	pi->color_g = g;
+	pi->color_b = b;
+	pi->color_a = a;
+
+	pi->border = border;
+
+	pan_item_text_compute_size(pi, pw->imd->pr);
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+gint pan_item_text_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+			gint x, gint y, gint width, gint height)
+{
+	PangoLayout *layout;
+
+	layout = pan_item_text_layout(pi, (GtkWidget *)pr);
+	pixbuf_draw_layout(pixbuf, layout, (GtkWidget *)pr,
+			   pi->x - x + pi->border, pi->y - y + pi->border,
+			   pi->color_r, pi->color_g, pi->color_b, pi->color_a);
+	g_object_unref(G_OBJECT(layout));
+
+	return FALSE;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item thumbnail type
+ *-----------------------------------------------------------------------------
+ */
+
+PanItem *pan_item_thumb_new(PanWindow *pw, FileData *fd, gint x, gint y)
+{
+	PanItem *pi;
+
+	pi = g_new0(PanItem, 1);
+	pi->type = PAN_ITEM_THUMB;
+	pi->fd = fd;
+	pi->x = x;
+	pi->y = y;
+	pi->width = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
+	pi->height = PAN_THUMB_SIZE + PAN_SHADOW_OFFSET * 2;
+
+	pi->pixbuf = NULL;
+
+	pi->queued = FALSE;
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+gint pan_item_thumb_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+			 gint x, gint y, gint width, gint height)
+{
+	gint tx, ty, tw, th;
+	gint rx, ry, rw, rh;
+
+	if (pi->pixbuf)
+		{
+		tw = gdk_pixbuf_get_width(pi->pixbuf);
+		th = gdk_pixbuf_get_height(pi->pixbuf);
+
+		tx = pi->x + (pi->width - tw) / 2;
+		ty = pi->y + (pi->height - th) / 2;
+
+		if (gdk_pixbuf_get_has_alpha(pi->pixbuf))
+			{
+			if (util_clip_region(x, y, width, height,
+					     tx + PAN_SHADOW_OFFSET, ty + PAN_SHADOW_OFFSET, tw, th,
+					     &rx, &ry, &rw, &rh))
+				{
+				pixbuf_draw_shadow(pixbuf,
+						   rx - x, ry - y, rw, rh,
+						   tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
+						   PAN_SHADOW_FADE,
+						   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+				}
+			}
+		else
+			{
+			if (util_clip_region(x, y, width, height,
+					     tx + tw, ty + PAN_SHADOW_OFFSET,
+					     PAN_SHADOW_OFFSET, th - PAN_SHADOW_OFFSET,
+					     &rx, &ry, &rw, &rh))
+				{
+				pixbuf_draw_shadow(pixbuf,
+						   rx - x, ry - y, rw, rh,
+						   tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
+						   PAN_SHADOW_FADE,
+						   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+				}
+			if (util_clip_region(x, y, width, height,
+					     tx + PAN_SHADOW_OFFSET, ty + th, tw, PAN_SHADOW_OFFSET,
+					     &rx, &ry, &rw, &rh))
+				{
+				pixbuf_draw_shadow(pixbuf,
+						   rx - x, ry - y, rw, rh,
+						   tx + PAN_SHADOW_OFFSET - x, ty + PAN_SHADOW_OFFSET - y, tw, th,
+						   PAN_SHADOW_FADE,
+						   PAN_SHADOW_COLOR, PAN_SHADOW_ALPHA);
+				}
+			}
+
+		if (util_clip_region(x, y, width, height,
+				     tx, ty, tw, th,
+				     &rx, &ry, &rw, &rh))
+			{
+			gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
+					     (double) tx - x,
+					     (double) ty - y,
+					     1.0, 1.0, GDK_INTERP_NEAREST,
+					     255);
+			}
+
+		if (util_clip_region(x, y, width, height,
+				     tx, ty, tw, PAN_OUTLINE_THICKNESS,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
+			}
+		if (util_clip_region(x, y, width, height,
+				     tx, ty, PAN_OUTLINE_THICKNESS, th,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_OUTLINE_COLOR_1, PAN_OUTLINE_ALPHA);
+			}
+		if (util_clip_region(x, y, width, height,
+				     tx + tw - PAN_OUTLINE_THICKNESS, ty +  PAN_OUTLINE_THICKNESS,
+				     PAN_OUTLINE_THICKNESS, th - PAN_OUTLINE_THICKNESS,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
+			}
+		if (util_clip_region(x, y, width, height,
+				     tx +  PAN_OUTLINE_THICKNESS, ty + th - PAN_OUTLINE_THICKNESS,
+				     tw - PAN_OUTLINE_THICKNESS * 2, PAN_OUTLINE_THICKNESS,
+				     &rx, &ry, &rw, &rh))
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_OUTLINE_COLOR_2, PAN_OUTLINE_ALPHA);
+			}
+		}
+	else
+		{
+		tw = pi->width - PAN_SHADOW_OFFSET * 2;
+		th = pi->height - PAN_SHADOW_OFFSET * 2;
+		tx = pi->x + PAN_SHADOW_OFFSET;
+		ty = pi->y + PAN_SHADOW_OFFSET;
+
+		if (util_clip_region(x, y, width, height,
+				     tx, ty, tw, th,
+				     &rx, &ry, &rw, &rh))
+			{
+			gint d;
+
+			d = (pw->size <= PAN_IMAGE_SIZE_THUMB_NONE) ? 2 : 8;
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      PAN_SHADOW_COLOR,
+					      PAN_SHADOW_ALPHA / d);
+			}
+		}
+
+	return (pi->pixbuf == NULL);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item image type
+ *-----------------------------------------------------------------------------
+ */
+
+static void pan_item_image_find_size(PanWindow *pw, PanItem *pi, gint w, gint h)
+{
+	GList *work;
+
+	pi->width = w;
+	pi->height = h;
+
+	if (!pi->fd) return;
+
+	work = pw->cache_list;
+	while (work)
+		{
+		PanCacheData *pc;
+		gchar *path;
+
+		pc = work->data;
+		work = work->next;
+
+		path = ((FileData *)pc)->path;
+
+		if (pc->cd && pc->cd->dimensions &&
+		    path && strcmp(path, pi->fd->path) == 0)
+			{
+			pi->width = MAX(1, pc->cd->width * pw->image_size / 100);
+			pi->height = MAX(1, pc->cd->height * pw->image_size / 100);
+
+			pw->cache_list = g_list_remove(pw->cache_list, pc);
+			cache_sim_data_free(pc->cd);
+			file_data_free((FileData *)pc);
+			return;
+			}
+		}
+}
+
+PanItem *pan_item_image_new(PanWindow *pw, FileData *fd, gint x, gint y, gint w, gint h)
+{
+	PanItem *pi;
+
+	pi = g_new0(PanItem, 1);
+	pi->type = PAN_ITEM_IMAGE;
+	pi->fd = fd;
+	pi->x = x;
+	pi->y = y;
+
+	pi->color_a = 255;
+
+	pi->color2_r = 0;
+	pi->color2_g = 0;
+	pi->color2_b = 0;
+	pi->color2_a = PAN_SHADOW_ALPHA / 2;
+
+	pan_item_image_find_size(pw, pi, w, h);
+
+	pw->list = g_list_prepend(pw->list, pi);
+
+	return pi;
+}
+
+gint pan_item_image_draw(PanWindow *pw, PanItem *pi, GdkPixbuf *pixbuf, PixbufRenderer *pr,
+			 gint x, gint y, gint width, gint height)
+{
+	gint rx, ry, rw, rh;
+
+	if (util_clip_region(x, y, width, height,
+			     pi->x, pi->y, pi->width, pi->height,
+			     &rx, &ry, &rw, &rh))
+		{
+		if (pi->pixbuf)
+			{
+			gdk_pixbuf_composite(pi->pixbuf, pixbuf, rx - x, ry - y, rw, rh,
+					     (double) pi->x - x,
+					     (double) pi->y - y,
+					     1.0, 1.0, GDK_INTERP_NEAREST,
+					     pi->color_a);
+			}
+		else
+			{
+			pixbuf_draw_rect_fill(pixbuf,
+					      rx - x, ry - y, rw, rh,
+					      pi->color2_r, pi->color2_g, pi->color2_b, pi->color2_a);
+			}
+		}
+
+	return (pi->pixbuf == NULL);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * item lookup/search
+ *-----------------------------------------------------------------------------
+ */
+
+PanItem *pan_item_find_by_key(PanWindow *pw, PanItemType type, const gchar *key)
+{
+	GList *work;
+
+	if (!key) return NULL;
+
+	work = g_list_last(pw->list);
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		if ((pi->type == type || type == PAN_ITEM_NONE) &&
+		     pi->key && strcmp(pi->key, key) == 0)
+			{
+			return pi;
+			}
+		work = work->prev;
+		}
+	work = g_list_last(pw->list_static);
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		if ((pi->type == type || type == PAN_ITEM_NONE) &&
+		     pi->key && strcmp(pi->key, key) == 0)
+			{
+			return pi;
+			}
+		work = work->prev;
+		}
+
+	return NULL;
+}
+
+/* when ignore_case and partial are TRUE, path should be converted to lower case */
+static GList *pan_item_find_by_path_l(GList *list, GList *search_list,
+				      PanItemType type, const gchar *path,
+				      gint ignore_case, gint partial)
+{
+	GList *work;
+
+	work = g_list_last(search_list);
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		if ((pi->type == type || type == PAN_ITEM_NONE) && pi->fd)
+			{
+			gint match = FALSE;
+
+			if (path[0] == '/')
+				{
+				if (pi->fd->path && strcmp(path, pi->fd->path) == 0) match = TRUE;
+				}
+			else if (pi->fd->name)
+				{
+				if (partial)
+					{
+					if (ignore_case)
+						{
+						gchar *haystack;
+
+						haystack = g_utf8_strdown(pi->fd->name, -1);
+						match = (strstr(haystack, path) != NULL);
+						g_free(haystack);
+						}
+					else
+						{
+						if (strstr(pi->fd->name, path)) match = TRUE;
+						}
+					}
+				else if (ignore_case)
+					{
+					if (strcasecmp(path, pi->fd->name) == 0) match = TRUE;
+					}
+				else
+					{
+					if (strcmp(path, pi->fd->name) == 0) match = TRUE;
+					}
+				}
+
+			if (match) list = g_list_prepend(list, pi);
+			}
+		work = work->prev;
+		}
+
+	return list;
+}
+
+/* when ignore_case and partial are TRUE, path should be converted to lower case */
+GList *pan_item_find_by_path(PanWindow *pw, PanItemType type, const gchar *path,
+			     gint ignore_case, gint partial)
+{
+	GList *list = NULL;
+
+	if (!path) return NULL;
+	if (partial && path[0] == '/') return NULL;
+
+	list = pan_item_find_by_path_l(list, pw->list_static, type, path, ignore_case, partial);
+	list = pan_item_find_by_path_l(list, pw->list, type, path, ignore_case, partial);
+
+	return g_list_reverse(list);
+}
+
+static PanItem *pan_item_find_by_coord_l(GList *list, PanItemType type, gint x, gint y, const gchar *key)
+{
+	GList *work;
+
+	work = list;
+	while (work)
+		{
+		PanItem *pi;
+
+		pi = work->data;
+		if ((pi->type == type || type == PAN_ITEM_NONE) &&
+		     x >= pi->x && x < pi->x + pi->width &&
+		     y >= pi->y && y < pi->y + pi->height &&
+		    (!key || (pi->key && strcmp(pi->key, key) == 0)))
+			{
+			return pi;
+			}
+		work = work->next;
+		}
+
+	return NULL;
+}
+
+PanItem *pan_item_find_by_coord(PanWindow *pw, PanItemType type,
+				gint x, gint y, const gchar *key)
+{
+	PanItem *pi;
+
+	pi = pan_item_find_by_coord_l(pw->list, type, x, y, key);
+	if (pi) return pi;
+
+	return pan_item_find_by_coord_l(pw->list_static, type, x, y, key);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * text alignments
+ *-----------------------------------------------------------------------------
+ */
+
+PanTextAlignment *pan_text_alignment_new(PanWindow *pw, gint x, gint y, const gchar *key)
+{
+	PanTextAlignment *ta;
+
+	ta = g_new0(PanTextAlignment, 1);
+
+	ta->pw = pw;
+	ta->column1 = NULL;
+	ta->column2 = NULL;
+	ta->x = x;
+	ta->y = y;
+	ta->key = g_strdup(key);
+
+	return ta;
+}
+
+void pan_text_alignment_free(PanTextAlignment *ta)
+{
+	if (!ta) return;
+
+	g_list_free(ta->column1);
+	g_list_free(ta->column2);
+	g_free(ta->key);
+	g_free(ta);
+}
+
+PanItem *pan_text_alignment_add(PanTextAlignment *ta, const gchar *label, const gchar *text)
+{
+	PanItem *item;
+
+	if (label)
+		{
+		item = pan_item_text_new(ta->pw, ta->x, ta->y, label,
+					 PAN_TEXT_ATTR_BOLD, 0,
+					 PAN_POPUP_TEXT_COLOR, 255);
+		pan_item_set_key(item, ta->key);
+		}
+	else
+		{
+		item = NULL;
+		}
+	ta->column1 = g_list_append(ta->column1, item);
+
+	if (text)
+		{
+		item = pan_item_text_new(ta->pw, ta->x, ta->y, text,
+					 PAN_TEXT_ATTR_NONE, 0,
+					 PAN_POPUP_TEXT_COLOR, 255);
+		pan_item_set_key(item, ta->key);
+		}
+	else
+		{
+		item = NULL;
+		}
+	ta->column2 = g_list_append(ta->column2, item);
+
+	return item;
+}
+
+void pan_text_alignment_calc(PanTextAlignment *ta, PanItem *box)
+{
+	gint cw1, cw2;
+	gint x, y;
+	GList *work1;
+	GList *work2;
+
+	cw1 = 0;
+	cw2 = 0;
+
+	work1 = ta->column1;
+	while (work1)
+		{
+		PanItem *p;
+
+		p = work1->data;
+		work1 = work1->next;
+
+		if (p && p->width > cw1) cw1 = p->width;
+		}
+
+	work2 = ta->column2;
+	while (work2)
+		{
+		PanItem *p;
+
+		p = work2->data;
+		work2 = work2->next;
+
+		if (p && p->width > cw2) cw2 = p->width;
+		}
+
+	x = ta->x;
+	y = ta->y;
+	work1 = ta->column1;
+	work2 = ta->column2;
+	while (work1 && work2)
+		{
+		PanItem *p1;
+		PanItem *p2;
+		gint height = 0;
+
+		p1 = work1->data;
+		p2 = work2->data;
+		work1 = work1->next;
+		work2 = work2->next;
+
+		if (p1)
+			{
+			p1->x = x;
+			p1->y = y;
+			pan_item_size_by_item(box, p1, PREF_PAD_BORDER);
+			height = p1->height;
+			}
+		if (p2)
+			{
+			p2->x = x + cw1 + PREF_PAD_SPACE;
+			p2->y = y;
+			pan_item_size_by_item(box, p2, PREF_PAD_BORDER);
+			if (height < p2->height) height = p2->height;
+			}
+
+		if (!p1 && !p2) height = PREF_PAD_GROUP;
+
+		y += height;
+		}
+}
+
+