//
// 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;
}