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