1/*
2 * notebook.vala - This file is part of the Geany MultiTerm plugin
3 *
4 * Copyright (c) 2012 Matthew Brush <matt@geany.org>.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22using Gtk;
23using Gdk;
24using Pango;
25using Vte;
26
27namespace MultiTerm
28{
29	public class Notebook : Gtk.Notebook
30	{
31		private Button add_button;
32		public Config cfg;
33		private ContextMenu? context_menu;
34
35		private void on_tab_label_close_clicked(int tab_num)
36		{
37			if (this.get_n_pages() > 1)
38				this.remove_terminal(tab_num);
39		}
40
41		private void on_show_tabs_activate(bool show_tabs)
42		{
43			this.show_tabs = show_tabs;
44			cfg.show_tabs = show_tabs;
45		}
46
47		private bool on_next_tab_activate()
48		{
49			int n_tabs = this.get_n_pages();
50			int current = this.get_current_page();
51
52			if (current < (n_tabs - 1))
53			{
54				current++;
55				this.set_current_page(current);
56			}
57
58			return (current < (n_tabs - 1)) ? true : false;
59		}
60
61		private bool on_previous_tab_activate()
62		{
63			int current = this.get_current_page();
64
65			if (current > 0)
66			{
67				current--;
68				this.set_current_page(current);
69			}
70
71			return (current > 0) ? true : false;
72		}
73
74		private void on_new_shell_activate(ShellConfig cfg)
75		{
76			add_terminal(cfg);
77		}
78
79		private void on_new_window_activate()
80		{
81			Pid pid;
82			string[] args = { cfg.external_terminal, null };
83
84			try
85			{
86				if (Process.spawn_async(null, args, null, SpawnFlags.SEARCH_PATH, null, out pid))
87					debug("Started external terminal '%s' with pid of '%d'", args[0], pid);
88			}
89			catch (SpawnError err)
90			{
91				warning(_("Unable to launch external terminal: %s").printf(err.message));
92			}
93		}
94
95		private void on_move_to_location(string location)
96		{
97			Container frame = this.get_parent() as Container;
98			Container parent = frame.get_parent() as Container;
99			Gtk.Notebook new_nb;
100
101			parent.remove(frame);
102
103			if (location == "msgwin")
104			{
105				new_nb = this.get_data<Notebook>("msgwin_notebook");
106				new_nb.append_page(frame, this.get_data<Label>("label"));
107			}
108			else
109			{
110				new_nb = this.get_data<Notebook>("sidebar_notebook");
111				new_nb.append_page(frame, this.get_data<Label>("label"));
112			}
113
114			new_nb.set_current_page(new_nb.page_num(frame));
115			cfg.location = location;
116		}
117
118		private void on_add_button_style_set()
119		{
120			int w, h;
121			Gtk.icon_size_lookup_for_settings(add_button.get_settings(),
122											  IconSize.MENU,
123											  out w, out h);
124			add_button.set_size_request(w+2, h+2);
125		}
126
127		private void on_add_button_clicked()
128		{
129			foreach (ShellConfig sh in cfg.shell_configs)
130			{
131				if (sh.section.strip() == "shell=default")
132				{
133					add_terminal(sh);
134					return;
135				}
136			}
137			warning(_("Unable to locate default shell in configuration file"));
138		}
139
140		private bool on_terminal_right_click_event(EventButton event)
141		{
142			if (context_menu == null)
143			{
144				context_menu = new ContextMenu(cfg);
145				context_menu.show_tabs_activate.connect(on_show_tabs_activate);
146				context_menu.next_tab_activate.connect(on_next_tab_activate);
147				context_menu.previous_tab_activate.connect(on_previous_tab_activate);
148				context_menu.new_shell_activate.connect(on_new_shell_activate);
149				context_menu.new_window_activate.connect(on_new_window_activate);
150				context_menu.move_to_location_activate.connect(on_move_to_location);
151			}
152			context_menu.popup(null, null, null, event.button, event.time);
153			return true;
154		}
155
156		public Terminal add_terminal(ShellConfig cfg)
157		{
158			TabLabel label = new TabLabel(cfg.name);
159			Terminal term = new Terminal(cfg);
160
161			label.show_all();
162			label.close_clicked.connect(on_tab_label_close_clicked);
163			label.set_data<Terminal>("terminal", term);
164
165			term.set_data<TabLabel>("label", label);
166			term.show_all();
167			term.right_click_event.connect(on_terminal_right_click_event);
168
169			this.append_page(term, label);
170			this.set_tab_reorderable(term, true);
171			/* TODO: this is deprecated, try and figure out alternative
172			 * from GtkNotebook docs. */
173			this.set_tab_label_packing(term, true, true, PackType.END);
174			this.scrollable = true;
175
176			return term;
177		}
178
179		public void remove_terminal(int tab_num)
180		{
181			this.remove_page(tab_num);
182		}
183
184		public class Notebook(string config_filename)
185		{
186			Gtk.Image img;
187			RcStyle style;
188
189			cfg = new Config(config_filename);
190
191			style = new RcStyle();
192			style.xthickness = 0;
193			style.ythickness = 0;
194
195			img = new Gtk.Image.from_stock(Gtk.Stock.ADD, Gtk.IconSize.MENU);
196
197			add_button = new Button();
198			add_button.modify_style(style);
199			add_button.relief = ReliefStyle.NONE;
200			add_button.focus_on_click = false;
201			add_button.set_border_width(2);
202			add_button.set_tooltip_text(_("New terminal"));
203			add_button.add(img);
204			add_button.clicked.connect(on_add_button_clicked);
205			add_button.show_all();
206			add_button.style_set.connect(on_add_button_style_set);
207
208			/* TODO: make this button show a list to select which shell
209			 * to open */
210			//this.set_action_widget(add_button, PackType.END);
211
212			this.show_tabs = cfg.show_tabs;
213
214			foreach (ShellConfig sh in cfg.shell_configs)
215			{
216				Terminal term = add_terminal(sh);
217				term.right_click_event.connect(on_terminal_right_click_event);
218			}
219		}
220	}
221}
222