1 /*
2  * xed-app.c
3  * This file is part of xed
4  *
5  * Copyright (C) 2005-2006 - Paolo Maggi
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 /*
24  * Modified by the xed Team, 2005. See the AUTHORS file for a
25  * list of people on the xed Team.
26  * See the ChangeLog files for a list of changes.
27  *
28  * $Id$
29  */
30 
31 #include <config.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 
36 #include <glib/gi18n.h>
37 #include <libpeas/peas-extension-set.h>
38 #include <gtksourceview/gtksource.h>
39 
40 #ifdef ENABLE_INTROSPECTION
41 #include <girepository.h>
42 #endif
43 
44 #include "xed-app.h"
45 #include "xed-commands.h"
46 #include "xed-notebook.h"
47 #include "xed-debug.h"
48 #include "xed-utils.h"
49 #include "xed-enum-types.h"
50 #include "xed-dirs.h"
51 #include "xed-app-activatable.h"
52 #include "xed-plugins-engine.h"
53 #include "xed-settings.h"
54 
55 #ifndef ENABLE_GVFS_METADATA
56 #include "xed-metadata-manager.h"
57 #define METADATA_FILE "xed-metadata.xml"
58 #endif
59 
60 #define XED_PAGE_SETUP_FILE     "xed-page-setup"
61 #define XED_PRINT_SETTINGS_FILE "xed-print-settings"
62 
63 /* Properties */
64 enum
65 {
66     PROP_0,
67 };
68 
69 struct _XedAppPrivate
70 {
71     XedPluginsEngine *engine;
72 
73     GtkPageSetup *page_setup;
74     GtkPrintSettings *print_settings;
75 
76     GObject *settings;
77     GSettings *window_settings;
78     GSettings *editor_settings;
79 
80     PeasExtensionSet *extensions;
81 
82     /* command line parsing */
83     gboolean new_window;
84     gboolean new_document;
85     gchar *geometry;
86     const GtkSourceEncoding *encoding;
87     GInputStream *stdin_stream;
88     GSList *file_list;
89     gint line_position;
90     GApplicationCommandLine *command_line;
91 };
92 
93 G_DEFINE_TYPE_WITH_PRIVATE (XedApp, xed_app, GTK_TYPE_APPLICATION)
94 
95 static const GOptionEntry options[] =
96 {
97     /* Version */
98     {
99         "version", 'V', 0, G_OPTION_ARG_NONE, NULL,
100         N_("Show the application's version"), NULL
101     },
102 
103     /* List available encodings */
104     {
105         "list-encodings", '\0', 0, G_OPTION_ARG_NONE, NULL,
106         N_("Display list of possible values for the encoding option"),
107         NULL
108     },
109 
110     /* Encoding */
111     {
112         "encoding", '\0', 0, G_OPTION_ARG_STRING, NULL,
113         N_("Set the character encoding to be used to open the files listed on the command line"),
114         N_("ENCODING")
115     },
116 
117     /* Open a new window */
118     {
119         "new-window", '\0', 0, G_OPTION_ARG_NONE, NULL,
120         N_("Create a new top-level window in an existing instance of xed"),
121         NULL
122     },
123 
124     /* Create a new empty document */
125     {
126         "new-document", '\0', 0, G_OPTION_ARG_NONE, NULL,
127         N_("Create a new document in an existing instance of xed"),
128         NULL
129     },
130 
131     /* Window geometry */
132     {
133         "geometry", 'g', 0, G_OPTION_ARG_STRING, NULL,
134         N_("Set the size and position of the window (WIDTHxHEIGHT+X+Y)"),
135         N_("GEOMETRY")
136     },
137 
138     /* Wait for closing documents */
139     {
140         "wait", 'w', 0, G_OPTION_ARG_NONE, NULL,
141         N_("Open files and block process until files are closed"),
142         NULL
143     },
144 
145     /* New instance */
146     {
147         "standalone", 's', 0, G_OPTION_ARG_NONE, NULL,
148         N_("Run xed in standalone mode"),
149         NULL
150     },
151 
152     /* collects file arguments */
153     {
154         G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, NULL,
155         N_("[FILE...] [+LINE]")
156     },
157 
158     {NULL}
159 };
160 
161 static void
xed_app_dispose(GObject * object)162 xed_app_dispose (GObject *object)
163 {
164     XedApp *app = XED_APP (object);
165 
166     g_clear_object (&app->priv->window_settings);
167     g_clear_object (&app->priv->editor_settings);
168     g_clear_object (&app->priv->settings);
169     g_clear_object (&app->priv->page_setup);
170     g_clear_object (&app->priv->print_settings);
171     g_clear_object (&app->priv->extensions);
172     g_clear_object (&app->priv->engine);
173 
174     G_OBJECT_CLASS (xed_app_parent_class)->dispose (object);
175 }
176 
177 static void
xed_app_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)178 xed_app_get_property (GObject    *object,
179                       guint       prop_id,
180                       GValue     *value,
181                       GParamSpec *pspec)
182 {
183     switch (prop_id)
184     {
185         default:
186             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
187             break;
188     }
189 }
190 
191 static void
extension_added(PeasExtensionSet * extensions,PeasPluginInfo * info,PeasExtension * exten,XedApp * app)192 extension_added (PeasExtensionSet *extensions,
193                  PeasPluginInfo   *info,
194                  PeasExtension    *exten,
195                  XedApp           *app)
196 {
197     peas_extension_call (exten, "activate");
198 }
199 
200 static void
extension_removed(PeasExtensionSet * extensions,PeasPluginInfo * info,PeasExtension * exten,XedApp * app)201 extension_removed (PeasExtensionSet *extensions,
202                    PeasPluginInfo   *info,
203                    PeasExtension    *exten,
204                    XedApp           *app)
205 {
206     peas_extension_call (exten, "deactivate");
207 }
208 
209 static void
set_initial_theme_style(XedApp * app)210 set_initial_theme_style (XedApp *app)
211 {
212     if (g_settings_get_boolean (app->priv->editor_settings, XED_SETTINGS_PREFER_DARK_THEME))
213     {
214         GtkSettings *gtk_settings;
215 
216         gtk_settings = gtk_settings_get_default ();
217         g_object_set (G_OBJECT (gtk_settings), "gtk-application-prefer-dark-theme", TRUE, NULL);
218     }
219 }
220 
221 static void
theme_changed(GtkSettings * settings,GParamSpec * pspec,gpointer data)222 theme_changed (GtkSettings *settings,
223                GParamSpec  *pspec,
224                gpointer     data)
225 {
226     static GtkCssProvider *provider;
227     gchar *theme;
228     GdkScreen *screen;
229 
230     g_object_get (settings, "gtk-theme-name", &theme, NULL);
231     screen = gdk_screen_get_default ();
232 
233     if (g_str_equal (theme, "Adwaita"))
234     {
235         if (provider == NULL)
236         {
237             GFile *file;
238 
239             provider = gtk_css_provider_new ();
240             file = g_file_new_for_uri ("resource:///org/x/editor/css/xed.adwaita.css");
241             gtk_css_provider_load_from_file (provider, file, NULL);
242             g_object_unref (file);
243         }
244 
245         gtk_style_context_add_provider_for_screen (screen,
246                                                    GTK_STYLE_PROVIDER (provider),
247                                                    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
248     }
249     else if (provider != NULL)
250     {
251         gtk_style_context_remove_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider));
252         g_clear_object (&provider);
253     }
254 
255     g_free (theme);
256 }
257 
258 static void
setup_theme_extensions(void)259 setup_theme_extensions (void)
260 {
261     GtkSettings *settings;
262 
263     settings = gtk_settings_get_default ();
264     g_signal_connect (settings, "notify::gtk-theme-name",
265                       G_CALLBACK (theme_changed), NULL);
266     theme_changed (settings, NULL, NULL);
267 }
268 
269 static void
xed_app_startup(GApplication * application)270 xed_app_startup (GApplication *application)
271 {
272     XedApp *app = XED_APP (application);
273     GtkSourceStyleSchemeManager *manager;
274     const gchar *dir;
275     gchar *icon_dir;
276 #ifndef ENABLE_GVFS_METADATA
277     const gchar *cache_dir;
278     gchar *metadata_filename;
279 #endif
280     GError *error = NULL;
281     GFile *css_file;
282     GtkCssProvider *provider;
283 
284     G_APPLICATION_CLASS (xed_app_parent_class)->startup (application);
285 
286     /* Setup debugging */
287     xed_debug_init ();
288     xed_debug_message (DEBUG_APP, "Startup");
289     xed_debug_message (DEBUG_APP, "Set icon");
290 
291     dir = xed_dirs_get_xed_data_dir ();
292     icon_dir = g_build_filename (dir, "icons", NULL);
293 
294     gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), icon_dir);
295     g_free (icon_dir);
296 
297     setup_theme_extensions ();
298 
299 #ifndef ENABLE_GVFS_METADATA
300     /* Setup metadata-manager */
301     cache_dir = xed_dirs_get_user_cache_dir ();
302 
303     metadata_filename = g_build_filename (cache_dir, METADATA_FILE, NULL);
304 
305     xed_metadata_manager_init (metadata_filename);
306 
307     g_free (metadata_filename);
308 #endif
309 
310     /* Load settings */
311     app->priv->settings = xed_settings_new ();
312     app->priv->window_settings = g_settings_new ("org.x.editor.state.window");
313     app->priv->editor_settings = g_settings_new ("org.x.editor.preferences.editor");
314 
315     set_initial_theme_style (app);
316 
317     /* Load custom css */
318     css_file = g_file_new_for_uri ("resource:///org/x/editor/css/xed-style.css");
319     provider = gtk_css_provider_new ();
320     if (gtk_css_provider_load_from_file (provider, css_file, &error))
321     {
322         gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
323                                                    GTK_STYLE_PROVIDER (provider),
324                                                    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
325     }
326     else
327     {
328         g_warning ("Could not load css provider: %s", error->message);
329         g_error_free (error);
330     }
331 
332     g_object_unref (css_file);
333 
334     /*
335      * We use the default gtksourceview style scheme manager so that plugins
336      * can obtain it easily without a xed specific api, but we need to
337      * add our search path at startup before the manager is actually used.
338      */
339     manager = gtk_source_style_scheme_manager_get_default ();
340     gtk_source_style_scheme_manager_append_search_path (manager, xed_dirs_get_user_styles_dir ());
341 
342     app->priv->engine = xed_plugins_engine_get_default ();
343     app->priv->extensions = peas_extension_set_new (PEAS_ENGINE (app->priv->engine),
344                                                     XED_TYPE_APP_ACTIVATABLE,
345                                                     "app", app,
346                                                     NULL);
347 
348     g_signal_connect (app->priv->extensions, "extension-added",
349                       G_CALLBACK (extension_added), app);
350 
351     g_signal_connect (app->priv->extensions, "extension-removed",
352                       G_CALLBACK (extension_removed), app);
353 
354     peas_extension_set_foreach (app->priv->extensions,
355                                 (PeasExtensionSetForeachFunc) extension_added,
356                                 app);
357 }
358 
359 static gboolean
is_in_viewport(GtkWindow * window,GdkScreen * screen,gint workspace,gint viewport_x,gint viewport_y)360 is_in_viewport (GtkWindow    *window,
361                 GdkScreen    *screen,
362                 gint          workspace,
363                 gint          viewport_x,
364                 gint          viewport_y)
365 {
366     GdkScreen *s;
367     GdkDisplay *display;
368     GdkMonitor *monitor;
369     GdkWindow *gdkwindow;
370     GdkRectangle geometry;
371     const gchar *cur_name;
372     const gchar *name;
373     gint ws;
374     gint x, y, width, height;
375 
376     /* Check for screen and display match */
377     display = gdk_screen_get_display (screen);
378     cur_name = gdk_display_get_name (display);
379 
380     s = gtk_window_get_screen (window);
381     display = gdk_screen_get_display (s);
382     name = gdk_display_get_name (display);
383 
384     if (strcmp (cur_name, name) != 0)
385     {
386         return FALSE;
387     }
388 
389     /* Check for workspace match */
390     ws = xed_utils_get_window_workspace (window);
391 
392     if (ws != workspace && ws != XED_ALL_WORKSPACES)
393     {
394         return FALSE;
395     }
396 
397     /* Check for viewport match */
398     gdkwindow = gtk_widget_get_window (GTK_WIDGET (window));
399     gdk_window_get_position (gdkwindow, &x, &y);
400     width = gdk_window_get_width (gdkwindow);
401     height = gdk_window_get_height (gdkwindow);
402     x += viewport_x;
403     y += viewport_y;
404 
405     monitor = gdk_display_get_monitor_at_window(display, gdkwindow);
406     gdk_monitor_get_geometry(monitor, &geometry);
407 
408     return x + width * .75 >= geometry.x &&
409            x + width * .25 <= geometry.x + geometry.width &&
410            y + height * .75 >= geometry.y &&
411            y + height * .25 <= geometry.y + geometry.height;
412 }
413 
414 static XedWindow *
get_active_window(GtkApplication * app)415 get_active_window (GtkApplication *app)
416 {
417     GdkScreen *screen;
418     guint workspace;
419     gint viewport_x, viewport_y;
420     GList *windows, *l;
421 
422     screen = gdk_screen_get_default ();
423 
424     workspace = xed_utils_get_current_workspace (screen);
425     xed_utils_get_current_viewport (screen, &viewport_x, &viewport_y);
426 
427     /* Gtk documentation says the window list is always in MRU order */
428     windows = gtk_application_get_windows (app);
429     for (l = windows; l != NULL; l = l->next)
430     {
431         GtkWindow *window = l->data;
432 
433         if (XED_IS_WINDOW (window) && is_in_viewport (window, screen, workspace, viewport_x, viewport_y))
434         {
435             return XED_WINDOW (window);
436         }
437     }
438 
439     return NULL;
440 }
441 
442 static void
set_command_line_wait(XedApp * app,XedTab * tab)443 set_command_line_wait (XedApp *app,
444                        XedTab *tab)
445 {
446     g_object_set_data_full (G_OBJECT (tab),
447                             "XedTabCommandLineWait",
448                             g_object_ref (app->priv->command_line),
449                             (GDestroyNotify)g_object_unref);
450 }
451 
452 static void
set_command_line_wait_doc(XedDocument * doc,XedApp * app)453 set_command_line_wait_doc (XedDocument *doc,
454                            XedApp      *app)
455 {
456     XedTab *tab = xed_tab_get_from_document (doc);
457 
458     set_command_line_wait (app, tab);
459 }
460 
461 static void
open_files(GApplication * application,gboolean new_window,gboolean new_document,gchar * geometry,gint line_position,const GtkSourceEncoding * encoding,GInputStream * stdin_stream,GSList * file_list,GApplicationCommandLine * command_line)462 open_files (GApplication            *application,
463             gboolean                 new_window,
464             gboolean                 new_document,
465             gchar                   *geometry,
466             gint                     line_position,
467             const GtkSourceEncoding *encoding,
468             GInputStream            *stdin_stream,
469             GSList                  *file_list,
470             GApplicationCommandLine *command_line)
471 {
472     XedWindow *window = NULL;
473     XedTab *tab;
474     gboolean doc_created = FALSE;
475 
476     if (!new_window)
477     {
478         window = get_active_window (GTK_APPLICATION (application));
479     }
480 
481     if (window == NULL)
482     {
483         xed_debug_message (DEBUG_APP, "Create main window");
484         window = xed_app_create_window (XED_APP (application), NULL);
485 
486         xed_debug_message (DEBUG_APP, "Show window");
487         gtk_widget_show (GTK_WIDGET (window));
488     }
489 
490     if (geometry)
491     {
492         gtk_window_parse_geometry (GTK_WINDOW (window), geometry);
493     }
494 
495     if (stdin_stream)
496     {
497         xed_debug_message (DEBUG_APP, "Load stdin");
498 
499         tab = xed_window_create_tab_from_stream (window,
500                                                  stdin_stream,
501                                                  encoding,
502                                                  line_position,
503                                                  TRUE);
504         doc_created = tab != NULL;
505 
506         if (doc_created && command_line)
507         {
508             set_command_line_wait (XED_APP (application), tab);
509         }
510         g_input_stream_close (stdin_stream, NULL, NULL);
511     }
512 
513     if (file_list != NULL)
514     {
515         GSList *loaded;
516 
517         xed_debug_message (DEBUG_APP, "Load files");
518         loaded = _xed_cmd_load_files_from_prompt (window, file_list, encoding, line_position);
519 
520         doc_created = doc_created || loaded != NULL;
521 
522         if (command_line)
523         {
524             g_slist_foreach (loaded, (GFunc)set_command_line_wait_doc, XED_APP (application));
525         }
526         g_slist_free (loaded);
527     }
528 
529     if (!doc_created || new_document)
530     {
531         xed_debug_message (DEBUG_APP, "Create tab");
532         tab = xed_window_create_tab (window, TRUE);
533 
534         if (command_line)
535         {
536             set_command_line_wait (XED_APP (application), tab);
537         }
538     }
539 
540     gtk_window_present (GTK_WINDOW (window));
541 }
542 
543 static void
xed_app_activate(GApplication * application)544 xed_app_activate (GApplication *application)
545 {
546     XedAppPrivate *priv = XED_APP (application)->priv;
547 
548     open_files (application,
549                 priv->new_window,
550                 priv->new_document,
551                 priv->geometry,
552                 priv->line_position,
553                 priv->encoding,
554                 priv->stdin_stream,
555                 priv->file_list,
556                 priv->command_line);
557 }
558 
559 static void
clear_options(XedApp * app)560 clear_options (XedApp *app)
561 {
562     XedAppPrivate *priv = app->priv;
563 
564     g_free (priv->geometry);
565     g_clear_object (&priv->stdin_stream);
566     g_slist_free_full (priv->file_list, g_object_unref);
567 
568     priv->new_window = FALSE;
569     priv->new_document = FALSE;
570     priv->geometry = NULL;
571     priv->encoding = NULL;
572     priv->file_list = NULL;
573     priv->line_position = 0;
574     priv->command_line = NULL;
575 }
576 
577 static void
get_line_position(const gchar * arg,gint * line)578 get_line_position (const gchar *arg,
579                    gint        *line)
580 {
581     *line = atoi (arg);
582 }
583 
584 static gint
xed_app_command_line(GApplication * application,GApplicationCommandLine * cl)585 xed_app_command_line (GApplication            *application,
586                       GApplicationCommandLine *cl)
587 {
588     XedAppPrivate *priv;
589     GVariantDict *options;
590     const gchar *encoding_charset;
591     const gchar **remaining_args;
592 
593     priv = XED_APP (application)->priv;
594 
595     options = g_application_command_line_get_options_dict (cl);
596 
597     g_variant_dict_lookup (options, "new-window", "b", &priv->new_window);
598     g_variant_dict_lookup (options, "new-document", "b", &priv->new_document);
599     g_variant_dict_lookup (options, "geometry", "s", &priv->geometry);
600 
601     if (g_variant_dict_contains (options, "wait"))
602     {
603         priv->command_line = cl;
604     }
605 
606     if (g_variant_dict_lookup (options, "encoding", "&s", &encoding_charset))
607     {
608         priv->encoding = gtk_source_encoding_get_from_charset (encoding_charset);
609 
610         if (priv->encoding == NULL)
611         {
612             g_application_command_line_printerr (cl, _("%s: invalid encoding."), encoding_charset);
613         }
614     }
615 
616     /* Parse filenames */
617     if (g_variant_dict_lookup (options, G_OPTION_REMAINING, "^a&ay", &remaining_args))
618     {
619         gint i;
620 
621         for (i = 0; remaining_args[i]; i++)
622         {
623             if (*remaining_args[i] == '+')
624             {
625                 if (*(remaining_args[i] + 1) == '\0')
626                 {
627                     /* goto the last line of the document */
628                     priv->line_position = G_MAXINT;
629                 }
630                 else
631                 {
632                     get_line_position (remaining_args[i] + 1, &priv->line_position);
633                 }
634             }
635 
636             else if (*remaining_args[i] == '-' && *(remaining_args[i] + 1) == '\0')
637             {
638                 priv->stdin_stream = g_application_command_line_get_stdin (cl);
639             }
640             else
641             {
642                 GFile *file;
643 
644                 file = g_application_command_line_create_file_for_arg (cl, remaining_args[i]);
645                 priv->file_list = g_slist_prepend (priv->file_list, file);
646             }
647         }
648 
649         priv->file_list = g_slist_reverse (priv->file_list);
650         g_free (remaining_args);
651     }
652 
653     g_application_activate (application);
654     clear_options (XED_APP (application));
655 
656     return 0;
657 }
658 
659 static void
print_all_encodings(void)660 print_all_encodings (void)
661 {
662     GSList *all_encodings;
663     GSList *l;
664 
665     all_encodings = gtk_source_encoding_get_all ();
666 
667     for (l = all_encodings; l != NULL; l = l->next)
668     {
669         const GtkSourceEncoding *encoding = l->data;
670         g_print ("%s\n", gtk_source_encoding_get_charset (encoding));
671     }
672 
673     g_slist_free (all_encodings);
674 }
675 
676 static gint
xed_app_handle_local_options(GApplication * application,GVariantDict * options)677 xed_app_handle_local_options (GApplication *application,
678                               GVariantDict *options)
679 {
680     if (g_variant_dict_contains (options, "version"))
681     {
682         g_print ("%s - Version %s\n", g_get_application_name (), VERSION);
683         return 0;
684     }
685 
686     if (g_variant_dict_contains (options, "list-encodings"))
687     {
688         print_all_encodings ();
689         return 0;
690     }
691 
692     if (g_variant_dict_contains (options, "standalone"))
693     {
694         GApplicationFlags old_flags;
695 
696         old_flags = g_application_get_flags (application);
697         g_application_set_flags (application, old_flags | G_APPLICATION_NON_UNIQUE);
698     }
699 
700     return -1;
701 }
702 
703 /* Note: when launched from command line we do not reach this method
704  * since we manually handle the command line parameters in order to
705  * parse +LINE, stdin, etc.
706  * However this method is called when open() is called via dbus, for
707  * instance when double clicking on a file in nautilus
708  */
709 static void
xed_app_open(GApplication * application,GFile ** files,gint n_files,const gchar * hint)710 xed_app_open (GApplication  *application,
711               GFile        **files,
712               gint           n_files,
713               const gchar   *hint)
714 {
715     gint i;
716     GSList *file_list = NULL;
717 
718     for (i = 0; i < n_files; i++)
719     {
720         file_list = g_slist_prepend (file_list, files[i]);
721     }
722 
723     file_list = g_slist_reverse (file_list);
724 
725     open_files (application,
726                 FALSE,
727                 FALSE,
728                 NULL,
729                 0,
730                 NULL,
731                 NULL,
732                 file_list,
733                 NULL);
734 
735     g_slist_free (file_list);
736 }
737 
738 static gboolean
ensure_user_config_dir(void)739 ensure_user_config_dir (void)
740 {
741     const gchar *config_dir;
742     gboolean ret = TRUE;
743     gint res;
744 
745     config_dir = xed_dirs_get_user_config_dir ();
746     if (config_dir == NULL)
747     {
748         g_warning ("Could not get config directory\n");
749         return FALSE;
750     }
751 
752     res = g_mkdir_with_parents (config_dir, 0755);
753     if (res < 0)
754     {
755         g_warning ("Could not create config directory\n");
756         ret = FALSE;
757     }
758 
759     return ret;
760 }
761 
762 static void
save_accels(void)763 save_accels (void)
764 {
765     gchar *filename;
766 
767     filename = g_build_filename (xed_dirs_get_user_config_dir (), "accels", NULL);
768     if (filename != NULL)
769     {
770         xed_debug_message (DEBUG_APP, "Saving keybindings in %s\n", filename);
771         gtk_accel_map_save (filename);
772         g_free (filename);
773     }
774 }
775 
776 static gchar *
get_page_setup_file(void)777 get_page_setup_file (void)
778 {
779     const gchar *config_dir;
780     gchar *setup = NULL;
781 
782     config_dir = xed_dirs_get_user_config_dir ();
783 
784     if (config_dir != NULL)
785     {
786         setup = g_build_filename (config_dir, XED_PAGE_SETUP_FILE, NULL);
787     }
788 
789     return setup;
790 }
791 
792 static void
save_page_setup(XedApp * app)793 save_page_setup (XedApp *app)
794 {
795     gchar *filename;
796     GError *error = NULL;
797 
798     if (app->priv->page_setup == NULL)
799     {
800         return;
801     }
802 
803     filename = get_page_setup_file ();
804 
805     gtk_page_setup_to_file (app->priv->page_setup, filename, &error);
806     if (error)
807     {
808         g_warning ("%s", error->message);
809         g_error_free (error);
810     }
811 
812     g_free (filename);
813 }
814 
815 static gchar *
get_print_settings_file(void)816 get_print_settings_file (void)
817 {
818     const gchar *config_dir;
819     gchar *settings = NULL;
820 
821     config_dir = xed_dirs_get_user_config_dir ();
822 
823     if (config_dir != NULL)
824     {
825         settings = g_build_filename (config_dir, XED_PRINT_SETTINGS_FILE, NULL);
826     }
827 
828     return settings;
829 }
830 
831 static void
save_print_settings(XedApp * app)832 save_print_settings (XedApp *app)
833 {
834     gchar *filename;
835     GError *error = NULL;
836 
837     if (app->priv->print_settings == NULL)
838     {
839         return;
840     }
841 
842     filename = get_print_settings_file ();
843 
844     gtk_print_settings_to_file (app->priv->print_settings, filename, &error);
845     if (error)
846     {
847         g_warning ("%s", error->message);
848         g_error_free (error);
849     }
850 
851     g_free (filename);
852 }
853 
854 static void
xed_app_shutdown(GApplication * app)855 xed_app_shutdown (GApplication *app)
856 {
857     xed_debug_message (DEBUG_APP, "Quitting\n");
858 
859     /* Last window is gone... save some settings and exit */
860     ensure_user_config_dir ();
861 
862     save_accels ();
863     save_page_setup (XED_APP (app));
864     save_print_settings (XED_APP (app));
865 
866     /* GTK+ can still hold references to some xed objects, for example
867      * XedDocument for the clipboard. So the metadata-manager should be
868      * shutdown after.
869      */
870     G_APPLICATION_CLASS (xed_app_parent_class)->shutdown (app);
871 
872 #ifndef ENABLE_GVFS_METADATA
873     xed_metadata_manager_shutdown ();
874 #endif
875 
876     xed_dirs_shutdown ();
877 }
878 
879 static void
xed_app_class_init(XedAppClass * klass)880 xed_app_class_init (XedAppClass *klass)
881 {
882     GObjectClass *object_class = G_OBJECT_CLASS (klass);
883     GApplicationClass *app_class =  G_APPLICATION_CLASS (klass);
884 
885     object_class->dispose = xed_app_dispose;
886     object_class->get_property = xed_app_get_property;
887 
888     app_class->startup = xed_app_startup;
889     app_class->activate = xed_app_activate;
890     app_class->command_line = xed_app_command_line;
891     app_class->handle_local_options = xed_app_handle_local_options;
892     app_class->open = xed_app_open;
893     app_class->shutdown = xed_app_shutdown;
894 }
895 
896 static void
load_accels(void)897 load_accels (void)
898 {
899     gchar *filename;
900 
901     filename = g_build_filename (xed_dirs_get_user_config_dir (), "accels", NULL);
902     if (filename != NULL)
903     {
904         xed_debug_message (DEBUG_APP, "Loading keybindings from %s\n", filename);
905         gtk_accel_map_load (filename);
906         g_free (filename);
907     }
908 }
909 
910 static void
load_page_setup(XedApp * app)911 load_page_setup (XedApp *app)
912 {
913     gchar *filename;
914     GError *error = NULL;
915 
916     g_return_if_fail (app->priv->page_setup == NULL);
917 
918     filename = get_page_setup_file ();
919 
920     app->priv->page_setup = gtk_page_setup_new_from_file (filename, &error);
921     if (error)
922     {
923         /* Ignore file not found error */
924         if (error->domain != G_FILE_ERROR || error->code != G_FILE_ERROR_NOENT)
925         {
926             g_warning ("%s", error->message);
927         }
928 
929         g_error_free (error);
930     }
931 
932     g_free (filename);
933 
934     /* fall back to default settings */
935     if (app->priv->page_setup == NULL)
936     {
937         app->priv->page_setup = gtk_page_setup_new ();
938     }
939 }
940 
941 static void
load_print_settings(XedApp * app)942 load_print_settings (XedApp *app)
943 {
944     gchar *filename;
945     GError *error = NULL;
946 
947     g_return_if_fail (app->priv->print_settings == NULL);
948 
949     filename = get_print_settings_file ();
950 
951     app->priv->print_settings = gtk_print_settings_new_from_file (filename, &error);
952     if (error)
953     {
954         /* Ignore file not found error */
955         if (error->domain != G_FILE_ERROR || error->code != G_FILE_ERROR_NOENT)
956         {
957             g_warning ("%s", error->message);
958         }
959 
960         g_error_free (error);
961     }
962 
963     g_free (filename);
964 
965     /* fall back to default settings */
966     if (app->priv->print_settings == NULL)
967     {
968         app->priv->print_settings = gtk_print_settings_new ();
969     }
970 }
971 
972 static void
setup_actions(XedApp * app)973 setup_actions (XedApp *app)
974 {
975     GSimpleAction *action;
976 
977     action = g_simple_action_new ("print-now", NULL);
978 
979     g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (action));
980     g_object_unref (action);
981 }
982 
983 static void
xed_app_init(XedApp * app)984 xed_app_init (XedApp *app)
985 {
986     app->priv = xed_app_get_instance_private (app);
987 
988     g_set_application_name ("xed");
989     gtk_window_set_default_icon_name ("accessories-text-editor");
990 
991     g_application_add_main_option_entries (G_APPLICATION (app), options);
992 
993 #ifdef ENABLE_INTROSPECTION
994     g_application_add_option_group (G_APPLICATION (app), g_irepository_get_option_group ());
995 #endif
996 
997     load_accels ();
998 
999     setup_actions (app);
1000 }
1001 
1002 static gboolean
window_delete_event(XedWindow * window,GdkEvent * event,XedApp * app)1003 window_delete_event (XedWindow *window,
1004                      GdkEvent  *event,
1005                      XedApp    *app)
1006 {
1007     XedWindowState ws;
1008 
1009     ws = xed_window_get_state (window);
1010 
1011     if (ws &
1012         (XED_WINDOW_STATE_SAVING |
1013          XED_WINDOW_STATE_PRINTING |
1014          XED_WINDOW_STATE_SAVING_SESSION))
1015     {
1016             return TRUE;
1017     }
1018 
1019     _xed_cmd_file_quit (NULL, window);
1020 
1021     /* Do not destroy the window */
1022     return TRUE;
1023 }
1024 
1025 /* Generates a unique string for a window role */
1026 static gchar *
gen_role(void)1027 gen_role (void)
1028 {
1029     GTimeVal result;
1030     static gint serial;
1031 
1032     g_get_current_time (&result);
1033 
1034     return g_strdup_printf ("xed-window-%ld-%ld-%d-%s",
1035                             result.tv_sec,
1036                             result.tv_usec,
1037                             serial++,
1038                             g_get_host_name ());
1039 }
1040 
1041 static XedWindow *
xed_app_create_window_real(XedApp * app,gboolean set_geometry,const gchar * role)1042 xed_app_create_window_real (XedApp      *app,
1043                             gboolean     set_geometry,
1044                             const gchar *role)
1045 {
1046     XedWindow *window;
1047 
1048     window = g_object_new (XED_TYPE_WINDOW, "application", app, NULL);
1049 
1050     xed_debug_message (DEBUG_APP, "Window created");
1051 
1052     if (role != NULL)
1053     {
1054         gtk_window_set_role (GTK_WINDOW (window), role);
1055     }
1056     else
1057     {
1058         gchar *newrole;
1059 
1060         newrole = gen_role ();
1061         gtk_window_set_role (GTK_WINDOW (window), newrole);
1062         g_free (newrole);
1063     }
1064 
1065     if (set_geometry)
1066     {
1067         GdkWindowState state;
1068         gint w, h;
1069 
1070         state = g_settings_get_int (app->priv->window_settings, XED_SETTINGS_WINDOW_STATE);
1071         g_settings_get (app->priv->window_settings, XED_SETTINGS_WINDOW_SIZE, "(ii)", &w, &h);
1072         gtk_window_set_default_size (GTK_WINDOW (window), w, h);
1073 
1074         if ((state & GDK_WINDOW_STATE_MAXIMIZED) != 0)
1075         {
1076             gtk_window_maximize (GTK_WINDOW (window));
1077         }
1078         else
1079         {
1080             gtk_window_unmaximize (GTK_WINDOW (window));
1081         }
1082 
1083         if ((state & GDK_WINDOW_STATE_STICKY ) != 0)
1084         {
1085             gtk_window_stick (GTK_WINDOW (window));
1086         }
1087         else
1088         {
1089             gtk_window_unstick (GTK_WINDOW (window));
1090         }
1091     }
1092 
1093     g_signal_connect (window, "delete_event", G_CALLBACK (window_delete_event), app);
1094 
1095     return window;
1096 }
1097 
1098 /**
1099  * xed_app_create_window:
1100  * @app: the #XedApp
1101  * @screen: (allow-none):
1102  *
1103  * Create a new #XedWindow part of @app.
1104  *
1105  * Return value: (transfer none): the new #XedWindow
1106  */
1107 XedWindow *
xed_app_create_window(XedApp * app,GdkScreen * screen)1108 xed_app_create_window (XedApp    *app,
1109                        GdkScreen *screen)
1110 {
1111     XedWindow *window;
1112 
1113     window = xed_app_create_window_real (app, TRUE, NULL);
1114 
1115     if (screen != NULL)
1116     {
1117         gtk_window_set_screen (GTK_WINDOW (window), screen);
1118     }
1119 
1120     return window;
1121 }
1122 
1123 /*
1124  * Same as _create_window, but doesn't set the geometry.
1125  * The session manager takes care of it. Used in mate-session.
1126  */
1127 XedWindow *
_xed_app_restore_window(XedApp * app,const gchar * role)1128 _xed_app_restore_window (XedApp *app,
1129                          const   gchar *role)
1130 {
1131     XedWindow *window;
1132 
1133     window = xed_app_create_window_real (app, FALSE, role);
1134 
1135     return window;
1136 }
1137 
1138 /**
1139  * xed_app_get_main_windows:
1140  * @app: the #XedApp
1141  *
1142  * Returns all #XedWindow currently open in #XedApp.
1143  * This differs from gtk_application_get_windows() since it does not
1144  * include the preferences dialog and other auxiliary windows.
1145  *
1146  * Return value: (element-type Xed.Window) (transfer container):
1147  * a newly allocated list of #XedWindow objects
1148  */
1149 GList *
xed_app_get_main_windows(XedApp * app)1150 xed_app_get_main_windows (XedApp *app)
1151 {
1152     GList *res = NULL;
1153     GList *windows, *l;
1154 
1155     g_return_val_if_fail (XED_IS_APP (app), NULL);
1156 
1157     windows = gtk_application_get_windows (GTK_APPLICATION (app));
1158     for (l = windows; l != NULL; l = g_list_next (l))
1159     {
1160         if (XED_IS_WINDOW (l->data))
1161         {
1162             res = g_list_prepend (res, l->data);
1163         }
1164     }
1165 
1166     return g_list_reverse (res);
1167 }
1168 
1169 /**
1170  * xed_app_get_documents:
1171  * @app: the #XedApp
1172  *
1173  * Returns all the documents currently open in #XedApp.
1174  *
1175  * Return value: (element-type Xed.Document) (transfer container):
1176  * a newly allocated list of #XedDocument objects
1177  */
1178 GList *
xed_app_get_documents(XedApp * app)1179 xed_app_get_documents   (XedApp *app)
1180 {
1181     GList *res = NULL;
1182     GList *windows, *l;
1183 
1184     g_return_val_if_fail (XED_IS_APP (app), NULL);
1185 
1186     windows = gtk_application_get_windows (GTK_APPLICATION (app));
1187     for (l = windows; l != NULL; l = g_list_next (l))
1188     {
1189         res = g_list_concat (res, xed_window_get_documents (XED_WINDOW (l->data)));
1190     }
1191 
1192     return res;
1193 }
1194 
1195 /**
1196  * xed_app_get_views:
1197  * @app: the #XedApp
1198  *
1199  * Returns all the views currently present in #XedApp.
1200  *
1201  * Return value: (element-type Xed.View) (transfer container):
1202  * a newly allocated list of #XedView objects
1203  */
1204 GList *
xed_app_get_views(XedApp * app)1205 xed_app_get_views (XedApp *app)
1206 {
1207     GList *res = NULL;
1208     GList *windows, *l;
1209 
1210     g_return_val_if_fail (XED_IS_APP (app), NULL);
1211 
1212     windows = gtk_application_get_windows (GTK_APPLICATION (app));
1213     for (l = windows; l != NULL; l = g_list_next (l))
1214     {
1215         res = g_list_concat (res, xed_window_get_views (XED_WINDOW (l->data)));
1216     }
1217 
1218     return res;
1219 }
1220 
1221 gboolean
xed_app_show_help(XedApp * app,GtkWindow * parent,const gchar * name,const gchar * link_id)1222 xed_app_show_help (XedApp      *app,
1223                    GtkWindow   *parent,
1224                    const gchar *name,
1225                    const gchar *link_id)
1226 {
1227     g_return_val_if_fail (XED_IS_APP (app), FALSE);
1228     g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), FALSE);
1229 
1230     GError *error = NULL;
1231     gboolean ret;
1232     gchar *link;
1233 
1234     if (name == NULL)
1235     {
1236         name = "xed";
1237     }
1238     else if (strcmp (name, "xed.xml") == 0)
1239     {
1240         g_warning ("%s: Using \"xed.xml\" for the help name is deprecated, use \"xed\" or simply NULL instead", G_STRFUNC);
1241         name = "xed";
1242     }
1243 
1244     if (link_id)
1245     {
1246         link = g_strdup_printf ("help:%s/%s", name, link_id);
1247     }
1248     else
1249     {
1250         link = g_strdup_printf ("help:%s", name);
1251     }
1252 
1253     ret = gtk_show_uri_on_window (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (parent))),
1254                                   link, GDK_CURRENT_TIME, &error);
1255 
1256     g_free (link);
1257 
1258     if (error != NULL)
1259     {
1260         GtkWidget *dialog;
1261 
1262         dialog = gtk_message_dialog_new (parent,
1263                                          GTK_DIALOG_DESTROY_WITH_PARENT,
1264                                          GTK_MESSAGE_ERROR,
1265                                          GTK_BUTTONS_CLOSE,
1266                                          _("There was an error displaying the help."));
1267 
1268         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
1269 
1270         g_signal_connect (G_OBJECT (dialog), "response",
1271                           G_CALLBACK (gtk_widget_destroy), NULL);
1272 
1273         gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
1274 
1275         gtk_widget_show (dialog);
1276 
1277         g_error_free (error);
1278     }
1279 
1280     return ret;
1281 }
1282 
1283 void
xed_app_set_window_title(XedApp * app,XedWindow * window,const gchar * title)1284 xed_app_set_window_title (XedApp      *app,
1285                           XedWindow   *window,
1286                           const gchar *title)
1287 {
1288    gtk_window_set_title (GTK_WINDOW (window), title);
1289 }
1290 
1291 /* Returns a copy */
1292 GtkPageSetup *
_xed_app_get_default_page_setup(XedApp * app)1293 _xed_app_get_default_page_setup (XedApp *app)
1294 {
1295     g_return_val_if_fail (XED_IS_APP (app), NULL);
1296 
1297     if (app->priv->page_setup == NULL)
1298     {
1299         load_page_setup (app);
1300     }
1301 
1302     return gtk_page_setup_copy (app->priv->page_setup);
1303 }
1304 
1305 void
_xed_app_set_default_page_setup(XedApp * app,GtkPageSetup * page_setup)1306 _xed_app_set_default_page_setup (XedApp       *app,
1307                                  GtkPageSetup *page_setup)
1308 {
1309     g_return_if_fail (XED_IS_APP (app));
1310     g_return_if_fail (GTK_IS_PAGE_SETUP (page_setup));
1311 
1312     if (app->priv->page_setup != NULL)
1313     {
1314         g_object_unref (app->priv->page_setup);
1315     }
1316 
1317     app->priv->page_setup = g_object_ref (page_setup);
1318 }
1319 
1320 /* Returns a copy */
1321 GtkPrintSettings *
_xed_app_get_default_print_settings(XedApp * app)1322 _xed_app_get_default_print_settings (XedApp *app)
1323 {
1324     g_return_val_if_fail (XED_IS_APP (app), NULL);
1325 
1326     if (app->priv->print_settings == NULL)
1327     {
1328         load_print_settings (app);
1329     }
1330 
1331     return gtk_print_settings_copy (app->priv->print_settings);
1332 }
1333 
1334 void
_xed_app_set_default_print_settings(XedApp * app,GtkPrintSettings * settings)1335 _xed_app_set_default_print_settings (XedApp           *app,
1336                                      GtkPrintSettings *settings)
1337 {
1338     g_return_if_fail (XED_IS_APP (app));
1339     g_return_if_fail (GTK_IS_PRINT_SETTINGS (settings));
1340 
1341     if (app->priv->print_settings != NULL)
1342     {
1343         g_object_unref (app->priv->print_settings);
1344     }
1345 
1346     app->priv->print_settings = g_object_ref (settings);
1347 }
1348 
1349 GObject *
_xed_app_get_settings(XedApp * app)1350 _xed_app_get_settings (XedApp *app)
1351 {
1352     g_return_val_if_fail (XED_IS_APP (app), NULL);
1353 
1354     return app->priv->settings;
1355 }
1356