diff options
Diffstat (limited to 'gedit/gedit-app.c')
-rw-r--r-- | gedit/gedit-app.c | 1700 |
1 files changed, 1700 insertions, 0 deletions
diff --git a/gedit/gedit-app.c b/gedit/gedit-app.c new file mode 100644 index 0000000..4dc8c29 --- /dev/null +++ b/gedit/gedit-app.c @@ -0,0 +1,1700 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "gedit-app.h" +#include "gedit-app-private.h" + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#include <glib/gi18n.h> +#include <libpeas/peas-extension-set.h> +#include <tepl/tepl.h> + +#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", "<Primary>N"); + add_accelerator (GTK_APPLICATION (application), "app.quit", "<Primary>Q"); + add_accelerator (GTK_APPLICATION (application), "app.help", "F1"); + add_accelerator (GTK_APPLICATION (application), "app.shortcuts", "<Primary>question"); + + add_accelerator (GTK_APPLICATION (application), "win.hamburger-menu", "F10"); + add_accelerator (GTK_APPLICATION (application), "win.open", "<Primary>O"); + add_accelerator (GTK_APPLICATION (application), "win.save", "<Primary>S"); + add_accelerator (GTK_APPLICATION (application), "win.save-as", "<Primary><Shift>S"); + add_accelerator (GTK_APPLICATION (application), "win.save-all", "<Primary><Shift>L"); + add_accelerator (GTK_APPLICATION (application), "win.new-tab", "<Primary>T"); + add_accelerator (GTK_APPLICATION (application), "win.reopen-closed-tab", "<Primary><Shift>T"); + add_accelerator (GTK_APPLICATION (application), "win.close", "<Primary>W"); + add_accelerator (GTK_APPLICATION (application), "win.close-all", "<Primary><Shift>W"); + add_accelerator (GTK_APPLICATION (application), "win.print", "<Primary>P"); + add_accelerator (GTK_APPLICATION (application), "win.find", "<Primary>F"); + add_accelerator (GTK_APPLICATION (application), "win.find-next", "<Primary>G"); + add_accelerator (GTK_APPLICATION (application), "win.find-prev", "<Primary><Shift>G"); + add_accelerator (GTK_APPLICATION (application), "win.replace", "<Primary>H"); + add_accelerator (GTK_APPLICATION (application), "win.clear-highlight", "<Primary><Shift>K"); + add_accelerator (GTK_APPLICATION (application), "win.goto-line", "<Primary>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", "<Primary>F9"); + add_accelerator (GTK_APPLICATION (application), "win.fullscreen", "F11"); + add_accelerator (GTK_APPLICATION (application), "win.new-tab-group", "<Primary><Alt>N"); + add_accelerator (GTK_APPLICATION (application), "win.previous-tab-group", "<Primary><Shift><Alt>Page_Up"); + add_accelerator (GTK_APPLICATION (application), "win.next-tab-group", "<Primary><Shift><Alt>Page_Down"); + add_accelerator (GTK_APPLICATION (application), "win.previous-document", "<Primary><Alt>Page_Up"); + add_accelerator (GTK_APPLICATION (application), "win.next-document", "<Primary><Alt>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: */ |