terminal: Silence -Wunused-function
[anjuta.git] / plugins / terminal / terminal.c
blobad5a6ccd3a35ad1678b41138e587e7cf49d99b37
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 plugin.c
4 Copyright (C) 2000 Naba Kumar
5 Copyright (C) 2014 Tristian Celestin
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include <config.h>
24 #include <signal.h>
26 #include <libanjuta/anjuta-shell.h>
27 #include <libanjuta/anjuta-debug.h>
29 #include <libanjuta/interfaces/ianjuta-terminal.h>
30 #include <libanjuta/interfaces/ianjuta-preferences.h>
31 #include <libanjuta/interfaces/ianjuta-project-manager.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <signal.h>
36 #define UI_FILE PACKAGE_DATA_DIR"/ui/anjuta-terminal-plugin.xml"
37 #define PREFS_BUILDER PACKAGE_DATA_DIR"/glade/anjuta-terminal-plugin.ui"
38 #define ICON_FILE "anjuta-terminal-plugin-48.png"
40 /* Gnome-Terminal GSettings Schemas and Keys */
41 #define TERM_PROFILE_LIST_SCHEMA "org.gnome.Terminal.ProfilesList"
42 #define TERM_LEGACY_SCHEMA "org.gnome.Terminal.Legacy.Settings"
43 #define TERM_PROFILE_DEFAULT "default"
45 /* Gnome Desktop GSettings Schemas and Keys */
46 #define GNOME_DESKTOP_INTERFACE_SCHEMA "org.gnome.desktop.interface"
47 #define GNOME_MONOSPACE_FONT "monospace-font-name"
49 /* Anjuta Terminal Plugin Schema and Keys */
50 #define PREF_SCHEMA "org.gnome.anjuta.terminal"
51 #define PREFS_TERMINAL_PROFILE_USE_DEFAULT "use-default-profile"
52 #define PREFS_TERMINAL_PROFILE "terminal-profile"
54 /* Columns in model for combo box */
55 enum {
56 TERM_STORE_UUID_COLUMN,
57 TERM_STORE_NAME_COLUMN
60 #include <vte/vte.h>
61 #include <pwd.h>
62 #include <gtk/gtk.h>
63 #include <libanjuta/anjuta-plugin.h>
64 #include "terminal-schemas.h"
66 extern GType terminal_plugin_get_type (GTypeModule *module);
67 #define ANJUTA_PLUGIN_TERMINAL_TYPE (terminal_plugin_get_type (NULL))
68 #define ANJUTA_PLUGIN_TERMINAL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ANJUTA_PLUGIN_TERMINAL_TYPE, TerminalPlugin))
69 #define ANJUTA_PLUGIN_TERMINAL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), ANJUTA_PLUGIN_TERMINAL_CLASS, TerminalPluginClass))
70 #define ANJUTA_IS_PLUGIN_TERMINAL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ANJUTA_PLUGIN_TERMINAL_TYPE))
71 #define ANJUTA_IS_PLUGIN_TERMINAL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), ANJUTA_PLUGIN_TERMINAL_TYPE))
72 #define ANJUTA_PLUGIN_TERMINAL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ANJUTA_PLUGIN_TERMINAL_TYPE, TerminalPluginClass))
74 typedef struct _TerminalPlugin TerminalPlugin;
75 typedef struct _TerminalPluginClass TerminalPluginClass;
77 struct _TerminalPlugin{
78 AnjutaPlugin parent;
80 gint uiid;
81 GtkActionGroup *action_group;
83 GPid child_pid;
84 GtkWidget *shell, *term;
85 GtkWidget *shell_box, *term_box;
86 GtkWidget *frame;
87 GtkWidget *pref_profile_combo;
88 GtkWidget *pref_default_button;
89 gboolean widget_added_to_shell;
90 GSettings *settings;
91 guint root_watch_id;
92 gboolean pty_flags;
93 #if OLD_VTE == 1
94 gboolean first_time_realization;
95 #endif
98 struct _TerminalPluginClass{
99 AnjutaPluginClass parent_class;
102 static gpointer parent_class;
104 #if 0
105 static const gchar*
106 get_profile_key (const gchar *profile, const gchar *key)
108 /* A reasonably safe buffer */
109 static gchar buffer[1024];
111 g_return_val_if_fail (profile != NULL && key != NULL, NULL);
113 snprintf (buffer, 1024, "%s/%s/%s", GCONF_PROFILE_PREFIX, profile, key);
114 return buffer;
116 #endif
118 static gboolean
119 strv_to_rgbav (const gchar **specs, gsize *size, GdkRGBA **colors)
121 GVariant *var;
122 gint i;
123 GVariantIter iter;
124 const char *str;
126 var = g_variant_new_strv (specs, -1);
127 g_variant_iter_init (&iter, var);
128 *size = g_variant_iter_n_children (&iter);
129 *colors = g_new (GdkRGBA, *size);
130 i = 0;
131 while (g_variant_iter_next (&iter, "&s", &str)) {
132 if (!gdk_rgba_parse (&(*colors)[i++], str)) {
133 g_free (*colors);
134 g_variant_unref (var);
135 return FALSE;
138 g_variant_unref (var);
139 return TRUE;
142 static void
143 terminal_set_preferences (VteTerminal *term, GSettings* settings, TerminalPlugin *term_plugin)
145 GSettingsSchemaSource *source;
146 GSettingsSchema *schema;
147 gchar *uuid;
148 gchar *path;
149 GSettings *profiles_list;
150 GSettings *profile_settings;
151 GSettings *interface_settings;
152 gboolean bool_val;
153 gchar *str_val;
154 gchar **str_vals;
155 gint int_val;
156 gsize size;
157 GdkRGBA color[2];
158 GdkRGBA *foreground;
159 GdkRGBA *background;
160 GdkRGBA *palette;
162 g_return_if_fail (settings != NULL);
164 /* Always autohide mouse */
165 vte_terminal_set_mouse_autohide (term, TRUE);
167 /* Check that terminal schemas really exist. They could be missing if gnome
168 * terminal is not installed or there is an old version */
169 source = g_settings_schema_source_get_default ();
170 schema = g_settings_schema_source_lookup (source, TERMINAL_PROFILES_LIST_SCHEMA, TRUE);
171 if (schema == NULL) return;
173 profiles_list = g_settings_new_full (schema, NULL, NULL);
174 g_settings_schema_unref (schema);
176 /* Get selected profile */
177 bool_val = g_settings_get_boolean (settings, PREFS_TERMINAL_PROFILE_USE_DEFAULT);
178 if (bool_val)
179 uuid = g_settings_get_string (profiles_list, TERMINAL_SETTINGS_LIST_DEFAULT_KEY);
180 else
181 uuid = g_settings_get_string (settings, PREFS_TERMINAL_PROFILE);
182 path = g_strdup_printf ("%s:%s/", TERMINAL_PROFILES_PATH_PREFIX, uuid);
183 profile_settings = g_settings_new_with_path (TERMINAL_PROFILE_SCHEMA, path);
184 g_free (uuid);
185 g_free (path);
187 /* Set terminal font */
188 bool_val = g_settings_get_boolean (profile_settings, TERMINAL_PROFILE_USE_SYSTEM_FONT_KEY);
189 if (bool_val)
191 interface_settings = g_settings_new (GNOME_DESKTOP_INTERFACE_SCHEMA);
192 str_val = g_settings_get_string (interface_settings, GNOME_MONOSPACE_FONT);
193 g_object_unref (interface_settings);
194 } else
196 str_val = g_settings_get_string (profile_settings, TERMINAL_PROFILE_FONT_KEY);
198 if (str_val != NULL)
199 vte_terminal_set_font_from_string (term, str_val);
201 /* Set cursor blink */
202 str_val = g_settings_get_string (profile_settings, TERMINAL_PROFILE_CURSOR_BLINK_MODE_KEY);
203 if (g_strcmp0 (str_val, "system") == 0)
204 vte_terminal_set_cursor_blink_mode (term, VTE_CURSOR_BLINK_SYSTEM);
205 else if (g_strcmp0 (str_val, "on") == 0)
206 vte_terminal_set_cursor_blink_mode (term, VTE_CURSOR_BLINK_ON);
207 else if (g_strcmp0 (str_val, "off") == 0)
208 vte_terminal_set_cursor_blink_mode (term, VTE_CURSOR_BLINK_OFF);
209 g_free (str_val);
211 /* Set audible bell */
212 bool_val = g_settings_get_boolean (profile_settings, TERMINAL_PROFILE_AUDIBLE_BELL_KEY);
213 vte_terminal_set_audible_bell (term, bool_val);
215 /* Set scrollback */
216 int_val = g_settings_get_int (profile_settings, TERMINAL_PROFILE_SCROLLBACK_LINES_KEY);
217 vte_terminal_set_scrollback_lines (term, (int_val == 0) ? 500 : int_val);
219 /* Set scroll on keystroke */
220 bool_val = g_settings_get_boolean (profile_settings, TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE_KEY);
221 vte_terminal_set_scroll_on_keystroke (term, bool_val);
223 /* Scroll on output */
224 bool_val = g_settings_get_boolean (profile_settings, TERMINAL_PROFILE_SCROLL_ON_OUTPUT_KEY);
225 vte_terminal_set_scroll_on_output (term, bool_val);
227 /* Set word characters */
228 str_val = g_settings_get_string (profile_settings, TERMINAL_PROFILE_WORD_CHARS_KEY);
229 if (str_val != NULL)
230 vte_terminal_set_word_chars (term, str_val);
231 g_free (str_val);
233 /* Set backspace key */
234 str_val = g_settings_get_string (profile_settings, TERMINAL_PROFILE_BACKSPACE_BINDING_KEY);
235 if (str_val != NULL)
237 if (g_strcmp0 (str_val, "ascii-del") == 0)
238 vte_terminal_set_backspace_binding (term, VTE_ERASE_ASCII_DELETE);
239 else if (g_strcmp0 (str_val, "escape-sequence") == 0)
240 vte_terminal_set_backspace_binding (term, VTE_ERASE_DELETE_SEQUENCE);
241 else if (g_strcmp0 (str_val, "control-h") == 0)
242 vte_terminal_set_backspace_binding (term, VTE_ERASE_ASCII_BACKSPACE);
243 else
244 vte_terminal_set_backspace_binding (term, VTE_ERASE_AUTO);
246 g_free (str_val);
248 /* Set delete key */
249 str_val = g_settings_get_string (profile_settings, TERMINAL_PROFILE_DELETE_BINDING_KEY);
250 if (str_val != NULL)
252 if (g_strcmp0 (str_val, "ascii-del") == 0)
253 vte_terminal_set_delete_binding (term, VTE_ERASE_ASCII_DELETE);
254 else if (g_strcmp0 (str_val, "escape-sequence") == 0)
255 vte_terminal_set_delete_binding (term, VTE_ERASE_DELETE_SEQUENCE);
256 else if (g_strcmp0 (str_val, "control-h") == 0)
257 vte_terminal_set_delete_binding (term, VTE_ERASE_ASCII_BACKSPACE);
258 else
259 vte_terminal_set_delete_binding (term, VTE_ERASE_AUTO);
261 g_free (str_val);
263 /* Set colors (foreground, background, and palette) */
264 str_val = g_settings_get_string (profile_settings, TERMINAL_PROFILE_BACKGROUND_COLOR_KEY);
265 if (str_val != NULL)
266 bool_val = gdk_rgba_parse (&color[0], str_val);
267 background = bool_val ? &color[0] : NULL;
268 g_free (str_val);
270 str_val = g_settings_get_string (profile_settings, TERMINAL_PROFILE_FOREGROUND_COLOR_KEY);
271 if (str_val != NULL)
272 bool_val = gdk_rgba_parse (&color[1], str_val);
273 foreground = bool_val ? &color[1] : NULL;
274 g_free (str_val);
276 str_vals = g_settings_get_strv(profile_settings, TERMINAL_PROFILE_PALETTE_KEY);
277 strv_to_rgbav (str_vals, &size, &palette);
278 g_free (str_vals);
280 /* vte_terminal_set_colors() works even if the terminal widget is not realized,
281 * which is not the case with vte_terminal_set_color_foreground() and
282 * vte_terminal_set_color_background()
284 vte_terminal_set_colors_rgba (term, foreground, background, palette, size);
286 g_free (palette);
287 g_object_unref (profiles_list);
288 g_object_unref (profile_settings);
291 static void
292 preferences_changed (GSettings* settings, TerminalPlugin *term)
294 terminal_set_preferences (VTE_TERMINAL (term->shell), settings, term);
295 terminal_set_preferences (VTE_TERMINAL (term->term), settings, term);
298 static void
299 on_notify_prefs_profile(GSettings* settings,
300 const gchar* key,
301 gpointer user_data)
303 TerminalPlugin *tp = ANJUTA_PLUGIN_TERMINAL (user_data);
304 preferences_changed (settings, tp);
307 static void
308 on_notify_prefs_default (GSettings* settings,
309 const gchar* key,
310 gpointer user_data)
312 TerminalPlugin *tp = ANJUTA_PLUGIN_TERMINAL (user_data);
313 preferences_changed (settings, tp);
316 static void
317 prefs_init (TerminalPlugin *tp)
319 g_signal_connect (tp->settings, "changed::" PREFS_TERMINAL_PROFILE,
320 G_CALLBACK (on_notify_prefs_profile), tp);
321 g_signal_connect (tp->settings, "changed::" PREFS_TERMINAL_PROFILE_USE_DEFAULT,
322 G_CALLBACK (on_notify_prefs_default), tp);
325 static void
326 use_default_profile_cb (GtkToggleButton *button,
327 TerminalPlugin *term)
329 if (gtk_toggle_button_get_active (button))
330 gtk_widget_set_sensitive (term->pref_profile_combo, FALSE);
331 else
332 gtk_widget_set_sensitive (term->pref_profile_combo, TRUE);
335 static void
336 terminal_child_exited_cb (VteTerminal *term, gpointer user_data)
338 TerminalPlugin *term_plugin = ANJUTA_PLUGIN_TERMINAL (user_data);
339 GPid pid = term_plugin->child_pid;
340 int status;
342 if (term_plugin->child_pid)
344 gboolean focus;
346 focus = gtk_widget_is_focus (term_plugin->term);
348 gtk_container_remove (GTK_CONTAINER (term_plugin->frame), term_plugin->term_box);
349 gtk_container_add (GTK_CONTAINER (term_plugin->frame), term_plugin->shell_box);
350 gtk_widget_show_all (term_plugin->shell_box);
351 if (focus)
352 gtk_widget_grab_focus (term_plugin->shell);
353 term_plugin->child_pid = 0;
356 status = vte_terminal_get_child_exit_status (term);
357 g_signal_emit_by_name(term_plugin, "child-exited", pid, status);
360 static pid_t
361 terminal_execute (TerminalPlugin *term_plugin, const gchar *directory,
362 const gchar *command, gchar **environment)
364 char **args, **args_ptr;
365 GList *args_list, *args_list_ptr;
366 gchar *dir;
367 VteTerminal *term;
368 GPid pid;
370 g_return_val_if_fail (command != NULL, 0);
372 /* Prepare command args */
373 args_list = anjuta_util_parse_args_from_string (command);
374 args = g_new (char*, g_list_length (args_list) + 1);
375 args_list_ptr = args_list;
376 args_ptr = args;
377 while (args_list_ptr)
379 *args_ptr = (char*) args_list_ptr->data;
380 args_list_ptr = g_list_next (args_list_ptr);
381 args_ptr++;
383 *args_ptr = NULL;
385 if (directory == NULL)
386 dir = g_path_get_dirname (args[0]);
387 else
388 dir = g_strdup (directory);
390 term = VTE_TERMINAL (term_plugin->term);
393 vte_terminal_reset (term, TRUE, TRUE);
396 if (vte_terminal_fork_command_full (term, term_plugin->pty_flags,
397 dir, args, environment,
398 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
399 &pid, NULL))
401 gboolean focus;
403 term_plugin->child_pid = pid;
405 /* Display terminal widget */
406 focus = gtk_widget_is_focus (term_plugin->shell);
407 gtk_container_remove (GTK_CONTAINER (term_plugin->frame), term_plugin->shell_box);
408 gtk_container_add (GTK_CONTAINER (term_plugin->frame), term_plugin->term_box);
409 gtk_widget_show_all (term_plugin->term_box);
410 if (focus)
411 gtk_widget_grab_focus (term_plugin->term);
413 if (term_plugin->widget_added_to_shell)
414 anjuta_shell_present_widget (ANJUTA_PLUGIN (term_plugin)->shell,
415 term_plugin->frame, NULL);
418 g_free (dir);
419 g_free (args);
420 g_list_foreach (args_list, (GFunc)g_free, NULL);
421 g_list_free (args_list);
423 return pid;
426 static void
427 init_shell (TerminalPlugin *term_plugin, const char *path)
429 gchar *shell[2] = {0, 0};
430 static gboolean first_time = TRUE;
431 VteTerminal *term = VTE_TERMINAL (term_plugin->shell);
433 shell[0] = vte_get_user_shell ();
434 if (shell[0] == NULL) shell[0] = g_strdup("/bin/sh");
436 if (!first_time)
437 vte_terminal_reset (term, FALSE, TRUE);
438 else
439 first_time = FALSE;
441 vte_terminal_fork_command_full (term, term_plugin->pty_flags,
442 path, shell, NULL,
443 0, NULL, NULL,
444 NULL, NULL);
445 g_free (shell[0]);
448 static gboolean
449 terminal_focus_cb (GtkWidget *widget, GdkEvent *event,
450 TerminalPlugin *term)
452 gtk_widget_grab_focus (widget);
453 return FALSE;
456 static gboolean
457 terminal_keypress_cb (GtkWidget *widget, GdkEventKey *event,
458 TerminalPlugin *terminal_plugin)
460 if (event->type != GDK_KEY_PRESS)
461 return FALSE;
463 /* ctrl-d */
464 if ((event->keyval == GDK_KEY_d || event->keyval == GDK_KEY_D) &&
465 (event->state & GDK_CONTROL_MASK))
467 if (terminal_plugin->child_pid)
469 kill (terminal_plugin->child_pid, SIGINT);
470 terminal_plugin->child_pid = 0;
472 return TRUE;
474 return FALSE;
477 static gboolean
478 terminal_click_cb (GtkWidget *widget, GdkEventButton *event,
479 TerminalPlugin *term)
481 if (event->button == 3)
483 AnjutaUI *ui;
484 GtkMenu *popup;
485 GtkAction *action;
487 ui = anjuta_shell_get_ui (ANJUTA_PLUGIN(term)->shell, NULL);
488 popup = GTK_MENU (gtk_ui_manager_get_widget (GTK_UI_MANAGER (ui), "/PopupTerminal"));
489 action = gtk_action_group_get_action (term->action_group, "ActionCopyFromTerminal");
490 gtk_action_set_sensitive (action,vte_terminal_get_has_selection(VTE_TERMINAL(widget)));
492 gtk_menu_popup (popup, NULL, NULL, NULL, NULL,
493 event->button, event->time);
496 return FALSE;
499 #if OLD_VTE == 1
500 /* VTE has a terrible bug where it could crash when container is changed.
501 * The problem has been traced in vte where its style-set handler does not
502 * adequately check if the widget is realized, resulting in a crash when
503 * style-set occurs in an unrealized vte widget.
505 * This work around blocks all style-set signal emissions when the vte
506 * widget is unrealized. -Naba
508 static void
509 terminal_realize_cb (GtkWidget *term, TerminalPlugin *plugin)
511 gint count;
513 if (plugin->first_time_realization)
515 /* First time realize does not have the signals blocked */
516 plugin->first_time_realization = FALSE;
517 return;
519 count = g_signal_handlers_unblock_matched (term,
520 G_SIGNAL_MATCH_DATA,
522 g_quark_from_string ("style-set"),
523 NULL,
524 NULL,
525 NULL);
526 DEBUG_PRINT ("Unlocked %d terminal signal", count);
529 static void
530 terminal_unrealize_cb (GtkWidget *term, TerminalPlugin *plugin)
532 gint count;
533 count = g_signal_handlers_block_matched (term,
534 G_SIGNAL_MATCH_DATA,
536 g_quark_from_string ("style-set"),
537 NULL,
538 NULL,
539 NULL);
540 DEBUG_PRINT ("Blocked %d terminal signal", count);
542 #endif
544 static void
545 on_terminal_copy_cb (GtkAction * action, TerminalPlugin *term_plugin)
547 VteTerminal *term;
549 if (term_plugin->child_pid)
550 term = VTE_TERMINAL (term_plugin->term);
551 else
552 term = VTE_TERMINAL (term_plugin->shell);
554 if (vte_terminal_get_has_selection(term))
555 vte_terminal_copy_clipboard(term);
558 static void
559 on_terminal_paste_cb (GtkAction * action, TerminalPlugin *term_plugin)
561 VteTerminal *term;
563 if (term_plugin->child_pid)
564 term = VTE_TERMINAL (term_plugin->term);
565 else
566 term = VTE_TERMINAL (term_plugin->shell);
568 vte_terminal_paste_clipboard(term);
571 static void
572 on_terminal_command_cb (GtkAction * action, TerminalPlugin *term_plugin)
574 VteTerminal *term;
575 gchar c;
577 if (term_plugin->child_pid)
578 term = VTE_TERMINAL (term_plugin->term);
579 else
580 term = VTE_TERMINAL (term_plugin->shell);
582 /* this only works for control + letter */
583 c = gtk_action_get_name (action) [strlen (gtk_action_get_name (action)) - 1] - 64;
585 vte_terminal_feed_child (term, &c, 1);
588 static GtkActionEntry actions_terminal[] = {
590 "ActionCopyFromTerminal", /* Action name */
591 GTK_STOCK_COPY, /* Stock icon, if any */
592 N_("_Copy"), /* Display label */
593 NULL, /* short-cut */
594 NULL, /* Tooltip */
595 G_CALLBACK (on_terminal_copy_cb) /* action callback */
598 "ActionPasteInTerminal",
599 GTK_STOCK_PASTE,
600 N_("_Paste"),
601 NULL,
602 NULL,
603 G_CALLBACK (on_terminal_paste_cb)
605 /* Add other control + letter commands here ending in -CTRL and then letter e.g. -CTRLT */
607 "ActionCommandToTerminal-CTRLC",
608 NULL,
609 N_("Ctrl-C"),
610 NULL,
611 NULL,
612 G_CALLBACK (on_terminal_command_cb)
615 "ActionCommandToTerminal-CTRLX",
616 NULL,
617 N_("Ctrl-X"),
618 NULL,
619 NULL,
620 G_CALLBACK (on_terminal_command_cb)
623 "ActionCommandToTerminal-CTRLZ",
624 NULL,
625 N_("Ctrl-Z"),
626 NULL,
627 NULL,
628 G_CALLBACK (on_terminal_command_cb)
632 static void
633 on_project_root_added (AnjutaPlugin *plugin, const gchar *name,
634 const GValue *value, gpointer user_data)
636 TerminalPlugin *term_plugin;
637 const gchar *root_uri;
639 term_plugin = (TerminalPlugin *)plugin;
641 root_uri = g_value_get_string (value);
642 if (root_uri)
644 GFile *file;
645 char *path;
647 file = g_file_new_for_uri (root_uri);
648 path = g_file_get_path (file);
650 init_shell (term_plugin, path);
652 g_object_unref (file);
653 g_free (path);
657 static GtkWidget *
658 create_terminal (TerminalPlugin *term_plugin)
660 GtkWidget *term;
662 /* Create new terminal. */
663 term = vte_terminal_new ();
664 gtk_widget_set_size_request (GTK_WIDGET (term), 10, 10);
665 vte_terminal_set_size (VTE_TERMINAL (term), 50, 1);
667 g_signal_connect (G_OBJECT (term), "focus-in-event",
668 G_CALLBACK (terminal_focus_cb), term_plugin);
670 g_signal_connect (G_OBJECT (term), "button-press-event",
671 G_CALLBACK (terminal_click_cb), term_plugin);
673 g_signal_connect (G_OBJECT (term), "child-exited",
674 G_CALLBACK (terminal_child_exited_cb), term_plugin);
676 #if OLD_VTE == 1
677 g_signal_connect (G_OBJECT (term), "realize",
678 G_CALLBACK (terminal_realize_cb), term_plugin);
679 g_signal_connect (G_OBJECT (term), "unrealize",
680 G_CALLBACK (terminal_unrealize_cb), term_plugin);
681 #endif
683 return term;
686 static GtkWidget *
687 create_box (GtkWidget *term)
689 GtkWidget *sb, *hbox;
691 sb = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vte_terminal_get_adjustment (VTE_TERMINAL (term))));
692 gtk_widget_set_can_focus (sb, FALSE);
694 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
695 gtk_box_pack_start (GTK_BOX (hbox), term, TRUE, TRUE, 0);
696 gtk_box_pack_start (GTK_BOX (hbox), sb, FALSE, TRUE, 0);
698 g_object_ref_sink (hbox);
700 return hbox;
703 static void
704 terminal_create (TerminalPlugin *term_plugin)
706 GtkWidget *frame;
708 g_return_if_fail(term_plugin != NULL);
710 term_plugin->child_pid = 0;
712 /* Create the terminals. */
713 term_plugin->shell = create_terminal (term_plugin);
714 term_plugin->shell_box = create_box (term_plugin->shell);
716 term_plugin->term = create_terminal (term_plugin);
717 term_plugin->term_box = create_box (term_plugin->term);
719 /* key-press handler for ctrl-d "kill" */
720 g_signal_connect (G_OBJECT (term_plugin->term), "key-press-event",
721 G_CALLBACK (terminal_keypress_cb), term_plugin);
723 frame = gtk_frame_new (NULL);
724 gtk_widget_show (frame);
725 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
727 gtk_container_add (GTK_CONTAINER (frame), term_plugin->shell_box);
728 gtk_widget_show_all (frame);
730 term_plugin->frame = frame;
732 init_shell (term_plugin, NULL);
735 static void
736 register_stock_icons (AnjutaPlugin *plugin)
738 static gboolean registered = FALSE;
740 if (registered)
741 return;
742 registered = TRUE;
744 BEGIN_REGISTER_ICON (plugin);
745 REGISTER_ICON (ICON_FILE, "terminal-plugin-icon");
746 END_REGISTER_ICON;
749 static gboolean
750 activate_plugin (AnjutaPlugin *plugin)
752 TerminalPlugin *term_plugin;
753 static gboolean initialized = FALSE;
754 AnjutaUI *ui;
756 DEBUG_PRINT ("%s", "TerminalPlugin: Activating Terminal plugin ...");
758 term_plugin = ANJUTA_PLUGIN_TERMINAL (plugin);
759 term_plugin->widget_added_to_shell = FALSE;
760 ui = anjuta_shell_get_ui (plugin->shell, NULL);
761 term_plugin->action_group = anjuta_ui_add_action_group_entries (ui,
762 "ActionGroupTerminal",
763 _("terminal operations"),
764 actions_terminal,
765 G_N_ELEMENTS (actions_terminal),
766 GETTEXT_PACKAGE, TRUE, term_plugin);
767 term_plugin->uiid = anjuta_ui_merge (ui, UI_FILE);
769 terminal_create (term_plugin);
771 if (!initialized)
773 register_stock_icons (plugin);
776 /* Setup prefs callbacks */
777 prefs_init (term_plugin);
779 /* Added widget in shell */
780 anjuta_shell_add_widget (plugin->shell, term_plugin->frame,
781 "AnjutaTerminal", _("Terminal"),
782 "terminal-plugin-icon",
783 ANJUTA_SHELL_PLACEMENT_BOTTOM, NULL);
784 /* terminal_focus_cb (term_plugin->term, NULL, term_plugin); */
785 term_plugin->widget_added_to_shell = TRUE;
786 initialized = TRUE;
788 /* Set all terminal preferences, at that time the terminal widget is
789 * not realized, a few vte functions are not working. Another
790 * possibility could be to call this when the widget is realized */
791 preferences_changed (term_plugin->settings, term_plugin);
793 /* set up project directory watch */
794 term_plugin->root_watch_id = anjuta_plugin_add_watch (plugin,
795 IANJUTA_PROJECT_MANAGER_PROJECT_ROOT_URI,
796 on_project_root_added,
797 0, NULL);
799 return TRUE;
802 static gboolean
803 deactivate_plugin (AnjutaPlugin *plugin)
805 TerminalPlugin *term_plugin;
806 AnjutaUI *ui;
808 term_plugin = ANJUTA_PLUGIN_TERMINAL (plugin);
810 ui = anjuta_shell_get_ui (plugin->shell, NULL);
811 anjuta_ui_unmerge (ui, term_plugin->uiid);
812 if (term_plugin->action_group)
814 anjuta_ui_remove_action_group (ui, term_plugin->action_group);
815 term_plugin->action_group = NULL;
818 /* terminal plugin widgets are destroyed as soon as it is removed */
819 anjuta_shell_remove_widget (plugin->shell, term_plugin->frame, NULL);
821 g_object_unref (term_plugin->shell_box);
822 g_object_unref (term_plugin->term_box);
824 /* remove watch */
825 anjuta_plugin_remove_watch (plugin, term_plugin->root_watch_id, FALSE);
827 term_plugin->child_pid = 0;
829 #if OLD_VTE == 1
830 g_signal_handlers_disconnect_by_func (G_OBJECT (term_plugin->term),
831 G_CALLBACK (terminal_unrealize_cb), term_plugin);
833 term_plugin->first_time_realization = TRUE;
834 #endif
836 return TRUE;
839 static void
840 terminal_plugin_dispose (GObject *obj)
842 TerminalPlugin *term_plugin = ANJUTA_PLUGIN_TERMINAL (obj);
844 g_object_unref (term_plugin->settings);
846 G_OBJECT_CLASS (parent_class)->dispose (obj);
849 static void
850 terminal_plugin_finalize (GObject *obj)
852 G_OBJECT_CLASS (parent_class)->finalize (obj);
855 static void
856 terminal_plugin_instance_init (GObject *obj)
858 TerminalPlugin *term_plugin = ANJUTA_PLUGIN_TERMINAL (obj);
860 term_plugin->settings = g_settings_new (PREF_SCHEMA);
861 term_plugin->child_pid = 0;
862 term_plugin->pref_profile_combo = NULL;
863 term_plugin->uiid = 0;
864 term_plugin->action_group = NULL;
865 term_plugin->pty_flags = VTE_PTY_DEFAULT;
866 #if OLD_VTE == 1
867 plugin->first_time_realization = TRUE;
868 #endif
871 static void
872 terminal_plugin_class_init (GObjectClass *klass)
874 AnjutaPluginClass *plugin_class = ANJUTA_PLUGIN_CLASS (klass);
876 parent_class = g_type_class_peek_parent (klass);
878 plugin_class->activate = activate_plugin;
879 plugin_class->deactivate = deactivate_plugin;
880 klass->dispose = terminal_plugin_dispose;
881 klass->finalize = terminal_plugin_finalize;
884 static pid_t
885 iterminal_execute_command (IAnjutaTerminal *terminal,
886 const gchar *directory,
887 const gchar *command,
888 gchar **environment, GError **error)
890 TerminalPlugin *plugin;
891 pid_t pid;
893 plugin = ANJUTA_PLUGIN_TERMINAL (terminal);
895 pid = terminal_execute (plugin, directory, command, environment);
896 if (pid <= 0)
898 g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Unable to execute command"));
901 return pid;
904 static void
905 iterminal_iface_init(IAnjutaTerminalIface *iface)
907 iface->execute_command = iterminal_execute_command;
910 static void
911 add_data_to_store (gchar *uuid, gchar *name, GtkListStore *model)
913 GtkTreeIter iter;
915 gtk_list_store_append (model, &iter);
916 gtk_list_store_set (model, &iter, TERM_STORE_UUID_COLUMN, uuid, TERM_STORE_NAME_COLUMN, name, -1);
919 static void
920 on_pref_profile_changed (GtkComboBox* combo, TerminalPlugin* term_plugin)
922 GtkTreeModel *model = gtk_combo_box_get_model (combo);
923 GtkTreeIter iter;
924 gchar *uuid;
926 gtk_combo_box_get_active_iter (combo, &iter);
927 gtk_tree_model_get (model, &iter, TERM_STORE_UUID_COLUMN, &uuid, -1);
928 g_settings_set_string (term_plugin->settings, PREFS_TERMINAL_PROFILE, uuid);
929 g_free (uuid);
932 static void
933 ipreferences_merge(IAnjutaPreferences* ipref, AnjutaPreferences* prefs, GError** e)
935 GSettingsSchemaSource *source;
936 GSettingsSchema *schema;
937 GSettings *terminal_settings;
938 GSettings *profile_settings;
939 GtkListStore *store;
940 GtkTreeIter iter;
941 gchar *default_uuid = NULL;
942 gchar *saved_uuid;
943 gchar *temp;
944 gchar **profiles;
945 gchar *path;
946 gboolean iter_valid;
947 gboolean found;
948 int i;
949 GError *error = NULL;
950 GtkCellRenderer *name_renderer;
951 GtkCellRenderer *uuid_renderer;
953 /* Create the terminal preferences page */
954 TerminalPlugin *term_plugin = ANJUTA_PLUGIN_TERMINAL (ipref);
955 GtkBuilder *bxml = gtk_builder_new ();
957 if (!gtk_builder_add_from_file (bxml, PREFS_BUILDER, &error))
959 g_warning ("Couldn't load builder file: %s", error->message);
960 g_error_free (error);
964 anjuta_preferences_add_from_builder (prefs, bxml,
965 term_plugin->settings,
966 "Terminal", _("Terminal"), ICON_FILE);
968 term_plugin->pref_profile_combo = GTK_WIDGET (gtk_builder_get_object (bxml, "profile_list_combo"));
969 term_plugin->pref_default_button = GTK_WIDGET (gtk_builder_get_object (bxml, "preferences_toggle:bool:1:0:use-default-profile"));
971 /* Add profile-name and profile-uuid renderers to combo box */
972 name_renderer = gtk_cell_renderer_text_new ();
973 uuid_renderer = gtk_cell_renderer_text_new ();
974 gtk_cell_layout_clear (GTK_CELL_LAYOUT(term_plugin->pref_profile_combo));
975 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(term_plugin->pref_profile_combo), name_renderer, TRUE);
976 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT(term_plugin->pref_profile_combo), uuid_renderer, FALSE);
977 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(term_plugin->pref_profile_combo), name_renderer, "text", TERM_STORE_NAME_COLUMN);
978 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT(term_plugin->pref_profile_combo), uuid_renderer, "text", TERM_STORE_UUID_COLUMN);
979 g_object_set (uuid_renderer, "style", PANGO_STYLE_ITALIC, NULL);
981 /* If at least a default profile exists ... */
982 source = g_settings_schema_source_get_default ();
983 schema = g_settings_schema_source_lookup (source, TERMINAL_PROFILES_LIST_SCHEMA, TRUE);
984 if (schema != NULL)
986 terminal_settings = g_settings_new_full (schema, NULL, NULL);
987 default_uuid = g_settings_get_string (terminal_settings, "default");
988 g_settings_schema_unref (schema);
991 if (default_uuid != NULL) {
993 /* Populate profiles store */
994 profiles = g_settings_get_strv (terminal_settings, "list");
995 store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (term_plugin->pref_profile_combo)));
996 gtk_list_store_clear (store);
997 for (i = 0; profiles[i] != NULL; i ++) {
998 path = g_strdup_printf ("%s:%s/", TERMINAL_PROFILES_PATH_PREFIX, profiles[i]);
999 profile_settings = g_settings_new_with_path (TERMINAL_PROFILE_SCHEMA, path);
1000 temp = g_settings_get_string (profile_settings, TERMINAL_PROFILE_VISIBLE_NAME_KEY);
1001 add_data_to_store (profiles[i], temp, store);
1004 /* Display saved profile in combo box */
1005 saved_uuid = g_settings_get_string (term_plugin->settings, PREFS_TERMINAL_PROFILE);
1006 if (saved_uuid != NULL)
1008 found = FALSE;
1009 iter_valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
1010 while (iter_valid && !found)
1012 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, TERM_STORE_UUID_COLUMN, &temp, -1);
1013 if (g_strcmp0 (saved_uuid, temp) == 0)
1015 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (term_plugin->pref_profile_combo), &iter);
1016 found = TRUE;
1018 iter_valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
1019 g_free (temp);
1021 g_free (saved_uuid);
1024 /* Save profile selection if changed */
1025 g_signal_connect (term_plugin->pref_profile_combo, "changed",
1026 G_CALLBACK (on_pref_profile_changed), term_plugin);
1028 /* Deactivate profile selection if not using default profile */
1029 use_default_profile_cb (GTK_TOGGLE_BUTTON (term_plugin->pref_default_button), term_plugin);
1030 g_signal_connect (G_OBJECT(term_plugin->pref_default_button), "toggled",
1031 G_CALLBACK (use_default_profile_cb), term_plugin);
1033 g_object_unref(terminal_settings);
1035 else
1037 /* No profile, perhaps GNOME Terminal is not installed,
1038 * Remove selection */
1039 gtk_widget_set_sensitive (term_plugin->pref_profile_combo, FALSE);
1040 gtk_widget_set_sensitive (term_plugin->pref_default_button, FALSE);
1043 g_object_unref (bxml);
1046 static void
1047 ipreferences_unmerge(IAnjutaPreferences* ipref, AnjutaPreferences* prefs, GError** e)
1049 TerminalPlugin* term_plugin = ANJUTA_PLUGIN_TERMINAL (ipref);
1050 g_signal_handlers_disconnect_by_func(G_OBJECT(term_plugin->pref_default_button),
1051 G_CALLBACK (use_default_profile_cb), term_plugin);
1052 anjuta_preferences_remove_page(prefs, _("Terminal"));
1053 term_plugin->pref_profile_combo = NULL;
1056 static void
1057 ipreferences_iface_init(IAnjutaPreferencesIface* iface)
1059 iface->merge = ipreferences_merge;
1060 iface->unmerge = ipreferences_unmerge;
1063 ANJUTA_PLUGIN_BEGIN (TerminalPlugin, terminal_plugin);
1064 ANJUTA_PLUGIN_ADD_INTERFACE (iterminal, IANJUTA_TYPE_TERMINAL);
1065 ANJUTA_PLUGIN_ADD_INTERFACE (ipreferences, IANJUTA_TYPE_PREFERENCES);
1066 ANJUTA_PLUGIN_END;
1068 ANJUTA_SIMPLE_PLUGIN (TerminalPlugin, terminal_plugin);