1 /*
2  * pluma-window.c
3  * This file is part of pluma
4  *
5  * Copyright (C) 2005 - Paolo Maggi
6  * Copyright (C) 2012-2021 MATE Developers
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 /*
25  * Modified by the pluma Team, 2005. See the AUTHORS file for a
26  * list of people on the pluma Team.
27  * See the ChangeLog files for a list of changes.
28  *
29  * $Id$
30  */
31 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 #include <time.h>
37 #include <sys/types.h>
38 #include <string.h>
39 
40 #include <gdk/gdk.h>
41 #include <glib/gi18n.h>
42 #include <gio/gio.h>
43 #include <gtk/gtk.h>
44 #include <gtksourceview/gtksource.h>
45 #include <libpeas/peas-extension-set.h>
46 
47 #include "pluma-ui.h"
48 #include "pluma-window.h"
49 #include "pluma-window-private.h"
50 #include "pluma-app.h"
51 #include "pluma-notebook.h"
52 #include "pluma-statusbar.h"
53 #include "pluma-utils.h"
54 #include "pluma-commands.h"
55 #include "pluma-debug.h"
56 #include "pluma-language-manager.h"
57 #include "pluma-panel.h"
58 #include "pluma-documents-panel.h"
59 #include "pluma-plugins-engine.h"
60 #include "pluma-window-activatable.h"
61 #include "pluma-enum-types.h"
62 #include "pluma-dirs.h"
63 #include "pluma-status-combo-box.h"
64 #include "pluma-settings.h"
65 
66 #define LANGUAGE_NONE (const gchar *)"LangNone"
67 #define TAB_WIDTH_DATA "PlumaWindowTabWidthData"
68 #define LANGUAGE_DATA "PlumaWindowLanguageData"
69 #define FULLSCREEN_ANIMATION_SPEED 4
70 
71 #define PLUMA_WINDOW_DEFAULT_WIDTH 650
72 #define PLUMA_WINDOW_DEFAULT_HEIGHT 500
73 
74 /* Local variables */
75 static gboolean cansave = TRUE;
76 
77 /* Signals */
78 enum
79 {
80     TAB_ADDED,
81     TAB_REMOVED,
82     TABS_REORDERED,
83     ACTIVE_TAB_CHANGED,
84     ACTIVE_TAB_STATE_CHANGED,
85     LAST_SIGNAL
86 };
87 
88 static guint signals[LAST_SIGNAL] = { 0 };
89 
90 enum
91 {
92     PROP_0,
93     PROP_STATE
94 };
95 
96 enum
97 {
98     TARGET_URI_LIST = 100
99 };
100 
101 G_DEFINE_TYPE_WITH_PRIVATE (PlumaWindow, pluma_window, GTK_TYPE_WINDOW)
102 
103 static void    recent_manager_changed    (GtkRecentManager *manager,
104                                           PlumaWindow      *window);
105 
106 static void
pluma_window_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)107 pluma_window_get_property (GObject    *object,
108                            guint       prop_id,
109                            GValue     *value,
110                            GParamSpec *pspec)
111 {
112     PlumaWindow *window = PLUMA_WINDOW (object);
113 
114     switch (prop_id)
115     {
116         case PROP_STATE:
117             g_value_set_enum (value,
118                               pluma_window_get_state (window));
119             break;
120         default:
121             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
122             break;
123     }
124 }
125 
126 static void
save_panes_state(PlumaWindow * window)127 save_panes_state (PlumaWindow *window)
128 {
129     gint pane_page;
130 
131     pluma_debug (DEBUG_WINDOW);
132 
133     g_settings_set (window->priv->editor_settings, PLUMA_SETTINGS_WINDOW_SIZE,
134                     "(ii)", window->priv->width, window->priv->height);
135 
136     g_settings_set_int (window->priv->editor_settings, PLUMA_SETTINGS_WINDOW_STATE,
137                         window->priv->window_state);
138 
139     if (window->priv->side_panel_size > 0)
140         g_settings_set_int (window->priv->editor_settings,
141                             PLUMA_SETTINGS_SIDE_PANEL_SIZE,
142                             window->priv->side_panel_size);
143 
144     pane_page = _pluma_panel_get_active_item_id (PLUMA_PANEL (window->priv->side_panel));
145     if (pane_page != 0)
146         g_settings_set_int (window->priv->editor_settings,
147                             PLUMA_SETTINGS_SIDE_PANEL_ACTIVE_PAGE,
148                             pane_page);
149 
150     if (window->priv->bottom_panel_size > 0)
151         g_settings_set_int (window->priv->editor_settings,
152                             PLUMA_SETTINGS_BOTTOM_PANEL_SIZE,
153                             window->priv->bottom_panel_size);
154 
155     pane_page = _pluma_panel_get_active_item_id (PLUMA_PANEL (window->priv->bottom_panel));
156     if (pane_page != 0)
157         g_settings_set_int (window->priv->editor_settings,
158                             PLUMA_SETTINGS_BOTTOM_PANEL_ACTIVE_PAGE, pane_page);
159 }
160 
161 static void
pluma_window_dispose(GObject * object)162 pluma_window_dispose (GObject *object)
163 {
164     PlumaWindow *window;
165 
166     pluma_debug (DEBUG_WINDOW);
167 
168     window = PLUMA_WINDOW (object);
169 
170     /* Stop tracking removal of panes otherwise we always
171      * end up with thinking we had no pane active, since they
172      * should all be removed below */
173     if (window->priv->bottom_panel_item_removed_handler_id != 0)
174     {
175         g_signal_handler_disconnect (window->priv->bottom_panel,
176                                      window->priv->bottom_panel_item_removed_handler_id);
177         window->priv->bottom_panel_item_removed_handler_id = 0;
178     }
179 
180     /* First of all, force collection so that plugins
181      * really drop some of the references.
182      */
183     peas_engine_garbage_collect (PEAS_ENGINE (pluma_plugins_engine_get_default ()));
184 
185     /* save the panes position and make sure to deactivate plugins
186      * for this window, but only once */
187     if (!window->priv->dispose_has_run)
188     {
189         save_panes_state (window);
190 
191         /* Note that unreffing the extensions will automatically remove
192            all extensions which in turn will deactivate the extension */
193         g_object_unref (window->priv->extensions);
194 
195         peas_engine_garbage_collect (PEAS_ENGINE (pluma_plugins_engine_get_default ()));
196 
197         window->priv->dispose_has_run = TRUE;
198     }
199 
200     if (window->priv->fullscreen_animation_timeout_id != 0)
201     {
202         g_source_remove (window->priv->fullscreen_animation_timeout_id);
203         window->priv->fullscreen_animation_timeout_id = 0;
204     }
205 
206     if (window->priv->fullscreen_controls != NULL)
207     {
208         gtk_widget_destroy (window->priv->fullscreen_controls);
209 
210         window->priv->fullscreen_controls = NULL;
211     }
212 
213     if (window->priv->recents_handler_id != 0)
214     {
215         GtkRecentManager *recent_manager;
216 
217         recent_manager =  gtk_recent_manager_get_default ();
218         g_signal_handler_disconnect (recent_manager,
219                                      window->priv->recents_handler_id);
220         window->priv->recents_handler_id = 0;
221     }
222 
223     if (window->priv->manager != NULL)
224     {
225         g_object_unref (window->priv->manager);
226         window->priv->manager = NULL;
227     }
228 
229     if (window->priv->message_bus != NULL)
230     {
231         g_object_unref (window->priv->message_bus);
232         window->priv->message_bus = NULL;
233     }
234 
235     if (window->priv->window_group != NULL)
236     {
237         g_object_unref (window->priv->window_group);
238         window->priv->window_group = NULL;
239     }
240 
241     /* We must free the settings after saving the panels */
242     g_clear_object (&window->priv->editor_settings);
243 
244     /* Now that there have broken some reference loops,
245      * force collection again.
246      */
247     peas_engine_garbage_collect (PEAS_ENGINE (pluma_plugins_engine_get_default ()));
248 
249     G_OBJECT_CLASS (pluma_window_parent_class)->dispose (object);
250 }
251 
252 static void
pluma_window_finalize(GObject * object)253 pluma_window_finalize (GObject *object)
254 {
255     PlumaWindow *window;
256 
257     pluma_debug (DEBUG_WINDOW);
258 
259     window = PLUMA_WINDOW (object);
260 
261     if (window->priv->default_location != NULL)
262         g_object_unref (window->priv->default_location);
263 
264     G_OBJECT_CLASS (pluma_window_parent_class)->finalize (object);
265 }
266 
267 static gboolean
pluma_window_window_state_event(GtkWidget * widget,GdkEventWindowState * event)268 pluma_window_window_state_event (GtkWidget           *widget,
269                                  GdkEventWindowState *event)
270 {
271     PlumaWindow *window = PLUMA_WINDOW (widget);
272 
273     window->priv->window_state = event->new_window_state;
274 
275     return GTK_WIDGET_CLASS (pluma_window_parent_class)->window_state_event (widget, event);
276 }
277 
278 static gboolean
pluma_window_configure_event(GtkWidget * widget,GdkEventConfigure * event)279 pluma_window_configure_event (GtkWidget         *widget,
280                               GdkEventConfigure *event)
281 {
282     PlumaWindow *window = PLUMA_WINDOW (widget);
283 
284     window->priv->width = event->width;
285     window->priv->height = event->height;
286 
287     return GTK_WIDGET_CLASS (pluma_window_parent_class)->configure_event (widget, event);
288 }
289 
290 /*
291  * GtkWindow catches keybindings for the menu items _before_ passing them to
292  * the focused widget. This is unfortunate and means that pressing ctrl+V
293  * in an entry on a panel ends up pasting text in the TextView.
294  * Here we override GtkWindow's handler to do the same things that it
295  * does, but in the opposite order and then we chain up to the grand
296  * parent handler, skipping gtk_window_key_press_event.
297  */
298 static gboolean
pluma_window_key_press_event(GtkWidget * widget,GdkEventKey * event)299 pluma_window_key_press_event (GtkWidget   *widget,
300                               GdkEventKey *event)
301 {
302     static gpointer grand_parent_class = NULL;
303     GtkWindow *window = GTK_WINDOW (widget);
304     gboolean handled = FALSE;
305     /* FIXME: avoid making a new gsettings variable here */
306     GSettings *settings = g_settings_new (PLUMA_SCHEMA_ID);
307 
308     if (event->state & GDK_CONTROL_MASK)
309     {
310         gchar     *font;
311         gchar     *tempsize;
312         gint       nsize;
313 
314         font = g_settings_get_string (settings, PLUMA_SETTINGS_EDITOR_FONT);
315         tempsize = g_strdup (font);
316 
317         g_strreverse (tempsize);
318         g_strcanon (tempsize, "1234567890", '\0');
319         g_strreverse (tempsize);
320 
321         gchar tempfont [strlen (font)];
322         strcpy (tempfont, font);
323         tempfont [strlen (font) - strlen (tempsize)] = 0;
324 
325         sscanf (tempsize, "%d", &nsize);
326 
327         if ((event->keyval == GDK_KEY_plus) || (event->keyval == GDK_KEY_KP_Add))
328         {
329             nsize = nsize + 1;
330             sprintf (tempsize, "%d", nsize);
331 
332             if (!g_settings_get_boolean (settings, PLUMA_SETTINGS_USE_DEFAULT_FONT) && (nsize < 73))
333             {
334                 gchar *tmp = g_strconcat (tempfont, tempsize, NULL);
335                 g_settings_set_string (settings, PLUMA_SETTINGS_EDITOR_FONT, tmp);
336                 g_free (tmp);
337             }
338         }
339         else if ((event->keyval == GDK_KEY_minus) || (event->keyval == GDK_KEY_KP_Subtract))
340         {
341             nsize = nsize - 1;
342             sprintf (tempsize, "%d", nsize);
343 
344             if (!g_settings_get_boolean (settings, PLUMA_SETTINGS_USE_DEFAULT_FONT) && (nsize > 5))
345             {
346                 gchar *tmp = g_strconcat (tempfont, tempsize, NULL);
347                 g_settings_set_string (settings, PLUMA_SETTINGS_EDITOR_FONT, tmp);
348                 g_free (tmp);
349             }
350         }
351         else if (event->keyval == GDK_KEY_y)
352         {
353             g_settings_set_boolean (settings, PLUMA_SETTINGS_DISPLAY_LINE_NUMBERS,
354                                     !g_settings_get_boolean (settings, PLUMA_SETTINGS_DISPLAY_LINE_NUMBERS));
355         }
356 
357         if (g_settings_get_boolean (settings, PLUMA_SETTINGS_CTRL_TABS_SWITCH_TABS))
358         {
359             GtkNotebook *notebook = GTK_NOTEBOOK (_pluma_window_get_notebook (PLUMA_WINDOW (window)));
360 
361             int pages = gtk_notebook_get_n_pages (notebook);
362             int page_num = gtk_notebook_get_current_page (notebook);
363 
364             if (event->keyval == GDK_KEY_ISO_Left_Tab)
365             {
366                 if (page_num != 0)
367                     gtk_notebook_prev_page (notebook);
368                 else
369                     gtk_notebook_set_current_page (notebook, (pages - 1));
370                 handled = TRUE;
371             }
372 
373             if (event->keyval == GDK_KEY_Tab)
374             {
375                 if (page_num != (pages -1))
376                     gtk_notebook_next_page (notebook);
377                 else
378                     gtk_notebook_set_current_page (notebook, 0);
379                 handled = TRUE;
380             }
381         }
382         g_free (font);
383         g_free (tempsize);
384     }
385 
386     g_object_unref (settings);
387 
388     if (grand_parent_class == NULL)
389         grand_parent_class = g_type_class_peek_parent (pluma_window_parent_class);
390 
391     /* handle focus widget key events */
392     if (!handled)
393         handled = gtk_window_propagate_key_event (window, event);
394 
395     /* handle mnemonics and accelerators */
396     if (!handled)
397         handled = gtk_window_activate_key (window, event);
398 
399     /* Chain up, invokes binding set */
400     if (!handled)
401         handled = GTK_WIDGET_CLASS (grand_parent_class)->key_press_event (widget, event);
402 
403     return handled;
404 }
405 
406 static void
pluma_window_tab_removed(PlumaWindow * window,PlumaTab * tab)407 pluma_window_tab_removed (PlumaWindow *window,
408                           PlumaTab    *tab)
409 {
410     peas_engine_garbage_collect (PEAS_ENGINE (pluma_plugins_engine_get_default ()));
411 }
412 
413 static void
pluma_window_class_init(PlumaWindowClass * klass)414 pluma_window_class_init (PlumaWindowClass *klass)
415 {
416     GObjectClass *object_class = G_OBJECT_CLASS (klass);
417     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
418 
419     klass->tab_removed = pluma_window_tab_removed;
420 
421     object_class->dispose = pluma_window_dispose;
422     object_class->finalize = pluma_window_finalize;
423     object_class->get_property = pluma_window_get_property;
424 
425     widget_class->window_state_event = pluma_window_window_state_event;
426     widget_class->configure_event = pluma_window_configure_event;
427     widget_class->key_press_event = pluma_window_key_press_event;
428 
429     signals[TAB_ADDED] =
430         g_signal_new ("tab_added",
431                       G_OBJECT_CLASS_TYPE (object_class),
432                       G_SIGNAL_RUN_FIRST,
433                       G_STRUCT_OFFSET (PlumaWindowClass, tab_added),
434                       NULL, NULL, NULL,
435                       G_TYPE_NONE,
436                       1,
437                       PLUMA_TYPE_TAB);
438     signals[TAB_REMOVED] =
439         g_signal_new ("tab_removed",
440                       G_OBJECT_CLASS_TYPE (object_class),
441                       G_SIGNAL_RUN_FIRST,
442                       G_STRUCT_OFFSET (PlumaWindowClass, tab_removed),
443                       NULL, NULL, NULL,
444                       G_TYPE_NONE,
445                       1,
446                       PLUMA_TYPE_TAB);
447     signals[TABS_REORDERED] =
448         g_signal_new ("tabs_reordered",
449                       G_OBJECT_CLASS_TYPE (object_class),
450                       G_SIGNAL_RUN_FIRST,
451                       G_STRUCT_OFFSET (PlumaWindowClass, tabs_reordered),
452                       NULL, NULL, NULL,
453                       G_TYPE_NONE,
454                       0);
455     signals[ACTIVE_TAB_CHANGED] =
456         g_signal_new ("active_tab_changed",
457                       G_OBJECT_CLASS_TYPE (object_class),
458                       G_SIGNAL_RUN_FIRST,
459                       G_STRUCT_OFFSET (PlumaWindowClass, active_tab_changed),
460                       NULL, NULL, NULL,
461                       G_TYPE_NONE,
462                       1,
463                       PLUMA_TYPE_TAB);
464     signals[ACTIVE_TAB_STATE_CHANGED] =
465         g_signal_new ("active_tab_state_changed",
466                       G_OBJECT_CLASS_TYPE (object_class),
467                       G_SIGNAL_RUN_FIRST,
468                       G_STRUCT_OFFSET (PlumaWindowClass, active_tab_state_changed),
469                       NULL, NULL, NULL,
470                       G_TYPE_NONE,
471                       0);
472 
473     g_object_class_install_property (object_class,
474                                      PROP_STATE,
475                                      g_param_spec_flags ("state",
476                                                          "State",
477                                                          "The window's state",
478                                                          PLUMA_TYPE_WINDOW_STATE,
479                                                          PLUMA_WINDOW_STATE_NORMAL,
480                                                          G_PARAM_READABLE |
481                                                          G_PARAM_STATIC_STRINGS));
482 }
483 
484 static void
menu_item_select_cb(GtkMenuItem * proxy,PlumaWindow * window)485 menu_item_select_cb (GtkMenuItem *proxy,
486                      PlumaWindow *window)
487 {
488     GtkAction *action;
489     char *message;
490 
491     action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (proxy));
492     g_return_if_fail (action != NULL);
493 
494     g_object_get (G_OBJECT (action), "tooltip", &message, NULL);
495     if (message)
496     {
497         gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar),
498                             window->priv->tip_message_cid, message);
499         g_free (message);
500     }
501 }
502 
503 static void
menu_item_deselect_cb(GtkMenuItem * proxy,PlumaWindow * window)504 menu_item_deselect_cb (GtkMenuItem *proxy,
505                        PlumaWindow *window)
506 {
507     gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar),
508                window->priv->tip_message_cid);
509 }
510 
511 static void
connect_proxy_cb(GtkUIManager * manager,GtkAction * action,GtkWidget * proxy,PlumaWindow * window)512 connect_proxy_cb (GtkUIManager *manager,
513                   GtkAction *action,
514                   GtkWidget *proxy,
515                   PlumaWindow *window)
516 {
517     if (GTK_IS_MENU_ITEM (proxy))
518     {
519         g_signal_connect (proxy, "select",
520                           G_CALLBACK (menu_item_select_cb), window);
521         g_signal_connect (proxy, "deselect",
522                           G_CALLBACK (menu_item_deselect_cb), window);
523     }
524 }
525 
526 static void
disconnect_proxy_cb(GtkUIManager * manager,GtkAction * action,GtkWidget * proxy,PlumaWindow * window)527 disconnect_proxy_cb (GtkUIManager *manager,
528                      GtkAction    *action,
529                      GtkWidget    *proxy,
530                      PlumaWindow  *window)
531 {
532     if (GTK_IS_MENU_ITEM (proxy))
533     {
534         g_signal_handlers_disconnect_by_func
535             (proxy, G_CALLBACK (menu_item_select_cb), window);
536         g_signal_handlers_disconnect_by_func
537             (proxy, G_CALLBACK (menu_item_deselect_cb), window);
538     }
539 }
540 
541 static void
apply_toolbar_style(PlumaWindow * window,GtkWidget * toolbar)542 apply_toolbar_style (PlumaWindow *window,
543                      GtkWidget   *toolbar)
544 {
545     switch (window->priv->toolbar_style)
546     {
547         case PLUMA_TOOLBAR_SYSTEM:
548             pluma_debug_message (DEBUG_WINDOW, "PLUMA: SYSTEM");
549             gtk_toolbar_unset_style (GTK_TOOLBAR (toolbar));
550             break;
551 
552         case PLUMA_TOOLBAR_ICONS:
553             pluma_debug_message (DEBUG_WINDOW, "PLUMA: ICONS");
554             gtk_toolbar_set_style (GTK_TOOLBAR (toolbar),
555                                    GTK_TOOLBAR_ICONS);
556             break;
557 
558         case PLUMA_TOOLBAR_ICONS_AND_TEXT:
559             pluma_debug_message (DEBUG_WINDOW, "PLUMA: ICONS_AND_TEXT");
560             gtk_toolbar_set_style (GTK_TOOLBAR (toolbar),
561                                    GTK_TOOLBAR_BOTH);
562             break;
563 
564         case PLUMA_TOOLBAR_ICONS_BOTH_HORIZ:
565             pluma_debug_message (DEBUG_WINDOW, "PLUMA: ICONS_BOTH_HORIZ");
566             gtk_toolbar_set_style (GTK_TOOLBAR (toolbar),
567                                    GTK_TOOLBAR_BOTH_HORIZ);
568             break;
569     }
570 }
571 
572 /* Returns TRUE if toolbar is visible */
573 static gboolean
set_toolbar_style(PlumaWindow * window,PlumaWindow * origin)574 set_toolbar_style (PlumaWindow *window,
575                    PlumaWindow *origin)
576 {
577     gboolean visible;
578     PlumaToolbarSetting style;
579     GtkAction *action;
580 
581     if (origin == NULL)
582         visible = g_settings_get_boolean (window->priv->editor_settings,
583                                           PLUMA_SETTINGS_TOOLBAR_VISIBLE);
584     else
585         visible = gtk_widget_get_visible (origin->priv->toolbar);
586 
587     /* Set visibility */
588     if (visible)
589         gtk_widget_show (window->priv->toolbar);
590     else
591         gtk_widget_hide (window->priv->toolbar);
592 
593     action = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
594                                           "ViewToolbar");
595 
596     if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible)
597         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
598 
599     /* Set style */
600     if (origin == NULL)
601     {
602         PlumaSettings *settings;
603 
604         settings = _pluma_settings_get_singleton ();
605         style = pluma_settings_get_toolbar_style (settings);
606     }
607     else
608     {
609         style = origin->priv->toolbar_style;
610     }
611 
612     window->priv->toolbar_style = style;
613 
614     apply_toolbar_style (window, window->priv->toolbar);
615 
616     return visible;
617 }
618 
619 static void
update_next_prev_doc_sensitivity(PlumaWindow * window,PlumaTab * tab)620 update_next_prev_doc_sensitivity (PlumaWindow *window,
621                                   PlumaTab    *tab)
622 {
623     gint         tab_number;
624     GtkNotebook *notebook;
625     GtkAction   *action;
626 
627     pluma_debug (DEBUG_WINDOW);
628 
629     notebook = GTK_NOTEBOOK (_pluma_window_get_notebook (window));
630 
631     tab_number = gtk_notebook_page_num (notebook, GTK_WIDGET (tab));
632     g_return_if_fail (tab_number >= 0);
633 
634     action = gtk_action_group_get_action (window->priv->action_group,
635                                           "DocumentsPreviousDocument");
636     gtk_action_set_sensitive (action, tab_number != 0);
637 
638     action = gtk_action_group_get_action (window->priv->action_group,
639                                           "DocumentsNextDocument");
640     gtk_action_set_sensitive (action,
641                               tab_number < gtk_notebook_get_n_pages (notebook) - 1);
642 }
643 
644 static void
update_next_prev_doc_sensitivity_per_window(PlumaWindow * window)645 update_next_prev_doc_sensitivity_per_window (PlumaWindow *window)
646 {
647     PlumaTab  *tab;
648     GtkAction *action;
649 
650     pluma_debug (DEBUG_WINDOW);
651 
652     tab = pluma_window_get_active_tab (window);
653     if (tab != NULL)
654     {
655         update_next_prev_doc_sensitivity (window, tab);
656 
657         return;
658     }
659 
660     action = gtk_action_group_get_action (window->priv->action_group,
661                                           "DocumentsPreviousDocument");
662     gtk_action_set_sensitive (action, FALSE);
663 
664     action = gtk_action_group_get_action (window->priv->action_group,
665                                           "DocumentsNextDocument");
666     gtk_action_set_sensitive (action, FALSE);
667 
668 }
669 
670 static void
received_clipboard_contents(GtkClipboard * clipboard,GtkSelectionData * selection_data,PlumaWindow * window)671 received_clipboard_contents (GtkClipboard     *clipboard,
672                              GtkSelectionData *selection_data,
673                              PlumaWindow      *window)
674 {
675     gboolean sens;
676     GtkAction *action;
677 
678     /* getting clipboard contents is async, so we need to
679      * get the current tab and its state */
680 
681     if (window->priv->active_tab != NULL)
682     {
683         PlumaTabState state;
684         gboolean state_normal;
685 
686         state = pluma_tab_get_state (window->priv->active_tab);
687         state_normal = (state == PLUMA_TAB_STATE_NORMAL);
688 
689         sens = state_normal &&
690                gtk_selection_data_targets_include_text (selection_data);
691     }
692     else
693     {
694         sens = FALSE;
695     }
696 
697     action = gtk_action_group_get_action (window->priv->action_group, "EditPaste");
698 
699     gtk_action_set_sensitive (action, sens);
700 
701     g_object_unref (window);
702 }
703 
704 static void
set_paste_sensitivity_according_to_clipboard(PlumaWindow * window,GtkClipboard * clipboard)705 set_paste_sensitivity_according_to_clipboard (PlumaWindow  *window,
706                                               GtkClipboard *clipboard)
707 {
708     GdkDisplay *display;
709 
710     display = gtk_clipboard_get_display (clipboard);
711 
712     if (gdk_display_supports_selection_notification (display))
713     {
714         gtk_clipboard_request_contents (clipboard,
715                                         gdk_atom_intern_static_string ("TARGETS"),
716                                         (GtkClipboardReceivedFunc) received_clipboard_contents,
717                                         g_object_ref (window));
718     }
719     else
720     {
721         GtkAction *action;
722 
723         action = gtk_action_group_get_action (window->priv->action_group,
724                                               "EditPaste");
725 
726         /* XFIXES extension not availbale, make
727          * Paste always sensitive */
728         gtk_action_set_sensitive (action, TRUE);
729     }
730 }
731 
732 static void
set_sensitivity_according_to_tab(PlumaWindow * window,PlumaTab * tab)733 set_sensitivity_according_to_tab (PlumaWindow *window,
734                                   PlumaTab    *tab)
735 {
736     PlumaDocument *doc;
737     PlumaView     *view;
738     GtkAction     *action;
739     gboolean       b;
740     gboolean       state_normal;
741     gboolean       editable;
742     PlumaTabState  state;
743     GtkClipboard  *clipboard;
744     PlumaLockdownMask lockdown;
745     gboolean       enable_syntax_highlighting;
746 
747     g_return_if_fail (PLUMA_TAB (tab));
748 
749     pluma_debug (DEBUG_WINDOW);
750 
751     enable_syntax_highlighting = g_settings_get_boolean (window->priv->editor_settings,
752                                                          PLUMA_SETTINGS_SYNTAX_HIGHLIGHTING);
753 
754     lockdown = pluma_app_get_lockdown (pluma_app_get_default ());
755 
756     state = pluma_tab_get_state (tab);
757     state_normal = (state == PLUMA_TAB_STATE_NORMAL);
758 
759     view = pluma_tab_get_view (tab);
760     editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (view));
761 
762     doc = PLUMA_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
763 
764     clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window),
765                                           GDK_SELECTION_CLIPBOARD);
766 
767     action = gtk_action_group_get_action (window->priv->action_group,
768                                           "FileSave");
769 
770     if (state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) {
771         gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (doc), TRUE);
772     }
773 
774     gtk_action_set_sensitive (action,
775                               (state_normal ||
776                               (state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) ||
777                               (state == PLUMA_TAB_STATE_SHOWING_PRINT_PREVIEW)) &&
778                               !pluma_document_get_readonly (doc) &&
779                               !(lockdown & PLUMA_LOCKDOWN_SAVE_TO_DISK) &&
780                               (cansave) &&
781                               (editable));
782 
783     action = gtk_action_group_get_action (window->priv->action_group,
784                                           "FileSaveAs");
785     gtk_action_set_sensitive (action,
786                               (state_normal ||
787                               (state == PLUMA_TAB_STATE_SAVING_ERROR) ||
788                               (state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) ||
789                               (state == PLUMA_TAB_STATE_SHOWING_PRINT_PREVIEW)) &&
790                               !(lockdown & PLUMA_LOCKDOWN_SAVE_TO_DISK));
791 
792     action = gtk_action_group_get_action (window->priv->action_group,
793                                           "FileRevert");
794     gtk_action_set_sensitive (action,
795                               (state_normal ||
796                               (state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) &&
797                               !pluma_document_is_untitled (doc));
798 
799     action = gtk_action_group_get_action (window->priv->action_group,
800                                           "FilePrintPreview");
801     gtk_action_set_sensitive (action,
802                               state_normal &&
803                               !(lockdown & PLUMA_LOCKDOWN_PRINTING));
804 
805     action = gtk_action_group_get_action (window->priv->action_group,
806                                           "FilePrint");
807     gtk_action_set_sensitive (action,
808                               (state_normal ||
809                               (state == PLUMA_TAB_STATE_SHOWING_PRINT_PREVIEW)) &&
810                               !(lockdown & PLUMA_LOCKDOWN_PRINTING));
811 
812     action = gtk_action_group_get_action (window->priv->close_action_group,
813                                           "FileClose");
814 
815     gtk_action_set_sensitive (action,
816                               (state != PLUMA_TAB_STATE_CLOSING) &&
817                               (state != PLUMA_TAB_STATE_SAVING) &&
818                               (state != PLUMA_TAB_STATE_SHOWING_PRINT_PREVIEW) &&
819                               (state != PLUMA_TAB_STATE_PRINTING) &&
820                               (state != PLUMA_TAB_STATE_PRINT_PREVIEWING) &&
821                               (state != PLUMA_TAB_STATE_SAVING_ERROR));
822 
823     action = gtk_action_group_get_action (window->priv->action_group,
824                                           "EditUndo");
825     gtk_action_set_sensitive (action,
826                               state_normal &&
827                               gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (doc)));
828 
829     action = gtk_action_group_get_action (window->priv->action_group,
830                                           "EditRedo");
831     gtk_action_set_sensitive (action,
832                               state_normal &&
833                               gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (doc)));
834 
835     action = gtk_action_group_get_action (window->priv->action_group,
836                                           "EditCut");
837     gtk_action_set_sensitive (action,
838                               state_normal &&
839                               editable &&
840                               gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc)));
841 
842     action = gtk_action_group_get_action (window->priv->action_group,
843                                           "EditCopy");
844     gtk_action_set_sensitive (action,
845                               (state_normal ||
846                                state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) &&
847                               gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc)));
848 
849     action = gtk_action_group_get_action (window->priv->action_group,
850                                           "EditPaste");
851     if (state_normal && editable)
852     {
853         set_paste_sensitivity_according_to_clipboard (window, clipboard);
854     }
855     else
856     {
857         gtk_action_set_sensitive (action, FALSE);
858     }
859 
860     action = gtk_action_group_get_action (window->priv->action_group,
861                                           "EditDelete");
862     gtk_action_set_sensitive (action,
863                               state_normal &&
864                               editable &&
865                               gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc)));
866 
867     action = gtk_action_group_get_action (window->priv->action_group,
868                                           "SearchFind");
869     gtk_action_set_sensitive (action,
870                               (state_normal ||
871                                state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION));
872 
873     action = gtk_action_group_get_action (window->priv->action_group,
874                                           "SearchIncrementalSearch");
875     gtk_action_set_sensitive (action,
876                               (state_normal ||
877                               state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION));
878 
879     action = gtk_action_group_get_action (window->priv->action_group,
880                                           "SearchReplace");
881     gtk_action_set_sensitive (action,
882                               state_normal &&
883                               editable);
884 
885     b = pluma_document_get_can_search_again (doc);
886     action = gtk_action_group_get_action (window->priv->action_group,
887                                           "SearchFindNext");
888     gtk_action_set_sensitive (action,
889                               (state_normal ||
890                               state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b);
891 
892     action = gtk_action_group_get_action (window->priv->action_group,
893                                           "SearchFindPrevious");
894     gtk_action_set_sensitive (action,
895                               (state_normal ||
896                               state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b);
897 
898     action = gtk_action_group_get_action (window->priv->action_group,
899                                          "SearchClearHighlight");
900     gtk_action_set_sensitive (action,
901                               (state_normal ||
902                               state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b);
903 
904     action = gtk_action_group_get_action (window->priv->action_group,
905                                           "SearchGoToLine");
906     gtk_action_set_sensitive (action,
907                               (state_normal ||
908                               state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION));
909 
910     action = gtk_action_group_get_action (window->priv->action_group,
911                                           "ViewHighlightMode");
912     gtk_action_set_sensitive (action,
913                               (state != PLUMA_TAB_STATE_CLOSING) &&
914                               enable_syntax_highlighting);
915 
916     update_next_prev_doc_sensitivity (window, tab);
917 
918     peas_extension_set_call (window->priv->extensions, "update_state");
919 }
920 
921 static void
language_toggled(GtkToggleAction * action,PlumaWindow * window)922 language_toggled (GtkToggleAction *action,
923                   PlumaWindow     *window)
924 {
925     PlumaDocument *doc;
926     GtkSourceLanguage *lang;
927     const gchar *lang_id;
928 
929     if (gtk_toggle_action_get_active (action) == FALSE)
930         return;
931 
932     doc = pluma_window_get_active_document (window);
933     if (doc == NULL)
934         return;
935 
936     lang_id = gtk_action_get_name (GTK_ACTION (action));
937 
938     if (strcmp (lang_id, LANGUAGE_NONE) == 0)
939     {
940         /* Normal (no highlighting) */
941         lang = NULL;
942     }
943     else
944     {
945         lang = gtk_source_language_manager_get_language (pluma_get_language_manager (),
946                                                          lang_id);
947         if (lang == NULL)
948         {
949             g_warning ("Could not get language %s\n", lang_id);
950         }
951     }
952 
953     pluma_document_set_language (doc, lang);
954 }
955 
956 static gchar *
escape_section_name(const gchar * name)957 escape_section_name (const gchar *name)
958 {
959     gchar *ret;
960 
961     ret = g_markup_escape_text (name, -1);
962 
963     /* Replace '/' with '-' to avoid problems in xml paths */
964     g_strdelimit (ret, "/", '-');
965 
966     return ret;
967 }
968 
969 static void
create_language_menu_item(GtkSourceLanguage * lang,gint index,guint ui_id,PlumaWindow * window)970 create_language_menu_item (GtkSourceLanguage *lang,
971                            gint           index,
972                            guint          ui_id,
973                            PlumaWindow       *window)
974 {
975     GtkAction *section_action;
976     GtkRadioAction *action;
977     GtkAction *normal_action;
978     GSList *group;
979     const gchar *section;
980     gchar *escaped_section;
981     const gchar *lang_id;
982     const gchar *lang_name;
983     gchar *escaped_lang_name;
984     gchar *tip;
985     gchar *path;
986 
987     section = gtk_source_language_get_section (lang);
988     escaped_section = escape_section_name (section);
989 
990     /* check if the section submenu exists or create it */
991     section_action = gtk_action_group_get_action (window->priv->languages_action_group,
992                                                   escaped_section);
993 
994     if (section_action == NULL)
995     {
996         gchar *section_name;
997 
998         section_name = pluma_utils_escape_underscores (section, -1);
999 
1000         section_action = gtk_action_new (escaped_section,
1001                                          section_name,
1002                                          NULL,
1003                                          NULL);
1004 
1005         g_free (section_name);
1006 
1007         gtk_action_group_add_action (window->priv->languages_action_group,
1008                                      section_action);
1009         g_object_unref (section_action);
1010 
1011         gtk_ui_manager_add_ui (window->priv->manager,
1012                                ui_id,
1013                                "/MenuBar/ViewMenu/ViewHighlightModeMenu/LanguagesMenuPlaceholder",
1014                                escaped_section,
1015                                escaped_section,
1016                                GTK_UI_MANAGER_MENU,
1017                                FALSE);
1018     }
1019 
1020     /* now add the language item to the section */
1021     lang_name = gtk_source_language_get_name (lang);
1022     lang_id = gtk_source_language_get_id (lang);
1023 
1024     escaped_lang_name = pluma_utils_escape_underscores (lang_name, -1);
1025 
1026     tip = g_strdup_printf (_("Use %s highlight mode"), lang_name);
1027     path = g_strdup_printf ("/MenuBar/ViewMenu/ViewHighlightModeMenu/LanguagesMenuPlaceholder/%s",
1028                             escaped_section);
1029 
1030     action = gtk_radio_action_new (lang_id,
1031                                    escaped_lang_name,
1032                                    tip,
1033                                    NULL,
1034                                    index);
1035 
1036     g_free (escaped_lang_name);
1037 
1038     /* Action is added with a NULL accel to make the accel overridable */
1039     gtk_action_group_add_action_with_accel (window->priv->languages_action_group,
1040                                             GTK_ACTION (action),
1041                                             NULL);
1042     g_object_unref (action);
1043 
1044     /* add the action to the same radio group of the "Normal" action */
1045     normal_action = gtk_action_group_get_action (window->priv->languages_action_group,
1046                                                  LANGUAGE_NONE);
1047     group = gtk_radio_action_get_group (GTK_RADIO_ACTION (normal_action));
1048     gtk_radio_action_set_group (action, group);
1049 
1050     g_signal_connect (action,
1051                       "activate",
1052                       G_CALLBACK (language_toggled),
1053                       window);
1054 
1055     gtk_ui_manager_add_ui (window->priv->manager,
1056                            ui_id,
1057                            path,
1058                            lang_id,
1059                            lang_id,
1060                            GTK_UI_MANAGER_MENUITEM,
1061                            FALSE);
1062 
1063     g_free (path);
1064     g_free (tip);
1065     g_free (escaped_section);
1066 }
1067 
1068 static void
create_languages_menu(PlumaWindow * window)1069 create_languages_menu (PlumaWindow *window)
1070 {
1071     GtkRadioAction *action_none;
1072     GSList *languages;
1073     GSList *l;
1074     guint id;
1075     gint i;
1076 
1077     pluma_debug (DEBUG_WINDOW);
1078 
1079     /* add the "Plain Text" item before all the others */
1080 
1081     /* Translators: "Plain Text" means that no highlight mode is selected in the
1082      * "View->Highlight Mode" submenu and so syntax highlighting is disabled */
1083     action_none = gtk_radio_action_new (LANGUAGE_NONE, _("Plain Text"),
1084                                         _("Disable syntax highlighting"),
1085                                         NULL,
1086                                         -1);
1087 
1088     gtk_action_group_add_action (window->priv->languages_action_group,
1089                                  GTK_ACTION (action_none));
1090     g_object_unref (action_none);
1091 
1092     g_signal_connect (action_none,
1093                       "activate",
1094                       G_CALLBACK (language_toggled),
1095                       window);
1096 
1097     id = gtk_ui_manager_new_merge_id (window->priv->manager);
1098 
1099     gtk_ui_manager_add_ui (window->priv->manager,
1100                            id,
1101                            "/MenuBar/ViewMenu/ViewHighlightModeMenu/LanguagesMenuPlaceholder",
1102                            LANGUAGE_NONE,
1103                            LANGUAGE_NONE,
1104                            GTK_UI_MANAGER_MENUITEM,
1105                            TRUE);
1106 
1107     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action_none), TRUE);
1108 
1109     /* now add all the known languages */
1110     languages = pluma_language_manager_list_languages_sorted (pluma_get_language_manager (),
1111                                                               FALSE);
1112 
1113     for (l = languages, i = 0; l != NULL; l = l->next, ++i)
1114     {
1115         create_language_menu_item (l->data,
1116                                    i,
1117                                    id,
1118                                    window);
1119     }
1120 
1121     g_slist_free (languages);
1122 }
1123 
1124 static void
update_languages_menu(PlumaWindow * window)1125 update_languages_menu (PlumaWindow *window)
1126 {
1127     PlumaDocument *doc;
1128     GList *actions;
1129     GList *l;
1130     GtkAction *action;
1131     GtkSourceLanguage *lang;
1132     const gchar *lang_id;
1133 
1134     doc = pluma_window_get_active_document (window);
1135     if (doc == NULL)
1136         return;
1137 
1138     lang = pluma_document_get_language (doc);
1139     if (lang != NULL)
1140         lang_id = gtk_source_language_get_id (lang);
1141     else
1142         lang_id = LANGUAGE_NONE;
1143 
1144     actions = gtk_action_group_list_actions (window->priv->languages_action_group);
1145 
1146     /* prevent recursion */
1147     for (l = actions; l != NULL; l = l->next)
1148     {
1149         g_signal_handlers_block_by_func (GTK_ACTION (l->data),
1150                                          G_CALLBACK (language_toggled),
1151                                          window);
1152     }
1153 
1154     action = gtk_action_group_get_action (window->priv->languages_action_group,
1155                                           lang_id);
1156 
1157     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1158 
1159     for (l = actions; l != NULL; l = l->next)
1160     {
1161         g_signal_handlers_unblock_by_func (GTK_ACTION (l->data),
1162                                            G_CALLBACK (language_toggled),
1163                                            window);
1164     }
1165 
1166     g_list_free (actions);
1167 }
1168 
1169 void
_pluma_recent_add(PlumaWindow * window,const gchar * uri,const gchar * mime)1170 _pluma_recent_add (PlumaWindow *window,
1171                    const gchar *uri,
1172                    const gchar *mime)
1173 {
1174     GtkRecentManager *recent_manager;
1175     GtkRecentData recent_data;
1176 
1177     static gchar *groups[2] = {
1178         "pluma",
1179         NULL
1180     };
1181 
1182     recent_manager =  gtk_recent_manager_get_default ();
1183 
1184     recent_data.display_name = NULL;
1185     recent_data.description = NULL;
1186     recent_data.mime_type = (gchar *) mime;
1187     recent_data.app_name = (gchar *) g_get_application_name ();
1188     recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
1189     recent_data.groups = groups;
1190     recent_data.is_private = FALSE;
1191 
1192     gtk_recent_manager_add_full (recent_manager,
1193                                  uri,
1194                                  &recent_data);
1195 
1196     g_free (recent_data.app_exec);
1197 }
1198 
1199 void
_pluma_recent_remove(PlumaWindow * window,const gchar * uri)1200 _pluma_recent_remove (PlumaWindow *window,
1201                       const gchar *uri)
1202 {
1203     GtkRecentManager *recent_manager;
1204 
1205     recent_manager =  gtk_recent_manager_get_default ();
1206 
1207     gtk_recent_manager_remove_item (recent_manager, uri, NULL);
1208 }
1209 
1210 static void
open_recent_file(const gchar * uri,PlumaWindow * window)1211 open_recent_file (const gchar *uri,
1212                   PlumaWindow *window)
1213 {
1214     GSList *uris = NULL;
1215 
1216     uris = g_slist_prepend (uris, (gpointer) uri);
1217 
1218     if (pluma_commands_load_uris (window, uris, NULL, 0) != 1)
1219     {
1220         _pluma_recent_remove (window, uri);
1221     }
1222 
1223     g_slist_free (uris);
1224 }
1225 
1226 static void
recent_chooser_item_activated(GtkRecentChooser * chooser,PlumaWindow * window)1227 recent_chooser_item_activated (GtkRecentChooser *chooser,
1228                                PlumaWindow      *window)
1229 {
1230     gchar *uri;
1231 
1232     uri = gtk_recent_chooser_get_current_uri (chooser);
1233 
1234     open_recent_file (uri, window);
1235 
1236     g_free (uri);
1237 }
1238 
1239 static void
recents_menu_activate(GtkAction * action,PlumaWindow * window)1240 recents_menu_activate (GtkAction   *action,
1241                        PlumaWindow *window)
1242 {
1243     GtkRecentInfo *info;
1244     const gchar *uri;
1245 
1246     info = g_object_get_data (G_OBJECT (action), "gtk-recent-info");
1247     g_return_if_fail (info != NULL);
1248 
1249     uri = gtk_recent_info_get_uri (info);
1250 
1251     open_recent_file (uri, window);
1252 }
1253 
1254 static gint
sort_recents_mru(GtkRecentInfo * a,GtkRecentInfo * b)1255 sort_recents_mru (GtkRecentInfo *a, GtkRecentInfo *b)
1256 {
1257     return (gtk_recent_info_get_modified (b) - gtk_recent_info_get_modified (a));
1258 }
1259 
1260 static void    update_recent_files_menu (PlumaWindow *window);
1261 
1262 static void
recent_manager_changed(GtkRecentManager * manager,PlumaWindow * window)1263 recent_manager_changed (GtkRecentManager *manager,
1264                         PlumaWindow      *window)
1265 {
1266     /* regenerate the menu when the model changes */
1267     update_recent_files_menu (window);
1268 }
1269 
1270 /*
1271  * Manually construct the inline recents list in the File menu.
1272  * Hopefully gtk 2.12 will add support for it.
1273  */
1274 static void
update_recent_files_menu(PlumaWindow * window)1275 update_recent_files_menu (PlumaWindow *window)
1276 {
1277     PlumaWindowPrivate *p = window->priv;
1278     GtkRecentManager *recent_manager;
1279     guint max_recents;
1280     GList *actions, *l, *items;
1281     GList *filtered_items = NULL;
1282     gint i;
1283 
1284     pluma_debug (DEBUG_WINDOW);
1285 
1286     max_recents = g_settings_get_uint (window->priv->editor_settings, PLUMA_SETTINGS_MAX_RECENTS);
1287 
1288     g_return_if_fail (p->recents_action_group != NULL);
1289 
1290     if (p->recents_menu_ui_id != 0)
1291         gtk_ui_manager_remove_ui (p->manager, p->recents_menu_ui_id);
1292 
1293     actions = gtk_action_group_list_actions (p->recents_action_group);
1294     for (l = actions; l != NULL; l = l->next)
1295     {
1296         g_signal_handlers_disconnect_by_func (GTK_ACTION (l->data),
1297                                               G_CALLBACK (recents_menu_activate),
1298                                               window);
1299          gtk_action_group_remove_action (p->recents_action_group,
1300                                          GTK_ACTION (l->data));
1301     }
1302     g_list_free (actions);
1303 
1304     p->recents_menu_ui_id = gtk_ui_manager_new_merge_id (p->manager);
1305 
1306     recent_manager =  gtk_recent_manager_get_default ();
1307     items = gtk_recent_manager_get_items (recent_manager);
1308 
1309     /* filter */
1310     for (l = items; l != NULL; l = l->next)
1311     {
1312         GtkRecentInfo *info = l->data;
1313 
1314         if (!gtk_recent_info_has_group (info, "pluma"))
1315             continue;
1316 
1317         filtered_items = g_list_prepend (filtered_items, info);
1318     }
1319 
1320     /* sort */
1321     filtered_items = g_list_sort (filtered_items,
1322                                   (GCompareFunc) sort_recents_mru);
1323 
1324     i = 0;
1325     for (l = filtered_items; l != NULL; l = l->next)
1326     {
1327         gchar *action_name;
1328         const gchar *display_name;
1329         gchar *escaped;
1330         gchar *label;
1331         gchar *uri;
1332         gchar *ruri;
1333         gchar *tip;
1334         GtkAction *action;
1335         GtkRecentInfo *info = l->data;
1336 
1337         /* clamp */
1338         if (i >= max_recents)
1339             break;
1340 
1341         i++;
1342 
1343         action_name = g_strdup_printf ("recent-info-%d", i);
1344 
1345         display_name = gtk_recent_info_get_display_name (info);
1346         escaped = pluma_utils_escape_underscores (display_name, -1);
1347         if (i >= 10)
1348             label = g_strdup_printf ("%d.  %s",
1349                                      i,
1350                                      escaped);
1351         else
1352             label = g_strdup_printf ("_%d.  %s",
1353                                      i,
1354                                      escaped);
1355         g_free (escaped);
1356 
1357         /* gtk_recent_info_get_uri_display (info) is buggy and
1358          * works only for local files */
1359         uri = pluma_utils_uri_for_display (gtk_recent_info_get_uri (info));
1360         ruri = pluma_utils_replace_home_dir_with_tilde (uri);
1361         g_free (uri);
1362 
1363         /* Translators: %s is a URI */
1364         tip = g_strdup_printf (_("Open '%s'"), ruri);
1365         g_free (ruri);
1366 
1367         action = gtk_action_new (action_name,
1368                                  label,
1369                                  tip,
1370                                  NULL);
1371 
1372         g_object_set_data_full (G_OBJECT (action),
1373                                 "gtk-recent-info",
1374                                 gtk_recent_info_ref (info),
1375                                 (GDestroyNotify) gtk_recent_info_unref);
1376 
1377         g_signal_connect (action,
1378                           "activate",
1379                           G_CALLBACK (recents_menu_activate),
1380                           window);
1381 
1382         gtk_action_group_add_action (p->recents_action_group,
1383                                      action);
1384         g_object_unref (action);
1385 
1386         gtk_ui_manager_add_ui (p->manager,
1387                                p->recents_menu_ui_id,
1388                                "/MenuBar/FileMenu/FileRecentsPlaceholder",
1389                                action_name,
1390                                action_name,
1391                                GTK_UI_MANAGER_MENUITEM,
1392                                FALSE);
1393 
1394         g_free (action_name);
1395         g_free (label);
1396         g_free (tip);
1397     }
1398 
1399     g_list_free (filtered_items);
1400 
1401     g_list_free_full (items, (GDestroyNotify) gtk_recent_info_unref);
1402 }
1403 
1404 static void
set_non_homogeneus(GtkWidget * widget,gpointer data)1405 set_non_homogeneus (GtkWidget *widget, gpointer data)
1406 {
1407     gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1408 }
1409 
1410 static void
toolbar_visibility_changed(GtkWidget * toolbar,PlumaWindow * window)1411 toolbar_visibility_changed (GtkWidget   *toolbar,
1412                             PlumaWindow *window)
1413 {
1414     gboolean visible;
1415     GtkAction *action;
1416 
1417     visible = gtk_widget_get_visible (toolbar);
1418 
1419     g_settings_set_boolean (window->priv->editor_settings,
1420                             PLUMA_SETTINGS_TOOLBAR_VISIBLE, visible);
1421 
1422     action = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
1423                                           "ViewToolbar");
1424 
1425     if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible)
1426         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
1427 }
1428 
1429 static GtkWidget *
setup_toolbar_open_button(PlumaWindow * window,GtkWidget * toolbar)1430 setup_toolbar_open_button (PlumaWindow *window,
1431                            GtkWidget *toolbar)
1432 {
1433     GtkRecentManager *recent_manager;
1434     GtkRecentFilter *filter;
1435     GtkWidget *toolbar_recent_menu;
1436     GtkToolItem *open_button;
1437     GtkAction *action;
1438     guint max_recents;
1439 
1440     recent_manager = gtk_recent_manager_get_default ();
1441 
1442     max_recents = g_settings_get_uint (window->priv->editor_settings, PLUMA_SETTINGS_MAX_RECENTS);
1443 
1444     /* recent files menu tool button */
1445     toolbar_recent_menu = gtk_recent_chooser_menu_new_for_manager (recent_manager);
1446 
1447     gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (toolbar_recent_menu),
1448                                        FALSE);
1449     gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (toolbar_recent_menu),
1450                                       GTK_RECENT_SORT_MRU);
1451     gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (toolbar_recent_menu),
1452                                   max_recents);
1453 
1454     filter = gtk_recent_filter_new ();
1455     gtk_recent_filter_add_group (filter, "pluma");
1456     gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (toolbar_recent_menu),
1457                                    filter);
1458 
1459     g_signal_connect (toolbar_recent_menu,
1460                       "item_activated",
1461                       G_CALLBACK (recent_chooser_item_activated),
1462                       window);
1463 
1464     /* add the custom Open button to the toolbar */
1465     open_button = gtk_menu_tool_button_new (gtk_image_new_from_icon_name ("document-open",
1466                                             GTK_ICON_SIZE_MENU),
1467                                             _("Open a file"));
1468 
1469     gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (open_button),
1470                                    toolbar_recent_menu);
1471 
1472     gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (open_button),
1473                                                  _("Open a recently used file"));
1474 
1475     action = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
1476                                          "FileOpen");
1477     g_object_set (action,
1478                   "is_important", TRUE,
1479                   "short_label", _("Open"),
1480                   NULL);
1481     gtk_activatable_set_related_action (GTK_ACTIVATABLE (open_button),
1482                                         action);
1483 
1484     gtk_toolbar_insert (GTK_TOOLBAR (toolbar),
1485                         open_button,
1486                         1);
1487 
1488     return toolbar_recent_menu;
1489 }
1490 
1491 static void
create_menu_bar_and_toolbar(PlumaWindow * window,GtkWidget * main_box)1492 create_menu_bar_and_toolbar (PlumaWindow *window,
1493                              GtkWidget   *main_box)
1494 {
1495     GtkActionGroup *action_group;
1496     GtkAction *action;
1497     GtkUIManager *manager;
1498     GtkRecentManager *recent_manager;
1499     GError *error = NULL;
1500 
1501     pluma_debug (DEBUG_WINDOW);
1502 
1503     manager = gtk_ui_manager_new ();
1504     window->priv->manager = manager;
1505 
1506     gtk_window_add_accel_group (GTK_WINDOW (window),
1507                                 gtk_ui_manager_get_accel_group (manager));
1508 
1509     action_group = gtk_action_group_new ("PlumaWindowAlwaysSensitiveActions");
1510     gtk_action_group_set_translation_domain (action_group, NULL);
1511     gtk_action_group_add_actions (action_group,
1512                                   pluma_always_sensitive_menu_entries,
1513                                   G_N_ELEMENTS (pluma_always_sensitive_menu_entries),
1514                                   window);
1515     gtk_action_group_add_toggle_actions (action_group,
1516                                          pluma_always_sensitive_toggle_menu_entries,
1517                                          G_N_ELEMENTS (pluma_always_sensitive_toggle_menu_entries),
1518                                          window);
1519 
1520     gtk_ui_manager_insert_action_group (manager, action_group, 0);
1521     g_object_unref (action_group);
1522     window->priv->always_sensitive_action_group = action_group;
1523 
1524     action_group = gtk_action_group_new ("PlumaWindowActions");
1525     gtk_action_group_set_translation_domain (action_group, NULL);
1526     gtk_action_group_add_actions (action_group,
1527                                   pluma_menu_entries,
1528                                   G_N_ELEMENTS (pluma_menu_entries),
1529                                   window);
1530     gtk_ui_manager_insert_action_group (manager, action_group, 0);
1531     g_object_unref (action_group);
1532     window->priv->action_group = action_group;
1533 
1534     /* set short labels to use in the toolbar */
1535     action = gtk_action_group_get_action (action_group, "FileSave");
1536     g_object_set (action, "short_label", _("Save"), NULL);
1537     action = gtk_action_group_get_action (action_group, "FilePrint");
1538     g_object_set (action, "short_label", _("Print"), NULL);
1539     action = gtk_action_group_get_action (action_group, "SearchFind");
1540     g_object_set (action, "short_label", _("Find"), NULL);
1541     action = gtk_action_group_get_action (action_group, "SearchReplace");
1542     g_object_set (action, "short_label", _("Replace"), NULL);
1543 
1544     /* set which actions should have priority on the toolbar */
1545     action = gtk_action_group_get_action (action_group, "FileSave");
1546     g_object_set (action, "is_important", TRUE, NULL);
1547     action = gtk_action_group_get_action (action_group, "EditUndo");
1548     g_object_set (action, "is_important", TRUE, NULL);
1549 
1550     action_group = gtk_action_group_new ("PlumaQuitWindowActions");
1551     gtk_action_group_set_translation_domain (action_group, NULL);
1552     gtk_action_group_add_actions (action_group,
1553                                   pluma_quit_menu_entries,
1554                                   G_N_ELEMENTS (pluma_quit_menu_entries),
1555                                   window);
1556 
1557     gtk_ui_manager_insert_action_group (manager, action_group, 0);
1558     g_object_unref (action_group);
1559     window->priv->quit_action_group = action_group;
1560 
1561     action_group = gtk_action_group_new ("PlumaCloseWindowActions");
1562     gtk_action_group_set_translation_domain (action_group, NULL);
1563     gtk_action_group_add_actions (action_group,
1564                                   pluma_close_menu_entries,
1565                                   G_N_ELEMENTS (pluma_close_menu_entries),
1566                                   window);
1567 
1568     gtk_ui_manager_insert_action_group (manager, action_group, 0);
1569     g_object_unref (action_group);
1570     window->priv->close_action_group = action_group;
1571 
1572     action_group = gtk_action_group_new ("PlumaWindowPanesActions");
1573     gtk_action_group_set_translation_domain (action_group, NULL);
1574     gtk_action_group_add_toggle_actions (action_group,
1575                                          pluma_panes_toggle_menu_entries,
1576                                          G_N_ELEMENTS (pluma_panes_toggle_menu_entries),
1577                                          window);
1578 
1579     gtk_ui_manager_insert_action_group (manager, action_group, 0);
1580     g_object_unref (action_group);
1581     window->priv->panes_action_group = action_group;
1582 
1583     /* now load the UI definition */
1584     gtk_ui_manager_add_ui_from_file (manager,
1585                                      PLUMA_DATADIR "/ui/pluma-ui.xml",
1586                                      &error);
1587     if (error != NULL)
1588     {
1589         g_warning ("Could not merge %s: %s",
1590                    PLUMA_DATADIR "/ui/pluma-ui.xml",
1591                    error->message);
1592         g_error_free (error);
1593     }
1594 
1595     /* show tooltips in the statusbar */
1596     g_signal_connect (manager,
1597                       "connect_proxy",
1598                       G_CALLBACK (connect_proxy_cb),
1599                       window);
1600     g_signal_connect (manager,
1601                       "disconnect_proxy",
1602                       G_CALLBACK (disconnect_proxy_cb),
1603                       window);
1604 
1605     /* recent files menu */
1606     action_group = gtk_action_group_new ("RecentFilesActions");
1607     gtk_action_group_set_translation_domain (action_group, NULL);
1608     window->priv->recents_action_group = action_group;
1609     gtk_ui_manager_insert_action_group (manager, action_group, 0);
1610     g_object_unref (action_group);
1611 
1612     recent_manager = gtk_recent_manager_get_default ();
1613     window->priv->recents_handler_id = g_signal_connect (recent_manager,
1614                                                          "changed",
1615                                                          G_CALLBACK (recent_manager_changed),
1616                                                          window);
1617     update_recent_files_menu (window);
1618 
1619     /* languages menu */
1620     action_group = gtk_action_group_new ("LanguagesActions");
1621     gtk_action_group_set_translation_domain (action_group, NULL);
1622     window->priv->languages_action_group = action_group;
1623     gtk_ui_manager_insert_action_group (manager, action_group, 0);
1624     g_object_unref (action_group);
1625     create_languages_menu (window);
1626 
1627     /* list of open documents menu */
1628     action_group = gtk_action_group_new ("DocumentsListActions");
1629     gtk_action_group_set_translation_domain (action_group, NULL);
1630     window->priv->documents_list_action_group = action_group;
1631     gtk_ui_manager_insert_action_group (manager, action_group, 0);
1632     g_object_unref (action_group);
1633 
1634     window->priv->menubar = gtk_ui_manager_get_widget (manager, "/MenuBar");
1635     gtk_box_pack_start (GTK_BOX (main_box),
1636                         window->priv->menubar,
1637                         FALSE,
1638                         FALSE,
1639                         0);
1640 
1641     window->priv->toolbar = gtk_ui_manager_get_widget (manager, "/ToolBar");
1642     gtk_style_context_add_class (gtk_widget_get_style_context (window->priv->toolbar),
1643                                  GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
1644     gtk_box_pack_start (GTK_BOX (main_box),
1645                         window->priv->toolbar,
1646                         FALSE,
1647                         FALSE,
1648                         0);
1649 
1650     set_toolbar_style (window, NULL);
1651 
1652     window->priv->toolbar_recent_menu = setup_toolbar_open_button (window, window->priv->toolbar);
1653 
1654     gtk_container_foreach (GTK_CONTAINER (window->priv->toolbar),
1655                            (GtkCallback)set_non_homogeneus,
1656                            NULL);
1657 
1658     g_signal_connect_after (G_OBJECT (window->priv->toolbar),
1659                             "show",
1660                             G_CALLBACK (toolbar_visibility_changed),
1661                             window);
1662     g_signal_connect_after (G_OBJECT (window->priv->toolbar),
1663                             "hide",
1664                             G_CALLBACK (toolbar_visibility_changed),
1665                             window);
1666 }
1667 
1668 static void
documents_list_menu_activate(GtkToggleAction * action,PlumaWindow * window)1669 documents_list_menu_activate (GtkToggleAction *action,
1670                               PlumaWindow     *window)
1671 {
1672     gint n;
1673 
1674     if (gtk_toggle_action_get_active (action) == FALSE)
1675         return;
1676 
1677     n = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
1678     gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook), n);
1679 }
1680 
1681 static gchar *
get_menu_tip_for_tab(PlumaTab * tab)1682 get_menu_tip_for_tab (PlumaTab *tab)
1683 {
1684     PlumaDocument *doc;
1685     gchar *uri;
1686     gchar *ruri;
1687     gchar *tip;
1688 
1689     doc = pluma_tab_get_document (tab);
1690 
1691     uri = pluma_document_get_uri_for_display (doc);
1692     ruri = pluma_utils_replace_home_dir_with_tilde (uri);
1693     g_free (uri);
1694 
1695     /* Translators: %s is a URI */
1696     tip =  g_strdup_printf (_("Activate '%s'"), ruri);
1697     g_free (ruri);
1698 
1699     return tip;
1700 }
1701 
1702 static void
update_documents_list_menu(PlumaWindow * window)1703 update_documents_list_menu (PlumaWindow *window)
1704 {
1705     PlumaWindowPrivate *p = window->priv;
1706     GList *actions, *l;
1707     gint n, i;
1708     guint id;
1709     GSList *group = NULL;
1710 
1711     pluma_debug (DEBUG_WINDOW);
1712 
1713     g_return_if_fail (p->documents_list_action_group != NULL);
1714 
1715     if (p->documents_list_menu_ui_id != 0)
1716         gtk_ui_manager_remove_ui (p->manager,
1717                                   p->documents_list_menu_ui_id);
1718 
1719     actions = gtk_action_group_list_actions (p->documents_list_action_group);
1720     for (l = actions; l != NULL; l = l->next)
1721     {
1722         g_signal_handlers_disconnect_by_func (GTK_ACTION (l->data),
1723                                               G_CALLBACK (documents_list_menu_activate),
1724                                               window);
1725          gtk_action_group_remove_action (p->documents_list_action_group,
1726                                          GTK_ACTION (l->data));
1727     }
1728     g_list_free (actions);
1729 
1730     n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (p->notebook));
1731 
1732     id = (n > 0) ? gtk_ui_manager_new_merge_id (p->manager) : 0;
1733 
1734     for (i = 0; i < n; i++)
1735     {
1736         GtkWidget *tab;
1737         GtkRadioAction *action;
1738         gchar *action_name;
1739         gchar *tab_name;
1740         gchar *name;
1741         gchar *tip;
1742         gchar *accel;
1743 
1744         tab = gtk_notebook_get_nth_page (GTK_NOTEBOOK (p->notebook), i);
1745 
1746         /* NOTE: the action is associated to the position of the tab in
1747          * the notebook not to the tab itself! This is needed to work
1748          * around the gtk+ bug #170727: gtk leaves around the accels
1749          * of the action. Since the accel depends on the tab position
1750          * the problem is worked around, action with the same name always
1751          * get the same accel.
1752          */
1753         action_name = g_strdup_printf ("Tab_%d", i);
1754         tab_name = _pluma_tab_get_name (PLUMA_TAB (tab));
1755         name = pluma_utils_escape_underscores (tab_name, -1);
1756         tip =  get_menu_tip_for_tab (PLUMA_TAB (tab));
1757 
1758         /* alt + 1, 2, 3... 0 to switch to the first ten tabs */
1759         accel = (i < 10) ? g_strdup_printf ("<alt>%d", (i + 1) % 10) : NULL;
1760 
1761         action = gtk_radio_action_new (action_name,
1762                                        name,
1763                                        tip,
1764                                        NULL,
1765                                        i);
1766 
1767         if (group != NULL)
1768             gtk_radio_action_set_group (action, group);
1769 
1770         /* note that group changes each time we add an action, so it must be updated */
1771         group = gtk_radio_action_get_group (action);
1772 
1773         gtk_action_group_add_action_with_accel (p->documents_list_action_group,
1774                                                 GTK_ACTION (action),
1775                                                 accel);
1776 
1777         g_signal_connect (action,
1778                           "activate",
1779                           G_CALLBACK (documents_list_menu_activate),
1780                           window);
1781 
1782         gtk_ui_manager_add_ui (p->manager,
1783                                id,
1784                                "/MenuBar/DocumentsMenu/DocumentsListPlaceholder",
1785                                action_name, action_name,
1786                                GTK_UI_MANAGER_MENUITEM,
1787                                FALSE);
1788 
1789         if (PLUMA_TAB (tab) == p->active_tab)
1790             gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1791 
1792         g_object_unref (action);
1793 
1794         g_free (action_name);
1795         g_free (tab_name);
1796         g_free (name);
1797         g_free (tip);
1798         g_free (accel);
1799     }
1800 
1801     p->documents_list_menu_ui_id = id;
1802 }
1803 
1804 /* Returns TRUE if status bar is visible */
1805 static gboolean
set_statusbar_style(PlumaWindow * window,PlumaWindow * origin)1806 set_statusbar_style (PlumaWindow *window,
1807                      PlumaWindow *origin)
1808 {
1809     GtkAction *action;
1810     gboolean visible;
1811 
1812     if (origin == NULL)
1813         visible = g_settings_get_boolean (window->priv->editor_settings,
1814                                           PLUMA_SETTINGS_STATUSBAR_VISIBLE);
1815     else
1816         visible = gtk_widget_get_visible (origin->priv->statusbar);
1817 
1818     if (visible)
1819         gtk_widget_show (window->priv->statusbar);
1820     else
1821         gtk_widget_hide (window->priv->statusbar);
1822 
1823     action = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
1824                                           "ViewStatusbar");
1825 
1826     if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible)
1827         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
1828 
1829     return visible;
1830 }
1831 
1832 static void
statusbar_visibility_changed(GtkWidget * statusbar,PlumaWindow * window)1833 statusbar_visibility_changed (GtkWidget   *statusbar,
1834                               PlumaWindow *window)
1835 {
1836     gboolean visible;
1837     GtkAction *action;
1838 
1839     visible = gtk_widget_get_visible (statusbar);
1840 
1841     g_settings_set_boolean (window->priv->editor_settings,
1842                             PLUMA_SETTINGS_STATUSBAR_VISIBLE, visible);
1843 
1844     action = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
1845                                           "ViewStatusbar");
1846 
1847     if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible)
1848         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
1849 }
1850 
1851 static void
tab_width_combo_changed(PlumaStatusComboBox * combo,GtkMenuItem * item,PlumaWindow * window)1852 tab_width_combo_changed (PlumaStatusComboBox *combo,
1853                          GtkMenuItem     *item,
1854                          PlumaWindow     *window)
1855 {
1856     PlumaView *view;
1857     guint width_data = 0;
1858 
1859     view = pluma_window_get_active_view (window);
1860 
1861     if (!view)
1862         return;
1863 
1864     width_data = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), TAB_WIDTH_DATA));
1865 
1866     if (width_data == 0)
1867         return;
1868 
1869     g_signal_handler_block (view, window->priv->tab_width_id);
1870     gtk_source_view_set_tab_width (GTK_SOURCE_VIEW (view), width_data);
1871     g_signal_handler_unblock (view, window->priv->tab_width_id);
1872 }
1873 
1874 static void
use_spaces_toggled(GtkCheckMenuItem * item,PlumaWindow * window)1875 use_spaces_toggled (GtkCheckMenuItem *item,
1876                     PlumaWindow      *window)
1877 {
1878     PlumaView *view;
1879 
1880     view = pluma_window_get_active_view (window);
1881 
1882     g_signal_handler_block (view, window->priv->spaces_instead_of_tabs_id);
1883     gtk_source_view_set_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (view),
1884                                                        gtk_check_menu_item_get_active (item));
1885     g_signal_handler_unblock (view, window->priv->spaces_instead_of_tabs_id);
1886 }
1887 
1888 static void
language_combo_changed(PlumaStatusComboBox * combo,GtkMenuItem * item,PlumaWindow * window)1889 language_combo_changed (PlumaStatusComboBox *combo,
1890                         GtkMenuItem     *item,
1891                         PlumaWindow     *window)
1892 {
1893     PlumaDocument *doc;
1894     GtkSourceLanguage *language;
1895 
1896     doc = pluma_window_get_active_document (window);
1897 
1898     if (!doc)
1899         return;
1900 
1901     language = GTK_SOURCE_LANGUAGE (g_object_get_data (G_OBJECT (item), LANGUAGE_DATA));
1902 
1903     g_signal_handler_block (doc, window->priv->language_changed_id);
1904     pluma_document_set_language (doc, language);
1905     g_signal_handler_unblock (doc, window->priv->language_changed_id);
1906 }
1907 
1908 typedef struct
1909 {
1910     const gchar *label;
1911     guint width;
1912 } TabWidthDefinition;
1913 
1914 static void
fill_tab_width_combo(PlumaWindow * window)1915 fill_tab_width_combo (PlumaWindow *window)
1916 {
1917     static TabWidthDefinition defs[] = {
1918         {"2", 2},
1919         {"4", 4},
1920         {"8", 8},
1921         {"", 0}, /* custom size */
1922         {NULL, 0}
1923     };
1924 
1925     PlumaStatusComboBox *combo = PLUMA_STATUS_COMBO_BOX (window->priv->tab_width_combo);
1926     guint i = 0;
1927     GtkWidget *item;
1928 
1929     while (defs[i].label != NULL)
1930     {
1931         item = gtk_menu_item_new_with_label (defs[i].label);
1932         g_object_set_data (G_OBJECT (item), TAB_WIDTH_DATA, GINT_TO_POINTER (defs[i].width));
1933 
1934         pluma_status_combo_box_add_item (combo,
1935                                          GTK_MENU_ITEM (item),
1936                                          defs[i].label);
1937 
1938         if (defs[i].width != 0)
1939             gtk_widget_show (item);
1940 
1941         ++i;
1942     }
1943 
1944     item = gtk_separator_menu_item_new ();
1945     pluma_status_combo_box_add_item (combo, GTK_MENU_ITEM (item), NULL);
1946     gtk_widget_show (item);
1947 
1948     item = gtk_check_menu_item_new_with_label (_("Use Spaces"));
1949     pluma_status_combo_box_add_item (combo, GTK_MENU_ITEM (item), NULL);
1950     gtk_widget_show (item);
1951 
1952     g_signal_connect (item,
1953                       "toggled",
1954                       G_CALLBACK (use_spaces_toggled),
1955                       window);
1956 }
1957 
1958 static void
fill_language_combo(PlumaWindow * window)1959 fill_language_combo (PlumaWindow *window)
1960 {
1961     GtkSourceLanguageManager *manager;
1962     GSList *languages;
1963     GSList *item;
1964     GtkWidget *menu_item;
1965     const gchar *name;
1966 
1967     manager = pluma_get_language_manager ();
1968     languages = pluma_language_manager_list_languages_sorted (manager, FALSE);
1969 
1970     name = _("Plain Text");
1971     menu_item = gtk_menu_item_new_with_label (name);
1972     gtk_widget_show (menu_item);
1973 
1974     g_object_set_data (G_OBJECT (menu_item), LANGUAGE_DATA, NULL);
1975     pluma_status_combo_box_add_item (PLUMA_STATUS_COMBO_BOX (window->priv->language_combo),
1976                                      GTK_MENU_ITEM (menu_item),
1977                                      name);
1978 
1979     for (item = languages; item; item = item->next)
1980     {
1981         GtkSourceLanguage *lang = GTK_SOURCE_LANGUAGE (item->data);
1982 
1983         name = gtk_source_language_get_name (lang);
1984         menu_item = gtk_menu_item_new_with_label (name);
1985         gtk_widget_show (menu_item);
1986 
1987         g_object_set_data_full (G_OBJECT (menu_item),
1988                                 LANGUAGE_DATA,
1989                                 g_object_ref (lang),
1990                                 (GDestroyNotify)g_object_unref);
1991 
1992         pluma_status_combo_box_add_item (PLUMA_STATUS_COMBO_BOX (window->priv->language_combo),
1993                                          GTK_MENU_ITEM (menu_item),
1994                                          name);
1995     }
1996 
1997     g_slist_free (languages);
1998 }
1999 
2000 static void
create_statusbar(PlumaWindow * window,GtkWidget * main_box)2001 create_statusbar (PlumaWindow *window,
2002                   GtkWidget   *main_box)
2003 {
2004     pluma_debug (DEBUG_WINDOW);
2005 
2006     window->priv->statusbar = pluma_statusbar_new ();
2007 
2008     window->priv->generic_message_cid = gtk_statusbar_get_context_id
2009         (GTK_STATUSBAR (window->priv->statusbar), "generic_message");
2010     window->priv->tip_message_cid = gtk_statusbar_get_context_id
2011         (GTK_STATUSBAR (window->priv->statusbar), "tip_message");
2012 
2013     gtk_box_pack_end (GTK_BOX (main_box),
2014                       window->priv->statusbar,
2015                       FALSE,
2016                       TRUE,
2017                       0);
2018 
2019     window->priv->tab_width_combo = pluma_status_combo_box_new (_("Tab Width"));
2020     gtk_widget_show (window->priv->tab_width_combo);
2021     gtk_box_pack_end (GTK_BOX (window->priv->statusbar),
2022                       window->priv->tab_width_combo,
2023                       FALSE,
2024                       TRUE,
2025                       0);
2026 
2027     fill_tab_width_combo (window);
2028 
2029     g_signal_connect (G_OBJECT (window->priv->tab_width_combo),
2030                       "changed",
2031                       G_CALLBACK (tab_width_combo_changed),
2032                       window);
2033 
2034     window->priv->language_combo = pluma_status_combo_box_new (NULL);
2035     gtk_widget_show (window->priv->language_combo);
2036     gtk_box_pack_end (GTK_BOX (window->priv->statusbar),
2037                       window->priv->language_combo,
2038                       FALSE,
2039                       TRUE,
2040                       0);
2041 
2042     fill_language_combo (window);
2043 
2044     g_signal_connect (G_OBJECT (window->priv->language_combo),
2045                       "changed",
2046                       G_CALLBACK (language_combo_changed),
2047                       window);
2048 
2049     g_signal_connect_after (G_OBJECT (window->priv->statusbar),
2050                             "show",
2051                             G_CALLBACK (statusbar_visibility_changed),
2052                             window);
2053     g_signal_connect_after (G_OBJECT (window->priv->statusbar),
2054                             "hide",
2055                             G_CALLBACK (statusbar_visibility_changed),
2056                             window);
2057 
2058     set_statusbar_style (window, NULL);
2059 }
2060 
2061 static PlumaWindow *
clone_window(PlumaWindow * origin)2062 clone_window (PlumaWindow *origin)
2063 {
2064     PlumaWindow *window;
2065     GdkScreen *screen;
2066     PlumaApp  *app;
2067     gint panel_page;
2068 
2069     pluma_debug (DEBUG_WINDOW);
2070 
2071     app = pluma_app_get_default ();
2072 
2073     screen = gtk_window_get_screen (GTK_WINDOW (origin));
2074     window = pluma_app_create_window (app, screen);
2075 
2076     if ((origin->priv->window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0)
2077     {
2078         gint w, h;
2079 
2080         _pluma_window_get_default_size (&w, &h);
2081         gtk_window_set_default_size (GTK_WINDOW (window), w, h);
2082         gtk_window_maximize (GTK_WINDOW (window));
2083     }
2084     else
2085     {
2086         gtk_window_set_default_size (GTK_WINDOW (window),
2087                                      origin->priv->width,
2088                                      origin->priv->height);
2089 
2090         gtk_window_unmaximize (GTK_WINDOW (window));
2091     }
2092 
2093     if ((origin->priv->window_state & GDK_WINDOW_STATE_STICKY ) != 0)
2094         gtk_window_stick (GTK_WINDOW (window));
2095     else
2096         gtk_window_unstick (GTK_WINDOW (window));
2097 
2098     /* set the panes size, the paned position will be set when
2099      * they are mapped */
2100     window->priv->side_panel_size = origin->priv->side_panel_size;
2101     window->priv->bottom_panel_size = origin->priv->bottom_panel_size;
2102 
2103     panel_page = _pluma_panel_get_active_item_id (PLUMA_PANEL (origin->priv->side_panel));
2104     _pluma_panel_set_active_item_by_id (PLUMA_PANEL (window->priv->side_panel), panel_page);
2105 
2106     panel_page = _pluma_panel_get_active_item_id (PLUMA_PANEL (origin->priv->bottom_panel));
2107     _pluma_panel_set_active_item_by_id (PLUMA_PANEL (window->priv->bottom_panel), panel_page);
2108 
2109     if (gtk_widget_get_visible (origin->priv->side_panel))
2110         gtk_widget_show (window->priv->side_panel);
2111     else
2112         gtk_widget_hide (window->priv->side_panel);
2113 
2114     if (gtk_widget_get_visible (origin->priv->bottom_panel))
2115         gtk_widget_show (window->priv->bottom_panel);
2116     else
2117         gtk_widget_hide (window->priv->bottom_panel);
2118 
2119     set_statusbar_style (window, origin);
2120     set_toolbar_style (window, origin);
2121 
2122     return window;
2123 }
2124 
2125 static void
update_cursor_position_statusbar(GtkTextBuffer * buffer,PlumaWindow * window)2126 update_cursor_position_statusbar (GtkTextBuffer *buffer,
2127                                   PlumaWindow   *window)
2128 {
2129     gint row, col;
2130     GtkTextIter iter;
2131     PlumaView *view;
2132 
2133     pluma_debug (DEBUG_WINDOW);
2134 
2135      if (buffer != GTK_TEXT_BUFFER (pluma_window_get_active_document (window)))
2136          return;
2137 
2138      view = pluma_window_get_active_view (window);
2139 
2140     gtk_text_buffer_get_iter_at_mark (buffer,
2141                                       &iter,
2142                                       gtk_text_buffer_get_insert (buffer));
2143 
2144     row = gtk_text_iter_get_line (&iter);
2145 
2146     col = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW(view), &iter);
2147 
2148     pluma_statusbar_set_cursor_position (PLUMA_STATUSBAR (window->priv->statusbar),
2149                                          row + 1,
2150                                          col + 1);
2151 }
2152 
2153 static void
update_overwrite_mode_statusbar(GtkTextView * view,PlumaWindow * window)2154 update_overwrite_mode_statusbar (GtkTextView *view,
2155                                  PlumaWindow *window)
2156 {
2157     if (view != GTK_TEXT_VIEW (pluma_window_get_active_view (window)))
2158         return;
2159 
2160     /* Note that we have to use !gtk_text_view_get_overwrite since we
2161        are in the in the signal handler of "toggle overwrite" that is
2162        G_SIGNAL_RUN_LAST
2163     */
2164     pluma_statusbar_set_overwrite (PLUMA_STATUSBAR (window->priv->statusbar),
2165                                    !gtk_text_view_get_overwrite (view));
2166 }
2167 
2168 #define MAX_TITLE_LENGTH 100
2169 
2170 static void
set_title(PlumaWindow * window)2171 set_title (PlumaWindow *window)
2172 {
2173     PlumaDocument *doc = NULL;
2174     gchar *name;
2175     gchar *dirname = NULL;
2176     gchar *title = NULL;
2177     gint len;
2178     GtkAction *action;
2179 
2180     if (window->priv->active_tab == NULL)
2181     {
2182         gtk_window_set_title (GTK_WINDOW (window), "Pluma");
2183         return;
2184     }
2185 
2186     doc = pluma_tab_get_document (window->priv->active_tab);
2187     g_return_if_fail (doc != NULL);
2188 
2189     name = pluma_document_get_short_name_for_display (doc);
2190 
2191     len = g_utf8_strlen (name, -1);
2192 
2193     /* if the name is awfully long, truncate it and be done with it,
2194      * otherwise also show the directory (ellipsized if needed)
2195      */
2196     if (len > MAX_TITLE_LENGTH)
2197     {
2198         gchar *tmp;
2199 
2200         tmp = pluma_utils_str_middle_truncate (name, MAX_TITLE_LENGTH);
2201         g_free (name);
2202         name = tmp;
2203     }
2204     else
2205     {
2206         GFile *file;
2207 
2208         file = pluma_document_get_location (doc);
2209         if (file != NULL)
2210         {
2211             gchar *str;
2212 
2213             str = pluma_utils_location_get_dirname_for_display (file);
2214             g_object_unref (file);
2215 
2216             /* use the remaining space for the dir, but use a min of 20 chars
2217              * so that we do not end up with a dirname like "(a...b)".
2218              * This means that in the worst case when the filename is long 99
2219              * we have a title long 99 + 20, but I think it's a rare enough
2220              * case to be acceptable. It's justa darn title afterall :)
2221              */
2222             dirname = pluma_utils_str_middle_truncate (str, MAX (20, MAX_TITLE_LENGTH - len));
2223             g_free (str);
2224         }
2225     }
2226 
2227     if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)))
2228     {
2229         gchar *tmp_name;
2230 
2231         tmp_name = g_strdup_printf ("*%s", name);
2232         g_free (name);
2233 
2234         name = tmp_name;
2235         cansave = TRUE;
2236     }
2237     else
2238         cansave = FALSE;
2239 
2240     if (pluma_document_get_readonly (doc))
2241     {
2242         if (dirname != NULL)
2243             title = g_strdup_printf ("%s [%s] (%s) - Pluma",
2244                                      name,
2245                                      _("Read-Only"),
2246                                      dirname);
2247         else
2248             title = g_strdup_printf ("%s [%s] - Pluma",
2249                                      name,
2250                                      _("Read-Only"));
2251     }
2252     else
2253     {
2254         if (dirname != NULL)
2255             title = g_strdup_printf ("%s (%s) - Pluma",
2256                                      name,
2257                                      dirname);
2258         else
2259             title = g_strdup_printf ("%s - Pluma",
2260                                      name);
2261     }
2262 
2263     action = gtk_action_group_get_action (window->priv->action_group, "FileSave");
2264     gtk_action_set_sensitive (action, cansave);
2265 
2266     gtk_window_set_title (GTK_WINDOW (window), title);
2267 
2268     g_free (dirname);
2269     g_free (name);
2270     g_free (title);
2271 }
2272 
2273 #undef MAX_TITLE_LENGTH
2274 
2275 static void
set_tab_width_item_blocked(PlumaWindow * window,GtkMenuItem * item)2276 set_tab_width_item_blocked (PlumaWindow *window,
2277                             GtkMenuItem *item)
2278 {
2279     g_signal_handlers_block_by_func (window->priv->tab_width_combo,
2280                                      tab_width_combo_changed,
2281                                      window);
2282 
2283     pluma_status_combo_box_set_item (PLUMA_STATUS_COMBO_BOX (window->priv->tab_width_combo),
2284                                      item);
2285 
2286     g_signal_handlers_unblock_by_func (window->priv->tab_width_combo,
2287                                        tab_width_combo_changed,
2288                                        window);
2289 }
2290 
2291 static void
spaces_instead_of_tabs_changed(GObject * object,GParamSpec * pspec,PlumaWindow * window)2292 spaces_instead_of_tabs_changed (GObject     *object,
2293                                 GParamSpec  *pspec,
2294                                 PlumaWindow *window)
2295 {
2296     PlumaView *view = PLUMA_VIEW (object);
2297     gboolean active = gtk_source_view_get_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (view));
2298     GList *children = pluma_status_combo_box_get_items (PLUMA_STATUS_COMBO_BOX (window->priv->tab_width_combo));
2299     GtkCheckMenuItem *item;
2300 
2301     item = GTK_CHECK_MENU_ITEM (g_list_last (children)->data);
2302 
2303     gtk_check_menu_item_set_active (item, active);
2304 
2305     g_list_free (children);
2306 }
2307 
2308 static void
tab_width_changed(GObject * object,GParamSpec * pspec,PlumaWindow * window)2309 tab_width_changed (GObject     *object,
2310                    GParamSpec  *pspec,
2311                    PlumaWindow *window)
2312 {
2313     GList *items;
2314     GList *item;
2315     PlumaStatusComboBox *combo = PLUMA_STATUS_COMBO_BOX (window->priv->tab_width_combo);
2316     guint new_tab_width;
2317     gboolean found = FALSE;
2318 
2319     items = pluma_status_combo_box_get_items (combo);
2320 
2321     new_tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (object));
2322 
2323     for (item = items; item; item = item->next)
2324     {
2325         guint tab_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item->data), TAB_WIDTH_DATA));
2326 
2327         if (tab_width == new_tab_width)
2328         {
2329             set_tab_width_item_blocked (window, GTK_MENU_ITEM (item->data));
2330             found = TRUE;
2331         }
2332 
2333         if (GTK_IS_SEPARATOR_MENU_ITEM (item->next->data))
2334         {
2335             if (!found)
2336             {
2337                 /* Set for the last item the custom thing */
2338                 gchar *text;
2339 
2340                 text = g_strdup_printf ("%u", new_tab_width);
2341                 pluma_status_combo_box_set_item_text (combo,
2342                                                       GTK_MENU_ITEM (item->data),
2343                                                       text);
2344 
2345                 gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item->data))),
2346                                     text);
2347 
2348                 set_tab_width_item_blocked (window, GTK_MENU_ITEM (item->data));
2349                 gtk_widget_show (GTK_WIDGET (item->data));
2350             }
2351             else
2352             {
2353                 gtk_widget_hide (GTK_WIDGET (item->data));
2354             }
2355 
2356             break;
2357         }
2358     }
2359 
2360     g_list_free (items);
2361 }
2362 
2363 static void
language_changed(GObject * object,GParamSpec * pspec,PlumaWindow * window)2364 language_changed (GObject     *object,
2365                   GParamSpec  *pspec,
2366                   PlumaWindow *window)
2367 {
2368     GList *items;
2369     GList *item;
2370     PlumaStatusComboBox *combo = PLUMA_STATUS_COMBO_BOX (window->priv->language_combo);
2371     GtkSourceLanguage *new_language;
2372     const gchar *new_id;
2373 
2374     items = pluma_status_combo_box_get_items (combo);
2375 
2376     new_language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (object));
2377 
2378     if (new_language)
2379         new_id = gtk_source_language_get_id (new_language);
2380     else
2381         new_id = NULL;
2382 
2383     for (item = items; item; item = item->next)
2384     {
2385         GtkSourceLanguage *lang = g_object_get_data (G_OBJECT (item->data), LANGUAGE_DATA);
2386 
2387         if ((new_id == NULL && lang == NULL) ||
2388             (new_id != NULL && lang != NULL && strcmp (gtk_source_language_get_id (lang), new_id) == 0))
2389         {
2390             g_signal_handlers_block_by_func (window->priv->language_combo,
2391                                              language_combo_changed,
2392                                              window);
2393 
2394             pluma_status_combo_box_set_item (PLUMA_STATUS_COMBO_BOX (window->priv->language_combo),
2395                                              GTK_MENU_ITEM (item->data));
2396 
2397             g_signal_handlers_unblock_by_func (window->priv->language_combo,
2398                                                language_combo_changed,
2399                                                window);
2400         }
2401     }
2402 
2403     g_list_free (items);
2404 }
2405 
2406 static void
notebook_switch_page(GtkNotebook * book,GtkWidget * pg,gint page_num,PlumaWindow * window)2407 notebook_switch_page (GtkNotebook     *book,
2408                       GtkWidget       *pg,
2409                       gint             page_num,
2410                       PlumaWindow     *window)
2411 {
2412     PlumaView *view;
2413     PlumaTab *tab;
2414     GtkAction *action;
2415     gchar *action_name;
2416 
2417     /* CHECK: I don't know why but it seems notebook_switch_page is called
2418     two times every time the user change the active tab */
2419 
2420     tab = PLUMA_TAB (gtk_notebook_get_nth_page (book, page_num));
2421     if (tab == window->priv->active_tab)
2422         return;
2423 
2424     if (window->priv->active_tab)
2425     {
2426         if (window->priv->tab_width_id)
2427         {
2428             g_signal_handler_disconnect (pluma_tab_get_view (window->priv->active_tab),
2429                                          window->priv->tab_width_id);
2430 
2431             window->priv->tab_width_id = 0;
2432         }
2433 
2434         if (window->priv->spaces_instead_of_tabs_id)
2435         {
2436             g_signal_handler_disconnect (pluma_tab_get_view (window->priv->active_tab),
2437                                          window->priv->spaces_instead_of_tabs_id);
2438 
2439             window->priv->spaces_instead_of_tabs_id = 0;
2440         }
2441     }
2442 
2443     /* set the active tab */
2444     window->priv->active_tab = tab;
2445 
2446     set_title (window);
2447     set_sensitivity_according_to_tab (window, tab);
2448 
2449     /* activate the right item in the documents menu */
2450     action_name = g_strdup_printf ("Tab_%d", page_num);
2451     action = gtk_action_group_get_action (window->priv->documents_list_action_group,
2452                                           action_name);
2453 
2454     /* sometimes the action doesn't exist yet, and the proper action
2455      * is set active during the documents list menu creation
2456      * CHECK: would it be nicer if active_tab was a property and we monitored the notify signal?
2457      */
2458     if (action != NULL)
2459         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
2460 
2461     g_free (action_name);
2462 
2463     /* update the syntax menu */
2464     update_languages_menu (window);
2465 
2466     view = pluma_tab_get_view (tab);
2467 
2468     /* sync the statusbar */
2469     update_cursor_position_statusbar (GTK_TEXT_BUFFER (pluma_tab_get_document (tab)),
2470                                       window);
2471     pluma_statusbar_set_overwrite (PLUMA_STATUSBAR (window->priv->statusbar),
2472                                    gtk_text_view_get_overwrite (GTK_TEXT_VIEW (view)));
2473 
2474     gtk_widget_show (window->priv->tab_width_combo);
2475     gtk_widget_show (window->priv->language_combo);
2476 
2477     window->priv->tab_width_id = g_signal_connect (view,
2478                                                    "notify::tab-width",
2479                                                    G_CALLBACK (tab_width_changed),
2480                                                    window);
2481     window->priv->spaces_instead_of_tabs_id = g_signal_connect (view,
2482                                                                 "notify::insert-spaces-instead-of-tabs",
2483                                                                 G_CALLBACK (spaces_instead_of_tabs_changed),
2484                                                                 window);
2485 
2486     window->priv->language_changed_id = g_signal_connect (pluma_tab_get_document (tab),
2487                                                           "notify::language",
2488                                                           G_CALLBACK (language_changed),
2489                                                           window);
2490 
2491     /* call it for the first time */
2492     tab_width_changed (G_OBJECT (view), NULL, window);
2493     spaces_instead_of_tabs_changed (G_OBJECT (view), NULL, window);
2494     language_changed (G_OBJECT (pluma_tab_get_document (tab)), NULL, window);
2495 
2496     g_signal_emit (G_OBJECT (window),
2497                    signals[ACTIVE_TAB_CHANGED],
2498                    0,
2499                    window->priv->active_tab);
2500 }
2501 
2502 static void
set_sensitivity_according_to_window_state(PlumaWindow * window)2503 set_sensitivity_according_to_window_state (PlumaWindow *window)
2504 {
2505     GtkAction *action;
2506     PlumaLockdownMask lockdown;
2507 
2508     lockdown = pluma_app_get_lockdown (pluma_app_get_default ());
2509 
2510     /* We disable File->Quit/SaveAll/CloseAll while printing to avoid to have two
2511        operations (save and print/print preview) that uses the message area at
2512        the same time (may be we can remove this limitation in the future) */
2513     /* We disable File->Quit/CloseAll if state is saving since saving cannot be
2514        cancelled (may be we can remove this limitation in the future) */
2515     gtk_action_group_set_sensitive (window->priv->quit_action_group,
2516                                     !(window->priv->state & PLUMA_WINDOW_STATE_SAVING) &&
2517                                     !(window->priv->state & PLUMA_WINDOW_STATE_PRINTING));
2518 
2519     action = gtk_action_group_get_action (window->priv->action_group,
2520                                           "FileCloseAll");
2521     gtk_action_set_sensitive (action,
2522                               !(window->priv->state & PLUMA_WINDOW_STATE_SAVING) &&
2523                               !(window->priv->state & PLUMA_WINDOW_STATE_PRINTING));
2524 
2525     action = gtk_action_group_get_action (window->priv->action_group,
2526                           "FileSaveAll");
2527     gtk_action_set_sensitive (action,
2528                               !(window->priv->state & PLUMA_WINDOW_STATE_PRINTING) &&
2529                               !(lockdown & PLUMA_LOCKDOWN_SAVE_TO_DISK));
2530 
2531     action = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
2532                                           "FileNew");
2533     gtk_action_set_sensitive (action,
2534                               !(window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION));
2535 
2536     action = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
2537                                           "FileOpen");
2538     gtk_action_set_sensitive (action,
2539                               !(window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION));
2540 
2541     gtk_action_group_set_sensitive (window->priv->recents_action_group,
2542                                     !(window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION));
2543 
2544     pluma_notebook_set_close_buttons_sensitive (PLUMA_NOTEBOOK (window->priv->notebook),
2545                                                 !(window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION));
2546 
2547     pluma_notebook_set_tab_drag_and_drop_enabled (PLUMA_NOTEBOOK (window->priv->notebook),
2548                                                   !(window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION));
2549 
2550     if ((window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION) != 0)
2551     {
2552         /* TODO: If we really care, Find could be active
2553          * when in SAVING_SESSION state */
2554 
2555         if (gtk_action_group_get_sensitive (window->priv->action_group))
2556             gtk_action_group_set_sensitive (window->priv->action_group, FALSE);
2557 
2558         if (gtk_action_group_get_sensitive (window->priv->quit_action_group))
2559             gtk_action_group_set_sensitive (window->priv->quit_action_group, FALSE);
2560 
2561         if (gtk_action_group_get_sensitive (window->priv->close_action_group))
2562             gtk_action_group_set_sensitive (window->priv->close_action_group, FALSE);
2563     }
2564     else
2565     {
2566         if (!gtk_action_group_get_sensitive (window->priv->action_group))
2567             gtk_action_group_set_sensitive (window->priv->action_group, window->priv->num_tabs > 0);
2568 
2569         if (!gtk_action_group_get_sensitive (window->priv->quit_action_group))
2570             gtk_action_group_set_sensitive (window->priv->quit_action_group, window->priv->num_tabs > 0);
2571 
2572         if (!gtk_action_group_get_sensitive (window->priv->close_action_group))
2573             gtk_action_group_set_sensitive (window->priv->close_action_group, window->priv->num_tabs > 0);
2574     }
2575 }
2576 
2577 static void
update_tab_autosave(GtkWidget * widget,gpointer data)2578 update_tab_autosave (GtkWidget *widget,
2579                      gpointer   data)
2580 {
2581     PlumaTab *tab = PLUMA_TAB (widget);
2582     gboolean *enabled = (gboolean *) data;
2583 
2584     pluma_tab_set_auto_save_enabled (tab, *enabled);
2585 }
2586 
2587 void
_pluma_window_set_lockdown(PlumaWindow * window,PlumaLockdownMask lockdown)2588 _pluma_window_set_lockdown (PlumaWindow       *window,
2589                             PlumaLockdownMask  lockdown)
2590 {
2591     PlumaTab *tab;
2592     GtkAction *action;
2593     gboolean autosave;
2594 
2595     /* start/stop autosave in each existing tab */
2596     autosave = g_settings_get_boolean (window->priv->editor_settings, PLUMA_SETTINGS_AUTO_SAVE);
2597     gtk_container_foreach (GTK_CONTAINER (window->priv->notebook),
2598                            update_tab_autosave,
2599                            &autosave);
2600 
2601     /* update menues wrt the current active tab */
2602     tab = pluma_window_get_active_tab (window);
2603 
2604     set_sensitivity_according_to_tab (window, tab);
2605 
2606     action = gtk_action_group_get_action (window->priv->action_group,
2607                                          "FileSaveAll");
2608     gtk_action_set_sensitive (action,
2609                               !(window->priv->state & PLUMA_WINDOW_STATE_PRINTING) &&
2610                               !(lockdown & PLUMA_LOCKDOWN_SAVE_TO_DISK));
2611 
2612 }
2613 
2614 static void
analyze_tab_state(PlumaTab * tab,PlumaWindow * window)2615 analyze_tab_state (PlumaTab    *tab,
2616                    PlumaWindow *window)
2617 {
2618     PlumaTabState ts;
2619 
2620     ts = pluma_tab_get_state (tab);
2621 
2622     switch (ts)
2623     {
2624         case PLUMA_TAB_STATE_LOADING:
2625         case PLUMA_TAB_STATE_REVERTING:
2626             window->priv->state |= PLUMA_WINDOW_STATE_LOADING;
2627             break;
2628 
2629         case PLUMA_TAB_STATE_SAVING:
2630             window->priv->state |= PLUMA_WINDOW_STATE_SAVING;
2631             break;
2632 
2633         case PLUMA_TAB_STATE_PRINTING:
2634         case PLUMA_TAB_STATE_PRINT_PREVIEWING:
2635             window->priv->state |= PLUMA_WINDOW_STATE_PRINTING;
2636             break;
2637 
2638         case PLUMA_TAB_STATE_LOADING_ERROR:
2639         case PLUMA_TAB_STATE_REVERTING_ERROR:
2640         case PLUMA_TAB_STATE_SAVING_ERROR:
2641         case PLUMA_TAB_STATE_GENERIC_ERROR:
2642             window->priv->state |= PLUMA_WINDOW_STATE_ERROR;
2643             ++window->priv->num_tabs_with_error;
2644         default:
2645             /* NOP */
2646             break;
2647     }
2648 }
2649 
2650 static void
update_window_state(PlumaWindow * window)2651 update_window_state (PlumaWindow *window)
2652 {
2653     PlumaWindowState old_ws;
2654     gint old_num_of_errors;
2655 
2656     pluma_debug_message (DEBUG_WINDOW, "Old state: %x", window->priv->state);
2657 
2658     old_ws = window->priv->state;
2659     old_num_of_errors = window->priv->num_tabs_with_error;
2660 
2661     window->priv->state = old_ws & PLUMA_WINDOW_STATE_SAVING_SESSION;
2662 
2663     window->priv->num_tabs_with_error = 0;
2664 
2665     gtk_container_foreach (GTK_CONTAINER (window->priv->notebook),
2666                            (GtkCallback)analyze_tab_state,
2667                            window);
2668 
2669     pluma_debug_message (DEBUG_WINDOW, "New state: %x", window->priv->state);
2670 
2671     if (old_ws != window->priv->state)
2672     {
2673         set_sensitivity_according_to_window_state (window);
2674 
2675         pluma_statusbar_set_window_state (PLUMA_STATUSBAR (window->priv->statusbar),
2676                                           window->priv->state,
2677                                           window->priv->num_tabs_with_error);
2678 
2679         g_object_notify (G_OBJECT (window), "state");
2680     }
2681     else if (old_num_of_errors != window->priv->num_tabs_with_error)
2682     {
2683         pluma_statusbar_set_window_state (PLUMA_STATUSBAR (window->priv->statusbar),
2684                                           window->priv->state,
2685                                           window->priv->num_tabs_with_error);
2686     }
2687 }
2688 
2689 static void
sync_state(PlumaTab * tab,GParamSpec * pspec,PlumaWindow * window)2690 sync_state (PlumaTab    *tab,
2691             GParamSpec  *pspec,
2692             PlumaWindow *window)
2693 {
2694     pluma_debug (DEBUG_WINDOW);
2695 
2696     update_window_state (window);
2697 
2698     if (tab != window->priv->active_tab)
2699         return;
2700 
2701     set_sensitivity_according_to_tab (window, tab);
2702 
2703     g_signal_emit (G_OBJECT (window), signals[ACTIVE_TAB_STATE_CHANGED], 0);
2704 }
2705 
2706 static void
sync_name(PlumaTab * tab,GParamSpec * pspec,PlumaWindow * window)2707 sync_name (PlumaTab    *tab,
2708            GParamSpec  *pspec,
2709            PlumaWindow *window)
2710 {
2711     GtkAction *action;
2712     gchar *action_name;
2713     gchar *tab_name;
2714     gchar *escaped_name;
2715     gchar *tip;
2716     gint n;
2717     PlumaDocument *doc;
2718 
2719     if (tab == window->priv->active_tab)
2720     {
2721         set_title (window);
2722 
2723         doc = pluma_tab_get_document (tab);
2724         action = gtk_action_group_get_action (window->priv->action_group,
2725                                               "FileRevert");
2726         gtk_action_set_sensitive (action, !pluma_document_is_untitled (doc));
2727     }
2728 
2729     /* sync the item in the documents list menu */
2730     n = gtk_notebook_page_num (GTK_NOTEBOOK (window->priv->notebook), GTK_WIDGET (tab));
2731 
2732     action_name = g_strdup_printf ("Tab_%d", n);
2733     action = gtk_action_group_get_action (window->priv->documents_list_action_group,
2734                                           action_name);
2735     g_free (action_name);
2736     g_return_if_fail (action != NULL);
2737 
2738     tab_name = _pluma_tab_get_name (tab);
2739     escaped_name = pluma_utils_escape_underscores (tab_name, -1);
2740     tip =  get_menu_tip_for_tab (tab);
2741 
2742     g_object_set (action, "label", escaped_name, NULL);
2743     g_object_set (action, "tooltip", tip, NULL);
2744 
2745     g_free (tab_name);
2746     g_free (escaped_name);
2747     g_free (tip);
2748 
2749     peas_extension_set_call (window->priv->extensions, "update_state");
2750 }
2751 
2752 static PlumaWindow *
get_drop_window(GtkWidget * widget)2753 get_drop_window (GtkWidget *widget)
2754 {
2755     GtkWidget *target_window;
2756 
2757     target_window = gtk_widget_get_toplevel (widget);
2758     g_return_val_if_fail (PLUMA_IS_WINDOW (target_window), NULL);
2759 
2760     if ((PLUMA_WINDOW(target_window)->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION) != 0)
2761         return NULL;
2762 
2763     return PLUMA_WINDOW (target_window);
2764 }
2765 
2766 static void
load_uris_from_drop(PlumaWindow * window,gchar ** uri_list)2767 load_uris_from_drop (PlumaWindow  *window,
2768                      gchar       **uri_list)
2769 {
2770     GSList *uris = NULL;
2771     gint i;
2772 
2773     if (uri_list == NULL)
2774         return;
2775 
2776     for (i = 0; uri_list[i] != NULL; ++i)
2777     {
2778         uris = g_slist_prepend (uris, uri_list[i]);
2779     }
2780 
2781     uris = g_slist_reverse (uris);
2782     pluma_commands_load_uris (window,
2783                               uris,
2784                               NULL,
2785                               0);
2786 
2787     g_slist_free (uris);
2788 }
2789 
2790 /* Handle drops on the PlumaWindow */
2791 static void
drag_data_received_cb(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint timestamp,gpointer data)2792 drag_data_received_cb (GtkWidget    *widget,
2793                        GdkDragContext   *context,
2794                        gint          x,
2795                        gint          y,
2796                        GtkSelectionData *selection_data,
2797                        guint         info,
2798                        guint         timestamp,
2799                        gpointer      data)
2800 {
2801     PlumaWindow *window;
2802     gchar **uri_list;
2803 
2804     window = get_drop_window (widget);
2805 
2806     if (window == NULL)
2807         return;
2808 
2809     if (info == TARGET_URI_LIST)
2810     {
2811         uri_list = pluma_utils_drop_get_uris(selection_data);
2812         load_uris_from_drop (window, uri_list);
2813         g_strfreev (uri_list);
2814     }
2815 }
2816 
2817 /* Handle drops on the PlumaView */
2818 static void
drop_uris_cb(GtkWidget * widget,gchar ** uri_list)2819 drop_uris_cb (GtkWidget    *widget,
2820               gchar       **uri_list)
2821 {
2822     PlumaWindow *window;
2823 
2824     window = get_drop_window (widget);
2825 
2826     if (window == NULL)
2827         return;
2828 
2829     load_uris_from_drop (window, uri_list);
2830 }
2831 
2832 static void
fullscreen_controls_show(PlumaWindow * window)2833 fullscreen_controls_show (PlumaWindow *window)
2834 {
2835     GdkScreen *screen;
2836     GdkDisplay *display;
2837     GdkRectangle fs_rect;
2838     gint w, h;
2839 
2840     screen = gtk_window_get_screen (GTK_WINDOW (window));
2841     display = gdk_screen_get_display (screen);
2842 
2843     gdk_monitor_get_geometry (gdk_display_get_monitor_at_window (display,
2844                               gtk_widget_get_window (GTK_WIDGET (window))),
2845                               &fs_rect);
2846 
2847     gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h);
2848 
2849     gtk_window_resize (GTK_WINDOW (window->priv->fullscreen_controls),
2850                        fs_rect.width, h);
2851 
2852     gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
2853                      fs_rect.x, fs_rect.y - h + 1);
2854 
2855     gtk_widget_show_all (window->priv->fullscreen_controls);
2856 }
2857 
2858 static gboolean
run_fullscreen_animation(gpointer data)2859 run_fullscreen_animation (gpointer data)
2860 {
2861     PlumaWindow *window = PLUMA_WINDOW (data);
2862     GdkScreen *screen;
2863     GdkDisplay *display;
2864     GdkRectangle fs_rect;
2865     gint x, y;
2866 
2867     screen = gtk_window_get_screen (GTK_WINDOW (window));
2868     display = gdk_screen_get_display (screen);
2869 
2870     gdk_monitor_get_geometry (gdk_display_get_monitor_at_window (display,
2871                               gtk_widget_get_window (GTK_WIDGET (window))),
2872                               &fs_rect);
2873 
2874     gtk_window_get_position (GTK_WINDOW (window->priv->fullscreen_controls),
2875                              &x,
2876                              &y);
2877 
2878     if (window->priv->fullscreen_animation_enter)
2879     {
2880         if (y == fs_rect.y)
2881         {
2882             window->priv->fullscreen_animation_timeout_id = 0;
2883             return FALSE;
2884         }
2885         else
2886         {
2887             gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
2888                              x, y + 1);
2889             return TRUE;
2890         }
2891     }
2892     else
2893     {
2894         gint w, h;
2895 
2896         gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls),
2897                              &w, &h);
2898 
2899         if (y == fs_rect.y - h + 1)
2900         {
2901             window->priv->fullscreen_animation_timeout_id = 0;
2902             return FALSE;
2903         }
2904         else
2905         {
2906             gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
2907                              x, y - 1);
2908             return TRUE;
2909         }
2910     }
2911 }
2912 
2913 static void
show_hide_fullscreen_toolbar(PlumaWindow * window,gboolean show,gint height)2914 show_hide_fullscreen_toolbar (PlumaWindow *window,
2915                               gboolean     show,
2916                               gint     height)
2917 {
2918     GtkSettings *settings;
2919     gboolean enable_animations;
2920 
2921     settings = gtk_widget_get_settings (GTK_WIDGET (window));
2922     g_object_get (G_OBJECT (settings),
2923                   "gtk-enable-animations",
2924                   &enable_animations,
2925                   NULL);
2926 
2927     if (enable_animations)
2928     {
2929         window->priv->fullscreen_animation_enter = show;
2930 
2931         if (window->priv->fullscreen_animation_timeout_id == 0)
2932         {
2933             window->priv->fullscreen_animation_timeout_id =
2934                 g_timeout_add (FULLSCREEN_ANIMATION_SPEED,
2935                                (GSourceFunc) run_fullscreen_animation,
2936                                window);
2937         }
2938     }
2939     else
2940     {
2941         GdkRectangle fs_rect;
2942         GdkScreen *screen;
2943         GdkDisplay *display;
2944 
2945         screen = gtk_window_get_screen (GTK_WINDOW (window));
2946         display = gdk_screen_get_display (screen);
2947 
2948         gdk_monitor_get_geometry (gdk_display_get_monitor_at_window (display,
2949                                   gtk_widget_get_window (GTK_WIDGET (window))),
2950                                   &fs_rect);
2951 
2952         if (show)
2953             gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
2954                              fs_rect.x, fs_rect.y);
2955         else
2956             gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls),
2957                              fs_rect.x, fs_rect.y - height + 1);
2958     }
2959 
2960 }
2961 
2962 static gboolean
on_fullscreen_controls_enter_notify_event(GtkWidget * widget,GdkEventCrossing * event,PlumaWindow * window)2963 on_fullscreen_controls_enter_notify_event (GtkWidget    *widget,
2964                                            GdkEventCrossing *event,
2965                                            PlumaWindow      *window)
2966 {
2967     show_hide_fullscreen_toolbar (window, TRUE, 0);
2968 
2969     return FALSE;
2970 }
2971 
2972 static gboolean
on_fullscreen_controls_leave_notify_event(GtkWidget * widget,GdkEventCrossing * event,PlumaWindow * window)2973 on_fullscreen_controls_leave_notify_event (GtkWidget    *widget,
2974                                            GdkEventCrossing *event,
2975                                            PlumaWindow      *window)
2976 {
2977     GdkDevice *device;
2978     gint w, h;
2979     gint x, y;
2980 
2981     device = gdk_event_get_device ((GdkEvent *)event);
2982 
2983     gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h);
2984     gdk_device_get_position (device, NULL, &x, &y);
2985 
2986     /* gtk seems to emit leave notify when clicking on tool items,
2987      * work around it by checking the coordinates
2988      */
2989     if (y >= h)
2990     {
2991         show_hide_fullscreen_toolbar (window, FALSE, h);
2992     }
2993 
2994     return FALSE;
2995 }
2996 
2997 static void
fullscreen_controls_build(PlumaWindow * window)2998 fullscreen_controls_build (PlumaWindow *window)
2999 {
3000     PlumaWindowPrivate *priv = window->priv;
3001     GtkWidget *toolbar;
3002     GtkAction *action;
3003 
3004     if (priv->fullscreen_controls != NULL)
3005         return;
3006 
3007     priv->fullscreen_controls = gtk_window_new (GTK_WINDOW_POPUP);
3008 
3009     gtk_window_set_transient_for (GTK_WINDOW (priv->fullscreen_controls),
3010                                   &window->window);
3011 
3012     /* popup toolbar */
3013     toolbar = gtk_ui_manager_get_widget (priv->manager, "/FullscreenToolBar");
3014     gtk_container_add (GTK_CONTAINER (priv->fullscreen_controls), toolbar);
3015 
3016     action = gtk_action_group_get_action (priv->always_sensitive_action_group,
3017                                           "LeaveFullscreen");
3018     g_object_set (action, "is-important", TRUE, NULL);
3019 
3020     setup_toolbar_open_button (window, toolbar);
3021 
3022     gtk_container_foreach (GTK_CONTAINER (toolbar),
3023                            (GtkCallback)set_non_homogeneus,
3024                            NULL);
3025 
3026     /* Set the toolbar style */
3027     gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
3028 
3029     g_signal_connect (priv->fullscreen_controls, "enter-notify-event",
3030                       G_CALLBACK (on_fullscreen_controls_enter_notify_event),
3031                       window);
3032     g_signal_connect (priv->fullscreen_controls, "leave-notify-event",
3033                       G_CALLBACK (on_fullscreen_controls_leave_notify_event),
3034                       window);
3035 }
3036 
3037 static void
can_search_again(PlumaDocument * doc,GParamSpec * pspec,PlumaWindow * window)3038 can_search_again (PlumaDocument *doc,
3039                   GParamSpec    *pspec,
3040                   PlumaWindow   *window)
3041 {
3042     gboolean sensitive;
3043     GtkAction *action;
3044 
3045     if (doc != pluma_window_get_active_document (window))
3046         return;
3047 
3048     sensitive = pluma_document_get_can_search_again (doc);
3049 
3050     action = gtk_action_group_get_action (window->priv->action_group,
3051                                           "SearchFindNext");
3052     gtk_action_set_sensitive (action, sensitive);
3053 
3054     action = gtk_action_group_get_action (window->priv->action_group,
3055                                           "SearchFindPrevious");
3056     gtk_action_set_sensitive (action, sensitive);
3057 
3058     action = gtk_action_group_get_action (window->priv->action_group,
3059                                           "SearchClearHighlight");
3060     gtk_action_set_sensitive (action, sensitive);
3061 }
3062 
3063 static void
can_undo(PlumaDocument * doc,GParamSpec * pspec,PlumaWindow * window)3064 can_undo (PlumaDocument *doc,
3065           GParamSpec    *pspec,
3066           PlumaWindow   *window)
3067 {
3068     GtkAction *action;
3069     gboolean sensitive;
3070 
3071     sensitive = gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (doc));
3072 
3073     if (doc != pluma_window_get_active_document (window))
3074         return;
3075 
3076     action = gtk_action_group_get_action (window->priv->action_group,
3077                                           "EditUndo");
3078     gtk_action_set_sensitive (action, sensitive);
3079 }
3080 
3081 static void
can_redo(PlumaDocument * doc,GParamSpec * pspec,PlumaWindow * window)3082 can_redo (PlumaDocument *doc,
3083           GParamSpec    *pspec,
3084           PlumaWindow   *window)
3085 {
3086     GtkAction *action;
3087     gboolean sensitive;
3088 
3089     sensitive = gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (doc));
3090 
3091     if (doc != pluma_window_get_active_document (window))
3092         return;
3093 
3094     action = gtk_action_group_get_action (window->priv->action_group,
3095                                           "EditRedo");
3096     gtk_action_set_sensitive (action, sensitive);
3097 }
3098 
3099 static void
selection_changed(PlumaDocument * doc,GParamSpec * pspec,PlumaWindow * window)3100 selection_changed (PlumaDocument *doc,
3101                    GParamSpec    *pspec,
3102                    PlumaWindow   *window)
3103 {
3104     PlumaTab *tab;
3105     PlumaView *view;
3106     GtkAction *action;
3107     PlumaTabState state;
3108     gboolean state_normal;
3109     gboolean editable;
3110 
3111     pluma_debug (DEBUG_WINDOW);
3112 
3113     if (doc != pluma_window_get_active_document (window))
3114         return;
3115 
3116     tab = pluma_tab_get_from_document (doc);
3117     state = pluma_tab_get_state (tab);
3118     state_normal = (state == PLUMA_TAB_STATE_NORMAL);
3119 
3120     view = pluma_tab_get_view (tab);
3121     editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (view));
3122 
3123     action = gtk_action_group_get_action (window->priv->action_group,
3124                                           "EditCut");
3125     gtk_action_set_sensitive (action,
3126                               state_normal &&
3127                               editable &&
3128                               gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc)));
3129 
3130     action = gtk_action_group_get_action (window->priv->action_group,
3131                                           "EditCopy");
3132     gtk_action_set_sensitive (action,
3133                               (state_normal ||
3134                               state == PLUMA_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) &&
3135                               gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc)));
3136 
3137     action = gtk_action_group_get_action (window->priv->action_group,
3138                                           "EditDelete");
3139     gtk_action_set_sensitive (action,
3140                               state_normal &&
3141                               editable &&
3142                               gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc)));
3143 
3144     peas_extension_set_call (window->priv->extensions, "update_state");
3145 }
3146 
3147 static void
sync_languages_menu(PlumaDocument * doc,GParamSpec * pspec,PlumaWindow * window)3148 sync_languages_menu (PlumaDocument *doc,
3149                      GParamSpec    *pspec,
3150                      PlumaWindow   *window)
3151 {
3152     update_languages_menu (window);
3153     peas_extension_set_call (window->priv->extensions, "update_state");
3154 }
3155 
3156 static void
readonly_changed(PlumaDocument * doc,GParamSpec * pspec,PlumaWindow * window)3157 readonly_changed (PlumaDocument *doc,
3158                   GParamSpec    *pspec,
3159                   PlumaWindow   *window)
3160 {
3161     set_sensitivity_according_to_tab (window, window->priv->active_tab);
3162 
3163     sync_name (window->priv->active_tab, NULL, window);
3164 
3165     peas_extension_set_call (window->priv->extensions, "update_state");
3166 }
3167 
3168 static void
editable_changed(PlumaView * view,GParamSpec * arg1,PlumaWindow * window)3169 editable_changed (PlumaView  *view,
3170                   GParamSpec  *arg1,
3171                   PlumaWindow *window)
3172 {
3173     peas_extension_set_call (window->priv->extensions, "update_state");
3174 }
3175 
3176 static void
update_sensitivity_according_to_open_tabs(PlumaWindow * window)3177 update_sensitivity_according_to_open_tabs (PlumaWindow *window)
3178 {
3179     GtkAction *action;
3180 
3181     /* Set sensitivity */
3182     gtk_action_group_set_sensitive (window->priv->action_group,
3183                                     window->priv->num_tabs != 0);
3184 
3185     action = gtk_action_group_get_action (window->priv->action_group,
3186                                           "DocumentsMoveToNewWindow");
3187     gtk_action_set_sensitive (action, window->priv->num_tabs > 1);
3188 
3189     gtk_action_group_set_sensitive (window->priv->close_action_group,
3190                                     window->priv->num_tabs != 0);
3191 }
3192 
3193 static void
notebook_tab_added(PlumaNotebook * notebook,PlumaTab * tab,PlumaWindow * window)3194 notebook_tab_added (PlumaNotebook *notebook,
3195                     PlumaTab      *tab,
3196                     PlumaWindow   *window)
3197 {
3198     PlumaView *view;
3199     PlumaDocument *doc;
3200 
3201     pluma_debug (DEBUG_WINDOW);
3202 
3203     g_return_if_fail ((window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION) == 0);
3204 
3205     ++window->priv->num_tabs;
3206 
3207     update_sensitivity_according_to_open_tabs (window);
3208 
3209     view = pluma_tab_get_view (tab);
3210     doc = pluma_tab_get_document (tab);
3211 
3212     /* IMPORTANT: remember to disconnect the signal in notebook_tab_removed
3213      * if a new signal is connected here */
3214 
3215     g_signal_connect (tab,
3216                       "notify::name",
3217                       G_CALLBACK (sync_name),
3218                       window);
3219     g_signal_connect (tab,
3220                       "notify::state",
3221                       G_CALLBACK (sync_state),
3222                       window);
3223 
3224     g_signal_connect (doc,
3225                       "cursor-moved",
3226                       G_CALLBACK (update_cursor_position_statusbar),
3227                       window);
3228     g_signal_connect (doc,
3229                       "notify::can-search-again",
3230                       G_CALLBACK (can_search_again),
3231                       window);
3232     g_signal_connect (doc,
3233                       "notify::can-undo",
3234                       G_CALLBACK (can_undo),
3235                       window);
3236     g_signal_connect (doc,
3237                       "notify::can-redo",
3238                       G_CALLBACK (can_redo),
3239                       window);
3240     g_signal_connect (doc,
3241                       "notify::has-selection",
3242                       G_CALLBACK (selection_changed),
3243                       window);
3244     g_signal_connect (doc,
3245                       "notify::language",
3246                       G_CALLBACK (sync_languages_menu),
3247                       window);
3248     g_signal_connect (doc,
3249                       "notify::read-only",
3250                       G_CALLBACK (readonly_changed),
3251                       window);
3252     g_signal_connect (view,
3253                       "toggle_overwrite",
3254                       G_CALLBACK (update_overwrite_mode_statusbar),
3255                       window);
3256     g_signal_connect (view,
3257                       "notify::editable",
3258                       G_CALLBACK (editable_changed),
3259                       window);
3260 
3261     update_documents_list_menu (window);
3262 
3263     g_signal_connect (view,
3264                       "drop_uris",
3265                       G_CALLBACK (drop_uris_cb),
3266                       NULL);
3267 
3268     update_window_state (window);
3269 
3270     g_signal_emit (G_OBJECT (window), signals[TAB_ADDED], 0, tab);
3271 }
3272 
3273 static void
notebook_tab_removed(PlumaNotebook * notebook,PlumaTab * tab,PlumaWindow * window)3274 notebook_tab_removed (PlumaNotebook *notebook,
3275                       PlumaTab      *tab,
3276                       PlumaWindow   *window)
3277 {
3278     PlumaView     *view;
3279     PlumaDocument *doc;
3280 
3281     pluma_debug (DEBUG_WINDOW);
3282 
3283     g_return_if_fail ((window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION) == 0);
3284 
3285     --window->priv->num_tabs;
3286 
3287     view = pluma_tab_get_view (tab);
3288     doc = pluma_tab_get_document (tab);
3289 
3290     g_signal_handlers_disconnect_by_func (tab,
3291                                           G_CALLBACK (sync_name),
3292                                           window);
3293     g_signal_handlers_disconnect_by_func (tab,
3294                                           G_CALLBACK (sync_state),
3295                                           window);
3296     g_signal_handlers_disconnect_by_func (doc,
3297                                           G_CALLBACK (update_cursor_position_statusbar),
3298                                           window);
3299     g_signal_handlers_disconnect_by_func (doc,
3300                                           G_CALLBACK (can_search_again),
3301                                           window);
3302     g_signal_handlers_disconnect_by_func (doc,
3303                                           G_CALLBACK (can_undo),
3304                                           window);
3305     g_signal_handlers_disconnect_by_func (doc,
3306                                           G_CALLBACK (can_redo),
3307                                           window);
3308     g_signal_handlers_disconnect_by_func (doc,
3309                                           G_CALLBACK (selection_changed),
3310                                           window);
3311     g_signal_handlers_disconnect_by_func (doc,
3312                                           G_CALLBACK (sync_languages_menu),
3313                                           window);
3314     g_signal_handlers_disconnect_by_func (doc,
3315                                           G_CALLBACK (readonly_changed),
3316                                           window);
3317     g_signal_handlers_disconnect_by_func (view,
3318                                           G_CALLBACK (update_overwrite_mode_statusbar),
3319                                           window);
3320     g_signal_handlers_disconnect_by_func (view,
3321                                           G_CALLBACK (editable_changed),
3322                                           window);
3323     g_signal_handlers_disconnect_by_func (view,
3324                                           G_CALLBACK (drop_uris_cb),
3325                                           NULL);
3326 
3327     if (window->priv->tab_width_id && tab == pluma_window_get_active_tab (window))
3328     {
3329         g_signal_handler_disconnect (view, window->priv->tab_width_id);
3330         window->priv->tab_width_id = 0;
3331     }
3332 
3333     if (window->priv->spaces_instead_of_tabs_id && tab == pluma_window_get_active_tab (window))
3334     {
3335         g_signal_handler_disconnect (view, window->priv->spaces_instead_of_tabs_id);
3336         window->priv->spaces_instead_of_tabs_id = 0;
3337     }
3338 
3339     if (window->priv->language_changed_id && tab == pluma_window_get_active_tab (window))
3340     {
3341         g_signal_handler_disconnect (doc, window->priv->language_changed_id);
3342         window->priv->language_changed_id = 0;
3343     }
3344 
3345     g_return_if_fail (window->priv->num_tabs >= 0);
3346     if (window->priv->num_tabs == 0)
3347     {
3348         window->priv->active_tab = NULL;
3349 
3350         set_title (window);
3351 
3352         /* Remove line and col info */
3353         pluma_statusbar_set_cursor_position (PLUMA_STATUSBAR (window->priv->statusbar),
3354                                              -1,
3355                                              -1);
3356 
3357         pluma_statusbar_clear_overwrite (PLUMA_STATUSBAR (window->priv->statusbar));
3358 
3359         /* hide the combos */
3360         gtk_widget_hide (window->priv->tab_width_combo);
3361         gtk_widget_hide (window->priv->language_combo);
3362     }
3363 
3364     if (!window->priv->removing_tabs)
3365     {
3366         update_documents_list_menu (window);
3367         update_next_prev_doc_sensitivity_per_window (window);
3368     }
3369     else
3370     {
3371         if (window->priv->num_tabs == 0)
3372         {
3373             update_documents_list_menu (window);
3374             update_next_prev_doc_sensitivity_per_window (window);
3375         }
3376     }
3377 
3378     update_sensitivity_according_to_open_tabs (window);
3379 
3380     if (window->priv->num_tabs == 0)
3381     {
3382         peas_extension_set_call (window->priv->extensions, "update_state");
3383     }
3384 
3385     update_window_state (window);
3386 
3387     g_signal_emit (G_OBJECT (window), signals[TAB_REMOVED], 0, tab);
3388 }
3389 
3390 static void
notebook_tabs_reordered(PlumaNotebook * notebook,PlumaWindow * window)3391 notebook_tabs_reordered (PlumaNotebook *notebook,
3392                          PlumaWindow   *window)
3393 {
3394     update_documents_list_menu (window);
3395     update_next_prev_doc_sensitivity_per_window (window);
3396 
3397     g_signal_emit (G_OBJECT (window), signals[TABS_REORDERED], 0);
3398 }
3399 
3400 static void
notebook_tab_detached(PlumaNotebook * notebook,PlumaTab * tab,PlumaWindow * window)3401 notebook_tab_detached (PlumaNotebook *notebook,
3402                        PlumaTab      *tab,
3403                        PlumaWindow   *window)
3404 {
3405     PlumaWindow *new_window;
3406 
3407     new_window = clone_window (window);
3408 
3409     pluma_notebook_move_tab (notebook,
3410                              PLUMA_NOTEBOOK (_pluma_window_get_notebook (new_window)),
3411                              tab, 0);
3412 
3413     gtk_window_set_position (GTK_WINDOW (new_window), GTK_WIN_POS_MOUSE);
3414 
3415     gtk_widget_show (GTK_WIDGET (new_window));
3416 }
3417 
3418 static void
notebook_tab_close_request(PlumaNotebook * notebook,PlumaTab * tab,GtkWindow * window)3419 notebook_tab_close_request (PlumaNotebook *notebook,
3420                             PlumaTab      *tab,
3421                             GtkWindow     *window)
3422 {
3423     /* Note: we are destroying the tab before the default handler
3424      * seems to be ok, but we need to keep an eye on this. */
3425     _pluma_cmd_file_close_tab (tab, PLUMA_WINDOW (window));
3426 }
3427 
3428 static gboolean
show_notebook_popup_menu(GtkNotebook * notebook,PlumaWindow * window,GdkEventButton * event)3429 show_notebook_popup_menu (GtkNotebook    *notebook,
3430                           PlumaWindow    *window,
3431                           GdkEventButton *event)
3432 {
3433     GtkWidget *menu;
3434 //    GtkAction *action;
3435 
3436     menu = gtk_ui_manager_get_widget (window->priv->manager, "/NotebookPopup");
3437     g_return_val_if_fail (menu != NULL, FALSE);
3438 
3439 // CHECK do we need this?
3440 #if 0
3441     /* allow extensions to sync when showing the popup */
3442     action = gtk_action_group_get_action (window->priv->action_group,
3443                           "NotebookPopupAction");
3444     g_return_val_if_fail (action != NULL, FALSE);
3445     gtk_action_activate (action);
3446 #endif
3447 
3448     GtkWidget *tab;
3449     GtkWidget *tab_label;
3450 
3451     tab = GTK_WIDGET (pluma_window_get_active_tab (window));
3452     g_return_val_if_fail (tab != NULL, FALSE);
3453 
3454     tab_label = gtk_notebook_get_tab_label (notebook, tab);
3455 
3456     gtk_menu_popup_at_widget (GTK_MENU (menu),
3457                               tab_label,
3458                               GDK_GRAVITY_SOUTH_WEST,
3459                               GDK_GRAVITY_NORTH_WEST,
3460                               (const GdkEvent*) event);
3461 
3462     gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
3463 
3464     return TRUE;
3465 }
3466 
3467 static gboolean
notebook_button_press_event(GtkNotebook * notebook,GdkEventButton * event,PlumaWindow * window)3468 notebook_button_press_event (GtkNotebook    *notebook,
3469                              GdkEventButton *event,
3470                              PlumaWindow    *window)
3471 {
3472     if (event->type == GDK_BUTTON_PRESS)
3473     {
3474         if (event->button == 3)
3475             return show_notebook_popup_menu (notebook, window, event);
3476 
3477         else if (event->button == 2)
3478         {
3479             PlumaTab *tab;
3480             tab = pluma_window_get_active_tab (window);
3481             notebook_tab_close_request (PLUMA_NOTEBOOK (notebook), tab, GTK_WINDOW (window));
3482         }
3483     }
3484     else if ((event->type == GDK_2BUTTON_PRESS) && (event->button == 1))
3485     {
3486         pluma_window_create_tab (window, TRUE);
3487     }
3488 
3489     return FALSE;
3490 }
3491 
3492 static gboolean
notebook_scroll_event(GtkNotebook * notebook,GdkEventScroll * event,PlumaWindow * window)3493 notebook_scroll_event (GtkNotebook    *notebook,
3494                        GdkEventScroll *event,
3495                        PlumaWindow    *window)
3496 {
3497     if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT)
3498     {
3499         gtk_notebook_prev_page (notebook);
3500     }
3501     else if (event->direction == GDK_SCROLL_DOWN || event->direction == GDK_SCROLL_RIGHT)
3502     {
3503         gtk_notebook_next_page (notebook);
3504     }
3505 
3506     return FALSE;
3507 }
3508 
3509 static gboolean
notebook_popup_menu(GtkNotebook * notebook,PlumaWindow * window)3510 notebook_popup_menu (GtkNotebook *notebook,
3511                      PlumaWindow *window)
3512 {
3513     /* Only respond if the notebook is the actual focus */
3514     if (PLUMA_IS_NOTEBOOK (gtk_window_get_focus (GTK_WINDOW (window))))
3515     {
3516         return show_notebook_popup_menu (notebook, window, NULL);
3517     }
3518 
3519     return FALSE;
3520 }
3521 
3522 static void
side_panel_size_allocate(GtkWidget * widget,GtkAllocation * allocation,PlumaWindow * window)3523 side_panel_size_allocate (GtkWidget     *widget,
3524                           GtkAllocation *allocation,
3525                           PlumaWindow   *window)
3526 {
3527     window->priv->side_panel_size = allocation->width;
3528 }
3529 
3530 static void
bottom_panel_size_allocate(GtkWidget * widget,GtkAllocation * allocation,PlumaWindow * window)3531 bottom_panel_size_allocate (GtkWidget     *widget,
3532                             GtkAllocation *allocation,
3533                             PlumaWindow   *window)
3534 {
3535     window->priv->bottom_panel_size = allocation->height;
3536 }
3537 
3538 static void
hpaned_restore_position(GtkWidget * widget,PlumaWindow * window)3539 hpaned_restore_position (GtkWidget   *widget,
3540                          PlumaWindow *window)
3541 {
3542     gint pos;
3543 
3544     pluma_debug_message (DEBUG_WINDOW,
3545                          "Restoring hpaned position: side panel size %d",
3546                          window->priv->side_panel_size);
3547 
3548     pos = MAX (100, window->priv->side_panel_size);
3549     gtk_paned_set_position (GTK_PANED (window->priv->hpaned), pos);
3550 
3551     /* start monitoring the size */
3552     g_signal_connect (window->priv->side_panel,
3553                       "size-allocate",
3554                       G_CALLBACK (side_panel_size_allocate),
3555                       window);
3556 
3557     /* run this only once */
3558     g_signal_handlers_disconnect_by_func (widget, hpaned_restore_position, window);
3559 }
3560 
3561 static void
vpaned_restore_position(GtkWidget * widget,PlumaWindow * window)3562 vpaned_restore_position (GtkWidget   *widget,
3563                          PlumaWindow *window)
3564 {
3565     GtkAllocation allocation;
3566     gint pos;
3567 
3568     gtk_widget_get_allocation (widget, &allocation);
3569 
3570     pluma_debug_message (DEBUG_WINDOW,
3571                          "Restoring vpaned position: bottom panel size %d",
3572                          window->priv->bottom_panel_size);
3573 
3574     pos = allocation.height - MAX (50, window->priv->bottom_panel_size);
3575     gtk_paned_set_position (GTK_PANED (window->priv->vpaned), pos);
3576 
3577     /* start monitoring the size */
3578     g_signal_connect (window->priv->bottom_panel,
3579                       "size-allocate",
3580                       G_CALLBACK (bottom_panel_size_allocate),
3581                       window);
3582 
3583     /* run this only once */
3584     g_signal_handlers_disconnect_by_func (widget, vpaned_restore_position, window);
3585 }
3586 
3587 static void
side_panel_visibility_changed(GtkWidget * side_panel,PlumaWindow * window)3588 side_panel_visibility_changed (GtkWidget   *side_panel,
3589                                PlumaWindow *window)
3590 {
3591     gboolean   visible;
3592     GtkAction *action;
3593 
3594     visible = gtk_widget_get_visible (side_panel);
3595 
3596     if (!g_settings_get_boolean (window->priv->editor_settings, "show-tabs-with-side-pane"))
3597     {
3598         if (visible)
3599             gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->priv->notebook), FALSE);
3600         else
3601             gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->priv->notebook),
3602                                         g_settings_get_boolean (window->priv->editor_settings, "show-single-tab") ||
3603                                         (gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->priv->notebook)) > 1));
3604     }
3605     else
3606         gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->priv->notebook),
3607                                     g_settings_get_boolean (window->priv->editor_settings, "show-single-tab") ||
3608                                     (gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->priv->notebook)) > 1));
3609 
3610     g_settings_set_boolean (window->priv->editor_settings,
3611                             PLUMA_SETTINGS_SIDE_PANE_VISIBLE,
3612                             visible);
3613 
3614     action = gtk_action_group_get_action (window->priv->panes_action_group,
3615                                           "ViewSidePane");
3616 
3617     if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible)
3618         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
3619 
3620     /* focus the document */
3621     if (!visible && window->priv->active_tab != NULL)
3622         gtk_widget_grab_focus (GTK_WIDGET (pluma_tab_get_view (PLUMA_TAB (window->priv->active_tab))));
3623 }
3624 
3625 static void
create_side_panel(PlumaWindow * window)3626 create_side_panel (PlumaWindow *window)
3627 {
3628     GtkWidget *documents_panel;
3629 
3630     pluma_debug (DEBUG_WINDOW);
3631 
3632     window->priv->side_panel = pluma_panel_new (GTK_ORIENTATION_VERTICAL);
3633 
3634     gtk_paned_pack1 (GTK_PANED (window->priv->hpaned),
3635                      window->priv->side_panel,
3636                      FALSE,
3637                      FALSE);
3638 
3639     g_signal_connect_after (window->priv->side_panel,
3640                             "show",
3641                             G_CALLBACK (side_panel_visibility_changed),
3642                             window);
3643     g_signal_connect_after (window->priv->side_panel,
3644                             "hide",
3645                             G_CALLBACK (side_panel_visibility_changed),
3646                             window);
3647 
3648     documents_panel = pluma_documents_panel_new (window);
3649     pluma_panel_add_item_with_icon (PLUMA_PANEL (window->priv->side_panel),
3650                                     documents_panel,
3651                                     _("Documents"),
3652                                     "text-x-generic");
3653 }
3654 
3655 static void
bottom_panel_visibility_changed(PlumaPanel * bottom_panel,PlumaWindow * window)3656 bottom_panel_visibility_changed (PlumaPanel  *bottom_panel,
3657                                  PlumaWindow *window)
3658 {
3659     gboolean visible;
3660     GtkAction *action;
3661 
3662     visible = gtk_widget_get_visible (GTK_WIDGET (bottom_panel));
3663 
3664     g_settings_set_boolean (window->priv->editor_settings,
3665                             PLUMA_SETTINGS_BOTTOM_PANE_VISIBLE,
3666                             visible);
3667 
3668     action = gtk_action_group_get_action (window->priv->panes_action_group,
3669                                           "ViewBottomPane");
3670 
3671     if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible)
3672         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
3673 
3674     /* focus the document */
3675     if (!visible && window->priv->active_tab != NULL)
3676         gtk_widget_grab_focus (GTK_WIDGET (pluma_tab_get_view (PLUMA_TAB (window->priv->active_tab))));
3677 }
3678 
3679 static void
bottom_panel_item_removed(PlumaPanel * panel,GtkWidget * item,PlumaWindow * window)3680 bottom_panel_item_removed (PlumaPanel  *panel,
3681                            GtkWidget   *item,
3682                            PlumaWindow *window)
3683 {
3684     if (pluma_panel_get_n_items (panel) == 0)
3685     {
3686         GtkAction *action;
3687 
3688         gtk_widget_hide (GTK_WIDGET (panel));
3689 
3690         action = gtk_action_group_get_action (window->priv->panes_action_group,
3691                                               "ViewBottomPane");
3692         gtk_action_set_sensitive (action, FALSE);
3693     }
3694 }
3695 
3696 static void
bottom_panel_item_added(PlumaPanel * panel,GtkWidget * item,PlumaWindow * window)3697 bottom_panel_item_added (PlumaPanel  *panel,
3698              GtkWidget   *item,
3699              PlumaWindow *window)
3700 {
3701     /* if it's the first item added, set the menu item
3702      * sensitive and if needed show the panel */
3703     if (pluma_panel_get_n_items (panel) == 1)
3704     {
3705         GtkAction *action;
3706         gboolean show;
3707 
3708         action = gtk_action_group_get_action (window->priv->panes_action_group,
3709                                               "ViewBottomPane");
3710         gtk_action_set_sensitive (action, TRUE);
3711 
3712         show = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
3713         if (show)
3714             gtk_widget_show (GTK_WIDGET (panel));
3715     }
3716 }
3717 
3718 static void
create_bottom_panel(PlumaWindow * window)3719 create_bottom_panel (PlumaWindow *window)
3720 {
3721     pluma_debug (DEBUG_WINDOW);
3722 
3723     window->priv->bottom_panel = pluma_panel_new (GTK_ORIENTATION_HORIZONTAL);
3724 
3725     gtk_paned_pack2 (GTK_PANED (window->priv->vpaned),
3726                      window->priv->bottom_panel,
3727                      FALSE,
3728                      FALSE);
3729 
3730     g_signal_connect_after (window->priv->bottom_panel,
3731                             "show",
3732                             G_CALLBACK (bottom_panel_visibility_changed),
3733                             window);
3734     g_signal_connect_after (window->priv->bottom_panel,
3735                             "hide",
3736                             G_CALLBACK (bottom_panel_visibility_changed),
3737                             window);
3738 }
3739 
3740 static void
init_panels_visibility(PlumaWindow * window)3741 init_panels_visibility (PlumaWindow *window)
3742 {
3743     gint active_page;
3744     gboolean side_pane_visible;
3745     gboolean bottom_pane_visible;
3746 
3747     pluma_debug (DEBUG_WINDOW);
3748 
3749     /* side pane */
3750     active_page = g_settings_get_int (window->priv->editor_settings,
3751                                       PLUMA_SETTINGS_SIDE_PANEL_ACTIVE_PAGE);
3752     _pluma_panel_set_active_item_by_id (PLUMA_PANEL (window->priv->side_panel),
3753                                         active_page);
3754 
3755     side_pane_visible = g_settings_get_boolean (window->priv->editor_settings,
3756                                                 PLUMA_SETTINGS_SIDE_PANE_VISIBLE);
3757     bottom_pane_visible = g_settings_get_boolean (window->priv->editor_settings,
3758                                                   PLUMA_SETTINGS_BOTTOM_PANE_VISIBLE);
3759 
3760     if (side_pane_visible)
3761 
3762     {
3763         gtk_widget_show (window->priv->side_panel);
3764     }
3765 
3766     /* bottom pane, it can be empty */
3767     if (pluma_panel_get_n_items (PLUMA_PANEL (window->priv->bottom_panel)) > 0)
3768     {
3769         active_page = g_settings_get_int (window->priv->editor_settings,
3770                                           PLUMA_SETTINGS_BOTTOM_PANEL_ACTIVE_PAGE);
3771         _pluma_panel_set_active_item_by_id (PLUMA_PANEL (window->priv->bottom_panel),
3772                                             active_page);
3773 
3774         if (bottom_pane_visible)
3775         {
3776             gtk_widget_show (window->priv->bottom_panel);
3777         }
3778     }
3779     else
3780     {
3781         GtkAction *action;
3782         action = gtk_action_group_get_action (window->priv->panes_action_group,
3783                                               "ViewBottomPane");
3784         gtk_action_set_sensitive (action, FALSE);
3785     }
3786 
3787     /* start track sensitivity after the initial state is set */
3788     window->priv->bottom_panel_item_removed_handler_id =
3789         g_signal_connect (window->priv->bottom_panel,
3790                           "item_removed",
3791                           G_CALLBACK (bottom_panel_item_removed),
3792                           window);
3793 
3794     g_signal_connect (window->priv->bottom_panel,
3795                       "item_added",
3796                       G_CALLBACK (bottom_panel_item_added),
3797                       window);
3798 }
3799 
3800 static void
clipboard_owner_change(GtkClipboard * clipboard,GdkEventOwnerChange * event,PlumaWindow * window)3801 clipboard_owner_change (GtkClipboard    *clipboard,
3802                         GdkEventOwnerChange *event,
3803                         PlumaWindow     *window)
3804 {
3805     set_paste_sensitivity_according_to_clipboard (window, clipboard);
3806 }
3807 
3808 static void
window_realized(GtkWidget * window,gpointer * data)3809 window_realized (GtkWidget *window,
3810                  gpointer  *data)
3811 {
3812     GtkClipboard *clipboard;
3813 
3814     clipboard = gtk_widget_get_clipboard (window, GDK_SELECTION_CLIPBOARD);
3815 
3816     g_signal_connect (clipboard,
3817                       "owner_change",
3818                       G_CALLBACK (clipboard_owner_change),
3819                       window);
3820 }
3821 
3822 static void
window_unrealized(GtkWidget * window,gpointer * data)3823 window_unrealized (GtkWidget *window,
3824                    gpointer  *data)
3825 {
3826     GtkClipboard *clipboard;
3827 
3828     clipboard = gtk_widget_get_clipboard (window, GDK_SELECTION_CLIPBOARD);
3829 
3830     g_signal_handlers_disconnect_by_func (clipboard,
3831                                           G_CALLBACK (clipboard_owner_change),
3832                                           window);
3833 }
3834 
3835 static void
check_window_is_active(PlumaWindow * window,GParamSpec * property,gpointer useless)3836 check_window_is_active (PlumaWindow *window,
3837                         GParamSpec *property,
3838                         gpointer useless)
3839 {
3840     if (window->priv->window_state & GDK_WINDOW_STATE_FULLSCREEN)
3841     {
3842         if (gtk_window_is_active (GTK_WINDOW (window)))
3843         {
3844             gtk_widget_show (window->priv->fullscreen_controls);
3845         }
3846         else
3847         {
3848             gtk_widget_hide (window->priv->fullscreen_controls);
3849         }
3850     }
3851 }
3852 
3853 static void
connect_notebook_signals(PlumaWindow * window,GtkWidget * notebook)3854 connect_notebook_signals (PlumaWindow *window,
3855                           GtkWidget   *notebook)
3856 {
3857     g_signal_connect (notebook,
3858                       "switch-page",
3859                       G_CALLBACK (notebook_switch_page),
3860                       window);
3861     g_signal_connect (notebook,
3862                       "tab-added",
3863                       G_CALLBACK (notebook_tab_added),
3864                       window);
3865     g_signal_connect (notebook,
3866                       "tab-removed",
3867                       G_CALLBACK (notebook_tab_removed),
3868                       window);
3869     g_signal_connect (notebook,
3870                       "tabs-reordered",
3871                       G_CALLBACK (notebook_tabs_reordered),
3872                       window);
3873     g_signal_connect (notebook,
3874                       "tab-detached",
3875                       G_CALLBACK (notebook_tab_detached),
3876                       window);
3877     g_signal_connect (notebook,
3878                       "tab-close-request",
3879                       G_CALLBACK (notebook_tab_close_request),
3880                       window);
3881     g_signal_connect (notebook,
3882                       "button-press-event",
3883                       G_CALLBACK (notebook_button_press_event),
3884                       window);
3885     g_signal_connect (notebook,
3886                       "popup-menu",
3887                       G_CALLBACK (notebook_popup_menu),
3888                       window);
3889     g_signal_connect (notebook,
3890                       "scroll-event",
3891                       G_CALLBACK (notebook_scroll_event),
3892                       window);
3893 }
3894 
3895 static void
add_notebook(PlumaWindow * window,GtkWidget * notebook)3896 add_notebook (PlumaWindow *window,
3897           GtkWidget   *notebook)
3898 {
3899     gtk_paned_pack1 (GTK_PANED (window->priv->vpaned),
3900                      notebook,
3901                      TRUE,
3902                      TRUE);
3903 
3904     gtk_widget_show (notebook);
3905 
3906     gtk_widget_add_events (notebook, GDK_SCROLL_MASK);
3907     connect_notebook_signals (window, notebook);
3908 }
3909 
3910 static void
on_extension_added(PeasExtensionSet * extensions,PeasPluginInfo * info,PeasExtension * exten,PlumaWindow * window)3911 on_extension_added (PeasExtensionSet *extensions,
3912                     PeasPluginInfo   *info,
3913                     PeasExtension    *exten,
3914                     PlumaWindow      *window)
3915 {
3916     peas_extension_call (exten, "activate", window);
3917 }
3918 
3919 static void
on_extension_removed(PeasExtensionSet * extensions,PeasPluginInfo * info,PeasExtension * exten,PlumaWindow * window)3920 on_extension_removed (PeasExtensionSet *extensions,
3921                       PeasPluginInfo   *info,
3922                       PeasExtension    *exten,
3923                       PlumaWindow      *window)
3924 {
3925     peas_extension_call (exten, "deactivate", window);
3926 
3927     /* Ensure update of ui manager, because we suspect it does something
3928      * with expected static strings in the type module (when unloaded the
3929      * strings don't exist anymore, and ui manager updates in an idle
3930      * func) */
3931     gtk_ui_manager_ensure_update (window->priv->manager);
3932 }
3933 
3934 static void
pluma_window_init(PlumaWindow * window)3935 pluma_window_init (PlumaWindow *window)
3936 {
3937     GtkWidget *main_box;
3938     GtkTargetList *tl;
3939 
3940     pluma_debug (DEBUG_WINDOW);
3941 
3942     window->priv = pluma_window_get_instance_private (window);
3943     window->priv->active_tab = NULL;
3944     window->priv->num_tabs = 0;
3945     window->priv->removing_tabs = FALSE;
3946     window->priv->state = PLUMA_WINDOW_STATE_NORMAL;
3947     window->priv->dispose_has_run = FALSE;
3948     window->priv->fullscreen_controls = NULL;
3949     window->priv->fullscreen_animation_timeout_id = 0;
3950     window->priv->editor_settings = g_settings_new (PLUMA_SCHEMA_ID);
3951 
3952     window->priv->message_bus = pluma_message_bus_new ();
3953 
3954     window->priv->window_group = gtk_window_group_new ();
3955     gtk_window_group_add_window (window->priv->window_group, GTK_WINDOW (window));
3956 
3957     GtkStyleContext *context;
3958 
3959     context = gtk_widget_get_style_context (GTK_WIDGET (window));
3960     gtk_style_context_add_class (context, "pluma-window");
3961 
3962     main_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3963     gtk_container_add (GTK_CONTAINER (window), main_box);
3964     gtk_widget_show (main_box);
3965 
3966     /* Add menu bar and toolbar bar */
3967     create_menu_bar_and_toolbar (window, main_box);
3968 
3969     /* Add status bar */
3970     create_statusbar (window, main_box);
3971 
3972     /* Add the main area */
3973     pluma_debug_message (DEBUG_WINDOW, "Add main area");
3974     window->priv->hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
3975     gtk_box_pack_start (GTK_BOX (main_box),
3976                         window->priv->hpaned,
3977                         TRUE,
3978                         TRUE,
3979                         0);
3980 
3981     window->priv->vpaned = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
3982       gtk_paned_pack2 (GTK_PANED (window->priv->hpaned),
3983                        window->priv->vpaned,
3984                        TRUE,
3985                        FALSE);
3986 
3987     pluma_debug_message (DEBUG_WINDOW, "Create pluma notebook");
3988     window->priv->notebook = pluma_notebook_new ();
3989     add_notebook (window, window->priv->notebook);
3990 
3991     /* side and bottom panels */
3992       create_side_panel (window);
3993     create_bottom_panel (window);
3994 
3995     /* panes' state must be restored after panels have been mapped,
3996      * since the bottom pane position depends on the size of the vpaned. */
3997     window->priv->side_panel_size = g_settings_get_int (window->priv->editor_settings,
3998                                                         PLUMA_SETTINGS_SIDE_PANEL_SIZE);
3999     window->priv->bottom_panel_size = g_settings_get_int (window->priv->editor_settings,
4000                                                           PLUMA_SETTINGS_BOTTOM_PANEL_SIZE);
4001 
4002     g_signal_connect_after (window->priv->hpaned,
4003                             "map",
4004                             G_CALLBACK (hpaned_restore_position),
4005                             window);
4006     g_signal_connect_after (window->priv->vpaned,
4007                             "map",
4008                             G_CALLBACK (vpaned_restore_position),
4009                             window);
4010 
4011     gtk_widget_show (window->priv->hpaned);
4012     gtk_widget_show (window->priv->vpaned);
4013 
4014     /* Drag and drop support, set targets to NULL because we add the
4015        default uri_targets below */
4016     gtk_drag_dest_set (GTK_WIDGET (window),
4017                        GTK_DEST_DEFAULT_MOTION |
4018                        GTK_DEST_DEFAULT_HIGHLIGHT |
4019                        GTK_DEST_DEFAULT_DROP,
4020                        NULL,
4021                        0,
4022                        GDK_ACTION_COPY);
4023 
4024     /* Add uri targets */
4025     tl = gtk_drag_dest_get_target_list (GTK_WIDGET (window));
4026 
4027     if (tl == NULL)
4028     {
4029         tl = gtk_target_list_new (NULL, 0);
4030         gtk_drag_dest_set_target_list (GTK_WIDGET (window), tl);
4031         gtk_target_list_unref (tl);
4032     }
4033 
4034     gtk_target_list_add_uri_targets (tl, TARGET_URI_LIST);
4035 
4036     /* connect instead of override, so that we can
4037      * share the cb code with the view */
4038     g_signal_connect (window,
4039                       "drag_data_received",
4040                       G_CALLBACK (drag_data_received_cb),
4041                       NULL);
4042 
4043     /* we can get the clipboard only after the widget
4044      * is realized */
4045     g_signal_connect (window,
4046                       "realize",
4047                       G_CALLBACK (window_realized),
4048                       NULL);
4049     g_signal_connect (window,
4050                       "unrealize",
4051                       G_CALLBACK (window_unrealized),
4052                       NULL);
4053 
4054     /* Check if the window is active for fullscreen */
4055     g_signal_connect (window,
4056                       "notify::is-active",
4057                       G_CALLBACK (check_window_is_active),
4058                       NULL);
4059 
4060     pluma_debug_message (DEBUG_WINDOW, "Update plugins ui");
4061 
4062     window->priv->extensions = peas_extension_set_new (PEAS_ENGINE (pluma_plugins_engine_get_default ()),
4063                                                        PLUMA_TYPE_WINDOW_ACTIVATABLE,
4064                                                        "window",
4065                                                        window,
4066                                                        NULL);
4067 
4068     g_signal_connect (window->priv->extensions, "extension-added",
4069                       G_CALLBACK (on_extension_added),
4070                       window);
4071     g_signal_connect (window->priv->extensions, "extension-removed",
4072                       G_CALLBACK (on_extension_removed),
4073                       window);
4074 
4075     peas_extension_set_call (window->priv->extensions, "activate");
4076 
4077      /* set visibility of panes.
4078       * This needs to be done after plugins activatation */
4079     init_panels_visibility (window);
4080 
4081     update_sensitivity_according_to_open_tabs (window);
4082 
4083     pluma_debug_message (DEBUG_WINDOW, "END");
4084 }
4085 
4086 /**
4087  * pluma_window_get_active_view:
4088  * @window: a #PlumaWindow
4089  *
4090  * Gets the active #PlumaView.
4091  *
4092  * Returns: (transfer none): the active #PlumaView
4093  */
4094 PlumaView *
pluma_window_get_active_view(PlumaWindow * window)4095 pluma_window_get_active_view (PlumaWindow *window)
4096 {
4097     PlumaView *view;
4098 
4099     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4100 
4101     if (window->priv->active_tab == NULL)
4102         return NULL;
4103 
4104     view = pluma_tab_get_view (PLUMA_TAB (window->priv->active_tab));
4105 
4106     return view;
4107 }
4108 
4109 /**
4110  * pluma_window_get_active_document:
4111  * @window: a #PlumaWindow
4112  *
4113  * Gets the active #PlumaDocument.
4114  *
4115  * Returns: (transfer none): the active #PlumaDocument
4116  */
4117 PlumaDocument *
pluma_window_get_active_document(PlumaWindow * window)4118 pluma_window_get_active_document (PlumaWindow *window)
4119 {
4120     PlumaView *view;
4121 
4122     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4123 
4124     view = pluma_window_get_active_view (window);
4125     if (view == NULL)
4126         return NULL;
4127 
4128     return PLUMA_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
4129 }
4130 
4131 GtkWidget *
_pluma_window_get_notebook(PlumaWindow * window)4132 _pluma_window_get_notebook (PlumaWindow *window)
4133 {
4134     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4135 
4136     return window->priv->notebook;
4137 }
4138 
4139 /**
4140  * pluma_window_create_tab:
4141  * @window: a #PlumaWindow
4142  * @jump_to: %TRUE to set the new #PlumaTab as active
4143  *
4144  * Creates a new #PlumaTab and adds the new tab to the #PlumaNotebook.
4145  * In case @jump_to is %TRUE the #PlumaNotebook switches to that new #PlumaTab.
4146  *
4147  * Returns: (transfer none): a new #PlumaTab
4148  */
4149 PlumaTab *
pluma_window_create_tab(PlumaWindow * window,gboolean jump_to)4150 pluma_window_create_tab (PlumaWindow *window,
4151                          gboolean     jump_to)
4152 {
4153     PlumaTab *tab;
4154 
4155     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4156 
4157     tab = PLUMA_TAB (_pluma_tab_new ());
4158     gtk_widget_show (GTK_WIDGET (tab));
4159 
4160     pluma_notebook_add_tab (PLUMA_NOTEBOOK (window->priv->notebook),
4161                             tab,
4162                             -1,
4163                             jump_to);
4164 
4165     if (!gtk_widget_get_visible (GTK_WIDGET (window)))
4166     {
4167         gtk_window_present (GTK_WINDOW (window));
4168     }
4169 
4170     return tab;
4171 }
4172 
4173 /**
4174  * pluma_window_create_tab_from_uri:
4175  * @window: a #PlumaWindow
4176  * @uri: the uri of the document
4177  * @encoding: a #PlumaEncoding
4178  * @line_pos: the line position to visualize
4179  * @create: %TRUE to create a new document in case @uri does exist
4180  * @jump_to: %TRUE to set the new #PlumaTab as active
4181  *
4182  * Creates a new #PlumaTab loading the document specified by @uri.
4183  * In case @jump_to is %TRUE the #PlumaNotebook swithes to that new #PlumaTab.
4184  * Whether @create is %TRUE, creates a new empty document if location does
4185  * not refer to an existing file
4186  *
4187  * Returns: (transfer none): a new #PlumaTab
4188  */
4189 PlumaTab *
pluma_window_create_tab_from_uri(PlumaWindow * window,const gchar * uri,const PlumaEncoding * encoding,gint line_pos,gboolean create,gboolean jump_to)4190 pluma_window_create_tab_from_uri (PlumaWindow         *window,
4191                                   const gchar         *uri,
4192                                   const PlumaEncoding *encoding,
4193                                   gint                 line_pos,
4194                                   gboolean             create,
4195                                   gboolean             jump_to)
4196 {
4197     GtkWidget *tab;
4198 
4199     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4200     g_return_val_if_fail (uri != NULL, NULL);
4201 
4202     tab = _pluma_tab_new_from_uri (uri,
4203                                    encoding,
4204                                    line_pos,
4205                                    create);
4206     if (tab == NULL)
4207         return NULL;
4208 
4209     gtk_widget_show (tab);
4210 
4211     pluma_notebook_add_tab (PLUMA_NOTEBOOK (window->priv->notebook),
4212                             PLUMA_TAB (tab),
4213                             -1,
4214                             jump_to);
4215 
4216     if (!gtk_widget_get_visible (GTK_WIDGET (window)))
4217     {
4218         gtk_window_present (GTK_WINDOW (window));
4219     }
4220 
4221     return PLUMA_TAB (tab);
4222 }
4223 
4224 /**
4225  * pluma_window_get_active_tab:
4226  * @window: a PlumaWindow
4227  *
4228  * Gets the active #PlumaTab in the @window.
4229  *
4230  * Returns: (transfer none): the active #PlumaTab in the @window.
4231  */
4232 PlumaTab *
pluma_window_get_active_tab(PlumaWindow * window)4233 pluma_window_get_active_tab (PlumaWindow *window)
4234 {
4235     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4236 
4237     return (window->priv->active_tab == NULL) ?
4238                 NULL : PLUMA_TAB (window->priv->active_tab);
4239 }
4240 
4241 static void
add_document(PlumaTab * tab,GList ** res)4242 add_document (PlumaTab *tab, GList **res)
4243 {
4244     PlumaDocument *doc;
4245 
4246     doc = pluma_tab_get_document (tab);
4247 
4248     *res = g_list_prepend (*res, doc);
4249 }
4250 
4251 /**
4252  * pluma_window_get_documents:
4253  * @window: a #PlumaWindow
4254  *
4255  * Gets a newly allocated list with all the documents in the window.
4256  * This list must be freed.
4257  *
4258  * Returns: (element-type Pluma.Document) (transfer container): a newly
4259  * allocated list with all the documents in the window
4260  */
4261 GList *
pluma_window_get_documents(PlumaWindow * window)4262 pluma_window_get_documents (PlumaWindow *window)
4263 {
4264     GList *res = NULL;
4265 
4266     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4267 
4268     gtk_container_foreach (GTK_CONTAINER (window->priv->notebook),
4269                            (GtkCallback)add_document,
4270                            &res);
4271 
4272     res = g_list_reverse (res);
4273 
4274     return res;
4275 }
4276 
4277 static void
add_view(PlumaTab * tab,GList ** res)4278 add_view (PlumaTab *tab, GList **res)
4279 {
4280     PlumaView *view;
4281 
4282     view = pluma_tab_get_view (tab);
4283 
4284     *res = g_list_prepend (*res, view);
4285 }
4286 
4287 /**
4288  * pluma_window_get_views:
4289  * @window: a #PlumaWindow
4290  *
4291  * Gets a list with all the views in the window. This list must be freed.
4292  *
4293  * Returns: (element-type Pluma.View) (transfer container): a newly allocated
4294  * list with all the views in the window
4295  */
4296 GList *
pluma_window_get_views(PlumaWindow * window)4297 pluma_window_get_views (PlumaWindow *window)
4298 {
4299     GList *res = NULL;
4300 
4301     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4302 
4303     gtk_container_foreach (GTK_CONTAINER (window->priv->notebook),
4304                            (GtkCallback)add_view,
4305                            &res);
4306 
4307     res = g_list_reverse (res);
4308 
4309     return res;
4310 }
4311 
4312 /**
4313  * pluma_window_close_tab:
4314  * @window: a #PlumaWindow
4315  * @tab: the #PlumaTab to close
4316  *
4317  * Closes the @tab.
4318  */
4319 void
pluma_window_close_tab(PlumaWindow * window,PlumaTab * tab)4320 pluma_window_close_tab (PlumaWindow *window,
4321             PlumaTab    *tab)
4322 {
4323     g_return_if_fail (PLUMA_IS_WINDOW (window));
4324     g_return_if_fail (PLUMA_IS_TAB (tab));
4325     g_return_if_fail ((pluma_tab_get_state (tab) != PLUMA_TAB_STATE_SAVING) &&
4326                       (pluma_tab_get_state (tab) != PLUMA_TAB_STATE_SHOWING_PRINT_PREVIEW));
4327 
4328     pluma_notebook_remove_tab (PLUMA_NOTEBOOK (window->priv->notebook), tab);
4329 }
4330 
4331 /**
4332  * pluma_window_close_all_tabs:
4333  * @window: a #PlumaWindow
4334  *
4335  * Closes all opened tabs.
4336  */
4337 void
pluma_window_close_all_tabs(PlumaWindow * window)4338 pluma_window_close_all_tabs (PlumaWindow *window)
4339 {
4340     g_return_if_fail (PLUMA_IS_WINDOW (window));
4341     g_return_if_fail (!(window->priv->state & PLUMA_WINDOW_STATE_SAVING) &&
4342                       !(window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION));
4343 
4344     window->priv->removing_tabs = TRUE;
4345 
4346     pluma_notebook_remove_all_tabs (PLUMA_NOTEBOOK (window->priv->notebook));
4347 
4348     window->priv->removing_tabs = FALSE;
4349 }
4350 
4351 /**
4352  * pluma_window_close_tabs:
4353  * @window: a #PlumaWindow
4354  * @tabs: (element-type Pluma.Tab): a list of #PlumaTab
4355  *
4356  * Closes all tabs specified by @tabs.
4357  */
4358 void
pluma_window_close_tabs(PlumaWindow * window,const GList * tabs)4359 pluma_window_close_tabs (PlumaWindow *window,
4360              const GList *tabs)
4361 {
4362     g_return_if_fail (PLUMA_IS_WINDOW (window));
4363     g_return_if_fail (!(window->priv->state & PLUMA_WINDOW_STATE_SAVING) &&
4364                       !(window->priv->state & PLUMA_WINDOW_STATE_SAVING_SESSION));
4365 
4366     if (tabs == NULL)
4367         return;
4368 
4369     window->priv->removing_tabs = TRUE;
4370 
4371     while (tabs != NULL)
4372     {
4373         if (tabs->next == NULL)
4374             window->priv->removing_tabs = FALSE;
4375 
4376         pluma_notebook_remove_tab (PLUMA_NOTEBOOK (window->priv->notebook),
4377                                    PLUMA_TAB (tabs->data));
4378 
4379         tabs = g_list_next (tabs);
4380     }
4381 
4382     g_return_if_fail (window->priv->removing_tabs == FALSE);
4383 }
4384 
4385 PlumaWindow *
_pluma_window_move_tab_to_new_window(PlumaWindow * window,PlumaTab * tab)4386 _pluma_window_move_tab_to_new_window (PlumaWindow *window,
4387                                       PlumaTab    *tab)
4388 {
4389     PlumaWindow *new_window;
4390 
4391     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4392     g_return_val_if_fail (PLUMA_IS_TAB (tab), NULL);
4393     g_return_val_if_fail (gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->priv->notebook)) > 1,
4394                           NULL);
4395 
4396     new_window = clone_window (window);
4397 
4398     pluma_notebook_move_tab (PLUMA_NOTEBOOK (window->priv->notebook),
4399                              PLUMA_NOTEBOOK (new_window->priv->notebook),
4400                              tab,
4401                              -1);
4402 
4403     gtk_widget_show (GTK_WIDGET (new_window));
4404 
4405     return new_window;
4406 }
4407 
4408 /**
4409  * pluma_window_set_active_tab:
4410  * @window: a #PlumaWindow
4411  * @tab: a #PlumaTab
4412  *
4413  * Switches to the tab that matches with @tab.
4414  */
4415 void
pluma_window_set_active_tab(PlumaWindow * window,PlumaTab * tab)4416 pluma_window_set_active_tab (PlumaWindow *window,
4417                              PlumaTab    *tab)
4418 {
4419     gint page_num;
4420 
4421     g_return_if_fail (PLUMA_IS_WINDOW (window));
4422     g_return_if_fail (PLUMA_IS_TAB (tab));
4423 
4424     page_num = gtk_notebook_page_num (GTK_NOTEBOOK (window->priv->notebook),
4425                                       GTK_WIDGET (tab));
4426     g_return_if_fail (page_num != -1);
4427 
4428     gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook),
4429                                    page_num);
4430 }
4431 
4432 /**
4433  * pluma_window_get_group:
4434  * @window: a #PlumaWindow
4435  *
4436  * Gets the #GtkWindowGroup in which @window resides.
4437  *
4438  * Returns: (transfer none): the #GtkWindowGroup
4439  */
4440 GtkWindowGroup *
pluma_window_get_group(PlumaWindow * window)4441 pluma_window_get_group (PlumaWindow *window)
4442 {
4443     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4444 
4445     return window->priv->window_group;
4446 }
4447 
4448 gboolean
_pluma_window_is_removing_tabs(PlumaWindow * window)4449 _pluma_window_is_removing_tabs (PlumaWindow *window)
4450 {
4451     g_return_val_if_fail (PLUMA_IS_WINDOW (window), FALSE);
4452 
4453     return window->priv->removing_tabs;
4454 }
4455 
4456 /**
4457  * pluma_window_get_ui_manager:
4458  * @window: a #PlumaWindow
4459  *
4460  * Gets the #GtkUIManager associated with the @window.
4461  *
4462  * Returns: (transfer none): the #GtkUIManager of the @window.
4463  */
4464 GtkUIManager *
pluma_window_get_ui_manager(PlumaWindow * window)4465 pluma_window_get_ui_manager (PlumaWindow *window)
4466 {
4467     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4468 
4469     return window->priv->manager;
4470 }
4471 
4472 /**
4473  * pluma_window_get_side_panel:
4474  * @window: a #PlumaWindow
4475  *
4476  * Gets the side #PlumaPanel of the @window.
4477  *
4478  * Returns: (transfer none): the side #PlumaPanel.
4479  */
4480 PlumaPanel *
pluma_window_get_side_panel(PlumaWindow * window)4481 pluma_window_get_side_panel (PlumaWindow *window)
4482 {
4483     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4484 
4485     return PLUMA_PANEL (window->priv->side_panel);
4486 }
4487 
4488 /**
4489  * pluma_window_get_bottom_panel:
4490  * @window: a #PlumaWindow
4491  *
4492  * Gets the bottom #PlumaPanel of the @window.
4493  *
4494  * Returns: (transfer none): the bottom #PlumaPanel.
4495  */
4496 PlumaPanel *
pluma_window_get_bottom_panel(PlumaWindow * window)4497 pluma_window_get_bottom_panel (PlumaWindow *window)
4498 {
4499     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4500 
4501     return PLUMA_PANEL (window->priv->bottom_panel);
4502 }
4503 
4504 /**
4505  * pluma_window_get_statusbar:
4506  * @window: a #PlumaWindow
4507  *
4508  * Gets the #PlumaStatusbar of the @window.
4509  *
4510  * Returns: (transfer none): the #PlumaStatusbar of the @window.
4511  */
4512 GtkWidget *
pluma_window_get_statusbar(PlumaWindow * window)4513 pluma_window_get_statusbar (PlumaWindow *window)
4514 {
4515     g_return_val_if_fail (PLUMA_IS_WINDOW (window), 0);
4516 
4517     return window->priv->statusbar;
4518 }
4519 
4520 /**
4521  * pluma_window_get_state:
4522  * @window: a #PlumaWindow
4523  *
4524  * Retrieves the state of the @window.
4525  *
4526  * Returns: the current #PlumaWindowState of the @window.
4527  */
4528 PlumaWindowState
pluma_window_get_state(PlumaWindow * window)4529 pluma_window_get_state (PlumaWindow *window)
4530 {
4531     g_return_val_if_fail (PLUMA_IS_WINDOW (window), PLUMA_WINDOW_STATE_NORMAL);
4532 
4533     return window->priv->state;
4534 }
4535 
4536 GFile *
_pluma_window_get_default_location(PlumaWindow * window)4537 _pluma_window_get_default_location (PlumaWindow *window)
4538 {
4539     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4540 
4541     return window->priv->default_location != NULL ?
4542            g_object_ref (window->priv->default_location) : NULL;
4543 }
4544 
4545 void
_pluma_window_set_default_location(PlumaWindow * window,GFile * location)4546 _pluma_window_set_default_location (PlumaWindow *window,
4547                                     GFile       *location)
4548 {
4549     GFile *dir;
4550 
4551     g_return_if_fail (PLUMA_IS_WINDOW (window));
4552     g_return_if_fail (G_IS_FILE (location));
4553 
4554     dir = g_file_get_parent (location);
4555     g_return_if_fail (dir != NULL);
4556 
4557     if (window->priv->default_location != NULL)
4558         g_object_unref (window->priv->default_location);
4559 
4560     window->priv->default_location = dir;
4561 }
4562 
4563 /**
4564  * pluma_window_get_unsaved_documents:
4565  * @window: a #PlumaWindow
4566  *
4567  * Gets the list of documents that need to be saved before closing the window.
4568  *
4569  * Returns: (element-type Pluma.Document) (transfer container): a list of
4570  * #PlumaDocument that need to be saved before closing the window
4571  */
4572 GList *
pluma_window_get_unsaved_documents(PlumaWindow * window)4573 pluma_window_get_unsaved_documents (PlumaWindow *window)
4574 {
4575     GList *unsaved_docs = NULL;
4576     GList *tabs;
4577     GList *l;
4578 
4579     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4580 
4581     tabs = gtk_container_get_children (GTK_CONTAINER (window->priv->notebook));
4582 
4583     l = tabs;
4584     while (l != NULL)
4585     {
4586         PlumaTab *tab;
4587 
4588         tab = PLUMA_TAB (l->data);
4589 
4590         if (!_pluma_tab_can_close (tab))
4591         {
4592             PlumaDocument *doc;
4593 
4594             doc = pluma_tab_get_document (tab);
4595             unsaved_docs = g_list_prepend (unsaved_docs, doc);
4596         }
4597 
4598         l = g_list_next (l);
4599     }
4600 
4601     g_list_free (tabs);
4602 
4603     return g_list_reverse (unsaved_docs);
4604 }
4605 
4606 void
_pluma_window_set_saving_session_state(PlumaWindow * window,gboolean saving_session)4607 _pluma_window_set_saving_session_state (PlumaWindow *window,
4608                                         gboolean     saving_session)
4609 {
4610     PlumaWindowState old_state;
4611 
4612     g_return_if_fail (PLUMA_IS_WINDOW (window));
4613 
4614     old_state = window->priv->state;
4615 
4616     if (saving_session)
4617         window->priv->state |= PLUMA_WINDOW_STATE_SAVING_SESSION;
4618     else
4619         window->priv->state &= ~PLUMA_WINDOW_STATE_SAVING_SESSION;
4620 
4621     if (old_state != window->priv->state)
4622     {
4623         set_sensitivity_according_to_window_state (window);
4624 
4625         g_object_notify (G_OBJECT (window), "state");
4626     }
4627 }
4628 
4629 static void
hide_notebook_tabs_on_fullscreen(GtkNotebook * notebook,GParamSpec * pspec,PlumaWindow * window)4630 hide_notebook_tabs_on_fullscreen (GtkNotebook    *notebook,
4631                                   GParamSpec    *pspec,
4632                                   PlumaWindow    *window)
4633 {
4634     gtk_notebook_set_show_tabs (notebook, FALSE);
4635 }
4636 
4637 void
_pluma_window_fullscreen(PlumaWindow * window)4638 _pluma_window_fullscreen (PlumaWindow *window)
4639 {
4640     g_return_if_fail (PLUMA_IS_WINDOW (window));
4641 
4642     if (_pluma_window_is_fullscreen (window))
4643         return;
4644 
4645     /* Go to fullscreen mode and hide bars */
4646     gtk_window_fullscreen (&window->window);
4647     gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->priv->notebook), FALSE);
4648     g_signal_connect (window->priv->notebook, "notify::show-tabs",
4649                       G_CALLBACK (hide_notebook_tabs_on_fullscreen), window);
4650 
4651     gtk_widget_hide (window->priv->menubar);
4652 
4653     g_signal_handlers_block_by_func (window->priv->toolbar,
4654                                      toolbar_visibility_changed,
4655                                      window);
4656     gtk_widget_hide (window->priv->toolbar);
4657 
4658     g_signal_handlers_block_by_func (window->priv->statusbar,
4659                                      statusbar_visibility_changed,
4660                                      window);
4661     gtk_widget_hide (window->priv->statusbar);
4662 
4663     fullscreen_controls_build (window);
4664     fullscreen_controls_show (window);
4665 }
4666 
4667 void
_pluma_window_unfullscreen(PlumaWindow * window)4668 _pluma_window_unfullscreen (PlumaWindow *window)
4669 {
4670     gboolean visible;
4671     GtkAction *action;
4672 
4673     g_return_if_fail (PLUMA_IS_WINDOW (window));
4674 
4675     if (!_pluma_window_is_fullscreen (window))
4676         return;
4677 
4678     /* Unfullscreen and show bars */
4679     gtk_window_unfullscreen (&window->window);
4680     g_signal_handlers_disconnect_by_func (window->priv->notebook,
4681                                           hide_notebook_tabs_on_fullscreen,
4682                                           window);
4683     gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->priv->notebook), TRUE);
4684     gtk_widget_show (window->priv->menubar);
4685 
4686     action = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
4687                                           "ViewToolbar");
4688     visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
4689     if (visible)
4690         gtk_widget_show (window->priv->toolbar);
4691     g_signal_handlers_unblock_by_func (window->priv->toolbar,
4692                                        toolbar_visibility_changed,
4693                                        window);
4694 
4695     action = gtk_action_group_get_action (window->priv->always_sensitive_action_group,
4696                                           "ViewStatusbar");
4697     visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
4698     if (visible)
4699         gtk_widget_show (window->priv->statusbar);
4700     g_signal_handlers_unblock_by_func (window->priv->statusbar,
4701                                        statusbar_visibility_changed,
4702                                        window);
4703 
4704     gtk_widget_hide (window->priv->fullscreen_controls);
4705 }
4706 
4707 gboolean
_pluma_window_is_fullscreen(PlumaWindow * window)4708 _pluma_window_is_fullscreen (PlumaWindow *window)
4709 {
4710     g_return_val_if_fail (PLUMA_IS_WINDOW (window), FALSE);
4711 
4712     return window->priv->window_state & GDK_WINDOW_STATE_FULLSCREEN;
4713 }
4714 
4715 /**
4716  * pluma_window_get_tab_from_location:
4717  * @window: a #PlumaWindow
4718  * @location: a #GFile
4719  *
4720  * Gets the #PlumaTab that matches with the given @location.
4721  *
4722  * Returns: (transfer none): the #PlumaTab that matches with the given @location.
4723  */
4724 PlumaTab *
pluma_window_get_tab_from_location(PlumaWindow * window,GFile * location)4725 pluma_window_get_tab_from_location (PlumaWindow *window,
4726                                     GFile       *location)
4727 {
4728     GList *tabs;
4729     GList *l;
4730     PlumaTab *ret = NULL;
4731 
4732     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4733     g_return_val_if_fail (G_IS_FILE (location), NULL);
4734 
4735     tabs = gtk_container_get_children (GTK_CONTAINER (window->priv->notebook));
4736 
4737     for (l = tabs; l != NULL; l = g_list_next (l))
4738     {
4739         PlumaDocument *d;
4740         PlumaTab *t;
4741         GFile *f;
4742 
4743         t = PLUMA_TAB (l->data);
4744         d = pluma_tab_get_document (t);
4745 
4746         f = pluma_document_get_location (d);
4747 
4748         if ((f != NULL))
4749         {
4750             gboolean found = g_file_equal (location, f);
4751 
4752             g_object_unref (f);
4753 
4754             if (found)
4755             {
4756                 ret = t;
4757                 break;
4758             }
4759         }
4760     }
4761 
4762     g_list_free (tabs);
4763 
4764     return ret;
4765 }
4766 
4767 /**
4768  * pluma_window_get_message_bus:
4769  * @window: a #PlumaWindow
4770  *
4771  * Gets the #PlumaMessageBus associated with @window. The returned reference
4772  * is owned by the window and should not be unreffed.
4773  *
4774  * Return value: (transfer none): the #PlumaMessageBus associated with @window
4775  */
4776 PlumaMessageBus    *
pluma_window_get_message_bus(PlumaWindow * window)4777 pluma_window_get_message_bus (PlumaWindow *window)
4778 {
4779     g_return_val_if_fail (PLUMA_IS_WINDOW (window), NULL);
4780 
4781     return window->priv->message_bus;
4782 }
4783 
4784 void
_pluma_window_get_default_size(gint * width,gint * height)4785 _pluma_window_get_default_size (gint *width, gint *height)
4786 {
4787     g_return_if_fail (width != NULL && height != NULL);
4788 
4789     *width = PLUMA_WINDOW_DEFAULT_WIDTH;
4790     *height = PLUMA_WINDOW_DEFAULT_HEIGHT;
4791 }
4792 
4793