/* * Gem-graph OpenGL experiments * * Desc: User interface functions * * Copyright (C) 2023 Arthur Menges * Copyright (C) 2023 Adrien Bourmault * * This file is part of Gem-graph. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include "../../include/base.h" #include "../../include/graphics.h" #include "../../include/parsing.h" #include "../../include/ui.h" void on_about_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { static const char *authors[] = { "Adrien Bourmault (neox@a-lec.org)", "Jean Sirmai (jean@a-lec.org)", "Arthur Menges (arthur.menges@a-lec.org)", NULL}; GemGraphClientApplication *self = user_data; GtkWindow *window = NULL; g_assert (GEM_GRAPH_CLIENT_IS_APPLICATION(self)); window = gtk_application_get_active_window(GTK_APPLICATION (self)); gtk_show_about_dialog(window, "program-name", "Gem-graph", "logo-icon-name", "application-x-executable", "authors", authors, "version", "0.1.0", "copyright", "Copyright © 2023 Libre en Communs", NULL); } void on_quit_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); g_application_quit(G_APPLICATION(self)); } void on_preferences_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); ui_send_internal_notification("Not implemented !"); } void on_togglesidebar_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); ui_toggle_sidebar(); } void on_editmode_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); ui_set_stack(EDIT_MODE); } void on_runmode_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); ui_set_stack(RUN_MODE); } void on_presentmode_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); ui_set_stack(PRESENTATION_MODE); } void on_openfile_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); // Create a new file selection dialog, using the "open" mode GtkFileChooserNative *native = gtk_file_chooser_native_new("Open File", GTK_WINDOW(self), GTK_FILE_CHOOSER_ACTION_OPEN, "_Open", "_Cancel"); // Connect the "response" signal of the file selection dialog; // this signal is emitted when the user selects a file, or when // they cancel the operation g_signal_connect (native, "response", G_CALLBACK(on_openfile_response), self); // Present the dialog to the user gtk_native_dialog_show (GTK_NATIVE_DIALOG (native)); } void on_closefile_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); model_shutdown(); ui_disable_action("closefile"); ui_disable_action("savefile"); ui_disable_action("runmode"); ui_disable_action("editmode"); ui_disable_action("presentmode"); ui_disable_action("togglesidebar"); ui_set_stack(HOME_MODE); } void on_savefile_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); } void on_toast_close_action(GSimpleAction *action, GVariant *parameter, gpointer user_data) { GemGraphClientApplication *self = user_data; g_assert(GEM_GRAPH_CLIENT_IS_APPLICATION(self)); ui_close_internal_notification(); } /* -------------------------------------------------------------------------- */ /* * Graphical/view related events */ void on_axis_value_change(GtkAdjustment *adjustment, gpointer data) { GtkWidget *slider = gtk_widget_get_parent(GTK_WIDGET(data)); GtkWidget *container_widget = gtk_widget_get_parent(GTK_WIDGET(slider)); const gchar *label_text = gtk_label_get_label(GTK_LABEL(data)); // THANKS ASCIIIII/Unicode/Whateverrr ! int axis = label_text[0] - 'X'; g_assert(axis >= 0 && axis < N_AXIS); /* Update the rotation angle */ ui_update_axis_stack(container_widget, axis, gtk_adjustment_get_value(adjustment)); /* Update the contents of the GL drawing area */ } gboolean on_glarea_render(GtkGLArea *area, GdkGLContext *context) { // Check if the widget is a glarea if(gtk_gl_area_get_error(area) != NULL) { ui_send_internal_notification("An OpenGL error occured !"); return false; } if (ui_render_stack(gtk_widget_get_parent(GTK_WIDGET(area))) == false) { ui_send_internal_notification( "Failed to render corresponding graphic stack !"); return false; } return true; } /* We need to set up our state when we realize the GtkGLArea widget */ void on_glarea_realize(GtkWidget *widget) { GError *internal_error = NULL; // Make the GL context current to be able to call the GL API gtk_gl_area_make_current(GTK_GL_AREA(widget)); // Check if the widget is a glarea if(gtk_gl_area_get_error(GTK_GL_AREA(widget)) != NULL) { ui_send_internal_notification("An OpenGL error occured !"); return; } // Link graphical stack to widget if (ui_init_graphic_stack(gtk_widget_get_parent(widget), internal_error) == false) { ui_send_internal_notification( "Failed to link the graphic stack to widgets !"); return; } gtk_gl_area_set_auto_render(GTK_GL_AREA(widget), true); } /* We should tear down the state when unrealizing */ void on_glarea_unrealize(GtkWidget *widget) { GError *internal_error = NULL; // Make the GL context current to be able to call the GL API gtk_gl_area_make_current(GTK_GL_AREA(widget)); // Check if the widget is a glarea if(gtk_gl_area_get_error(GTK_GL_AREA(widget)) != NULL) { ui_send_internal_notification("An OpenGL error occured !"); return; } // Destroy graphic stack if (ui_shutdown_graphic_stack(gtk_widget_get_parent(widget), internal_error) == false) { ui_send_internal_notification( "Failed to shutdown the graphic stack !"); return; } } void on_close_window(GtkWidget *widget) { ui_shutdown_all_graphic_stacks(); ui_clean_stack_index(); } /* -------------------------------------------------------------------------- */ /* * Responses */ void on_openfile_response(GtkNativeDialog *native, int response, GemGraphClientWindow *self) { g_autoptr(GFile) file; GtkFileChooser *chooser; if (response == GTK_RESPONSE_ACCEPT) { chooser = GTK_FILE_CHOOSER(native); file = gtk_file_chooser_get_file(chooser); // Load asynchroneously not to block control flow g_file_load_contents_async (file, NULL, (GAsyncReadyCallback)ui_model_loading, self); } g_object_unref(native); } void ui_model_loading(GObject *source_object, GAsyncResult *result, GemGraphClientWindow *self) { GFile *file = G_FILE(source_object); char *content = NULL; size_t length = 0; GError *error = NULL; char *basename = g_file_get_basename(file); // Gives you the contents of the file as a byte array, or // set the error argument g_file_load_contents_finish(file, result, &content, &length, NULL, &error); // In case of error, print a warning to the standard error output if (error != NULL) { g_printerr("Unable to open “%s”: %s\n", g_file_peek_path(file), error->message); ui_send_internal_notification("Unable to open file !"); g_free(error); g_free(basename); g_free(content); return; } if (model_init(content, length, basename) == false) { ui_send_internal_notification("Error while loading the model"); g_free(basename); g_free(content); return; } ui_enable_action("closefile"); ui_enable_action("savefile"); ui_enable_action("runmode"); ui_enable_action("editmode"); ui_enable_action("presentmode"); ui_enable_action("togglesidebar"); ui_set_stack(RUN_MODE); g_free(basename); g_free(content); }