diff options
| author | Mattias Andrée <m@maandree.se> | 2026-10-11 23:01:38 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-10-11 23:29:28 +0200 |
| commit | 94659550993f454e12e4bcb8cf93a5ff258bc08c (patch) | |
| tree | f8e1cfe9fbcdec6f09b6cc18c761684dc34505d2 /ui.c | |
| parent | First commit (diff) | |
| download | gbild-94659550993f454e12e4bcb8cf93a5ff258bc08c.tar.gz gbild-94659550993f454e12e4bcb8cf93a5ff258bc08c.tar.bz2 gbild-94659550993f454e12e4bcb8cf93a5ff258bc08c.tar.xz | |
Second commit
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to '')
| -rw-r--r-- | ui.c | 307 |
1 files changed, 307 insertions, 0 deletions
@@ -0,0 +1,307 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +#define LIST_MENU(TOPLEVEL, SEPARATOR, ACTION, TOGGLE, D)\ + TOPLEVEL(0, "", "File", D)\ + ACTION(0, 0u, "", "Open", GTK_STOCK_OPEN, "<control>0", "Ctrl+0", open_action, D)\ + ACTION(0, 1u, "", "Reload", GTK_STOCK_REFRESH, "F5", "F5", reload_action, D)\ + SEPARATOR(0, D)\ + ACTION(0, 1u, "", "Print", GTK_STOCK_PRINT, "<control>P", "Ctrl+P", print_action, D)\ + SEPARATOR(0, D)\ + ACTION(0, 1u, "Proper", "ties", GTK_STOCK_PROPERTIES, "<alt>Return", "Alt+Return", properties_action, D)\ + SEPARATOR(0, D)\ + ACTION(0, 0u, "", "Close", GTK_STOCK_CLOSE, "<control>W", "Ctrl+W", close_action, D)\ + TOPLEVEL(0, "", "View", D)\ + TOGGLE(1, 1u, "F", "ullscreen", GTK_STOCK_FULLSCREEN, "F11", "F11", fullscreen_action, GROUP_NONE, 0, D)\ + SEPARATOR(1, D)\ + ACTION(0, 1u, "Re", "set transformations", "document-revert", "<alt>R", "Alt+R", reset_transformations_action, D)\ + ACTION(1, 1u, "Rotate anti", "clockwise", "image-rotate-left", "<shift>R", "Shift+R", rotate_anticlockwise_action, D)\ + ACTION(0, 1u, "R", "otate 180°", "image-rotate", "<control>space", "Ctrl+Space", rotate_180_degrees_action, D)\ + ACTION(1, 1u, "", "Rotate clockwise", "image-rotate-right", "R", "R", rotate_clockwise_action, D)\ + ACTION(1, 1u, "", "Mirror horizontally", "object-flip-horizontal", "H", "H", flop_action, D)\ + ACTION(1, 1u, "Mirror ", "vertically", "object-flip-vertical", "V", "V", flip_action, D)\ + ACTION(1, 1u, "", "Transpose", "object-transpose", "T", "T", transpose_action, D)\ + ACTION(1, 1u, "", "Antitranspose", "object-transpose-mirrored", "<shift>T", "Shift+T", antitranspose_action, D)\ + SEPARATOR(1, D)\ + TOGGLE(1, 1u, "", "Invert colours", "filters-invert", "<control>I", "Ctrl+I", invert_action, GROUP_NONE, 0, D)\ + SEPARATOR(1, D)\ + ACTION(1, 1u, "", "Zoom in", "zoom-in", "plus", "+", zoom_in_action, D)\ + ACTION(1, 1u, "Zoom ", "out", "zoom-out", "minus", "-", zoom_out_action, D)\ + TOGGLE(1, 1u, "", "Normal size", "zoom-original", "0", "0", normal_size_action, GROUP_ZOOM, 0, D)\ + TOGGLE(1, 1u, "Fit to ", "width", "zoom-fit-width", "<control>H", "Ctrl+H", width_fit_action, GROUP_ZOOM, 0, D)\ + TOGGLE(1, 1u, "Fit to ", "height", "zoom-fit-height", "<control>V", "Ctrl+V", height_fit_action, GROUP_ZOOM, 0, D)\ + TOGGLE(1, 1u, "", "Fit to window", "zoom-fit-page", "<control><shift>F", "Ctrl+Shift+F", window_fit_action, GROUP_ZOOM, 0, D)\ + TOGGLE(1, 1u, "", "Best fit", "zoom-fit-best", "<control>F", "Ctrl+F", best_fit_action, GROUP_ZOOM, 1, D)\ + TOPLEVEL(1, "", "Go", D)\ + ACTION(1, 2u, "", "Previous image", GTK_STOCK_GO_BACK, "<control>Left", "Ctrl+Left", previous_action, D)\ + ACTION(1, 2u, "", "Next image", GTK_STOCK_GO_FORWARD, "<control>Right", "Ctrl+Right", next_action, D)\ + SEPARATOR(0, D)\ + ACTION(1, 2u, "", "First image", GTK_STOCK_GOTO_FIRST, "<control>Home", "Ctrl+Home", first_action, D)\ + ACTION(1, 2u, "", "Last image", GTK_STOCK_GOTO_LAST, "<control>End", "Ctrl+End", last_action,) + +GtkWidget *window; +GtkWidget *display_area; +GtkWidget *scroll; +GtkToggleAction *fullscreen; + +static GtkWidget *statusbar; +static GtkWidget *menubar; +static GtkWidget *toolbar; +static GtkActionGroup *actions; + +#define X_SKIP(...) +#define X_TOPLEVEL(T, A, B, ...) {A B, NULL, A "_" B, NULL, NULL, NULL} __VA_ARGS__ +#define X_ACTION(T, N, A, B, ICON, ACCEL, ATEXT, F, ...) {A B, ICON, A "_" B, ACCEL, NULL, G_CALLBACK(&F)} __VA_ARGS__ +#define X_TOGGLE(T, N, A, B, ICON, ACCEL, ATEXT, F, G, ACTIVE, ...) {A B, ICON, A "_" B, ACCEL, NULL, G_CALLBACK(&F), ACTIVE} __VA_ARGS__ + +static GtkActionEntry entries[] = { + LIST_MENU(X_TOPLEVEL, X_SKIP, X_ACTION, X_SKIP, COMMA) +}; + +static GtkToggleActionEntry toggle_entries[] = { + LIST_MENU(X_SKIP, X_SKIP, X_SKIP, X_TOGGLE, COMMA) +}; + +#undef X_SKIP +#undef X_TOPLEVEL +#undef X_ACTION +#undef X_TOGGLE + +static GtkAction * +get_action(const char *name) +{ + return gtk_action_group_get_action(actions, name); +} + +static GtkWidget * +append_toplevel_menu(int add_separator_to_toolbar, const char *name) +{ + GtkWidget *item, *menu; + + if (add_separator_to_toolbar) + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1); + + item = gtk_action_create_menu_item(get_action(name)); + menu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu); + gtk_menu_shell_append(GTK_MENU_SHELL(menubar), item); + return gtk_menu_item_get_submenu(GTK_MENU_ITEM(item)); +} + +static void +append_separator(int add_to_toolbar, GtkWidget *menu) +{ + if (add_to_toolbar) + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1); + if (menu) + gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new()); +} + +static void +append_action(int add_to_toolbar, GtkWidget *menu, const char *name, const char *tooltip) +{ + GtkWidget *item; + if (add_to_toolbar) { + item = gtk_action_create_tool_item(get_action(name)); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(item), -1); + if (tooltip) + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(item), tooltip); + } + if (menu) { + item = gtk_action_create_menu_item(get_action(name)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } +} + +static void +create_menu_and_toolbar(void) +{ + GtkWidget *menu; + + menubar = gtk_menu_bar_new(); + toolbar = gtk_toolbar_new(); + +#define X_TOPLEVEL(T, A, B, D) menu = append_toplevel_menu(T, A B) D +#define X_SEPARTOR(T, D) append_separator(T, menu) D +#define X_ACTION(T, N, A, B, ICON, ACCEL, ACCEL_TEXT, F, D) append_action(T, menu, A B, A B " (" ACCEL_TEXT ")") D +#define X_TOGGLE(T, N, A, B, ICON, ACCEL, ACCEL_TEXT, F, G, ACTIVE, D) append_action(T, menu, A B, A B " (" ACCEL_TEXT ")") D + + LIST_MENU(X_TOPLEVEL, X_SEPARTOR, X_ACTION, X_TOGGLE, ;); + +#undef X_TOPLEVEL +#undef X_SEPARTOR +#undef X_ACTION +#undef X_TOGGLE +} + +static void +connect_accelerators(GtkAccelGroup *accelerators) +{ + GList *item, *list; + + list = gtk_action_group_list_actions(actions); + for (item = list; item; item = item->next) { + gtk_action_set_accel_group(item->data, accelerators); + gtk_action_connect_accelerator(item->data); + } + g_list_free(list); +} + +static int +window_state_changed(GtkWidget *widget, GdkEventWindowState *event, void *user) +{ + (void) widget; + (void) user; + gtk_toggle_action_set_active(fullscreen, !!(event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)); + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) + g_idle_add(&recalculate_zoom_mode, NULL); + return 0; +} + +void +set_up_ui(void) +{ + GtkAccelGroup *accelerators; + GtkWidget *box; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), "gbild"); + gtk_window_set_icon_name(GTK_WINDOW(window), "image-viewer"); + gtk_window_set_default_size(GTK_WINDOW(window), 900, 700); + gtk_window_set_wmclass(GTK_WINDOW(window), "gbild", "gbild"); + + g_signal_connect(window, "destroy", G_CALLBACK(>k_main_quit), NULL); + g_signal_connect(window, "key-press-event", G_CALLBACK(&key_pressed), NULL); + g_signal_connect(window, "window-state-event", G_CALLBACK(&window_state_changed), NULL); + + actions = gtk_action_group_new("gbild"); + gtk_action_group_add_actions(actions, entries, ELEMSOF(entries), NULL); + gtk_action_group_add_toggle_actions(actions, toggle_entries, ELEMSOF(toggle_entries), NULL); + fullscreen = GTK_TOGGLE_ACTION(gtk_action_group_get_action(actions, "Fullscreen")); + accelerators = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(window), accelerators); + connect_accelerators(accelerators); + create_menu_and_toolbar(); + + box = gtk_vbox_new(0, 0); + gtk_container_add(GTK_CONTAINER(window), box); + gtk_box_pack_start(GTK_BOX(box), menubar, 0, 0, 0u); + gtk_box_pack_start(GTK_BOX(box), toolbar, 0, 0, 0u); + display_area = gtk_drawing_area_new(); + gtk_widget_set_app_paintable(display_area, 1); + gtk_widget_set_double_buffered(display_area, 0); + scroll = logical_scroll_pane_new(); + logical_scroll_pane_set_child(LOGICAL_SCROLL_PANE(scroll), display_area); + gtk_widget_add_events(scroll, GDK_SCROLL_MASK); + g_signal_connect(scroll, "scroll-event", G_CALLBACK(&scroll_event), NULL); + gtk_box_pack_start(GTK_BOX(box), scroll, 1, 1, 0u); + statusbar = gtk_statusbar_new(); + gtk_box_pack_end(GTK_BOX(box), statusbar, 0, 0, 0u); + + gtk_widget_add_events(display_area, GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK); + g_signal_connect(display_area, "expose-event", G_CALLBACK(&expose_image), NULL); +} + +void +set_actions_sensitive(void) +{ +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#define X_SKIP(...) +#define X_ACTION(T, N, A, B, ICON, ACCEL, ATEXT, F, ...)\ + if (N) gtk_action_set_sensitive(get_action(A B), nfiles >= N) __VA_ARGS__ +#define X_TOGGLE(T, N, A, B, ICON, ACCEL, ATEXT, F, GROUP, ACTIVE, ...)\ + if (N) gtk_action_set_sensitive(get_action(A B), nfiles >= N) __VA_ARGS__ + + LIST_MENU(X_SKIP, X_SKIP, X_ACTION, X_TOGGLE, ;); + +#undef X_SKIP +#undef X_ACTION +#undef X_TOGGLE + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +} + +void +clear_toggles(enum toggle_group group, GtkToggleAction *except) +{ + static int clearing_toggles = 0; + + GtkToggleAction *action; + + if (clearing_toggles) + return; + + clearing_toggles = 1; + +#define X_SKIP(...) +#define X_TOGGLE(T, N, A, B, ICON, ACCEL, ATEXT, F, GROUP, ACTIVE, ...)\ + do {\ + if (GROUP == group) {\ + action = GTK_TOGGLE_ACTION(get_action(A B));\ + if (action != except)\ + gtk_toggle_action_set_active(action, 0);\ + }\ + } while (0) __VA_ARGS__ + + LIST_MENU(X_SKIP, X_SKIP, X_SKIP, X_TOGGLE, ;); + +#undef X_SKIP +#undef X_TOGGLE + + clearing_toggles = 0; +} + +void +set_fullscreen_mode(int active) +{ + gtk_widget_set_visible(menubar, !active); + gtk_widget_set_visible(toolbar, !active); + gtk_widget_set_visible(statusbar, !active); + logical_scroll_pane_set_hscrollbar_visible(LOGICAL_SCROLL_PANE(scroll), !active); + logical_scroll_pane_set_vscrollbar_visible(LOGICAL_SCROLL_PANE(scroll), !active); + + if (active) + gtk_window_fullscreen(GTK_WINDOW(window)); + else + gtk_window_unfullscreen(GTK_WINDOW(window)); +} + +int +activate_fullscreen(void *user) +{ + (void) user; + if (!gtk_toggle_action_get_active(fullscreen)) + gtk_toggle_action_set_active(fullscreen, 1); + return 0; +} + +int +deactivate_fullscreen(void *user) +{ + (void) user; + if (gtk_toggle_action_get_active(fullscreen)) + gtk_toggle_action_set_active(fullscreen, 0); + return 0; +} + +void +set_statusbar_message(const char *message) +{ + gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 0u); + if (message) + gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0u, message); +} + +void +free_ui(void) +{ + g_object_unref(actions); +} |
