/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * Copyright (C) 2008 Banco do Brasil S.A. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "private.h" #include #include #include #include #include enum { PROP_ZERO, PROP_UI_STYLE, PROP_LOGFILE, NUM_PROPERTIES }; static GParamSpec * props[NUM_PROPERTIES]; static void startup(GApplication * application); static void activate(GApplication * application); static void finalize(GObject *object); #ifdef __APPLE__ G_DEFINE_TYPE(pw3270Application, pw3270Application, GTKOSX_TYPE_APPLICATION); #else G_DEFINE_TYPE(pw3270Application, pw3270Application, GTK_TYPE_APPLICATION); #endif // __APPLE__ static void get_property(GObject *object, guint prop_id, GValue *value, GParamSpec G_GNUC_UNUSED(*pspec)) { switch (prop_id) { case PROP_UI_STYLE: g_value_set_uint(value,pw3270_application_get_ui_style(G_APPLICATION(object))); break; case PROP_LOGFILE: g_value_set_string(value,pw3270_application_get_log_filename(G_APPLICATION(object))); break; default: g_assert_not_reached (); } } static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec G_GNUC_UNUSED(*pspec)) { switch (prop_id) { case PROP_UI_STYLE: pw3270_application_set_ui_style(G_APPLICATION(object),g_value_get_uint(value)); break; case PROP_LOGFILE: pw3270_application_set_log_filename(G_APPLICATION(object),g_value_get_string(value)); break; default: g_assert_not_reached (); } } static void window_added(GtkApplication *application, GtkWindow *window) { GTK_APPLICATION_CLASS(pw3270Application_parent_class)->window_added(application,window); void (*call)(GtkWindow *window); GSList * item; for(item = PW3270_APPLICATION(application)->plugins; item; item = g_slist_next(item)) { if(g_module_symbol((GModule *) item->data, "pw3270_plugin_window_added", (gpointer *) &call)) { call(window); } } } static void window_removed(GtkApplication *application, GtkWindow *window) { debug("%s(%p)",__FUNCTION__,window); void (*call)(GtkWindow *window); GSList * item; for(item = PW3270_APPLICATION(application)->plugins; item; item = g_slist_next(item)) { if(g_module_symbol((GModule *) item->data, "pw3270_plugin_window_removed", (gpointer *) &call)) { call(window); } } GTK_APPLICATION_CLASS(pw3270Application_parent_class)->window_removed(application,window); } static void g_log_to_lib3270(const gchar *log_domain,GLogLevelFlags G_GNUC_UNUSED(log_level),const gchar *message,gpointer G_GNUC_UNUSED(user_data)) { debug("%s",message); lib3270_write_log( NULL, log_domain ? log_domain : "gtk", "%s", message ); } static void pw3270Application_class_init(pw3270ApplicationClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); g_log_set_default_handler(g_log_to_lib3270,NULL); object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; { GtkApplicationClass *application_class = GTK_APPLICATION_CLASS(klass); application_class->window_added = window_added; application_class->window_removed = window_removed; } { GApplicationClass * application_class = G_APPLICATION_CLASS(klass); application_class->startup = startup; application_class->activate = activate; application_class->open = pw3270_application_open; } props[PROP_UI_STYLE] = g_param_spec_uint( "ui-style", _("UI Type"), _("The code of the User interface type"), PW3270_UI_STYLE_CLASSICAL, PW3270_UI_STYLE_AUTOMATIC, #ifdef _WIN32 PW3270_UI_STYLE_CLASSICAL, #else PW3270_UI_STYLE_AUTOMATIC, #endif // _WIN32 G_PARAM_READABLE|G_PARAM_WRITABLE ); props[PROP_LOGFILE] = g_param_spec_string( "logfile", _("Log name"), _("The full path of the default log file"), NULL, G_PARAM_READABLE|G_PARAM_WRITABLE ); g_object_class_install_properties(object_class, NUM_PROPERTIES, props); } #ifndef __APPLE__ static gboolean on_user_interface(const gchar G_GNUC_UNUSED(*option), const gchar *value, gpointer G_GNUC_UNUSED(dunno), GError **error) { g_autoptr(GSettings) app_settings = pw3270_application_settings_new(); if(!app_settings) { g_warning("Can't get application settings"); return FALSE; } g_autoptr(GSettings) win_settings = pw3270_application_window_settings_new(); if(!win_settings) { g_warning("Can't get window settings"); return FALSE; } if(!g_ascii_strcasecmp(value,"gnome")) { g_settings_set_uint(app_settings,"ui-style",PW3270_UI_STYLE_GNOME); g_settings_set_boolean(win_settings,"toolbar-visible",TRUE); g_settings_set_boolean(win_settings,"menubar-visible",FALSE); } else if(!g_ascii_strcasecmp(value,"classic")) { g_settings_set_uint(app_settings,"ui-style",PW3270_UI_STYLE_CLASSICAL); g_settings_set_boolean(win_settings,"toolbar-visible",TRUE); g_settings_set_boolean(win_settings,"menubar-visible",TRUE); } else if(!g_ascii_strcasecmp(value,"default")) { g_settings_set_uint(app_settings,"ui-style",PW3270_UI_STYLE_AUTOMATIC); } else { g_set_error( error, g_quark_from_static_string(G_STRINGIFY(PRODUCT_NAME)), EINVAL, _( "\"%s\" is not a valid user interface name" ), value ); } return TRUE; } #endif // __APPLE__ static gboolean on_logfile(const gchar G_GNUC_UNUSED(*option), const gchar *value, gpointer G_GNUC_UNUSED(dunno), GError G_GNUC_UNUSED(**error)) { pw3270_application_set_log_filename(g_application_get_default(),value); return TRUE; } static void pw3270Application_init(pw3270Application *app) { static GOptionEntry cmd_options[] = { #ifndef __APPLE__ { "user-interface", 'U', 0, G_OPTION_ARG_CALLBACK, &on_user_interface, N_( "Set the user-interface type" ), NULL }, #endif // __APPLE__ { "logfile", 'l', 0, G_OPTION_ARG_CALLBACK, &on_logfile, N_( "Set default log file name" ), NULL }, { NULL } }; g_application_add_main_option_entries(G_APPLICATION(app), cmd_options); app->settings = pw3270_application_settings_new(); #if defined(_WIN32) // // Setup windows UI // app->ui_style = PW3270_UI_STYLE_CLASSICAL; { // https://stackoverflow.com/questions/37035936/how-to-get-native-windows-decorations-on-gtk3-on-windows-7-and-msys2 int gtk_csd = g_settings_get_int(app->settings,"gtk-csd"); if(gtk_csd != -1) { g_autofree gchar * env = g_strdup_printf("GTK_CSD=%d",gtk_csd); putenv(env); } } if(app->settings) { g_object_ref_sink(G_OBJECT(app->settings)); g_settings_bind(app->settings, "ui-style", app, "ui-style", G_SETTINGS_BIND_DEFAULT); } { lib3270_autoptr(char) plugin_path = lib3270_build_data_filename("plugins",NULL); pw3270_load_plugins_from_path(app, plugin_path); } #elif defined(__APPLE__) // // Setup Apple UI // { lib3270_autoptr(char) plugin_path = lib3270_build_data_filename("plugins",NULL); pw3270_load_plugins_from_path(app, plugin_path); } #else // // Setup linux UI // app->ui_style = PW3270_UI_STYLE_AUTOMATIC; app->settings = pw3270_application_settings_new(); if(app->settings) { g_object_ref_sink(G_OBJECT(app->settings)); g_settings_bind(app->settings, "ui-style", app, "ui-style", G_SETTINGS_BIND_DEFAULT); } pw3270_load_plugins_from_path(app, G_STRINGIFY(LIBDIR) G_DIR_SEPARATOR_S G_STRINGIFY(PRODUCT_NAME) "-plugins"); #endif // _WIN32 /* FIX-ME: Move to other place. // Get plugins. { if(g_file_test(path,G_FILE_TEST_IS_DIR)) { g_message("Loading plugins from %s",path); GError * err = NULL; GDir * dir = g_dir_open(path,0,&err); if(dir) { const gchar *name; while((name = g_dir_read_name(dir)) != NULL) { g_autofree gchar *filename = g_build_filename(path,name,NULL); if(g_str_has_suffix(filename,G_MODULE_SUFFIX)) { g_message("Loading %s",filename); GModule *handle = g_module_open(filename,G_MODULE_BIND_LOCAL); if(handle) { app->plugins = g_slist_append(app->plugins,handle); } else { g_warning("Can't load %s: %s",filename,g_module_error()); } } } g_dir_close(dir); } if(err) { g_warning("Can't load plugins from %s: %s",path,err->message); g_error_free(err); } } } */ // Initialize plugins { GSList * item; void (*call)(GtkApplication *application); for(item = app->plugins; item; item = g_slist_next(item)) { if(g_module_symbol((GModule *) item->data, "pw3270_plugin_set_application", (gpointer *) &call)) { call(GTK_APPLICATION(app)); } } } } static void finalize(GObject *object) { pw3270Application * application = PW3270_APPLICATION(object); if(application->plugins) { #pragma GCC diagnostic push #ifdef _WIN32 #pragma GCC diagnostic ignored "-Wcast-function-type" #endif // _WIN32 g_slist_free_full(application->plugins,(GDestroyNotify) g_module_close); #pragma GCC diagnostic pop application->plugins = NULL; } if(application->settings) { g_object_unref(application->settings); application->settings = NULL; } lib3270_set_log_handler(NULL,NULL,NULL); if(application->logfile) { g_free(application->logfile); application->logfile = NULL; } g_list_free_full(application->keypads,g_object_unref); G_OBJECT_CLASS(pw3270Application_parent_class)->finalize(object); } GtkApplication * pw3270_application_new(const gchar *application_id, GApplicationFlags flags) { return g_object_new( PW3270_TYPE_APPLICATION, "application-id", application_id, "flags", flags, NULL); } void startup(GApplication *application) { size_t ix; pw3270Application * app = PW3270_APPLICATION(application); G_APPLICATION_CLASS(pw3270Application_parent_class)->startup(application); GSettings *settings = pw3270_application_get_settings(application); // // Common actions // GAction * actions[] = { pw3270_about_action_new(), pw3270_preferences_action_new(), pw3270_quit_action_new() }; for(ix = 0; ix < G_N_ELEMENTS(actions); ix++) { g_action_map_add_action(G_ACTION_MAP(application),actions[ix]); } // // Open session actions. // if(g_settings_get_boolean(settings,"allow-open-session-actions")) { g_action_map_add_action(G_ACTION_MAP(application),pw3270_open_session_action_new()); } // // New tab actions // if(g_settings_get_boolean(settings,"allow-new-tab-actions")) { GAction * new_tab_actions[] = { pw3270_open_tab_action_new(), pw3270_new_tab_action_new() }; for(ix = 0; ix < G_N_ELEMENTS(new_tab_actions); ix++) { g_action_map_add_action(G_ACTION_MAP(application),new_tab_actions[ix]); } } // // New window actions // if(g_settings_get_boolean(settings,"allow-new-window-actions")) { GAction * new_window_actions[] = { pw3270_open_window_action_new(), pw3270_new_window_action_new() }; for(ix = 0; ix < G_N_ELEMENTS(new_window_actions); ix++) { g_action_map_add_action(G_ACTION_MAP(application),new_window_actions[ix]); } } // // Setup application menus // GtkBuilder * builder; #ifdef DEBUG builder = gtk_builder_new_from_file("ui/application.xml"); #else { lib3270_autoptr(char) build_file = lib3270_build_data_filename("ui","application.xml",NULL); builder = gtk_builder_new_from_file(build_file); } #endif // DEBUG // // Load keypad models // { lib3270_autoptr(char) keypad_path = lib3270_build_data_filename("keypad",NULL); g_autoptr(GError) error = NULL; g_autoptr(GDir) dir = g_dir_open(keypad_path,0,&error); if(dir) { const gchar *name = g_dir_read_name(dir); while(!error && name) { g_autofree gchar * path = g_build_filename(keypad_path,name,NULL); app->keypads = pw3270_keypad_model_new_from_xml(app->keypads,path); name = g_dir_read_name(dir); } } if(error) { g_message("Can't read %s: %s",keypad_path,error->message); } } if(gtk_application_prefers_app_menu(GTK_APPLICATION(application))) gtk_application_set_app_menu(GTK_APPLICATION (application), G_MENU_MODEL(gtk_builder_get_object (builder, "app-menu"))); gtk_application_set_menubar(GTK_APPLICATION (application), G_MENU_MODEL(gtk_builder_get_object (builder, "menubar"))); pw3270_load_placeholders(application, builder); g_object_unref(builder); } void activate(GApplication *application) { GtkWidget * window = pw3270_application_window_new(GTK_APPLICATION(application),NULL); // Present the new window pw3270_window_set_current_page(window,0); gtk_window_present(GTK_WINDOW(window)); } void pw3270_application_set_ui_style(GApplication *app, PW3270_UI_STYLE type) { #ifndef __APPLE__ g_return_if_fail(PW3270_IS_APPLICATION(app)); pw3270Application * application = PW3270_APPLICATION(app); if(application->ui_style == type) return; application->ui_style = type; g_object_notify_by_pspec(G_OBJECT(app), props[PROP_UI_STYLE]); #endif // !__APPLE } PW3270_UI_STYLE pw3270_application_get_ui_style(GApplication *app) { #ifdef __APPLE__ return PW3270_UI_STYLE_GNOME; #else g_return_val_if_fail(PW3270_IS_APPLICATION(app),PW3270_UI_STYLE_CLASSICAL); return PW3270_APPLICATION(app)->ui_style; #endif // __APPLE__ } GSettings * pw3270_application_get_settings(GApplication *app) { g_return_val_if_fail(PW3270_IS_APPLICATION(app),NULL); return PW3270_APPLICATION(app)->settings; } GSList * pw3270_application_get_plugins(GApplication *app) { g_return_val_if_fail(PW3270_IS_APPLICATION(app),NULL); return PW3270_APPLICATION(app)->plugins; } void pw3270_application_plugin_foreach(GApplication *app, GFunc func, gpointer user_data) { g_return_if_fail(PW3270_IS_APPLICATION(app)); GSList * item; for(item = PW3270_APPLICATION(app)->plugins; item; item = g_slist_next(item)) { func(item->data,user_data); } } void pw3270_application_plugin_call(GApplication *app, const gchar *method, gpointer user_data) { g_return_if_fail(PW3270_IS_APPLICATION(app)); int (*call)(GtkWidget *); GSList * item; for(item = PW3270_APPLICATION(app)->plugins; item; item = g_slist_next(item)) { if(g_module_symbol((GModule *) item->data, method, (gpointer *) &call)) { call(user_data); } } } GList * pw3270_application_get_keypad_models(GApplication *app) { g_return_val_if_fail(PW3270_IS_APPLICATION(app),NULL); return PW3270_APPLICATION(app)->keypads; } static int loghandler(const H3270 G_GNUC_UNUSED(*hSession), pw3270Application *app, const char *module, int G_GNUC_UNUSED(code), const char *message) { if(!app->logfile) { return -1; } FILE *f = fopen(app->logfile,"a"); if(f) { time_t ltime = time(0); char timestamp[80]; #ifdef HAVE_LOCALTIME_R struct tm tm; strftime(timestamp, 79, "%x %X", localtime_r(<ime,&tm)); #else strftime(timestamp, 79, "%x %X", localtime(<ime)); #endif // HAVE_LOCALTIME_R fprintf(f,"%s %s\t%s\n",timestamp,module,message); fclose(f); } return 0; } const gchar * pw3270_application_get_log_filename(GApplication *app) { g_return_val_if_fail(PW3270_IS_APPLICATION(app),NULL); return PW3270_APPLICATION(app)->logfile; } void pw3270_application_set_log_filename(GApplication *app, const gchar *filename) { g_return_if_fail(PW3270_IS_APPLICATION(app)); pw3270Application * application = PW3270_APPLICATION(app); if(application->logfile) { g_free(application->logfile); application->logfile = NULL; } if(filename) { application->logfile = g_strdup(filename); lib3270_set_log_handler(NULL,(LIB3270_LOG_HANDLER) loghandler, app); } else { lib3270_set_log_handler(NULL,NULL,NULL); } }