// // fastiv.c: fast image viewer // // Copyright (c) 2021, Přemysl Eric Janouch // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // #include #include #include #include #include #include #include "config.h" #include "fastiv-view.h" // --- Utilities --------------------------------------------------------------- static void exit_fatal(const gchar *format, ...) G_GNUC_PRINTF(1, 2); static void exit_fatal(const gchar *format, ...) { va_list ap; va_start(ap, format); gchar *format_nl = g_strdup_printf("%s\n", format); vfprintf(stderr, format_nl, ap); free(format_nl); va_end(ap); exit(EXIT_FAILURE); } // --- Main -------------------------------------------------------------------- struct { // TODO(p): Directory filenames, index within the list, ... GtkWidget *window; GtkWidget *view; } g; static void open(const gchar *path) { GError *error = NULL; if (!fastiv_view_open(FASTIV_VIEW(g.view), path, &error)) { GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(g.window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", error->message); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return; } gtk_window_set_title(GTK_WINDOW(g.window), path); } static void on_open(void) { GtkWidget *dialog = gtk_file_chooser_dialog_new("Open file", GTK_WINDOW(g.window), GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { gchar *path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); open(path); g_free(path); } gtk_widget_destroy(dialog); } int main(int argc, char *argv[]) { if (!setlocale(LC_CTYPE, "")) exit_fatal("cannot set locale"); gboolean show_version = FALSE; gchar **files = NULL; const GOptionEntry options[] = { {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &files, NULL, "[FILE | DIRECTORY]..."}, {"version", 'V', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &show_version, "output version information and exit", NULL}, {}, }; GError *error = NULL; if (!gtk_init_with_args(&argc, &argv, " - fast image viewer", options, NULL, &error)) exit_fatal("%s", error->message); if (show_version) { printf(PROJECT_NAME " " PROJECT_VERSION "\n"); return 0; } gtk_window_set_default_icon_name(PROJECT_NAME); g.view = g_object_new(FASTIV_TYPE_VIEW, NULL); g.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(g.window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_container_add(GTK_CONTAINER(g.window), g.view); // The references to closures are initially floating and sunk on connect. GtkAccelGroup *accel_group = gtk_accel_group_new(); gtk_accel_group_connect(accel_group, GDK_KEY_Escape, 0, 0, g_cclosure_new(G_CALLBACK(gtk_main_quit), NULL, NULL)); gtk_accel_group_connect(accel_group, GDK_KEY_q, 0, 0, g_cclosure_new(G_CALLBACK(gtk_main_quit), NULL, NULL)); gtk_accel_group_connect(accel_group, GDK_KEY_o, 0, 0, g_cclosure_new(G_CALLBACK(on_open), NULL, NULL)); gtk_accel_group_connect(accel_group, GDK_KEY_o, GDK_CONTROL_MASK, 0, g_cclosure_new(G_CALLBACK(on_open), NULL, NULL)); gtk_window_add_accel_group(GTK_WINDOW(g.window), accel_group); // TODO(p): Load directory entries, store in `g`. // - Only when there's just one filename. // - stat() it if it's a dictionary or a filename; // can just blindly try to readdir(), followed by dirname and retry // - But how do we filter these? We don't want to have non-images // on the list. // - When there are multiple, just take them verbatim as a list. // - Not entirely sure about how much sense this makes, // we might want to rather open several windows, or simply fork, // or even disallow this completely. gsize files_len = g_strv_length(files); if (files_len) { GDir *dir = NULL; if ((dir = g_dir_open(files[0], 0, NULL))) { const gchar *name = NULL; while ((name = g_dir_read_name(dir))) ; g_dir_close(dir); } open(files[0]); } gtk_widget_show_all(g.window); gtk_main(); return 0; }