Add a file information dialog based on ExifTool
Right now, it isn't very pleasing to use.
This commit is contained in:
parent
24f9d21ca7
commit
9899a26635
|
@ -5,8 +5,7 @@ fastiv
|
||||||
raw photos, HEIC, AVIF, WebP, SVG, X11 cursors and TIFF, or whatever gdk-pixbuf
|
raw photos, HEIC, AVIF, WebP, SVG, X11 cursors and TIFF, or whatever gdk-pixbuf
|
||||||
loads.
|
loads.
|
||||||
|
|
||||||
It still has some road to go, but it's already become quite usable,
|
Its development status can be summarized as '`beta`'.
|
||||||
and it has received basic polishing.
|
|
||||||
|
|
||||||
Non-goals
|
Non-goals
|
||||||
---------
|
---------
|
||||||
|
@ -27,7 +26,7 @@ Build dependencies: Meson, pkg-config +
|
||||||
Runtime dependencies: gtk+-3.0, glib>=2.64, pixman-1, shared-mime-info,
|
Runtime dependencies: gtk+-3.0, glib>=2.64, pixman-1, shared-mime-info,
|
||||||
spng>=0.7.0, libturbojpeg +
|
spng>=0.7.0, libturbojpeg +
|
||||||
Optional dependencies: LibRaw, librsvg-2.0, xcursor, libwebp, libheif, libtiff,
|
Optional dependencies: LibRaw, librsvg-2.0, xcursor, libwebp, libheif, libtiff,
|
||||||
gdk-pixbuf-2.0
|
gdk-pixbuf-2.0, ExifTool
|
||||||
|
|
||||||
$ git clone --recursive https://git.janouch.name/p/fastiv.git
|
$ git clone --recursive https://git.janouch.name/p/fastiv.git
|
||||||
$ meson builddir
|
$ meson builddir
|
||||||
|
|
6
fastiv.c
6
fastiv.c
|
@ -147,6 +147,7 @@ static struct key_group help_keys_view[] = {
|
||||||
{"<control>p", "Print..."},
|
{"<control>p", "Print..."},
|
||||||
{"<control>s", "Save page as..."},
|
{"<control>s", "Save page as..."},
|
||||||
{"<control><shift>s", "Save frame as..."},
|
{"<control><shift>s", "Save frame as..."},
|
||||||
|
{"<alt>Return", "Show file information"},
|
||||||
{}
|
{}
|
||||||
}},
|
}},
|
||||||
{}
|
{}
|
||||||
|
@ -203,7 +204,6 @@ make_key_window(void)
|
||||||
|
|
||||||
// TODO(p): See if it's possible to give separators room to shrink
|
// TODO(p): See if it's possible to give separators room to shrink
|
||||||
// by some minor amount of pixels, margin-wise.
|
// by some minor amount of pixels, margin-wise.
|
||||||
// TODO(p): Implement commented-out actions.
|
|
||||||
#define B make_toolbar_button
|
#define B make_toolbar_button
|
||||||
#define T make_toolbar_toggle
|
#define T make_toolbar_toggle
|
||||||
#define TOOLBAR(XX) \
|
#define TOOLBAR(XX) \
|
||||||
|
@ -234,7 +234,7 @@ make_key_window(void)
|
||||||
/* XX(COLOR, B("preferences-color-symbolic", "Color management")) */ \
|
/* XX(COLOR, B("preferences-color-symbolic", "Color management")) */ \
|
||||||
XX(SAVE, B("document-save-as-symbolic", "Save as...")) \
|
XX(SAVE, B("document-save-as-symbolic", "Save as...")) \
|
||||||
XX(PRINT, B("document-print-symbolic", "Print...")) \
|
XX(PRINT, B("document-print-symbolic", "Print...")) \
|
||||||
/* XX(INFO, B("info-symbolic", "Information")) */ \
|
XX(INFO, B("info-symbolic", "Information")) \
|
||||||
XX(S5, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)) \
|
XX(S5, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)) \
|
||||||
XX(LEFT, B("object-rotate-left-symbolic", "Rotate left")) \
|
XX(LEFT, B("object-rotate-left-symbolic", "Rotate left")) \
|
||||||
XX(MIRROR, B("object-flip-horizontal-symbolic", "Mirror")) \
|
XX(MIRROR, B("object-flip-horizontal-symbolic", "Mirror")) \
|
||||||
|
@ -948,6 +948,7 @@ on_view_actions_changed(void)
|
||||||
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_CHECKERBOARD], has_image);
|
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_CHECKERBOARD], has_image);
|
||||||
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_SAVE], has_image);
|
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_SAVE], has_image);
|
||||||
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_PRINT], has_image);
|
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_PRINT], has_image);
|
||||||
|
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_INFO], has_image);
|
||||||
|
|
||||||
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_LEFT], has_image);
|
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_LEFT], has_image);
|
||||||
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_MIRROR], has_image);
|
gtk_widget_set_sensitive(g.toolbar[TOOLBAR_MIRROR], has_image);
|
||||||
|
@ -1092,6 +1093,7 @@ make_view_toolbar(void)
|
||||||
toolbar_toggler(TOOLBAR_CHECKERBOARD, "checkerboard");
|
toolbar_toggler(TOOLBAR_CHECKERBOARD, "checkerboard");
|
||||||
toolbar_command(TOOLBAR_PRINT, FIV_VIEW_COMMAND_PRINT);
|
toolbar_command(TOOLBAR_PRINT, FIV_VIEW_COMMAND_PRINT);
|
||||||
toolbar_command(TOOLBAR_SAVE, FIV_VIEW_COMMAND_SAVE_PAGE);
|
toolbar_command(TOOLBAR_SAVE, FIV_VIEW_COMMAND_SAVE_PAGE);
|
||||||
|
toolbar_command(TOOLBAR_INFO, FIV_VIEW_COMMAND_INFO);
|
||||||
toolbar_command(TOOLBAR_LEFT, FIV_VIEW_COMMAND_ROTATE_LEFT);
|
toolbar_command(TOOLBAR_LEFT, FIV_VIEW_COMMAND_ROTATE_LEFT);
|
||||||
toolbar_command(TOOLBAR_MIRROR, FIV_VIEW_COMMAND_MIRROR);
|
toolbar_command(TOOLBAR_MIRROR, FIV_VIEW_COMMAND_MIRROR);
|
||||||
toolbar_command(TOOLBAR_RIGHT, FIV_VIEW_COMMAND_ROTATE_RIGHT);
|
toolbar_command(TOOLBAR_RIGHT, FIV_VIEW_COMMAND_ROTATE_RIGHT);
|
||||||
|
|
78
fiv-view.c
78
fiv-view.c
|
@ -786,6 +786,76 @@ save_as(FivView *self, gboolean frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
info(FivView *self)
|
||||||
|
{
|
||||||
|
// TODO(p): Add a fallback to internal capabilities.
|
||||||
|
// The simplest is to specify the filename and the resolution.
|
||||||
|
GtkWindow *window = get_toplevel(GTK_WIDGET(self));
|
||||||
|
int flags = G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE;
|
||||||
|
|
||||||
|
GError *error = NULL;
|
||||||
|
GSubprocess *subprocess = g_subprocess_new(flags, &error, "exiftool",
|
||||||
|
"-tab", "-groupNames", "-duplicates", "-extractEmbedded", "--binary",
|
||||||
|
"-quiet", "--", self->path, NULL);
|
||||||
|
if (error) {
|
||||||
|
show_error_dialog(window, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar *out = NULL, *err = NULL;
|
||||||
|
if (!g_subprocess_communicate_utf8(
|
||||||
|
subprocess, NULL, NULL, &out, &err, &error)) {
|
||||||
|
show_error_dialog(window, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *dialog = gtk_widget_new(GTK_TYPE_DIALOG,
|
||||||
|
"use-header-bar", TRUE,
|
||||||
|
"title", "Information",
|
||||||
|
"transient-for", window,
|
||||||
|
"destroy-with-parent", TRUE, NULL);
|
||||||
|
|
||||||
|
GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
||||||
|
if (*err) {
|
||||||
|
GtkWidget *info = gtk_info_bar_new();
|
||||||
|
GtkInfoBar *info_bar = GTK_INFO_BAR(info);
|
||||||
|
gtk_info_bar_set_message_type(info_bar, GTK_MESSAGE_WARNING);
|
||||||
|
|
||||||
|
GtkWidget *info_area = gtk_info_bar_get_content_area(info_bar);
|
||||||
|
GtkWidget *label = gtk_label_new(g_strstrip(err));
|
||||||
|
gtk_container_add(GTK_CONTAINER(info_area), label);
|
||||||
|
|
||||||
|
gtk_container_add(GTK_CONTAINER(content_area), info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(p): Parse as GROUP\tTAG\tVALUE, do s/_/ /g in the group name,
|
||||||
|
// put the data in a GtkTreeModel/GtkTreeView.
|
||||||
|
GtkWidget *label = gtk_label_new(out);
|
||||||
|
gtk_label_set_selectable(GTK_LABEL(label), TRUE);
|
||||||
|
gtk_label_set_xalign(GTK_LABEL(label), 0.);
|
||||||
|
gtk_label_set_yalign(GTK_LABEL(label), 0.);
|
||||||
|
gtk_widget_set_hexpand(label, TRUE);
|
||||||
|
gtk_widget_set_vexpand(label, TRUE);
|
||||||
|
|
||||||
|
GtkWidget *scroller = gtk_scrolled_window_new(NULL, NULL);
|
||||||
|
gtk_scrolled_window_set_max_content_width(
|
||||||
|
GTK_SCROLLED_WINDOW(scroller), 800);
|
||||||
|
gtk_scrolled_window_set_max_content_height(
|
||||||
|
GTK_SCROLLED_WINDOW(scroller), 800);
|
||||||
|
gtk_scrolled_window_set_propagate_natural_width(
|
||||||
|
GTK_SCROLLED_WINDOW(scroller), TRUE);
|
||||||
|
gtk_scrolled_window_set_propagate_natural_height(
|
||||||
|
GTK_SCROLLED_WINDOW(scroller), TRUE);
|
||||||
|
gtk_container_add(GTK_CONTAINER(scroller), label);
|
||||||
|
gtk_container_add(GTK_CONTAINER(content_area), scroller);
|
||||||
|
|
||||||
|
g_free(out);
|
||||||
|
g_free(err);
|
||||||
|
g_object_unref(subprocess);
|
||||||
|
gtk_widget_show_all(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static inline gboolean
|
static inline gboolean
|
||||||
|
@ -823,6 +893,12 @@ fiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)
|
||||||
return save_as(self, TRUE);
|
return save_as(self, TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (state == GDK_MOD1_MASK) {
|
||||||
|
switch (event->keyval) {
|
||||||
|
case GDK_KEY_Return:
|
||||||
|
return command(self, FIV_VIEW_COMMAND_INFO);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (state != 0)
|
if (state != 0)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
@ -1043,6 +1119,8 @@ fiv_view_command(FivView *self, FivViewCommand command)
|
||||||
print(self);
|
print(self);
|
||||||
break; case FIV_VIEW_COMMAND_SAVE_PAGE:
|
break; case FIV_VIEW_COMMAND_SAVE_PAGE:
|
||||||
save_as(self, FALSE);
|
save_as(self, FALSE);
|
||||||
|
break; case FIV_VIEW_COMMAND_INFO:
|
||||||
|
info(self);
|
||||||
|
|
||||||
break; case FIV_VIEW_COMMAND_ZOOM_IN:
|
break; case FIV_VIEW_COMMAND_ZOOM_IN:
|
||||||
set_scale(self, self->scale * SCALE_STEP);
|
set_scale(self, self->scale * SCALE_STEP);
|
||||||
|
|
|
@ -45,6 +45,7 @@ typedef enum _FivViewCommand {
|
||||||
FIV_VIEW_COMMAND_TOGGLE_CHECKERBOARD,
|
FIV_VIEW_COMMAND_TOGGLE_CHECKERBOARD,
|
||||||
FIV_VIEW_COMMAND_PRINT,
|
FIV_VIEW_COMMAND_PRINT,
|
||||||
FIV_VIEW_COMMAND_SAVE_PAGE,
|
FIV_VIEW_COMMAND_SAVE_PAGE,
|
||||||
|
FIV_VIEW_COMMAND_INFO,
|
||||||
|
|
||||||
FIV_VIEW_COMMAND_ZOOM_IN,
|
FIV_VIEW_COMMAND_ZOOM_IN,
|
||||||
FIV_VIEW_COMMAND_ZOOM_OUT,
|
FIV_VIEW_COMMAND_ZOOM_OUT,
|
||||||
|
|
Loading…
Reference in New Issue