aboutsummaryrefslogtreecommitdiff
path: root/fiv.c
diff options
context:
space:
mode:
Diffstat (limited to 'fiv.c')
-rw-r--r--fiv.c140
1 files changed, 113 insertions, 27 deletions
diff --git a/fiv.c b/fiv.c
index 449d113..26d48a8 100644
--- a/fiv.c
+++ b/fiv.c
@@ -36,6 +36,7 @@
#include "config.h"
#include "fiv-browser.h"
+#include "fiv-collection.h"
#include "fiv-io.h"
#include "fiv-sidebar.h"
#include "fiv-thumbnail.h"
@@ -59,6 +60,17 @@ exit_fatal(const char *format, ...)
exit(EXIT_FAILURE);
}
+static gchar **
+slist_to_strv(GSList *slist)
+{
+ gchar **strv = g_malloc0_n(g_slist_length(slist) + 1, sizeof *strv),
+ **p = strv;
+ for (GSList *link = slist; link; link = link->next)
+ *p++ = link->data;
+ g_slist_free(slist);
+ return strv;
+}
+
// --- Keyboard shortcuts ------------------------------------------------------
// Fuck XML, this can be easily represented in static structures.
// Though it would be nice if the accelerators could be customized.
@@ -707,7 +719,7 @@ load_directory_without_switching(const char *uri)
GError *error = NULL;
GFile *file = g_file_new_for_uri(g.directory);
if (fiv_io_model_open(g.model, file, &error)) {
- // Handled by the signal callback.
+ // This is handled by our ::files-changed callback.
} else if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
g_error_free(error);
} else {
@@ -829,6 +841,7 @@ create_open_dialog(void)
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL);
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
GtkFileFilter *filter = gtk_file_filter_new();
for (const char **p = fiv_io_supported_media_types; *p; p++)
@@ -858,13 +871,20 @@ on_open(void)
(void) gtk_file_chooser_set_current_folder_uri(
GTK_FILE_CHOOSER(dialog), g.directory);
- // The default is local-only, single item.
switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
- gchar *uri;
+ GSList *uri_list;
case GTK_RESPONSE_ACCEPT:
- uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
- open_image(uri);
- g_free(uri);
+ if (!(uri_list = gtk_file_chooser_get_uris(GTK_FILE_CHOOSER(dialog))))
+ break;
+
+ gchar **uris = slist_to_strv(uri_list);
+ if (g_strv_length(uris) == 1) {
+ open_image(uris[0]);
+ } else {
+ fiv_collection_reload(uris);
+ load_directory(FIV_COLLECTION_SCHEME ":/");
+ }
+ g_strfreev(uris);
break;
case GTK_RESPONSE_NONE:
dialog = NULL;
@@ -892,16 +912,61 @@ on_next(void)
}
}
+static gchar **
+build_spawn_argv(const char *uri)
+{
+ // Because we only pass URIs, there is no need to prepend "--" here.
+ GPtrArray *a = g_ptr_array_new();
+ g_ptr_array_add(a, g_strdup(PROJECT_NAME));
+
+ // Process-local VFS URIs need to be resolved to globally accessible URIs.
+ // It doesn't seem possible to reliably tell if a GFile is process-local,
+ // but our collection VFS is the only one to realistically cause problems.
+ if (!fiv_collection_uri_matches(uri)) {
+ g_ptr_array_add(a, g_strdup(uri));
+ goto out;
+ }
+
+ GFile *file = g_file_new_for_uri(uri);
+ GError *error = NULL;
+ GFileInfo *info =
+ g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI,
+ G_FILE_QUERY_INFO_NONE, NULL, &error);
+ g_object_unref(file);
+ if (!info) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ goto out;
+ }
+
+ const char *target_uri = g_file_info_get_attribute_string(
+ info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
+ if (target_uri) {
+ g_ptr_array_add(a, g_strdup(target_uri));
+ } else {
+ gsize len = 0;
+ GFile **files = fiv_collection_get_contents(&len);
+ for (gsize i = 0; i < len; i++)
+ g_ptr_array_add(a, g_file_get_uri(files[i]));
+ }
+ g_object_unref(info);
+
+out:
+ g_ptr_array_add(a, NULL);
+ return (gchar **) g_ptr_array_free(a, FALSE);
+}
+
static void
spawn_uri(const char *uri)
{
- char *argv[] = {PROJECT_NAME, (char *) uri, NULL};
+ gchar **argv = build_spawn_argv(uri);
GError *error = NULL;
if (!g_spawn_async(
NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error)) {
g_warning("%s", error->message);
g_error_free(error);
}
+ g_strfreev(argv);
}
static void
@@ -1005,8 +1070,13 @@ on_view_drag_data_received(G_GNUC_UNUSED GtkWidget *widget,
return;
}
- // TODO(p): Once we're able to open a virtual directory, open all of them.
- GFile *file = g_file_new_for_uri(uris[0]);
+ GFile *file = NULL;
+ if (g_strv_length(uris) == 1) {
+ file = g_file_new_for_uri(uris[0]);
+ } else {
+ fiv_collection_reload(uris);
+ file = g_file_new_for_uri(FIV_COLLECTION_SCHEME ":/");
+ }
open_any_file(file, FALSE);
g_object_unref(file);
gtk_drag_finish(context, TRUE, FALSE, time);
@@ -1854,10 +1924,12 @@ static const char stylesheet[] = "@define-color fiv-tile @content_view_bg; \
.fiv-information label { padding: 0 4px; }";
static void
-output_thumbnail(const char *path_arg, gboolean extract, const char *size_arg)
+output_thumbnail(gchar **uris, gboolean extract, const char *size_arg)
{
- if (!path_arg)
- exit_fatal("no path given");
+ if (!uris)
+ exit_fatal("No path given");
+ if (uris[1])
+ exit_fatal("Only one thumbnail at a time may be produced");
FivThumbnailSize size = FIV_THUMBNAIL_SIZE_COUNT;
if (size_arg) {
@@ -1875,7 +1947,7 @@ output_thumbnail(const char *path_arg, gboolean extract, const char *size_arg)
#endif // G_OS_WIN32
GError *error = NULL;
- GFile *file = g_file_new_for_commandline_arg(path_arg);
+ GFile *file = g_file_new_for_uri(uris[0]);
cairo_surface_t *surface = NULL;
if (extract && (surface = fiv_thumbnail_extract(file, size, &error)))
fiv_io_serialize_to_stdout(surface, FIV_IO_SERIALIZE_LOW_QUALITY);
@@ -1898,10 +1970,10 @@ main(int argc, char *argv[])
{
gboolean show_version = FALSE, show_supported_media_types = FALSE,
invalidate_cache = FALSE, browse = FALSE, extract_thumbnail = FALSE;
- gchar **path_args = NULL, *thumbnail_size = NULL;
+ gchar **args = NULL, *thumbnail_size = NULL;
const GOptionEntry options[] = {
- {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &path_args,
- NULL, "[FILE | DIRECTORY | URI]"},
+ {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args,
+ NULL, "[PATH | URI]..."},
{"list-supported-media-types", 0, G_OPTION_FLAG_IN_MAIN,
G_OPTION_ARG_NONE, &show_supported_media_types,
"Output supported media types and exit", NULL},
@@ -1941,19 +2013,22 @@ main(int argc, char *argv[])
if (!initialized)
exit_fatal("%s", error->message);
- // NOTE: Firefox and Eye of GNOME both interpret multiple arguments
- // in a special way. This is problematic, because one-element lists
- // are unrepresentable.
- // TODO(p): Require a command line switch, load a virtual folder.
- // We may want or need to create a custom GVfs.
- // TODO(p): Complain to the user if there's more than one argument.
- // Best show the help message, if we can figure that out.
- const gchar *path_arg = path_args ? path_args[0] : NULL;
+ // Normalize all arguments to URIs.
+ for (gsize i = 0; args && args[i]; i++) {
+ GFile *resolved = g_file_new_for_commandline_arg(args[i]);
+ g_free(args[i]);
+ args[i] = g_file_get_uri(resolved);
+ g_object_unref(resolved);
+ }
if (extract_thumbnail || thumbnail_size) {
- output_thumbnail(path_arg, extract_thumbnail, thumbnail_size);
+ output_thumbnail(args, extract_thumbnail, thumbnail_size);
return 0;
}
+ // It doesn't make much sense to have command line arguments able to
+ // resolve to the VFS they may end up being contained within.
+ fiv_collection_register();
+
g.model = g_object_new(FIV_TYPE_IO_MODEL, NULL);
g_signal_connect(g.model, "files-changed",
G_CALLBACK(on_model_files_changed), NULL);
@@ -2088,11 +2163,22 @@ main(int argc, char *argv[])
// XXX: The widget wants to read the display's profile. The realize is ugly.
gtk_widget_realize(g.view);
+ // XXX: We follow the behaviour of Firefox and Eye of GNOME, which both
+ // interpret multiple command line arguments differently, as a collection.
+ // However, single-element collections are unrepresentable this way.
+ // Should we allow multiple targets only in a special new mode?
g.files = g_ptr_array_new_full(0, g_free);
- if (path_arg) {
- GFile *file = g_file_new_for_commandline_arg(path_arg);
+ if (args) {
+ const gchar *target = *args;
+ if (args[1]) {
+ fiv_collection_reload(args);
+ target = FIV_COLLECTION_SCHEME ":/";
+ }
+
+ GFile *file = g_file_new_for_uri(target);
open_any_file(file, browse);
g_object_unref(file);
+ g_strfreev(args);
}
if (!g.directory) {
GFile *file = g_file_new_for_path(".");