diff src/osd.c @ 2866:87242753ed2c

Ref #160: Replace print dialog by standard GTK dialog https://github.com/BestImageViewer/geeqie/issues/160 Permit exif tags to be included in the image text annotations (as for the Overlay screen Display)
author Colin Clark <colin.clark@cclark.uk>
date Thu, 22 Nov 2018 15:08:54 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/osd.c	Thu Nov 22 15:08:54 2018 +0000
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2018 The Geeqie Team
+ *
+ * Author: Colin Clark
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Routines for creating the Overlay Screen Display text. Also
+ * used for the same purposes by the Print routines
+ */
+
+#include "main.h"
+#include "osd.h"
+
+#include "dnd.h"
+#include "exif.h"
+#include "glua.h"
+#include "metadata.h"
+#include "ui_fileops.h"
+#include "ui_misc.h"
+
+#include <math.h>
+
+static const gchar *predefined_tags[][2] = {
+	{"%name%",							N_("Name")},
+	{"%path:60%",						N_("Path")},
+	{"%date%",							N_("Date")},
+	{"%size%",							N_("Size")},
+	{"%zoom%",							N_("Zoom")},
+	{"%dimensions%",					N_("Dimensions")},
+	{"%collection%",					N_("Collection")},
+	{"%number%",						N_("Image index")},
+	{"%total%",							N_("Images total")},
+	{"%comment%",						N_("Comment")},
+	{"%keywords%",						N_("Keywords")},
+	{"%file.ctime%",					N_("File ctime")},
+	{"%file.mode%",						N_("File mode")},
+	{"%file.owner%",					N_("File owner")},
+	{"%file.group%",					N_("File group")},
+	{"%file.link%",						N_("File link")},
+	{"%file.class%",					N_("File class")},
+	{"%formatted.DateTime%",			N_("Image date")},
+	{"%formatted.DateTimeDigitized%",	N_("Date digitized")},
+	{"%formatted.ShutterSpeed%",		N_("ShutterSpeed")},
+	{"%formatted.Aperture%",			N_("Aperture")},
+	{"%formatted.ExposureBias%",		N_("Exposure bias")},
+	{"%formatted.Resolution%",			N_("Resolution")},
+	{"%formatted.Camera%",				N_("Camera")},
+	{"%formatted.ISOSpeedRating%",		N_("ISO")},
+	{"%formatted.FocalLength%",			N_("Focal length")},
+	{"%formatted.FocalLength35mmFilm%",	N_("Focal len. 35mm")},
+	{"%formatted.SubjectDistance%",		N_("Subject distance")},
+	{"%formatted.Flash%",				N_("Flash")},
+	{"%formatted.ColorProfile%",		N_("Color profile")},
+	{"%formatted.GPSPosition%",			N_("Lat, Long")},
+	{"%formatted.GPSAltitude%",			N_("Altitude")},
+	{"%formatted.localtime%",			N_("Local time")},
+	{"%formatted.timezone%",			N_("Timezone")},
+	{"%formatted.countryname%",			N_("Country name")},
+	{"%formatted.countrycode%",			N_("Country code")},
+	{"%rating%",						N_("Rating")},
+	{"%formatted.star_rating%",			N_("Star rating")},
+	{"%Xmp.dc.creator%",				N_("© Creator")},
+	{"%Xmp.dc.contributor%",			N_("© Contributor")},
+	{"%Xmp.dc.rights%",					N_("© Rights")},
+	{NULL, NULL}};
+
+static GtkTargetEntry osd_drag_types[] = {
+	{ "text/plain", GTK_TARGET_SAME_APP, TARGET_TEXT_PLAIN }
+};
+
+typedef struct _TagData TagData;
+struct _TagData
+{
+	gchar *key;
+	gchar *title;
+};
+
+static void tag_button_cb(GtkWidget *widget, gpointer data)
+{
+	GtkTextView *image_overlay_template_view = data;
+	GtkTextBuffer *buffer;
+	TagData *td;
+
+	buffer = gtk_text_view_get_buffer(image_overlay_template_view);
+	td = g_object_get_data(G_OBJECT(widget), "tag_data");
+	gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(buffer), td->key, -1);
+
+	gtk_widget_grab_focus(GTK_WIDGET(image_overlay_template_view));
+}
+
+static void osd_dnd_get_cb(GtkWidget *btn, GdkDragContext *context,
+								GtkSelectionData *selection_data, guint info,
+								guint time, gpointer data)
+{
+	TagData *td;
+	GtkTextView *image_overlay_template_view = data;
+
+	td = g_object_get_data(G_OBJECT(btn), "tag_data");
+	gtk_selection_data_set_text(selection_data, td->key, -1);
+
+	gtk_widget_grab_focus(GTK_WIDGET(image_overlay_template_view));
+}
+
+static void osd_btn_destroy_cb(GtkWidget *btn, GdkDragContext *context,
+								GtkSelectionData *selection_data, guint info,
+								guint time, gpointer data)
+{
+	TagData *td;
+
+	td = g_object_get_data(G_OBJECT(btn), "tag_data");
+	g_free(td->key);
+	g_free(td->title);
+}
+
+static void set_osd_button(GtkTable *table, const gint rows, const gint cols, const gchar *key, const gchar *title, GtkWidget *template_view)
+{
+	GtkWidget *new_button;
+	TagData *td;
+
+	new_button = gtk_button_new_with_label(title);
+	g_signal_connect(G_OBJECT(new_button), "clicked", G_CALLBACK(tag_button_cb), template_view);
+	gtk_widget_show(new_button);
+
+	td = g_new0(TagData, 1);
+	td->key = g_strdup(key);
+	td->title = g_strdup(title);
+
+	g_object_set_data(G_OBJECT(new_button), "tag_data", td);
+
+	gtk_drag_source_set(new_button, GDK_BUTTON1_MASK, osd_drag_types, 1, GDK_ACTION_COPY);
+	g_signal_connect(G_OBJECT(new_button), "drag_data_get",
+							G_CALLBACK(osd_dnd_get_cb), template_view);
+	g_signal_connect(G_OBJECT(new_button), "destroy",
+							G_CALLBACK(osd_btn_destroy_cb), new_button);
+
+	gtk_table_attach_defaults(table, new_button, cols, cols+1, rows, rows+1);
+
+}
+
+GtkWidget *osd_new(gint max_cols, GtkWidget *template_view)
+{
+	GtkWidget *hbox;
+	GtkWidget *vbox;
+	GtkWidget *vbox_buttons;
+	GtkWidget *group;
+	GtkWidget *button;
+	GtkWidget *scrolled;
+	GtkTextBuffer *buffer;
+	GtkWidget *label;
+	GtkWidget *	subgroup;
+	gint i = 0;
+	gint rows = 0;
+	gint max_rows = 0;
+	gint col = 0;
+	gint cols = 0;
+	gdouble entries;
+	GtkWidget *viewport;
+
+	vbox = gtk_vbox_new(FALSE, 0);
+
+	pref_label_new(vbox, _("To include predefined tags in the template, click a button or drag-and-drop"));
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_box_pack_start(GTK_BOX(vbox), scrolled, FALSE, FALSE, 0);
+	gtk_container_set_border_width(GTK_CONTAINER(scrolled), PREF_PAD_BORDER);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+				       GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	label = gtk_label_new("title");
+	gtk_widget_show(scrolled);
+	gtk_widget_set_size_request(scrolled, -1, 140);
+
+	viewport = gtk_viewport_new(NULL, NULL);
+	gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
+	gtk_container_add(GTK_CONTAINER(scrolled), viewport);
+	gtk_widget_show(viewport);
+
+	entries = (sizeof(predefined_tags) / sizeof(predefined_tags[0])) - 1;
+	max_rows = ceil(entries / max_cols);
+
+	GtkTable *table;
+	table = GTK_TABLE(gtk_table_new(max_rows, max_cols, FALSE));
+	gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(table));
+	gtk_widget_show(GTK_WIDGET(table));
+
+	for (rows = 0; rows < max_rows; rows++)
+		{
+		cols = 0;
+
+		while (cols < max_cols && predefined_tags[i][0])
+			{
+			set_osd_button(table, rows, cols, predefined_tags[i][0], predefined_tags[i][1], template_view);
+			i = i + 1;
+			cols++;
+			}
+		}
+	return vbox;
+}
+static gchar *keywords_to_string(FileData *fd)
+{
+	GList *keywords;
+	GString *kwstr = NULL;
+	gchar *ret = NULL;
+
+	g_assert(fd);
+
+	keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
+
+	if (keywords)
+		{
+		GList *work = keywords;
+
+		while (work)
+			{
+			gchar *kw = work->data;
+			work = work->next;
+
+			if (!kw) continue;
+			if (!kwstr)
+				kwstr = g_string_new("");
+			else
+				g_string_append(kwstr, ", ");
+
+			g_string_append(kwstr, kw);
+			}
+		string_list_free(keywords);
+		}
+
+	if (kwstr)
+		{
+		ret = kwstr->str;
+		g_string_free(kwstr, FALSE);
+		}
+
+	return ret;
+}
+
+gchar *image_osd_mkinfo(const gchar *str, FileData *fd, GHashTable *vars)
+{
+	gchar delim = '%', imp = '|', sep[] = " - ";
+	gchar *start, *end;
+	guint pos, prev;
+	gboolean want_separator = FALSE;
+	gchar *name, *data;
+	GString *new;
+	gchar *ret;
+
+	if (!str || !*str) return g_strdup("");
+
+	new = g_string_new(str);
+
+	prev = -1;
+
+	while (TRUE)
+		{
+		guint limit = 0;
+		gchar *trunc = NULL;
+		gchar *limpos = NULL;
+		gchar *extra = NULL;
+		gchar *extrapos = NULL;
+		gchar *p;
+
+		start = strchr(new->str + (prev + 1), delim);
+		if (!start)
+			break;
+		end = strchr(start+1, delim);
+		if (!end)
+			break;
+
+		/* Search for optionnal modifiers
+		 * %name:99:extra% -> name = "name", limit=99, extra = "extra"
+		 */
+		for (p = start + 1; p < end; p++)
+			{
+			if (p[0] == ':')
+				{
+				if (g_ascii_isdigit(p[1]) && !limpos)
+					{
+					limpos = p + 1;
+					if (!trunc) trunc = p;
+					}
+				else
+					{
+					extrapos = p + 1;
+					if (!trunc) trunc = p;
+					break;
+					}
+				}
+			}
+
+		if (limpos)
+			limit = (guint) atoi(limpos);
+
+		if (extrapos)
+			extra = g_strndup(extrapos, end - extrapos);
+
+		name = g_strndup(start+1, (trunc ? trunc : end)-start-1);
+		pos = start - new->str;
+		data = NULL;
+
+		if (strcmp(name, "keywords") == 0)
+			{
+			data = keywords_to_string(fd);
+			}
+		else if (strcmp(name, "comment") == 0)
+			{
+			data = metadata_read_string(fd, COMMENT_KEY, METADATA_PLAIN);
+			}
+		else if (strcmp(name, "imagecomment") == 0)
+			{
+			data = exif_get_image_comment(fd);
+			}
+		else if (strcmp(name, "rating") == 0)
+			{
+			data = metadata_read_string(fd, RATING_KEY, METADATA_PLAIN);
+			}
+#ifdef HAVE_LUA
+		else if (strncmp(name, "lua/", 4) == 0)
+			{
+			gchar *tmp;
+			tmp = strchr(name+4, '/');
+			if (!tmp)
+				break;
+			*tmp = '\0';
+			data = lua_callvalue(fd, name+4, tmp+1);
+			}
+#endif
+		else
+			{
+			data = g_strdup(g_hash_table_lookup(vars, name));
+			if (!data)
+				data = metadata_read_string(fd, name, METADATA_FORMATTED);
+			}
+
+		if (data && *data && limit > 0 && strlen(data) > limit + 3)
+			{
+			gchar *new_data = g_strdup_printf("%-*.*s...", limit, limit, data);
+			g_free(data);
+			data = new_data;
+			}
+
+		if (data)
+			{
+			/* Since we use pango markup to display, we need to escape here */
+			gchar *escaped = g_markup_escape_text(data, -1);
+			g_free(data);
+			data = escaped;
+			}
+
+		if (extra)
+			{
+			if (data && *data)
+				{
+				/* Display data between left and right parts of extra string
+				 * the data is expressed by a '*' character. A '*' may be escaped
+				 * by a \. You should escape all '*' characters, do not rely on the
+				 * current implementation which only replaces the first unescaped '*'.
+				 * If no "*" is present, the extra string is just appended to data string.
+				 * Pango mark up is accepted in left and right parts.
+				 * Any \n is replaced by a newline
+				 * Examples:
+				 * "<i>*</i>\n" -> data is displayed in italics ended with a newline
+				 * "\n" 	-> ended with newline
+				 * "ISO *"	-> prefix data with "ISO " (ie. "ISO 100")
+				 * "\**\*"	-> prefix data with a star, and append a star (ie. "*100*")
+				 * "\\*"	-> prefix data with an anti slash (ie "\100")
+				 * "Collection <b>*</b>\n" -> display data in bold prefixed by "Collection " and a newline is appended
+				 *
+				 * FIXME: using background / foreground colors lead to weird results.
+				 */
+				gchar *new_data;
+				gchar *left = NULL;
+				gchar *right = extra;
+				gchar *p;
+				guint len = strlen(extra);
+
+				/* Search for left and right parts and unescape characters */
+				for (p = extra; *p; p++, len--)
+					if (p[0] == '\\')
+						{
+						if (p[1] == 'n')
+							{
+							memmove(p+1, p+2, --len);
+							p[0] = '\n';
+							}
+						else if (p[1] != '\0')
+							memmove(p, p+1, len--); // includes \0
+						}
+					else if (p[0] == '*' && !left)
+						{
+						right = p + 1;
+						left = extra;
+						}
+
+				if (left) right[-1] = '\0';
+
+				new_data = g_strdup_printf("%s%s%s", left ? left : "", data, right);
+				g_free(data);
+				data = new_data;
+				}
+			g_free(extra);
+			}
+
+		g_string_erase(new, pos, end-start+1);
+		if (data && *data)
+			{
+			if (want_separator)
+				{
+				/* insert separator */
+				g_string_insert(new, pos, sep);
+				pos += strlen(sep);
+				want_separator = FALSE;
+				}
+
+			g_string_insert(new, pos, data);
+			pos += strlen(data);
+		}
+
+		if (pos-prev >= 1 && new->str[pos] == imp)
+			{
+			/* pipe character is replaced by a separator, delete it
+			 * and raise a flag if needed */
+			g_string_erase(new, pos--, 1);
+			want_separator |= (data && *data);
+			}
+
+		if (new->str[pos] == '\n') want_separator = FALSE;
+
+		prev = pos - 1;
+
+		g_free(name);
+		g_free(data);
+		}
+
+	/* search and destroy empty lines */
+	end = new->str;
+	while ((start = strchr(end, '\n')))
+		{
+		end = start;
+		while (*++(end) == '\n')
+			;
+		g_string_erase(new, start-new->str, end-start-1);
+		}
+
+	g_strchomp(new->str);
+
+	ret = new->str;
+	g_string_free(new, FALSE);
+
+	return ret;
+}
+
+void osd_template_insert(GHashTable *vars, gchar *keyword, gchar *value, OsdTemplateFlags flags)
+{
+	if (!value)
+		{
+		g_hash_table_insert(vars, keyword, g_strdup(""));
+		return;
+		}
+
+	if (flags & OSDT_NO_DUP)
+		{
+		g_hash_table_insert(vars, keyword, value);
+		return;
+		}
+	else
+		{
+		g_hash_table_insert(vars, keyword, g_strdup(value));
+		}
+
+	if (flags & OSDT_FREE) g_free((gpointer) value);
+}
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */