/* * gedit-app.c * This file is part of gedit * * Copyright (C) 2005-2006 - Paolo Maggi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License * along with this program; if not, see . */ #include "config.h" #include "gedit-app.h" #include "gedit-app-private.h" #include #include #include #include #include #include #include "gedit-commands-private.h" #include "gedit-notebook.h" #include "gedit-debug.h" #include "gedit-utils.h" #include "gedit-enum-types.h" #include "gedit-dirs.h" #include "gedit-settings.h" #include "gedit-app-activatable.h" #include "gedit-plugins-engine.h" #include "gedit-commands.h" #include "gedit-preferences-dialog.h" #include "gedit-tab.h" #define GEDIT_PAGE_SETUP_FILE "gedit-page-setup" #define GEDIT_PRINT_SETTINGS_FILE "gedit-print-settings" typedef struct { GeditPluginsEngine *engine; GtkCssProvider *theme_provider; GtkPageSetup *page_setup; GtkPrintSettings *print_settings; GSettings *window_settings; GMenuModel *hamburger_menu; GMenuModel *notebook_menu; GMenuModel *tab_width_menu; PeasExtensionSet *extensions; /* command line parsing */ gboolean new_window; gboolean new_document; const GtkSourceEncoding *encoding; GInputStream *stdin_stream; GSList *file_list; gint line_position; gint column_position; GApplicationCommandLine *command_line; } GeditAppPrivate; static const GOptionEntry options[] = { /* Version */ { "version", 'V', 0, G_OPTION_ARG_NONE, NULL, N_("Show the application’s version"), NULL }, /* List available encodings */ { "list-encodings", '\0', 0, G_OPTION_ARG_NONE, NULL, N_("Display list of possible values for the encoding option"), NULL }, /* Encoding */ { "encoding", '\0', 0, G_OPTION_ARG_STRING, NULL, N_("Set the character encoding to be used to open the files listed on the command line"), N_("ENCODING") }, /* Open a new window */ { "new-window", '\0', 0, G_OPTION_ARG_NONE, NULL, N_("Create a new top-level window in an existing instance of gedit"), NULL }, /* Create a new empty document */ { "new-document", '\0', 0, G_OPTION_ARG_NONE, NULL, N_("Create a new document in an existing instance of gedit"), NULL }, /* Wait for closing documents */ { "wait", 'w', 0, G_OPTION_ARG_NONE, NULL, N_("Open files and block process until files are closed"), NULL }, /* New instance */ { "standalone", 's', 0, G_OPTION_ARG_NONE, NULL, N_("Run gedit in standalone mode"), NULL }, /* collects file arguments */ { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, NULL, N_("[FILE…] [+LINE[:COLUMN]]") }, {NULL} }; G_DEFINE_TYPE_WITH_PRIVATE (GeditApp, gedit_app, GTK_TYPE_APPLICATION) static void gedit_app_dispose (GObject *object) { GeditAppPrivate *priv; priv = gedit_app_get_instance_private (GEDIT_APP (object)); g_clear_object (&priv->window_settings); g_clear_object (&priv->page_setup); g_clear_object (&priv->print_settings); /* Note that unreffing the extensions will automatically remove * all extensions which in turn will deactivate the extension */ g_clear_object (&priv->extensions); g_clear_object (&priv->engine); if (priv->theme_provider != NULL) { gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (priv->theme_provider)); g_clear_object (&priv->theme_provider); } g_clear_object (&priv->hamburger_menu); g_clear_object (&priv->notebook_menu); g_clear_object (&priv->tab_width_menu); G_OBJECT_CLASS (gedit_app_parent_class)->dispose (object); } static gchar * gedit_app_get_help_uri_impl (GeditApp *app, const gchar *name_of_user_manual, const gchar *link_id_within_user_manual) { if (link_id_within_user_manual != NULL) { return g_strdup_printf ("help:%s/%s", name_of_user_manual, link_id_within_user_manual); } else { return g_strdup_printf ("help:%s", name_of_user_manual); } } static gboolean gedit_app_show_help_impl (GeditApp *app, GtkWindow *parent_window, const gchar *name_of_user_manual, const gchar *link_id_within_user_manual) { gchar *uri; gboolean ret; GError *error = NULL; if (name_of_user_manual == NULL) { name_of_user_manual = "gedit"; } uri = GEDIT_APP_GET_CLASS (app)->get_help_uri (app, name_of_user_manual, link_id_within_user_manual); ret = gtk_show_uri_on_window (parent_window, uri, GDK_CURRENT_TIME, &error); g_free (uri); if (error != NULL) { GtkWidget *dialog; dialog = gtk_message_dialog_new (parent_window, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("There was an error displaying the help.")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message); g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (gtk_widget_destroy), NULL); gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); gtk_widget_show (dialog); g_error_free (error); } return ret; } static void gedit_app_set_window_title_impl (GeditApp *app, GeditWindow *window, const gchar *title) { gtk_window_set_title (GTK_WINDOW (window), title); } static GeditWindow * get_active_window (GtkApplication *app) { GList *windows; GList *l; /* Gtk documentation says the window list is always in MRU order. */ windows = gtk_application_get_windows (app); for (l = windows; l != NULL; l = l->next) { GtkWindow *window = l->data; if (GEDIT_IS_WINDOW (window)) { return GEDIT_WINDOW (window); } } return NULL; } static void set_command_line_wait (GeditApp *app, GeditTab *tab) { GeditAppPrivate *priv; priv = gedit_app_get_instance_private (app); g_object_set_data_full (G_OBJECT (tab), "GeditTabCommandLineWait", g_object_ref (priv->command_line), (GDestroyNotify)g_object_unref); } static void set_command_line_wait_doc (GeditDocument *doc, GeditApp *app) { GeditTab *tab = gedit_tab_get_from_document (doc); set_command_line_wait (app, tab); } static void open_files (GApplication *application, gboolean new_window, gboolean new_document, gint line_position, gint column_position, const GtkSourceEncoding *encoding, GInputStream *stdin_stream, GSList *file_list, GApplicationCommandLine *command_line) { GeditWindow *window = NULL; GeditTab *tab; gboolean doc_created = FALSE; if (!new_window) { window = get_active_window (GTK_APPLICATION (application)); } if (window == NULL) { gedit_debug_message (DEBUG_APP, "Create main window"); window = gedit_app_create_window (GEDIT_APP (application), NULL); gedit_debug_message (DEBUG_APP, "Show window"); gtk_widget_show (GTK_WIDGET (window)); } if (stdin_stream) { gedit_debug_message (DEBUG_APP, "Load stdin"); tab = gedit_window_create_tab_from_stream (window, stdin_stream, encoding, line_position, column_position, TRUE); doc_created = tab != NULL; if (doc_created && command_line) { set_command_line_wait (GEDIT_APP (application), tab); } g_input_stream_close (stdin_stream, NULL, NULL); } if (file_list != NULL) { GSList *loaded; gedit_debug_message (DEBUG_APP, "Load files"); loaded = _gedit_cmd_load_files_from_prompt (window, file_list, encoding, line_position, column_position); doc_created = doc_created || loaded != NULL; if (command_line) { g_slist_foreach (loaded, (GFunc)set_command_line_wait_doc, GEDIT_APP (application)); } g_slist_free (loaded); } if (!doc_created || new_document) { gedit_debug_message (DEBUG_APP, "Create tab"); tab = gedit_window_create_tab (window, TRUE); if (command_line) { set_command_line_wait (GEDIT_APP (application), tab); } } gtk_window_present (GTK_WINDOW (window)); } static void new_window_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) { GeditApp *app; GeditWindow *window; app = GEDIT_APP (user_data); window = gedit_app_create_window (app, NULL); gedit_debug_message (DEBUG_APP, "Show window"); gtk_widget_show (GTK_WIDGET (window)); gedit_debug_message (DEBUG_APP, "Create tab"); gedit_window_create_tab (window, TRUE); gtk_window_present (GTK_WINDOW (window)); } static void new_document_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) { GApplication *application = G_APPLICATION (user_data); open_files (application, FALSE, TRUE, 0, 0, NULL, NULL, NULL, NULL); } static void preferences_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) { GtkApplication *app; GeditWindow *window; app = GTK_APPLICATION (user_data); window = GEDIT_WINDOW (gtk_application_get_active_window (app)); gedit_show_preferences_dialog (window); } static void keyboard_shortcuts_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) { GtkApplication *app; GeditWindow *window; app = GTK_APPLICATION (user_data); window = GEDIT_WINDOW (gtk_application_get_active_window (app)); _gedit_cmd_help_keyboard_shortcuts (window); } static void help_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) { GtkApplication *app; GeditWindow *window; app = GTK_APPLICATION (user_data); window = GEDIT_WINDOW (gtk_application_get_active_window (app)); _gedit_cmd_help_contents (window); } static void about_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) { GtkApplication *app; GeditWindow *window; app = GTK_APPLICATION (user_data); window = GEDIT_WINDOW (gtk_application_get_active_window (app)); _gedit_cmd_help_about (window); } static void quit_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) { _gedit_cmd_file_quit (NULL, NULL, NULL); } static GActionEntry app_entries[] = { { "new-window", new_window_activated, NULL, NULL, NULL }, { "new-document", new_document_activated, NULL, NULL, NULL }, { "preferences", preferences_activated, NULL, NULL, NULL }, { "shortcuts", keyboard_shortcuts_activated, NULL, NULL, NULL }, { "help", help_activated, NULL, NULL, NULL }, { "about", about_activated, NULL, NULL, NULL }, { "quit", quit_activated, NULL, NULL, NULL } }; static void extension_added (PeasExtensionSet *extensions, PeasPluginInfo *info, PeasExtension *exten, GeditApp *app) { gedit_app_activatable_activate (GEDIT_APP_ACTIVATABLE (exten)); } static void extension_removed (PeasExtensionSet *extensions, PeasPluginInfo *info, PeasExtension *exten, GeditApp *app) { gedit_app_activatable_deactivate (GEDIT_APP_ACTIVATABLE (exten)); } static void load_accels (void) { gchar *filename; filename = g_build_filename (gedit_dirs_get_user_config_dir (), "accels", NULL); if (filename != NULL) { gedit_debug_message (DEBUG_APP, "Loading keybindings from %s\n", filename); gtk_accel_map_load (filename); g_free (filename); } } static GtkCssProvider * load_css_from_resource (const gchar *filename, gboolean required) { GError *error = NULL; GFile *css_file; GtkCssProvider *provider; gchar *resource_name; resource_name = g_strdup_printf ("resource:///org/gnome/gedit/css/%s", filename); css_file = g_file_new_for_uri (resource_name); g_free (resource_name); if (!required && !g_file_query_exists (css_file, NULL)) { g_object_unref (css_file); return NULL; } provider = gtk_css_provider_new (); if (gtk_css_provider_load_from_file (provider, css_file, &error)) { gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } else { g_warning ("Could not load css provider: %s", error->message); g_error_free (error); } g_object_unref (css_file); return provider; } static void theme_changed (GtkSettings *settings, GParamSpec *pspec, GeditApp *app) { GeditAppPrivate *priv; priv = gedit_app_get_instance_private (app); gchar *theme, *lc_theme, *theme_css; g_object_get (settings, "gtk-theme-name", &theme, NULL); lc_theme = g_ascii_strdown (theme, -1); g_free (theme); theme_css = g_strdup_printf ("gedit.%s.css", lc_theme); g_free (lc_theme); if (priv->theme_provider != NULL) { gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (priv->theme_provider)); g_clear_object (&priv->theme_provider); } priv->theme_provider = load_css_from_resource (theme_css, FALSE); g_free (theme_css); } static void setup_theme_extensions (GeditApp *app) { GtkSettings *settings; settings = gtk_settings_get_default (); g_signal_connect (settings, "notify::gtk-theme-name", G_CALLBACK (theme_changed), app); theme_changed (settings, NULL, app); } static GMenuModel * get_menu_model (GeditApp *app, const char *id) { GMenu *menu; menu = gtk_application_get_menu_by_id (GTK_APPLICATION (app), id); return menu ? G_MENU_MODEL (g_object_ref_sink (menu)) : NULL; } static void add_accelerator (GtkApplication *app, const gchar *action_name, const gchar *accel) { const gchar *vaccels[] = { accel, NULL }; gtk_application_set_accels_for_action (app, action_name, vaccels); } static gboolean show_menubar (void) { GtkSettings *settings = gtk_settings_get_default (); gboolean result; g_object_get (settings, "gtk-shell-shows-menubar", &result, NULL); return result; } static void init_tepl_settings (void) { GeditSettings *gedit_settings; GSettings *editor_settings; TeplSettings *tepl_settings; gedit_settings = _gedit_settings_get_singleton (); editor_settings = _gedit_settings_peek_editor_settings (gedit_settings); tepl_settings = tepl_settings_get_singleton (); tepl_settings_provide_font_settings (tepl_settings, editor_settings, GEDIT_SETTINGS_USE_DEFAULT_FONT, GEDIT_SETTINGS_EDITOR_FONT); } static void gedit_app_startup (GApplication *application) { GeditAppPrivate *priv; GtkCssProvider *css_provider; GtkSourceStyleSchemeManager *manager; priv = gedit_app_get_instance_private (GEDIT_APP (application)); G_APPLICATION_CLASS (gedit_app_parent_class)->startup (application); /* Setup debugging */ gedit_debug_init (); gedit_debug_message (DEBUG_APP, "Startup"); setup_theme_extensions (GEDIT_APP (application)); /* Load/init settings */ _gedit_settings_get_singleton (); priv->window_settings = g_settings_new ("org.gnome.gedit.state.window"); init_tepl_settings (); g_action_map_add_action_entries (G_ACTION_MAP (application), app_entries, G_N_ELEMENTS (app_entries), application); /* menus */ if (!show_menubar ()) { gtk_application_set_menubar (GTK_APPLICATION (application), NULL); priv->hamburger_menu = get_menu_model (GEDIT_APP (application), "hamburger-menu"); } priv->notebook_menu = get_menu_model (GEDIT_APP (application), "notebook-menu"); priv->tab_width_menu = get_menu_model (GEDIT_APP (application), "tab-width-menu"); /* Accelerators */ add_accelerator (GTK_APPLICATION (application), "app.new-window", "N"); add_accelerator (GTK_APPLICATION (application), "app.quit", "Q"); add_accelerator (GTK_APPLICATION (application), "app.help", "F1"); add_accelerator (GTK_APPLICATION (application), "app.shortcuts", "question"); add_accelerator (GTK_APPLICATION (application), "win.hamburger-menu", "F10"); add_accelerator (GTK_APPLICATION (application), "win.open", "O"); add_accelerator (GTK_APPLICATION (application), "win.save", "S"); add_accelerator (GTK_APPLICATION (application), "win.save-as", "S"); add_accelerator (GTK_APPLICATION (application), "win.save-all", "L"); add_accelerator (GTK_APPLICATION (application), "win.new-tab", "T"); add_accelerator (GTK_APPLICATION (application), "win.reopen-closed-tab", "T"); add_accelerator (GTK_APPLICATION (application), "win.close", "W"); add_accelerator (GTK_APPLICATION (application), "win.close-all", "W"); add_accelerator (GTK_APPLICATION (application), "win.print", "P"); add_accelerator (GTK_APPLICATION (application), "win.find", "F"); add_accelerator (GTK_APPLICATION (application), "win.find-next", "G"); add_accelerator (GTK_APPLICATION (application), "win.find-prev", "G"); add_accelerator (GTK_APPLICATION (application), "win.replace", "H"); add_accelerator (GTK_APPLICATION (application), "win.clear-highlight", "K"); add_accelerator (GTK_APPLICATION (application), "win.goto-line", "I"); add_accelerator (GTK_APPLICATION (application), "win.focus-active-view", "Escape"); add_accelerator (GTK_APPLICATION (application), "win.side-panel", "F9"); add_accelerator (GTK_APPLICATION (application), "win.bottom-panel", "F9"); add_accelerator (GTK_APPLICATION (application), "win.fullscreen", "F11"); add_accelerator (GTK_APPLICATION (application), "win.new-tab-group", "N"); add_accelerator (GTK_APPLICATION (application), "win.previous-tab-group", "Page_Up"); add_accelerator (GTK_APPLICATION (application), "win.next-tab-group", "Page_Down"); add_accelerator (GTK_APPLICATION (application), "win.previous-document", "Page_Up"); add_accelerator (GTK_APPLICATION (application), "win.next-document", "Page_Down"); load_accels (); /* Load custom css */ g_object_unref (load_css_from_resource ("gedit-style.css", TRUE)); css_provider = load_css_from_resource ("gedit-style-os.css", FALSE); g_clear_object (&css_provider); /* * We use the default gtksourceview style scheme manager so that plugins * can obtain it easily without a gedit specific api, but we need to * add our search path at startup before the manager is actually used. */ manager = gtk_source_style_scheme_manager_get_default (); gtk_source_style_scheme_manager_append_search_path (manager, gedit_dirs_get_user_styles_dir ()); priv->engine = gedit_plugins_engine_get_default (); priv->extensions = peas_extension_set_new (PEAS_ENGINE (priv->engine), GEDIT_TYPE_APP_ACTIVATABLE, "app", GEDIT_APP (application), NULL); g_signal_connect (priv->extensions, "extension-added", G_CALLBACK (extension_added), application); g_signal_connect (priv->extensions, "extension-removed", G_CALLBACK (extension_removed), application); peas_extension_set_foreach (priv->extensions, (PeasExtensionSetForeachFunc) extension_added, application); } static void gedit_app_activate (GApplication *application) { GeditAppPrivate *priv; priv = gedit_app_get_instance_private (GEDIT_APP (application)); open_files (application, priv->new_window, priv->new_document, priv->line_position, priv->column_position, priv->encoding, priv->stdin_stream, priv->file_list, priv->command_line); } static void clear_options (GeditApp *app) { GeditAppPrivate *priv; priv = gedit_app_get_instance_private (app); g_clear_object (&priv->stdin_stream); g_slist_free_full (priv->file_list, g_object_unref); priv->new_window = FALSE; priv->new_document = FALSE; priv->encoding = NULL; priv->file_list = NULL; priv->line_position = 0; priv->column_position = 0; priv->command_line = NULL; } static void get_line_column_position (const gchar *arg, gint *line, gint *column) { gchar **split; split = g_strsplit (arg, ":", 2); if (split != NULL) { if (split[0] != NULL) { *line = atoi (split[0]); } if (split[1] != NULL) { *column = atoi (split[1]); } } g_strfreev (split); } static gint gedit_app_command_line (GApplication *application, GApplicationCommandLine *cl) { GeditAppPrivate *priv; GVariantDict *options; const gchar *encoding_charset; const gchar **remaining_args; priv = gedit_app_get_instance_private (GEDIT_APP (application)); options = g_application_command_line_get_options_dict (cl); g_variant_dict_lookup (options, "new-window", "b", &priv->new_window); g_variant_dict_lookup (options, "new-document", "b", &priv->new_document); if (g_variant_dict_contains (options, "wait")) { priv->command_line = cl; } if (g_variant_dict_lookup (options, "encoding", "&s", &encoding_charset)) { priv->encoding = gtk_source_encoding_get_from_charset (encoding_charset); if (priv->encoding == NULL) { g_application_command_line_printerr (cl, _("%s: invalid encoding."), encoding_charset); } } /* Parse filenames */ if (g_variant_dict_lookup (options, G_OPTION_REMAINING, "^a&ay", &remaining_args)) { gint i; for (i = 0; remaining_args[i]; i++) { if (*remaining_args[i] == '+') { if (*(remaining_args[i] + 1) == '\0') { /* goto the last line of the document */ priv->line_position = G_MAXINT; priv->column_position = 0; } else { get_line_column_position (remaining_args[i] + 1, &priv->line_position, &priv->column_position); } } else if (*remaining_args[i] == '-' && *(remaining_args[i] + 1) == '\0') { priv->stdin_stream = g_application_command_line_get_stdin (cl); } else { GFile *file; file = g_application_command_line_create_file_for_arg (cl, remaining_args[i]); priv->file_list = g_slist_prepend (priv->file_list, file); } } priv->file_list = g_slist_reverse (priv->file_list); g_free (remaining_args); } g_application_activate (application); clear_options (GEDIT_APP (application)); return 0; } static void print_all_encodings (void) { GSList *all_encodings; GSList *l; all_encodings = gtk_source_encoding_get_all (); for (l = all_encodings; l != NULL; l = l->next) { const GtkSourceEncoding *encoding = l->data; g_print ("%s\n", gtk_source_encoding_get_charset (encoding)); } g_slist_free (all_encodings); } static gint gedit_app_handle_local_options (GApplication *application, GVariantDict *options) { if (g_variant_dict_contains (options, "version")) { g_print ("%s - Version %s\n", g_get_application_name (), VERSION); return 0; } if (g_variant_dict_contains (options, "list-encodings")) { print_all_encodings (); return 0; } if (g_variant_dict_contains (options, "standalone")) { GApplicationFlags old_flags; old_flags = g_application_get_flags (application); g_application_set_flags (application, old_flags | G_APPLICATION_NON_UNIQUE); } if (g_variant_dict_contains (options, "wait")) { GApplicationFlags old_flags; old_flags = g_application_get_flags (application); g_application_set_flags (application, old_flags | G_APPLICATION_IS_LAUNCHER); } return -1; } /* Note: when launched from command line we do not reach this method * since we manually handle the command line parameters in order to * parse +LINE:COL, stdin, etc. * However this method is called when open() is called via dbus, for * instance when double clicking on a file in nautilus */ static void gedit_app_open (GApplication *application, GFile **files, gint n_files, const gchar *hint) { gint i; GSList *file_list = NULL; for (i = 0; i < n_files; i++) { file_list = g_slist_prepend (file_list, files[i]); } file_list = g_slist_reverse (file_list); open_files (application, FALSE, FALSE, 0, 0, NULL, NULL, file_list, NULL); g_slist_free (file_list); } static gboolean ensure_user_config_dir (void) { const gchar *config_dir; gboolean ret = TRUE; gint res; config_dir = gedit_dirs_get_user_config_dir (); if (config_dir == NULL) { g_warning ("Could not get config directory\n"); return FALSE; } res = g_mkdir_with_parents (config_dir, 0755); if (res < 0) { g_warning ("Could not create config directory\n"); ret = FALSE; } return ret; } static void save_accels (void) { gchar *filename; filename = g_build_filename (gedit_dirs_get_user_config_dir (), "accels", NULL); if (filename != NULL) { gedit_debug_message (DEBUG_APP, "Saving keybindings in %s\n", filename); gtk_accel_map_save (filename); g_free (filename); } } static gchar * get_page_setup_file (void) { const gchar *config_dir; gchar *setup = NULL; config_dir = gedit_dirs_get_user_config_dir (); if (config_dir != NULL) { setup = g_build_filename (config_dir, GEDIT_PAGE_SETUP_FILE, NULL); } return setup; } static void save_page_setup (GeditApp *app) { GeditAppPrivate *priv; priv = gedit_app_get_instance_private (app); if (priv->page_setup != NULL) { gchar *filename; GError *error = NULL; filename = get_page_setup_file (); gtk_page_setup_to_file (priv->page_setup, filename, &error); if (error) { g_warning ("%s", error->message); g_error_free (error); } g_free (filename); } } static gchar * get_print_settings_file (void) { const gchar *config_dir; gchar *settings = NULL; config_dir = gedit_dirs_get_user_config_dir (); if (config_dir != NULL) { settings = g_build_filename (config_dir, GEDIT_PRINT_SETTINGS_FILE, NULL); } return settings; } static void save_print_settings (GeditApp *app) { GeditAppPrivate *priv; priv = gedit_app_get_instance_private (app); if (priv->print_settings != NULL) { gchar *filename; GError *error = NULL; filename = get_print_settings_file (); gtk_print_settings_to_file (priv->print_settings, filename, &error); if (error) { g_warning ("%s", error->message); g_error_free (error); } g_free (filename); } } static void gedit_app_shutdown (GApplication *app) { gedit_debug_message (DEBUG_APP, "Quitting\n"); /* Last window is gone... save some settings and exit */ ensure_user_config_dir (); save_accels (); save_page_setup (GEDIT_APP (app)); save_print_settings (GEDIT_APP (app)); G_APPLICATION_CLASS (gedit_app_parent_class)->shutdown (app); } static gboolean window_delete_event (GeditWindow *window, GdkEvent *event, GeditApp *app) { GeditWindowState ws; ws = gedit_window_get_state (window); if (ws & (GEDIT_WINDOW_STATE_SAVING | GEDIT_WINDOW_STATE_PRINTING)) { return TRUE; } _gedit_cmd_file_quit (NULL, NULL, window); /* Do not destroy the window */ return TRUE; } static GeditWindow * gedit_app_create_window_impl (GeditApp *app) { GeditWindow *window; window = g_object_new (GEDIT_TYPE_WINDOW, "application", app, NULL); gedit_debug_message (DEBUG_APP, "Window created"); g_signal_connect (window, "delete_event", G_CALLBACK (window_delete_event), app); return window; } static void gedit_app_class_init (GeditAppClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GApplicationClass *app_class = G_APPLICATION_CLASS (klass); object_class->dispose = gedit_app_dispose; app_class->startup = gedit_app_startup; app_class->activate = gedit_app_activate; app_class->command_line = gedit_app_command_line; app_class->handle_local_options = gedit_app_handle_local_options; app_class->open = gedit_app_open; app_class->shutdown = gedit_app_shutdown; klass->show_help = gedit_app_show_help_impl; klass->get_help_uri = gedit_app_get_help_uri_impl; klass->set_window_title = gedit_app_set_window_title_impl; klass->create_window = gedit_app_create_window_impl; } static void load_page_setup (GeditApp *app) { GeditAppPrivate *priv; gchar *filename; GError *error = NULL; priv = gedit_app_get_instance_private (app); g_return_if_fail (priv->page_setup == NULL); filename = get_page_setup_file (); priv->page_setup = gtk_page_setup_new_from_file (filename, &error); if (error) { /* Ignore file not found error */ if (error->domain != G_FILE_ERROR || error->code != G_FILE_ERROR_NOENT) { g_warning ("%s", error->message); } g_error_free (error); } g_free (filename); /* fall back to default settings */ if (priv->page_setup == NULL) { priv->page_setup = gtk_page_setup_new (); } } static void load_print_settings (GeditApp *app) { GeditAppPrivate *priv; gchar *filename; GError *error = NULL; priv = gedit_app_get_instance_private (app); g_return_if_fail (priv->print_settings == NULL); filename = get_print_settings_file (); priv->print_settings = gtk_print_settings_new_from_file (filename, &error); if (error != NULL) { /* - Ignore file not found error. * - Ignore empty file error, i.e. group not found. This happens * when we click on cancel in the print dialog, when using the * printing for the first time in gedit. */ if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) && !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { g_warning ("Load print settings error: %s", error->message); } g_error_free (error); } g_free (filename); /* fall back to default settings */ if (priv->print_settings == NULL) { priv->print_settings = gtk_print_settings_new (); } } static void gedit_app_init (GeditApp *app) { TeplApplication *tepl_app; g_set_application_name ("gedit"); gtk_window_set_default_icon_name ("org.gnome.gedit"); g_application_add_main_option_entries (G_APPLICATION (app), options); tepl_app = tepl_application_get_from_gtk_application (GTK_APPLICATION (app)); tepl_application_handle_metadata (tepl_app); } /** * gedit_app_create_window: * @app: the #GeditApp * @screen: (allow-none): * * Create a new #GeditWindow part of @app. * * Return value: (transfer none): the new #GeditWindow */ GeditWindow * gedit_app_create_window (GeditApp *app, GdkScreen *screen) { GeditAppPrivate *priv; GeditWindow *window; GdkWindowState state; gint w, h; gedit_debug (DEBUG_APP); priv = gedit_app_get_instance_private (app); window = GEDIT_APP_GET_CLASS (app)->create_window (app); if (screen != NULL) { gtk_window_set_screen (GTK_WINDOW (window), screen); } state = g_settings_get_int (priv->window_settings, GEDIT_SETTINGS_WINDOW_STATE); g_settings_get (priv->window_settings, GEDIT_SETTINGS_WINDOW_SIZE, "(ii)", &w, &h); gtk_window_set_default_size (GTK_WINDOW (window), w, h); if ((state & GDK_WINDOW_STATE_MAXIMIZED) != 0) { gtk_window_maximize (GTK_WINDOW (window)); } else { gtk_window_unmaximize (GTK_WINDOW (window)); } if ((state & GDK_WINDOW_STATE_STICKY ) != 0) { gtk_window_stick (GTK_WINDOW (window)); } else { gtk_window_unstick (GTK_WINDOW (window)); } return window; } /** * gedit_app_get_main_windows: * @app: the #GeditApp * * Returns all #GeditWindows currently open in #GeditApp. * This differs from gtk_application_get_windows() since it does not * include the preferences dialog and other auxiliary windows. * * Return value: (element-type Gedit.Window) (transfer container): * a newly allocated list of #GeditWindow objects */ GList * gedit_app_get_main_windows (GeditApp *app) { GList *res = NULL; GList *windows, *l; g_return_val_if_fail (GEDIT_IS_APP (app), NULL); windows = gtk_application_get_windows (GTK_APPLICATION (app)); for (l = windows; l != NULL; l = g_list_next (l)) { if (GEDIT_IS_WINDOW (l->data)) { res = g_list_prepend (res, l->data); } } return g_list_reverse (res); } /** * gedit_app_get_documents: * @app: the #GeditApp * * Returns all the documents currently open in #GeditApp. * * Return value: (element-type Gedit.Document) (transfer container): * a newly allocated list of #GeditDocument objects */ GList * gedit_app_get_documents (GeditApp *app) { GList *res = NULL; GList *windows, *l; g_return_val_if_fail (GEDIT_IS_APP (app), NULL); windows = gtk_application_get_windows (GTK_APPLICATION (app)); for (l = windows; l != NULL; l = g_list_next (l)) { if (GEDIT_IS_WINDOW (l->data)) { res = g_list_concat (res, gedit_window_get_documents (GEDIT_WINDOW (l->data))); } } return res; } /** * gedit_app_get_views: * @app: the #GeditApp * * Returns all the views currently present in #GeditApp. * * Return value: (element-type Gedit.View) (transfer container): * a newly allocated list of #GeditView objects */ GList * gedit_app_get_views (GeditApp *app) { GList *res = NULL; GList *windows, *l; g_return_val_if_fail (GEDIT_IS_APP (app), NULL); windows = gtk_application_get_windows (GTK_APPLICATION (app)); for (l = windows; l != NULL; l = g_list_next (l)) { if (GEDIT_IS_WINDOW (l->data)) { res = g_list_concat (res, gedit_window_get_views (GEDIT_WINDOW (l->data))); } } return res; } /** * gedit_app_show_help: * @app: a #GeditApp. * @parent_window: (nullable): the #GtkWindow where the request originates from. * @name_of_user_manual: (nullable): %NULL for gedit's user manual, otherwise * the name of another user manual (e.g., one from another application). * @link_id_within_user_manual: (nullable): a link ID within the user manual, or * %NULL to show the start page. * * To show the user manual. * * As a useful information to know, the gedit user documentation is currently * written in Mallard. As such, this functionality can easily be tested with * Yelp on Linux: * * With @name_of_user_manual and @link_id_within_user_manual both %NULL, it is * equivalent to: * * `$ yelp 'help:gedit'` * * With @link_id_within_user_manual set to `"gedit-replace"` (a Mallard page * id): * * `$ yelp 'help:gedit/gedit-replace'` * * It is also possible to refer to a section id within a page id, for example: * * `$ yelp 'help:gedit/gedit-spellcheck#dict'` * * Returns: whether the operation was successful. */ gboolean gedit_app_show_help (GeditApp *app, GtkWindow *parent_window, const gchar *name_of_user_manual, const gchar *link_id_within_user_manual) { g_return_val_if_fail (GEDIT_IS_APP (app), FALSE); g_return_val_if_fail (parent_window == NULL || GTK_IS_WINDOW (parent_window), FALSE); return GEDIT_APP_GET_CLASS (app)->show_help (app, parent_window, name_of_user_manual, link_id_within_user_manual); } void gedit_app_set_window_title (GeditApp *app, GeditWindow *window, const gchar *title) { g_return_if_fail (GEDIT_IS_APP (app)); g_return_if_fail (GEDIT_IS_WINDOW (window)); GEDIT_APP_GET_CLASS (app)->set_window_title (app, window, title); } gboolean gedit_app_process_window_event (GeditApp *app, GeditWindow *window, GdkEvent *event) { g_return_val_if_fail (GEDIT_IS_APP (app), FALSE); g_return_val_if_fail (GEDIT_IS_WINDOW (window), FALSE); if (GEDIT_APP_GET_CLASS (app)->process_window_event) { return GEDIT_APP_GET_CLASS (app)->process_window_event (app, window, event); } return FALSE; } static GMenuModel * find_extension_point_section (GMenuModel *model, const gchar *extension_point) { gint i, n_items; GMenuModel *section = NULL; n_items = g_menu_model_get_n_items (model); for (i = 0; i < n_items && !section; i++) { gchar *id = NULL; if (g_menu_model_get_item_attribute (model, i, "id", "s", &id) && strcmp (id, extension_point) == 0) { section = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION); } else { GMenuModel *subsection; GMenuModel *submenu; gint j, j_items; subsection = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION); if (subsection == NULL) { subsection = model; } j_items = g_menu_model_get_n_items (subsection); for (j = 0; j < j_items && !section; j++) { submenu = g_menu_model_get_item_link (subsection, j, G_MENU_LINK_SUBMENU); if (submenu) { section = find_extension_point_section (submenu, extension_point); } } } g_free (id); } return section; } /* Returns a copy */ GtkPageSetup * _gedit_app_get_default_page_setup (GeditApp *app) { GeditAppPrivate *priv; g_return_val_if_fail (GEDIT_IS_APP (app), NULL); priv = gedit_app_get_instance_private (app); if (priv->page_setup == NULL) { load_page_setup (app); } return gtk_page_setup_copy (priv->page_setup); } void _gedit_app_set_default_page_setup (GeditApp *app, GtkPageSetup *page_setup) { GeditAppPrivate *priv; g_return_if_fail (GEDIT_IS_APP (app)); g_return_if_fail (GTK_IS_PAGE_SETUP (page_setup)); priv = gedit_app_get_instance_private (app); g_set_object (&priv->page_setup, page_setup); } /* Returns a copy */ GtkPrintSettings * _gedit_app_get_default_print_settings (GeditApp *app) { GeditAppPrivate *priv; g_return_val_if_fail (GEDIT_IS_APP (app), NULL); priv = gedit_app_get_instance_private (app); if (priv->print_settings == NULL) { load_print_settings (app); } return gtk_print_settings_copy (priv->print_settings); } void _gedit_app_set_default_print_settings (GeditApp *app, GtkPrintSettings *settings) { GeditAppPrivate *priv; g_return_if_fail (GEDIT_IS_APP (app)); g_return_if_fail (GTK_IS_PRINT_SETTINGS (settings)); priv = gedit_app_get_instance_private (app); if (priv->print_settings != NULL) { g_object_unref (priv->print_settings); } priv->print_settings = g_object_ref (settings); } GMenuModel * _gedit_app_get_hamburger_menu (GeditApp *app) { GeditAppPrivate *priv; g_return_val_if_fail (GEDIT_IS_APP (app), NULL); priv = gedit_app_get_instance_private (app); return priv->hamburger_menu; } GMenuModel * _gedit_app_get_notebook_menu (GeditApp *app) { GeditAppPrivate *priv; g_return_val_if_fail (GEDIT_IS_APP (app), NULL); priv = gedit_app_get_instance_private (app); return priv->notebook_menu; } GMenuModel * _gedit_app_get_tab_width_menu (GeditApp *app) { GeditAppPrivate *priv; g_return_val_if_fail (GEDIT_IS_APP (app), NULL); priv = gedit_app_get_instance_private (app); return priv->tab_width_menu; } GeditMenuExtension * _gedit_app_extend_menu (GeditApp *app, const gchar *extension_point) { GeditAppPrivate *priv; GMenuModel *model; GMenuModel *section; g_return_val_if_fail (GEDIT_IS_APP (app), NULL); g_return_val_if_fail (extension_point != NULL, NULL); priv = gedit_app_get_instance_private (app); /* First look in the gear or window menu */ if (priv->hamburger_menu) { model = priv->hamburger_menu; } else { model = gtk_application_get_menubar (GTK_APPLICATION (app)); } section = find_extension_point_section (model, extension_point); /* otherwise look in the app menu */ if (section == NULL) { model = gtk_application_get_app_menu (GTK_APPLICATION (app)); if (model != NULL) { section = find_extension_point_section (model, extension_point); } } return section != NULL ? gedit_menu_extension_new (G_MENU (section)) : NULL; } /* ex:set ts=8 noet: */