1 /*
2  * Copyright © 2002 Havoc Pennington
3  * Copyright © 2002 Mathias Hasselmann
4  * Copyright © 2008 Christian Persch
5  * Copyright (C) 2012-2021 MATE Developers
6  *
7  * Mate-terminal 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 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Mate-terminal is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 
23 #include <string.h>
24 #include <math.h>
25 
26 #include <glib.h>
27 #include <gio/gio.h>
28 
29 #include "terminal-intl.h"
30 #include "profile-editor.h"
31 #include "terminal-util.h"
32 
33 typedef struct _TerminalColorScheme TerminalColorScheme;
34 
35 struct _TerminalColorScheme
36 {
37 	const char *name;
38 	const GdkRGBA foreground;
39 	const GdkRGBA background;
40 };
41 
42 static const TerminalColorScheme color_schemes[] =
43 {
44 	{
45 		N_("Black on light yellow"),
46 		{ 0, 0, 0, 1 },
47 		{ 1, 1, 0.866667, 1 }
48 	},
49 	{
50 		N_("Black on white"),
51 		{ 0, 0, 0, 1 },
52 		{ 1, 1, 1, 1 }
53 	},
54 	{
55 		N_("Gray on black"),
56 		{ 0.666667, 0.666667, 0.666667, 1 },
57 		{ 0, 0, 0, 1 }
58 	},
59 	{
60 		N_("Green on black"),
61 		{ 0, 1, 0, 1 },
62 		{ 0, 0, 0, 1 }
63 	},
64 	{
65 		N_("White on black"),
66 		{ 1, 1, 1, 1 },
67 		{ 0, 0, 0, 1 }
68 	},
69 	/* Translators: "Solarized" is the name of a colour scheme, "light" can be translated */
70 	{ N_("Solarized light"),
71 		{ 0.396078, 0.482352, 0.513725, 1 },
72 		{ 0.992156, 0.964705, 0.890196, 1 }
73 	},
74 	/* Translators: "Solarized" is the name of a colour scheme, "dark" can be translated */
75 	{ N_("Solarized dark"),
76 		{ 0.513725, 0.580392, 0.588235, 1 },
77 		{ 0,        0.168627, 0.211764, 1 }
78 	},
79 };
80 
81 static void profile_forgotten_cb (TerminalProfile           *profile,
82                                   GtkWidget                 *editor);
83 
84 static void profile_notify_sensitivity_cb (TerminalProfile *profile,
85         GParamSpec *pspec,
86         GtkWidget *editor);
87 
88 static void profile_colors_notify_scheme_combo_cb (TerminalProfile *profile,
89         GParamSpec *pspec,
90         GtkComboBox *combo);
91 
92 static void profile_palette_notify_scheme_combo_cb (TerminalProfile *profile,
93         GParamSpec *pspec,
94         GtkComboBox *combo);
95 
96 static void profile_palette_notify_colorpickers_cb (TerminalProfile *profile,
97         GParamSpec *pspec,
98         GtkWidget *editor);
99 
100 static GtkWidget*
profile_editor_get_widget(GtkWidget * editor,const char * widget_name)101 profile_editor_get_widget (GtkWidget  *editor,
102                            const char *widget_name)
103 {
104 	GtkBuilder *builder;
105 
106 	builder = g_object_get_data (G_OBJECT (editor), "builder");
107 	g_assert (builder != NULL);
108 
109 	return (GtkWidget *) gtk_builder_get_object  (builder, widget_name);
110 }
111 
112 static void
widget_and_labels_set_sensitive(GtkWidget * widget,gboolean sensitive)113 widget_and_labels_set_sensitive (GtkWidget *widget, gboolean sensitive)
114 {
115 	GList *labels, *i;
116 
117 	labels = gtk_widget_list_mnemonic_labels (widget);
118 	for (i = labels; i; i = i->next)
119 	{
120 		gtk_widget_set_sensitive (GTK_WIDGET (i->data), sensitive);
121 	}
122 	g_list_free (labels);
123 
124 	gtk_widget_set_sensitive (widget, sensitive);
125 }
126 
127 static void
profile_forgotten_cb(TerminalProfile * profile,GtkWidget * editor)128 profile_forgotten_cb (TerminalProfile *profile,
129                       GtkWidget *editor)
130 {
131 	gtk_widget_destroy (editor);
132 }
133 
134 static void
profile_notify_sensitivity_cb(TerminalProfile * profile,GParamSpec * pspec,GtkWidget * editor)135 profile_notify_sensitivity_cb (TerminalProfile *profile,
136                                GParamSpec *pspec,
137                                GtkWidget *editor)
138 {
139 	TerminalBackgroundType bg_type;
140 	const char *prop_name;
141 
142 	if (pspec)
143 		prop_name = pspec->name;
144 	else
145 		prop_name = NULL;
146 
147 #define SET_SENSITIVE(name, setting) widget_and_labels_set_sensitive (profile_editor_get_widget (editor, name), setting)
148 
149 	if (!prop_name ||
150 	        prop_name == I_(TERMINAL_PROFILE_USE_CUSTOM_COMMAND) ||
151 	        prop_name == I_(TERMINAL_PROFILE_CUSTOM_COMMAND))
152 	{
153 		gboolean use_custom_command_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND);
154 		SET_SENSITIVE ("use-custom-command-checkbutton", !use_custom_command_locked);
155 		SET_SENSITIVE ("custom-command-box",
156 		               terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND) &&
157 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_CUSTOM_COMMAND));
158 	}
159 
160 	gtk_widget_hide (profile_editor_get_widget (editor, "darken-background-transparent-or-image-scale-label"));
161 	gtk_widget_show (profile_editor_get_widget (editor, "darken-background-transparent-scale-label"));
162 	gtk_widget_hide (profile_editor_get_widget (editor, "scroll-background-checkbutton"));
163 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_BACKGROUND_TYPE))
164 	{
165 		gboolean bg_type_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKGROUND_TYPE);
166 		SET_SENSITIVE ("solid-radiobutton", !bg_type_locked);
167 		SET_SENSITIVE ("transparent-radiobutton", !bg_type_locked);
168 
169 		bg_type = terminal_profile_get_property_enum (profile, TERMINAL_PROFILE_BACKGROUND_TYPE);
170 		if (bg_type == TERMINAL_BACKGROUND_TRANSPARENT)
171 		{
172 			SET_SENSITIVE ("darken-background-vbox", !terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKGROUND_DARKNESS));
173 		}
174 		else
175 		{
176 			SET_SENSITIVE ("darken-background-vbox", FALSE);
177 		}
178 	}
179 
180 	if (!prop_name ||
181 	        prop_name == I_(TERMINAL_PROFILE_USE_SYSTEM_FONT) ||
182 	        prop_name == I_(TERMINAL_PROFILE_FONT))
183 	{
184 		SET_SENSITIVE ("font-hbox",
185 		               !terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_SYSTEM_FONT) &&
186 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_FONT));
187 		SET_SENSITIVE ("system-font-checkbutton",
188 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_SYSTEM_FONT));
189 	}
190 
191 	if (!prop_name ||
192 	        prop_name == I_(TERMINAL_PROFILE_FOREGROUND_COLOR) ||
193 	        prop_name == I_(TERMINAL_PROFILE_BACKGROUND_COLOR) ||
194 	        prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR) ||
195 	        prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG) ||
196 	        prop_name == I_(TERMINAL_PROFILE_USE_THEME_COLORS))
197 	{
198 		gboolean bg_locked, use_theme_colors, fg_locked;
199 
200 		use_theme_colors = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_THEME_COLORS);
201 		fg_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_FOREGROUND_COLOR);
202 		bg_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKGROUND_COLOR);
203 
204 		SET_SENSITIVE ("foreground-colorpicker", !use_theme_colors && !fg_locked);
205 		SET_SENSITIVE ("foreground-colorpicker-label", !use_theme_colors && !fg_locked);
206 		SET_SENSITIVE ("background-colorpicker", !use_theme_colors && !bg_locked);
207 		SET_SENSITIVE ("background-colorpicker-label", !use_theme_colors && !bg_locked);
208 		SET_SENSITIVE ("color-scheme-combobox", !use_theme_colors && !fg_locked && !bg_locked);
209 		SET_SENSITIVE ("color-scheme-combobox-label", !use_theme_colors && !fg_locked && !bg_locked);
210 	}
211 
212 	if (!prop_name ||
213 	        prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR) ||
214 	        prop_name == I_(TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG) ||
215 	        prop_name == I_(TERMINAL_PROFILE_USE_THEME_COLORS))
216 	{
217 		gboolean bold_locked, bold_same_as_fg_locked, bold_same_as_fg, use_theme_colors;
218 
219 		use_theme_colors = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_THEME_COLORS);
220 		bold_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_BOLD_COLOR);
221 		bold_same_as_fg_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG);
222 		bold_same_as_fg = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG);
223 
224 		SET_SENSITIVE ("bold-color-same-as-fg-checkbox", !use_theme_colors && !bold_same_as_fg_locked);
225 		SET_SENSITIVE ("bold-colorpicker", !use_theme_colors && !bold_locked && !bold_same_as_fg);
226 		SET_SENSITIVE ("bold-colorpicker-label", !use_theme_colors && ((!bold_same_as_fg && !bold_locked) || !bold_same_as_fg_locked));
227 	}
228 
229 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_VISIBLE_NAME))
230 		SET_SENSITIVE ("profile-name-entry",
231 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_VISIBLE_NAME));
232 
233 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_DEFAULT_SHOW_MENUBAR))
234 		SET_SENSITIVE ("show-menubar-checkbutton",
235 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_DEFAULT_SHOW_MENUBAR));
236 
237 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_TITLE))
238 		SET_SENSITIVE ("title-entry",
239 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_TITLE));
240 
241 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_TITLE_MODE))
242 		SET_SENSITIVE ("title-mode-combobox",
243 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_TITLE_MODE));
244 
245 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_ALLOW_BOLD))
246 		SET_SENSITIVE ("allow-bold-checkbutton",
247 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_ALLOW_BOLD));
248 
249 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SILENT_BELL))
250 		SET_SENSITIVE ("bell-checkbutton",
251 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_SILENT_BELL));
252 
253 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_COPY_SELECTION))
254 		SET_SENSITIVE ("copy-checkbutton",
255 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_COPY_SELECTION));
256 
257 #ifdef ENABLE_SKEY
258 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_USE_SKEY))
259 		SET_SENSITIVE ("use-skey-checkbutton",
260 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_SKEY));
261 #endif
262 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_USE_URLS))
263 		SET_SENSITIVE ("use-urls-checkbutton",
264 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_URLS));
265 
266 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_WORD_CHARS))
267 		SET_SENSITIVE ("word-chars-entry",
268 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_WORD_CHARS));
269 
270 	if (!prop_name ||
271 	        prop_name == I_(TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE) ||
272 	        prop_name == I_(TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS) ||
273 	        prop_name == I_(TERMINAL_PROFILE_DEFAULT_SIZE_ROWS))
274 	{
275 		gboolean use_custom_default_size_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE);
276 		gboolean use_custom_default_size = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE);
277 		gboolean columns_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS);
278 		gboolean rows_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_DEFAULT_SIZE_ROWS);
279 
280 		SET_SENSITIVE ("use-custom-default-size-checkbutton", !use_custom_default_size_locked);
281 		SET_SENSITIVE ("default-size-hbox", use_custom_default_size);
282 		SET_SENSITIVE ("default-size-label", (!columns_locked || !rows_locked));
283 		SET_SENSITIVE ("default-size-columns-label", !columns_locked);
284 		SET_SENSITIVE ("default-size-columns-spinbutton", !columns_locked);
285 		SET_SENSITIVE ("default-size-rows-label", !rows_locked);
286 		SET_SENSITIVE ("default-size-rows-spinbutton", !rows_locked);
287 	}
288 
289 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLLBAR_POSITION))
290 		SET_SENSITIVE ("scrollbar-position-combobox",
291 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLLBAR_POSITION));
292 
293 	if (!prop_name ||
294 	        prop_name == I_(TERMINAL_PROFILE_SCROLLBACK_LINES) ||
295 	        prop_name == I_(TERMINAL_PROFILE_SCROLLBACK_UNLIMITED))
296 	{
297 		gboolean scrollback_lines_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLLBACK_LINES);
298 		gboolean scrollback_unlimited_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLLBACK_UNLIMITED);
299 		gboolean scrollback_unlimited = terminal_profile_get_property_boolean (profile, TERMINAL_PROFILE_SCROLLBACK_UNLIMITED);
300 
301 		SET_SENSITIVE ("scrollback-label", !scrollback_lines_locked);
302 		SET_SENSITIVE ("scrollback-box", !scrollback_lines_locked && !scrollback_unlimited);
303 		SET_SENSITIVE ("scrollback-unlimited-checkbutton", !scrollback_lines_locked && !scrollback_unlimited_locked);
304 	}
305 
306 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE))
307 		SET_SENSITIVE ("scroll-on-keystroke-checkbutton",
308 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE));
309 
310 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_SCROLL_ON_OUTPUT))
311 		SET_SENSITIVE ("scroll-on-output-checkbutton",
312 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_SCROLL_ON_OUTPUT));
313 
314 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_EXIT_ACTION))
315 		SET_SENSITIVE ("exit-action-combobox",
316 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_EXIT_ACTION));
317 
318 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_LOGIN_SHELL))
319 		SET_SENSITIVE ("login-shell-checkbutton",
320 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_LOGIN_SHELL));
321 
322 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_PALETTE))
323 	{
324 		gboolean palette_locked = terminal_profile_property_locked (profile, TERMINAL_PROFILE_PALETTE);
325 		SET_SENSITIVE ("palette-combobox", !palette_locked);
326 		SET_SENSITIVE ("palette-table", !palette_locked);
327 	}
328 
329 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_BACKSPACE_BINDING))
330 		SET_SENSITIVE ("backspace-binding-combobox",
331 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_BACKSPACE_BINDING));
332 
333 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_DELETE_BINDING))
334 		SET_SENSITIVE ("delete-binding-combobox",
335 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_DELETE_BINDING));
336 
337 	if (!prop_name || prop_name == I_(TERMINAL_PROFILE_USE_THEME_COLORS))
338 		SET_SENSITIVE ("use-theme-colors-checkbutton",
339 		               !terminal_profile_property_locked (profile, TERMINAL_PROFILE_USE_THEME_COLORS));
340 
341 #undef SET_INSENSITIVE
342 }
343 
344 static void
color_scheme_combo_changed_cb(GtkWidget * combo,GParamSpec * pspec,TerminalProfile * profile)345 color_scheme_combo_changed_cb (GtkWidget *combo,
346                                GParamSpec *pspec,
347                                TerminalProfile *profile)
348 {
349 	guint i;
350 
351 	i = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
352 
353 	if (i < G_N_ELEMENTS (color_schemes))
354 	{
355 		g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo);
356 		g_object_set (profile,
357 		              TERMINAL_PROFILE_FOREGROUND_COLOR, &color_schemes[i].foreground,
358 		              TERMINAL_PROFILE_BACKGROUND_COLOR, &color_schemes[i].background,
359 		              NULL);
360 		g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo);
361 	}
362 	else
363 	{
364 		/* "custom" selected, no change */
365 	}
366 }
367 
368 static void
profile_colors_notify_scheme_combo_cb(TerminalProfile * profile,GParamSpec * pspec,GtkComboBox * combo)369 profile_colors_notify_scheme_combo_cb (TerminalProfile *profile,
370                                        GParamSpec *pspec,
371                                        GtkComboBox *combo)
372 {
373 	const GdkRGBA *fg, *bg;
374 	guint i;
375 
376 	fg = terminal_profile_get_property_boxed (profile, TERMINAL_PROFILE_FOREGROUND_COLOR);
377 	bg = terminal_profile_get_property_boxed (profile, TERMINAL_PROFILE_BACKGROUND_COLOR);
378 
379 	if (fg && bg)
380 	{
381 		for (i = 0; i < G_N_ELEMENTS (color_schemes); ++i)
382 		{
383 			if (gdk_rgba_equal (&fg, &color_schemes[i].foreground) &&
384 			        gdk_rgba_equal (&bg, &color_schemes[i].background))
385 				break;
386 		}
387 	}
388 	else
389 	{
390 		i = G_N_ELEMENTS (color_schemes);
391 	}
392 	/* If we didn't find a match, then we get the last combo box item which is "custom" */
393 
394 	g_signal_handlers_block_by_func (combo, G_CALLBACK (color_scheme_combo_changed_cb), profile);
395 	gtk_combo_box_set_active (GTK_COMBO_BOX (combo), i);
396 	g_signal_handlers_unblock_by_func (combo, G_CALLBACK (color_scheme_combo_changed_cb), profile);
397 }
398 
399 static void
palette_scheme_combo_changed_cb(GtkComboBox * combo,GParamSpec * pspec,TerminalProfile * profile)400 palette_scheme_combo_changed_cb (GtkComboBox *combo,
401                                  GParamSpec *pspec,
402                                  TerminalProfile *profile)
403 {
404 	int i;
405 
406 	i = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
407 
408 	g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo);
409 	if (i < TERMINAL_PALETTE_N_BUILTINS)
410 		terminal_profile_set_palette_builtin (profile, i);
411 	else
412 	{
413 		/* "custom" selected, no change */
414 	}
415 	g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo);
416 }
417 
418 static void
profile_palette_notify_scheme_combo_cb(TerminalProfile * profile,GParamSpec * pspec,GtkComboBox * combo)419 profile_palette_notify_scheme_combo_cb (TerminalProfile *profile,
420                                         GParamSpec *pspec,
421                                         GtkComboBox *combo)
422 {
423 	guint i;
424 
425 	if (!terminal_profile_get_palette_is_builtin (profile, &i))
426 		/* If we didn't find a match, then we want the last combo
427 		 * box item which is "custom"
428 		 */
429 		i = TERMINAL_PALETTE_N_BUILTINS;
430 
431 	g_signal_handlers_block_by_func (combo, G_CALLBACK (palette_scheme_combo_changed_cb), profile);
432 	gtk_combo_box_set_active (combo, i);
433 	g_signal_handlers_unblock_by_func (combo, G_CALLBACK (palette_scheme_combo_changed_cb), profile);
434 }
435 
436 static void
palette_color_notify_cb(GtkColorChooser * button,GParamSpec * pspec,TerminalProfile * profile)437 palette_color_notify_cb (GtkColorChooser *button,
438                          GParamSpec *pspec,
439                          TerminalProfile *profile)
440 {
441 	GtkWidget *editor;
442 	GdkRGBA color;
443 	guint i;
444 
445 	gtk_color_chooser_get_rgba (button, &color);
446 	i = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (button), "palette-entry-index"));
447 
448 	editor = gtk_widget_get_toplevel (GTK_WIDGET (button));
449 	g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_palette_notify_colorpickers_cb), editor);
450 	terminal_profile_modify_palette_entry (profile, i, &color);
451 	g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_palette_notify_colorpickers_cb), editor);
452 }
453 
454 static void
profile_palette_notify_colorpickers_cb(TerminalProfile * profile,GParamSpec * pspec,GtkWidget * editor)455 profile_palette_notify_colorpickers_cb (TerminalProfile *profile,
456                                         GParamSpec *pspec,
457                                         GtkWidget *editor)
458 {
459 	GtkWidget *w;
460 	GdkRGBA colors[TERMINAL_PALETTE_SIZE];
461 	guint n_colors, i;
462 
463 	n_colors = G_N_ELEMENTS (colors);
464 	terminal_profile_get_palette (profile, colors, &n_colors);
465 
466 	n_colors = MIN (n_colors, TERMINAL_PALETTE_SIZE);
467 	for (i = 0; i < n_colors; i++)
468 	{
469 		char name[32];
470 
471 		g_snprintf (name, sizeof (name), "palette-colorpicker-%d", i + 1);
472 		w = profile_editor_get_widget (editor, name);
473 
474 		g_signal_handlers_block_by_func (w, G_CALLBACK (palette_color_notify_cb), profile);
475 		gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (w), &colors[i]);
476 		g_signal_handlers_unblock_by_func (w, G_CALLBACK (palette_color_notify_cb), profile);
477 	}
478 }
479 
480 static void
custom_command_entry_changed_cb(GtkEntry * entry)481 custom_command_entry_changed_cb (GtkEntry *entry)
482 {
483 	const char *command;
484 	GError *error = NULL;
485 
486 	command = gtk_entry_get_text (entry);
487 
488 	if (g_shell_parse_argv (command, NULL, NULL, &error))
489 	{
490 		gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
491 	}
492 	else
493 	{
494 		char *tooltip;
495 
496 		gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, "dialog-warning");
497 
498 		tooltip = g_strdup_printf (_("Error parsing command: %s"), error->message);
499 		gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, tooltip);
500 		g_free (tooltip);
501 
502 		g_error_free (error);
503 	}
504 }
505 
506 static void
visible_name_entry_changed_cb(GtkEntry * entry,GtkWindow * window)507 visible_name_entry_changed_cb (GtkEntry *entry,
508                                GtkWindow *window)
509 {
510 	const char *visible_name;
511 	char *text;
512 
513 	visible_name = gtk_entry_get_text (entry);
514 
515 	text = g_strdup_printf (_("Editing Profile “%s”"), visible_name);
516 	gtk_window_set_title (window, text);
517 	g_free (text);
518 }
519 
520 static void
reset_compat_defaults_cb(GtkWidget * button,TerminalProfile * profile)521 reset_compat_defaults_cb (GtkWidget       *button,
522                           TerminalProfile *profile)
523 {
524 	terminal_profile_reset_property (profile, TERMINAL_PROFILE_DELETE_BINDING);
525 	terminal_profile_reset_property (profile, TERMINAL_PROFILE_BACKSPACE_BINDING);
526 }
527 
528 /*
529  * initialize widgets
530  */
531 
532 static void
init_color_scheme_menu(GtkWidget * widget)533 init_color_scheme_menu (GtkWidget *widget)
534 {
535 	GtkCellRenderer *renderer;
536 	GtkTreeIter iter;
537 	GtkListStore *store;
538 	gsize i;
539 
540 	store = gtk_list_store_new (1, G_TYPE_STRING);
541 	for (i = 0; i < G_N_ELEMENTS (color_schemes); ++i)
542 	gtk_list_store_insert_with_values (store, &iter, -1,
543 	                                   0, _(color_schemes[i].name),
544 	                                   -1);
545 	gtk_list_store_insert_with_values (store, &iter, -1,
546 	                                  0, _("Custom"),
547 	                                  -1);
548 
549 	gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store));
550 	g_object_unref (store);
551 
552 	renderer = gtk_cell_renderer_text_new ();
553 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, TRUE);
554 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), renderer, "text", 0, NULL);
555 }
556 
557 static char*
format_percent_value(GtkScale * scale,double val,void * data)558 format_percent_value (GtkScale *scale,
559                       double    val,
560                       void     *data)
561 {
562 	return g_strdup_printf ("%d%%", (int) (val * 100.0 + 0.5));
563 }
564 
565 static void
init_background_darkness_scale(GtkWidget * scale)566 init_background_darkness_scale (GtkWidget *scale)
567 {
568 	g_signal_connect (scale, "format-value",
569 	                  G_CALLBACK (format_percent_value),
570 	                  NULL);
571 }
572 
573 static void
editor_response_cb(GtkWidget * editor,int response,gpointer use_data)574 editor_response_cb (GtkWidget *editor,
575                     int response,
576                     gpointer use_data)
577 {
578 	if (response == GTK_RESPONSE_HELP)
579 	{
580 		terminal_util_show_help ("mate-terminal-prefs", GTK_WINDOW (editor));
581 		return;
582 	}
583 
584 	gtk_widget_destroy (editor);
585 }
586 
587 static void
setup_background_filechooser(GtkWidget * filechooser,TerminalProfile * profile)588 setup_background_filechooser (GtkWidget *filechooser,
589                               TerminalProfile *profile)
590 {
591 	GtkFileFilter *filter;
592 	const char *home_dir;
593 
594 	filter = gtk_file_filter_new ();
595 	gtk_file_filter_add_pixbuf_formats (filter);
596 	gtk_file_filter_set_name (filter, _("Images"));
597 	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (filechooser), filter);
598 
599 	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filechooser), TRUE);
600 
601 	/* Start filechooser in $HOME instead of the current dir of the factory which is "/" */
602 	home_dir = g_get_home_dir ();
603 	if (home_dir)
604 		gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filechooser), home_dir);
605 }
606 
607 static void
profile_editor_destroyed(GtkWidget * editor,TerminalProfile * profile)608 profile_editor_destroyed (GtkWidget       *editor,
609                           TerminalProfile *profile)
610 {
611 	g_signal_handlers_disconnect_by_func (profile, G_CALLBACK (profile_forgotten_cb), editor);
612 	g_signal_handlers_disconnect_by_func (profile, G_CALLBACK (profile_notify_sensitivity_cb), editor);
613 	g_signal_handlers_disconnect_matched (profile, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
614 	                                      G_CALLBACK (profile_colors_notify_scheme_combo_cb), NULL);
615 	g_signal_handlers_disconnect_matched (profile, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
616 	                                      G_CALLBACK (profile_palette_notify_scheme_combo_cb), NULL);
617 	g_signal_handlers_disconnect_matched (profile, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
618 	                                      G_CALLBACK (profile_palette_notify_colorpickers_cb), NULL);
619 
620 	g_object_set_data (G_OBJECT (profile), "editor-window", NULL);
621 	g_object_set_data (G_OBJECT (editor), "builder", NULL);
622 }
623 
624 static void
terminal_profile_editor_focus_widget(GtkWidget * editor,const char * widget_name)625 terminal_profile_editor_focus_widget (GtkWidget *editor,
626                                       const char *widget_name)
627 {
628 	GtkBuilder *builder;
629 	GtkWidget *widget, *page, *page_parent;
630 
631 	if (widget_name == NULL)
632 		return;
633 
634 	builder = g_object_get_data (G_OBJECT (editor), "builder");
635 	widget = GTK_WIDGET (gtk_builder_get_object (builder, widget_name));
636 	if (widget == NULL)
637 		return;
638 
639 	page = widget;
640 	while (page != NULL &&
641 	        (page_parent = gtk_widget_get_parent (page)) != NULL &&
642 	        !GTK_IS_NOTEBOOK (page_parent))
643 		page = page_parent;
644 
645 	page_parent = gtk_widget_get_parent (page);
646 	if (page != NULL && GTK_IS_NOTEBOOK (page_parent))
647 	{
648 		GtkNotebook *notebook;
649 
650 		notebook = GTK_NOTEBOOK (page_parent);
651 		gtk_notebook_set_current_page (notebook, gtk_notebook_page_num (notebook, page));
652 	}
653 
654 	if (gtk_widget_is_sensitive (widget))
655 		gtk_widget_grab_focus (widget);
656 }
657 
658 static gboolean
on_profile_editor_notebook_scroll_event(GtkWidget * widget,GdkEventScroll * event)659 on_profile_editor_notebook_scroll_event (GtkWidget        *widget,
660                                          GdkEventScroll   *event)
661 {
662     GtkNotebook *notebook = GTK_NOTEBOOK (widget);
663     GtkWidget *child, *event_widget, *action_widget;
664 
665     child = gtk_notebook_get_nth_page (notebook, gtk_notebook_get_current_page (notebook));
666     if (child == NULL)
667         return FALSE;
668 
669     event_widget = gtk_get_event_widget ((GdkEvent*) event);
670 
671     /* Ignore scroll events from the content of the page */
672     if (event_widget == NULL || event_widget == child || gtk_widget_is_ancestor (event_widget, child))
673         return FALSE;
674 
675     /* And also from the action widgets */
676     action_widget = gtk_notebook_get_action_widget (notebook, GTK_PACK_START);
677     if (event_widget == action_widget || (action_widget != NULL && gtk_widget_is_ancestor (event_widget, action_widget)))
678         return FALSE;
679 
680     action_widget = gtk_notebook_get_action_widget (notebook, GTK_PACK_END);
681     if (event_widget == action_widget || (action_widget != NULL && gtk_widget_is_ancestor (event_widget, action_widget)))
682         return FALSE;
683 
684     switch (event->direction) {
685         case GDK_SCROLL_RIGHT:
686         case GDK_SCROLL_DOWN:
687             gtk_notebook_next_page (notebook);
688             break;
689         case GDK_SCROLL_LEFT:
690         case GDK_SCROLL_UP:
691             gtk_notebook_prev_page (notebook);
692             break;
693         case GDK_SCROLL_SMOOTH:
694             switch (gtk_notebook_get_tab_pos (notebook)) {
695                 case GTK_POS_LEFT:
696                 case GTK_POS_RIGHT:
697                     if (event->delta_y > 0)
698                         gtk_notebook_next_page (notebook);
699                     else if (event->delta_y < 0)
700                         gtk_notebook_prev_page (notebook);
701                     break;
702                 case GTK_POS_TOP:
703                 case GTK_POS_BOTTOM:
704                     if (event->delta_x > 0)
705                         gtk_notebook_next_page (notebook);
706                     else if (event->delta_x < 0)
707                         gtk_notebook_prev_page (notebook);
708                     break;
709             }
710             break;
711     }
712 
713     return TRUE;
714 }
715 
716 /**
717  * terminal_profile_edit:
718  * @profile: a #TerminalProfile
719  * @transient_parent: a #GtkWindow, or %NULL
720  * @widget_name: a widget name in the profile editor's UI, or %NULL
721  *
722  * Shows the profile editor with @profile, anchored to @transient_parent.
723  * If @widget_name is non-%NULL, focuses the corresponding widget and
724  * switches the notebook to its containing page.
725  */
726 void
terminal_profile_edit(TerminalProfile * profile,GtkWindow * transient_parent,const char * widget_name)727 terminal_profile_edit (TerminalProfile *profile,
728                        GtkWindow       *transient_parent,
729                        const char      *widget_name)
730 {
731 	GtkBuilder *builder;
732 	GError *error = NULL;
733 	GtkWidget *editor, *w;
734 	guint i;
735 
736 	editor = g_object_get_data (G_OBJECT (profile), "editor-window");
737 	if (editor)
738 	{
739 		terminal_profile_editor_focus_widget (editor, widget_name);
740 
741 		gtk_window_set_transient_for (GTK_WINDOW (editor),
742 		                              GTK_WINDOW (transient_parent));
743 		gtk_window_present (GTK_WINDOW (editor));
744 		return;
745 	}
746 
747 	builder = gtk_builder_new ();
748 	gtk_builder_add_from_resource (builder, TERMINAL_RESOURCES_PATH_PREFIX G_DIR_SEPARATOR_S "ui/profile-preferences.ui", &error);
749 	g_assert_no_error (error);
750 
751 	editor = (GtkWidget *) gtk_builder_get_object  (builder, "profile-editor-dialog");
752 	g_object_set_data_full (G_OBJECT (editor), "builder",
753 	                        builder, (GDestroyNotify) g_object_unref);
754 #ifndef ENABLE_SKEY
755 	gtk_widget_hide (profile_editor_get_widget (editor, "use-skey-checkbutton"));
756 #endif
757 	/* Store the dialogue on the profile, so we can acccess it above to check if
758 	 * there's already a profile editor for this profile.
759 	 */
760 	g_object_set_data (G_OBJECT (profile), "editor-window", editor);
761 
762 	g_signal_connect (editor, "destroy",
763 	                  G_CALLBACK (profile_editor_destroyed),
764 	                  profile);
765 
766 	g_signal_connect (editor, "response",
767 	                  G_CALLBACK (editor_response_cb),
768 	                  NULL);
769 
770 	w = (GtkWidget *) gtk_builder_get_object  (builder, "color-scheme-combobox");
771 	init_color_scheme_menu (w);
772 
773 	w = (GtkWidget *) gtk_builder_get_object  (builder, "darken-background-scale");
774 	init_background_darkness_scale (w);
775 
776 	w = (GtkWidget *) gtk_builder_get_object  (builder, "background-image-filechooser");
777 	setup_background_filechooser (w, profile);
778 
779 	/* Hook up the palette colorpickers and combo box */
780 
781 	for (i = 0; i < TERMINAL_PALETTE_SIZE; ++i)
782 	{
783 		char name[32];
784 		char *text;
785 
786 		g_snprintf (name, sizeof (name), "palette-colorpicker-%u", i + 1);
787 		w = (GtkWidget *) gtk_builder_get_object  (builder, name);
788 
789 		g_object_set_data (G_OBJECT (w), "palette-entry-index", GUINT_TO_POINTER (i));
790 
791 		text = g_strdup_printf (_("Choose Palette Color %d"), i + 1);
792 		gtk_color_button_set_title (GTK_COLOR_BUTTON (w), text);
793 		g_free (text);
794 
795 		text = g_strdup_printf (_("Palette entry %d"), i + 1);
796 		gtk_widget_set_tooltip_text (w, text);
797 		g_free (text);
798 
799 		g_signal_connect (w, "notify::rgba",
800 		                  G_CALLBACK (palette_color_notify_cb),
801 		                  profile);
802 	}
803 
804 	profile_palette_notify_colorpickers_cb (profile, NULL, editor);
805 	g_signal_connect (profile, "notify::" TERMINAL_PROFILE_PALETTE,
806 	                  G_CALLBACK (profile_palette_notify_colorpickers_cb),
807 	                  editor);
808 
809 	w = (GtkWidget *) gtk_builder_get_object  (builder, "palette-combobox");
810 	g_signal_connect (w, "notify::active",
811 	                  G_CALLBACK (palette_scheme_combo_changed_cb),
812 	                  profile);
813 
814 	profile_palette_notify_scheme_combo_cb (profile, NULL, GTK_COMBO_BOX (w));
815 	g_signal_connect (profile, "notify::" TERMINAL_PROFILE_PALETTE,
816 	                  G_CALLBACK (profile_palette_notify_scheme_combo_cb),
817 	                  w);
818 
819 	/* Hook up the color scheme pickers and combo box */
820 	w = (GtkWidget *) gtk_builder_get_object  (builder, "color-scheme-combobox");
821 	g_signal_connect (w, "notify::active",
822 	                  G_CALLBACK (color_scheme_combo_changed_cb),
823 	                  profile);
824 
825 	profile_colors_notify_scheme_combo_cb (profile, NULL, GTK_COMBO_BOX (w));
826 	g_signal_connect (profile, "notify::" TERMINAL_PROFILE_FOREGROUND_COLOR,
827 	                  G_CALLBACK (profile_colors_notify_scheme_combo_cb),
828 	                  w);
829 	g_signal_connect (profile, "notify::" TERMINAL_PROFILE_BACKGROUND_COLOR,
830 	                  G_CALLBACK (profile_colors_notify_scheme_combo_cb),
831 	                  w);
832 
833 #define CONNECT_WITH_FLAGS(name, prop, flags) terminal_util_bind_object_property_to_widget (G_OBJECT (profile), prop, (GtkWidget *) gtk_builder_get_object (builder, name), flags)
834 #define CONNECT(name, prop) CONNECT_WITH_FLAGS (name, prop, 0)
835 #define SET_ENUM_VALUE(name, value) g_object_set_data (gtk_builder_get_object (builder, name), "enum-value", GINT_TO_POINTER (value))
836 
837 	w = GTK_WIDGET (gtk_builder_get_object (builder, "custom-command-entry"));
838 	custom_command_entry_changed_cb (GTK_ENTRY (w));
839 	g_signal_connect (w, "changed",
840 	                  G_CALLBACK (custom_command_entry_changed_cb), NULL);
841 	w = GTK_WIDGET (gtk_builder_get_object (builder, "profile-name-entry"));
842 	g_signal_connect (w, "changed",
843 	                  G_CALLBACK (visible_name_entry_changed_cb), editor);
844 
845 	g_signal_connect (gtk_builder_get_object  (builder, "reset-compat-defaults-button"),
846 	                  "clicked",
847 	                  G_CALLBACK (reset_compat_defaults_cb),
848 	                  profile);
849 
850 	SET_ENUM_VALUE ("image-radiobutton", TERMINAL_BACKGROUND_IMAGE);
851 	SET_ENUM_VALUE ("solid-radiobutton", TERMINAL_BACKGROUND_SOLID);
852 	SET_ENUM_VALUE ("transparent-radiobutton", TERMINAL_BACKGROUND_TRANSPARENT);
853 	CONNECT ("allow-bold-checkbutton", TERMINAL_PROFILE_ALLOW_BOLD);
854 	CONNECT ("background-colorpicker", TERMINAL_PROFILE_BACKGROUND_COLOR);
855 	CONNECT ("background-image-filechooser", TERMINAL_PROFILE_BACKGROUND_IMAGE_FILE);
856 	CONNECT ("backspace-binding-combobox", TERMINAL_PROFILE_BACKSPACE_BINDING);
857 	CONNECT ("bold-color-same-as-fg-checkbox", TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG);
858 	CONNECT ("bold-colorpicker", TERMINAL_PROFILE_BOLD_COLOR);
859 	CONNECT ("cursor-shape-combobox", TERMINAL_PROFILE_CURSOR_SHAPE);
860 	CONNECT ("cursor-blink-combobox", TERMINAL_PROFILE_CURSOR_BLINK_MODE);
861 	CONNECT ("custom-command-entry", TERMINAL_PROFILE_CUSTOM_COMMAND);
862 	CONNECT ("darken-background-scale", TERMINAL_PROFILE_BACKGROUND_DARKNESS);
863 	CONNECT ("default-size-columns-spinbutton", TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS);
864 	CONNECT ("default-size-rows-spinbutton", TERMINAL_PROFILE_DEFAULT_SIZE_ROWS);
865 	CONNECT ("delete-binding-combobox", TERMINAL_PROFILE_DELETE_BINDING);
866 	CONNECT ("exit-action-combobox", TERMINAL_PROFILE_EXIT_ACTION);
867 	CONNECT ("font-selector", TERMINAL_PROFILE_FONT);
868 	CONNECT ("foreground-colorpicker", TERMINAL_PROFILE_FOREGROUND_COLOR);
869 	CONNECT ("image-radiobutton", TERMINAL_PROFILE_BACKGROUND_TYPE);
870 	CONNECT ("login-shell-checkbutton", TERMINAL_PROFILE_LOGIN_SHELL);
871 	CONNECT ("profile-name-entry", TERMINAL_PROFILE_VISIBLE_NAME);
872 	CONNECT ("scrollback-lines-spinbutton", TERMINAL_PROFILE_SCROLLBACK_LINES);
873 	CONNECT ("scrollback-unlimited-checkbutton", TERMINAL_PROFILE_SCROLLBACK_UNLIMITED);
874 	CONNECT ("scroll-background-checkbutton", TERMINAL_PROFILE_SCROLL_BACKGROUND);
875 	CONNECT ("scrollbar-position-combobox", TERMINAL_PROFILE_SCROLLBAR_POSITION);
876 	CONNECT ("scroll-on-keystroke-checkbutton", TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE);
877 	CONNECT ("scroll-on-output-checkbutton", TERMINAL_PROFILE_SCROLL_ON_OUTPUT);
878 	CONNECT ("show-menubar-checkbutton", TERMINAL_PROFILE_DEFAULT_SHOW_MENUBAR);
879 	CONNECT ("solid-radiobutton", TERMINAL_PROFILE_BACKGROUND_TYPE);
880 	CONNECT ("system-font-checkbutton", TERMINAL_PROFILE_USE_SYSTEM_FONT);
881 	CONNECT ("title-entry", TERMINAL_PROFILE_TITLE);
882 	CONNECT ("title-mode-combobox", TERMINAL_PROFILE_TITLE_MODE);
883 	CONNECT ("transparent-radiobutton", TERMINAL_PROFILE_BACKGROUND_TYPE);
884 	CONNECT ("use-custom-command-checkbutton", TERMINAL_PROFILE_USE_CUSTOM_COMMAND);
885 	CONNECT ("use-custom-default-size-checkbutton", TERMINAL_PROFILE_USE_CUSTOM_DEFAULT_SIZE);
886 	CONNECT ("use-theme-colors-checkbutton", TERMINAL_PROFILE_USE_THEME_COLORS);
887 	CONNECT ("word-chars-entry", TERMINAL_PROFILE_WORD_CHARS);
888 	CONNECT_WITH_FLAGS ("bell-checkbutton", TERMINAL_PROFILE_SILENT_BELL, FLAG_INVERT_BOOL);
889 	/* CONNECT_WITH_FLAGS ("copy-checkbutton", TERMINAL_PROFILE_COPY_SELECTION, FLAG_INVERT_BOOL); */
890 	CONNECT ("copy-checkbutton", TERMINAL_PROFILE_COPY_SELECTION);
891 #ifdef ENABLE_SKEY
892 	CONNECT ("use-skey-checkbutton", TERMINAL_PROFILE_USE_SKEY);
893 #endif
894 	CONNECT ("use-urls-checkbutton", TERMINAL_PROFILE_USE_URLS);
895 
896 #undef CONNECT
897 #undef CONNECT_WITH_FLAGS
898 #undef SET_ENUM_VALUE
899 
900 	profile_notify_sensitivity_cb (profile, NULL, editor);
901 	g_signal_connect (profile, "notify",
902 	                  G_CALLBACK (profile_notify_sensitivity_cb),
903 	                  editor);
904 	g_signal_connect (profile,
905 	                  "forgotten",
906 	                  G_CALLBACK (profile_forgotten_cb),
907 	                  editor);
908 
909 	terminal_profile_editor_focus_widget (editor, widget_name);
910 
911     w = GTK_WIDGET (gtk_builder_get_object (builder, "profile-editor-notebook"));
912     gtk_widget_add_events (w, GDK_SCROLL_MASK);
913     g_signal_connect (w,
914                       "scroll-event",
915                       G_CALLBACK (on_profile_editor_notebook_scroll_event),
916                       NULL);
917 
918 	gtk_window_set_transient_for (GTK_WINDOW (editor),
919 	                              GTK_WINDOW (transient_parent));
920 	gtk_window_present (GTK_WINDOW (editor));
921 }
922