1 /*
2  * Copyright © 2001 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 <stdlib.h>
25 
26 #include <gtk/gtk.h>
27 
28 #include "terminal-app.h"
29 #include "terminal-debug.h"
30 #include "terminal-intl.h"
31 #include "terminal-profile.h"
32 #include "terminal-screen.h"
33 #include "terminal-type-builtins.h"
34 
35 /* To add a new key, you need to:
36  *
37  *  - add an entry to the enum below
38  *  - add a #define with its name in terminal-profile.h
39  *  - add a gobject property for it in terminal_profile_class_init
40  *  - if the property's type needs special casing, add that to
41  *    terminal_profile_gsettings_notify_cb and
42  *    terminal_profile_gsettings_changeset_add
43  *  - if necessary the default value cannot be handled via the paramspec,
44  *    handle that in terminal_profile_reset_property_internal
45  */
46 enum
47 {
48     PROP_0,
49     PROP_ALLOW_BOLD,
50     PROP_BACKGROUND_COLOR,
51     PROP_BACKGROUND_DARKNESS,
52     PROP_BACKGROUND_IMAGE,
53     PROP_BACKGROUND_IMAGE_FILE,
54     PROP_BACKGROUND_TYPE,
55     PROP_BACKSPACE_BINDING,
56     PROP_BOLD_COLOR,
57     PROP_BOLD_COLOR_SAME_AS_FG,
58     PROP_CURSOR_BLINK_MODE,
59     PROP_CURSOR_SHAPE,
60     PROP_CUSTOM_COMMAND,
61     PROP_DEFAULT_SIZE_COLUMNS,
62     PROP_DEFAULT_SIZE_ROWS,
63     PROP_DEFAULT_SHOW_MENUBAR,
64     PROP_DELETE_BINDING,
65     PROP_EXIT_ACTION,
66     PROP_FONT,
67     PROP_FOREGROUND_COLOR,
68     PROP_LOGIN_SHELL,
69     PROP_NAME,
70     PROP_PALETTE,
71     PROP_SCROLL_BACKGROUND,
72     PROP_SCROLLBACK_LINES,
73     PROP_SCROLLBACK_UNLIMITED,
74     PROP_SCROLLBAR_POSITION,
75     PROP_SCROLL_ON_KEYSTROKE,
76     PROP_SCROLL_ON_OUTPUT,
77     PROP_SILENT_BELL,
78     PROP_TITLE,
79     PROP_TITLE_MODE,
80     PROP_USE_CUSTOM_COMMAND,
81     PROP_USE_CUSTOM_DEFAULT_SIZE,
82     PROP_USE_SKEY,
83     PROP_USE_URLS,
84     PROP_USE_SYSTEM_FONT,
85     PROP_USE_THEME_COLORS,
86     PROP_VISIBLE_NAME,
87     PROP_WORD_CHARS,
88     PROP_COPY_SELECTION,
89     LAST_PROP
90 };
91 
92 #define KEY_ALLOW_BOLD "allow-bold"
93 #define KEY_BACKGROUND_COLOR "background-color"
94 #define KEY_BACKGROUND_DARKNESS "background-darkness"
95 #define KEY_BACKGROUND_IMAGE_FILE "background-image"
96 #define KEY_BACKGROUND_TYPE "background-type"
97 #define KEY_BACKSPACE_BINDING "backspace-binding"
98 #define KEY_BOLD_COLOR "bold-color"
99 #define KEY_BOLD_COLOR_SAME_AS_FG "bold-color-same-as-fg"
100 #define KEY_CURSOR_BLINK_MODE "cursor-blink-mode"
101 #define KEY_CURSOR_SHAPE "cursor-shape"
102 #define KEY_CUSTOM_COMMAND "custom-command"
103 #define KEY_DEFAULT_SHOW_MENUBAR "default-show-menubar"
104 #define KEY_DEFAULT_SIZE_COLUMNS "default-size-columns"
105 #define KEY_DEFAULT_SIZE_ROWS "default-size-rows"
106 #define KEY_DELETE_BINDING "delete-binding"
107 #define KEY_EXIT_ACTION "exit-action"
108 #define KEY_FONT "font"
109 #define KEY_FOREGROUND_COLOR "foreground-color"
110 #define KEY_LOGIN_SHELL "login-shell"
111 #define KEY_PALETTE "palette"
112 #define KEY_SCROLL_BACKGROUND "scroll-background"
113 #define KEY_SCROLLBACK_LINES "scrollback-lines"
114 #define KEY_SCROLLBACK_UNLIMITED "scrollback-unlimited"
115 #define KEY_SCROLLBAR_POSITION "scrollbar-position"
116 #define KEY_SCROLL_ON_KEYSTROKE "scroll-on-keystroke"
117 #define KEY_SCROLL_ON_OUTPUT "scroll-on-output"
118 #define KEY_SILENT_BELL "silent-bell"
119 #define KEY_COPY_SELECTION "copy-selection"
120 #define KEY_TITLE_MODE "title-mode"
121 #define KEY_TITLE "title"
122 #define KEY_USE_CUSTOM_COMMAND "use-custom-command"
123 #define KEY_USE_CUSTOM_DEFAULT_SIZE "use-custom-default-size"
124 #define KEY_USE_SKEY "use-skey"
125 #define KEY_USE_URLS "use-urls"
126 #define KEY_USE_SYSTEM_FONT "use-system-font"
127 #define KEY_USE_THEME_COLORS "use-theme-colors"
128 #define KEY_VISIBLE_NAME "visible-name"
129 #define KEY_WORD_CHARS "word-chars"
130 
131 /* Keep these in sync with the GSettings schema! */
132 #define DEFAULT_ALLOW_BOLD            (TRUE)
133 #define DEFAULT_BACKGROUND_COLOR      ("#FFFFDD")
134 #define DEFAULT_BOLD_COLOR_SAME_AS_FG (TRUE)
135 #define DEFAULT_BACKGROUND_DARKNESS   (0.5)
136 #define DEFAULT_BACKGROUND_IMAGE_FILE ("")
137 #define DEFAULT_BACKGROUND_TYPE       (TERMINAL_BACKGROUND_SOLID)
138 #define DEFAULT_BACKSPACE_BINDING     (VTE_ERASE_ASCII_DELETE)
139 #define DEFAULT_CURSOR_BLINK_MODE     (VTE_CURSOR_BLINK_SYSTEM)
140 #define DEFAULT_CURSOR_SHAPE          (VTE_CURSOR_SHAPE_BLOCK)
141 #define DEFAULT_CUSTOM_COMMAND        ("")
142 #define DEFAULT_DEFAULT_SHOW_MENUBAR  (TRUE)
143 #define DEFAULT_DEFAULT_SIZE_COLUMNS  (80)
144 #define DEFAULT_DEFAULT_SIZE_ROWS     (24)
145 #define DEFAULT_DELETE_BINDING        (VTE_ERASE_DELETE_SEQUENCE)
146 #define DEFAULT_EXIT_ACTION           (TERMINAL_EXIT_CLOSE)
147 #define DEFAULT_FONT                  ("Monospace 12")
148 #define DEFAULT_FOREGROUND_COLOR      ("#000000")
149 #define DEFAULT_LOGIN_SHELL           (FALSE)
150 #define DEFAULT_NAME                  (NULL)
151 #define DEFAULT_PALETTE               (terminal_palettes[TERMINAL_PALETTE_TANGO])
152 #define DEFAULT_SCROLL_BACKGROUND     (TRUE)
153 #define DEFAULT_SCROLLBACK_LINES      (512)
154 #define DEFAULT_SCROLLBACK_UNLIMITED  (FALSE)
155 #define DEFAULT_SCROLLBAR_POSITION    (TERMINAL_SCROLLBAR_RIGHT)
156 #define DEFAULT_SCROLL_ON_KEYSTROKE   (TRUE)
157 #define DEFAULT_SCROLL_ON_OUTPUT      (FALSE)
158 #define DEFAULT_SILENT_BELL           (FALSE)
159 #define DEFAULT_COPY_SELECTION        (FALSE)
160 #define DEFAULT_TITLE_MODE            (TERMINAL_TITLE_REPLACE)
161 #define DEFAULT_TITLE                 (N_("Terminal"))
162 #define DEFAULT_USE_CUSTOM_COMMAND    (FALSE)
163 #define DEFAULT_USE_CUSTOM_DEFAULT_SIZE (FALSE)
164 #define DEFAULT_USE_SKEY              (TRUE)
165 #define DEFAULT_USE_URLS              (TRUE)
166 #define DEFAULT_USE_SYSTEM_FONT       (TRUE)
167 #define DEFAULT_USE_THEME_COLORS      (TRUE)
168 #define DEFAULT_VISIBLE_NAME          (N_("Unnamed"))
169 #define DEFAULT_WORD_CHARS            ("-A-Za-z0-9,./?%&#:_=+@~")
170 
171 struct _TerminalProfilePrivate
172 {
173 	GValueArray *properties;
174 	gboolean *locked;
175 
176 	GSettings *settings;
177 	char *profile_dir;
178 
179 	GSList *dirty_pspecs;
180 	guint save_idle_id;
181 
182 	GParamSpec *gsettings_notification_pspec;
183 
184 	gboolean background_load_failed;
185 
186 	guint forgotten : 1;
187 };
188 
189 static const GdkRGBA terminal_palettes[TERMINAL_PALETTE_N_BUILTINS][TERMINAL_PALETTE_SIZE] =
190 {
191 	/* Tango palette */
192 	{
193 		{ 0,         0,        0,         1 },
194 		{ 0.8,       0,        0,         1 },
195 		{ 0.305882,  0.603922, 0.0235294, 1 },
196 		{ 0.768627,  0.627451, 0,         1 },
197 		{ 0.203922,  0.396078, 0.643137,  1 },
198 		{ 0.458824,  0.313725, 0.482353,  1 },
199 		{ 0.0235294, 0.596078, 0.603922,  1 },
200 		{ 0.827451,  0.843137, 0.811765,  1 },
201 		{ 0.333333,  0.341176, 0.32549,   1 },
202 		{ 0.937255,  0.160784, 0.160784,  1 },
203 		{ 0.541176,  0.886275, 0.203922,  1 },
204 		{ 0.988235,  0.913725, 0.309804,  1 },
205 		{ 0.447059,  0.623529, 0.811765,  1 },
206 		{ 0.678431,  0.498039, 0.658824,  1 },
207 		{ 0.203922,  0.886275, 0.886275,  1 },
208 		{ 0.933333,  0.933333, 0.92549,   1 },
209 	},
210 
211 	/* Linux palette */
212 	{
213 		{ 0,        0,        0,        1 },
214 		{ 0.666667, 0,        0,        1 },
215 		{ 0,        0.666667, 0,        1 },
216 		{ 0.666667, 0.333333, 0,        1 },
217 		{ 0,        0,        0.666667, 1 },
218 		{ 0.666667, 0,        0.666667, 1 },
219 		{ 0,        0.666667, 0.666667, 1 },
220 		{ 0.666667, 0.666667, 0.666667, 1 },
221 		{ 0.333333, 0.333333, 0.333333, 1 },
222 		{ 1,        0.333333, 0.333333, 1 },
223 		{ 0.333333, 1,        0.333333, 1 },
224 		{ 1,        1,        0.333333, 1 },
225 		{ 0.333333, 0.333333, 1,        1 },
226 		{ 1,        0.333333, 1,        1 },
227 		{ 0.333333, 1,        1,        1 },
228 		{ 1,        1,        1,        1 },
229 	},
230 
231 	/* XTerm palette */
232 	{
233 		{ 0,        0,        0,        1 },
234 		{ 0.803922, 0,        0,        1 },
235 		{ 0,        0.803922, 0,        1 },
236 		{ 0.803922, 0.803922, 0,        1 },
237 		{ 0.117647, 0.564706, 1,        1 },
238 		{ 0.803922, 0,        0.803922, 1 },
239 		{ 0,        0.803922, 0.803922, 1 },
240 		{ 0.898039, 0.898039, 0.898039, 1 },
241 		{ 0.298039, 0.298039, 0.298039, 1 },
242 		{ 1,        0,        0,        1 },
243 		{ 0,        1,        0,        1 },
244 		{ 1,        1,        0,        1 },
245 		{ 0.27451,  0.509804, 0.705882, 1 },
246 		{ 1,        0,        1,        1 },
247 		{ 0,        1,        1,        1 },
248 		{ 1,        1,        1,        1 },
249 	},
250 
251 	/* RXVT palette */
252 	{
253 		{ 0,        0,        0,        1 },
254 		{ 0.803922, 0,        0,        1 },
255 		{ 0,        0.803922, 0,        1 },
256 		{ 0.803922, 0.803922, 0,        1 },
257 		{ 0,        0,        0.803922, 1 },
258 		{ 0.803922, 0,        0.803922, 1 },
259 		{ 0,        0.803922, 0.803922, 1 },
260 		{ 0.980392, 0.921569, 0.843137, 1 },
261 		{ 0.25098,  0.25098,  0.25098,  1 },
262 		{ 1, 0, 0, 1 },
263 		{ 0, 1, 0, 1 },
264 		{ 1, 1, 0, 1 },
265 		{ 0, 0, 1, 1 },
266 		{ 1, 0, 1, 1 },
267 		{ 0, 1, 1, 1 },
268 		{ 1, 1, 1, 1 },
269 	},
270 
271 	/* Solarized palette (1.0.0beta2): http://ethanschoonover.com/solarized */
272 	{
273 		{ 0.02745,  0.211764, 0.258823, 1 },
274 		{ 0.862745, 0.196078, 0.184313, 1 },
275 		{ 0.521568, 0.6,      0,        1 },
276 		{ 0.709803, 0.537254, 0,        1 },
277 		{ 0.149019, 0.545098, 0.823529, 1 },
278 		{ 0.82745,  0.211764, 0.509803, 1 },
279 		{ 0.164705, 0.631372, 0.596078, 1 },
280 		{ 0.933333, 0.909803, 0.835294, 1 },
281 		{ 0,        0.168627, 0.211764, 1 },
282 		{ 0.796078, 0.294117, 0.086274, 1 },
283 		{ 0.345098, 0.431372, 0.458823, 1 },
284 		{ 0.396078, 0.482352, 0.513725, 1 },
285 		{ 0.513725, 0.580392, 0.588235, 1 },
286 		{ 0.423529, 0.443137, 0.768627, 1 },
287 		{ 0.57647,  0.631372, 0.631372, 1 },
288 		{ 0.992156, 0.964705, 0.890196, 1 },
289 	},
290 };
291 
292 enum
293 {
294     FORGOTTEN,
295     LAST_SIGNAL
296 };
297 
298 static void terminal_profile_finalize    (GObject              *object);
299 static void terminal_profile_set_property (GObject *object,
300         guint prop_id,
301         const GValue *value,
302         GParamSpec *pspec);
303 static void ensure_pixbuf_property (TerminalProfile *profile,
304                                     guint path_prop_id,
305                                     guint pixbuf_prop_id,
306                                     gboolean *load_failed);
307 
308 static guint signals[LAST_SIGNAL] = { 0 };
309 static GQuark gsettings_key_quark;
310 
311 G_DEFINE_TYPE_WITH_PRIVATE (TerminalProfile, terminal_profile, G_TYPE_OBJECT);
312 
313 /* gdk_rgba_equal is too strict! */
314 static gboolean
rgba_equal(const GdkRGBA * a,const GdkRGBA * b)315 rgba_equal (const GdkRGBA *a,
316             const GdkRGBA *b)
317 {
318     gdouble dr, dg, db, da;
319 
320     dr = a->red - b->red;
321     dg = a->green - b->green;
322     db = a->blue - b->blue;
323     da = a->alpha - b->alpha;
324 
325     return (dr * dr + dg * dg + db * db + da * da) < 1e-4;
326 }
327 
328 static gboolean
palette_cmp(const GdkRGBA * ca,const GdkRGBA * cb)329 palette_cmp (const GdkRGBA *ca,
330              const GdkRGBA *cb)
331 {
332     guint i;
333 
334     for (i = 0; i < TERMINAL_PALETTE_SIZE; ++i)
335         if (!rgba_equal (&ca[i], &cb[i]))
336             return FALSE;
337 
338     return TRUE;
339 }
340 
341 static GParamSpec *
get_pspec_from_name(TerminalProfile * profile,const char * prop_name)342 get_pspec_from_name (TerminalProfile *profile,
343                      const char *prop_name)
344 {
345 	TerminalProfileClass *klass = TERMINAL_PROFILE_GET_CLASS (profile);
346 	GParamSpec *pspec;
347 
348 	pspec = g_object_class_find_property (G_OBJECT_CLASS (klass), prop_name);
349 	if (pspec &&
350 	        pspec->owner_type != TERMINAL_TYPE_PROFILE)
351 		pspec = NULL;
352 
353 	return pspec;
354 }
355 
356 static const GValue *
get_prop_value_from_prop_name(TerminalProfile * profile,const char * prop_name)357 get_prop_value_from_prop_name (TerminalProfile *profile,
358                                const char *prop_name)
359 {
360 	TerminalProfilePrivate *priv = profile->priv;
361 	GParamSpec *pspec;
362 
363 	pspec = get_pspec_from_name (profile, prop_name);
364 	if (!pspec)
365 		return NULL;
366 
367 	if (G_UNLIKELY (pspec->param_id == PROP_BACKGROUND_IMAGE))
368 		ensure_pixbuf_property (profile, PROP_BACKGROUND_IMAGE_FILE, PROP_BACKGROUND_IMAGE, &priv->background_load_failed);
369 
370 	return g_value_array_get_nth (priv->properties, pspec->param_id);
371 }
372 
373 static void
set_value_from_palette(GValue * ret_value,const GdkRGBA * colors,guint n_colors)374 set_value_from_palette (GValue *ret_value,
375                         const GdkRGBA *colors,
376                         guint n_colors)
377 {
378 	GValueArray *array;
379 	guint i, max_n_colors;
380 
381 	max_n_colors = MAX (n_colors, TERMINAL_PALETTE_SIZE);
382 	array = g_value_array_new (max_n_colors);
383 	for (i = 0; i < max_n_colors; ++i)
384 		g_value_array_append (array, NULL);
385 
386 	for (i = 0; i < n_colors; ++i)
387 	{
388 		GValue *value = g_value_array_get_nth (array, i);
389 
390 		g_value_init (value, GDK_TYPE_RGBA);
391 		g_value_set_boxed (value, &colors[i]);
392 	}
393 
394 	/* If we haven't enough colours yet, fill up with the default palette */
395 	for (i = n_colors; i < TERMINAL_PALETTE_SIZE; ++i)
396 	{
397 		GValue *value = g_value_array_get_nth (array, i);
398 
399 		g_value_init (value, GDK_TYPE_RGBA);
400 		g_value_set_boxed (value, &DEFAULT_PALETTE[i]);
401 	}
402 
403 	g_value_take_boxed (ret_value, array);
404 }
405 
406 static int
values_equal(GParamSpec * pspec,const GValue * va,const GValue * vb)407 values_equal (GParamSpec *pspec,
408               const GValue *va,
409               const GValue *vb)
410 {
411 	/* g_param_values_cmp isn't good enough for some types, since e.g.
412 	 * it compares colours and font descriptions by pointer value, not
413 	 * with the correct compare functions. Providing extra
414 	 * PangoParamSpecFontDescription and GdkParamSpecColor wouldn't
415 	 * have fixed this either, since it's unclear how to _order_ them.
416 	 * Luckily we only need to check them for equality here.
417 	 */
418 
419 	if (g_param_values_cmp (pspec, va, vb) == 0)
420 		return TRUE;
421 
422 	if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
423 		return rgba_equal (g_value_get_boxed (va), g_value_get_boxed (vb));
424 
425 	if (G_PARAM_SPEC_VALUE_TYPE (pspec) == PANGO_TYPE_FONT_DESCRIPTION)
426 		return pango_font_description_equal (g_value_get_boxed (va), g_value_get_boxed (vb));
427 
428 	if (G_IS_PARAM_SPEC_VALUE_ARRAY (pspec) &&
429 	        G_PARAM_SPEC_VALUE_TYPE (G_PARAM_SPEC_VALUE_ARRAY (pspec)->element_spec) == GDK_TYPE_RGBA)
430 	{
431 		GValueArray *ara, *arb;
432 		guint i;
433 
434 		ara = g_value_get_boxed (va);
435 		arb = g_value_get_boxed (vb);
436 
437 		if (!ara || !arb || ara->n_values != arb->n_values)
438 			return FALSE;
439 
440 		for (i = 0; i < ara->n_values; ++i)
441 			if (!rgba_equal (g_value_get_boxed (g_value_array_get_nth (ara, i)),
442 			                      g_value_get_boxed (g_value_array_get_nth (arb, i))))
443 				return FALSE;
444 
445 		return TRUE;
446 	}
447 
448 	return FALSE;
449 }
450 
451 static void
ensure_pixbuf_property(TerminalProfile * profile,guint path_prop_id,guint pixbuf_prop_id,gboolean * load_failed)452 ensure_pixbuf_property (TerminalProfile *profile,
453                         guint path_prop_id,
454                         guint pixbuf_prop_id,
455                         gboolean *load_failed)
456 {
457 	TerminalProfilePrivate *priv = profile->priv;
458 	GValue *path_value, *pixbuf_value;
459 	GdkPixbuf *pixbuf;
460 	const char *path_utf8;
461 	char *path;
462 	GError *error = NULL;
463 
464 	pixbuf_value = g_value_array_get_nth (priv->properties, pixbuf_prop_id);
465 
466 	pixbuf = g_value_get_object (pixbuf_value);
467 	if (pixbuf)
468 		return;
469 
470 	if (*load_failed)
471 		return;
472 
473 	path_value = g_value_array_get_nth (priv->properties, path_prop_id);
474 	path_utf8 = g_value_get_string (path_value);
475 	if (!path_utf8 || !path_utf8[0])
476 		goto failed;
477 
478 	path = g_filename_from_utf8 (path_utf8, -1, NULL, NULL, NULL);
479 	if (!path)
480 		goto failed;
481 
482 	pixbuf = gdk_pixbuf_new_from_file (path, &error);
483 	if (!pixbuf)
484 	{
485 		_terminal_debug_print (TERMINAL_DEBUG_PROFILE,
486 		                       "Failed to load image \"%s\": %s\n",
487 		                       path, error->message);
488 
489 		g_error_free (error);
490 		g_free (path);
491 		goto failed;
492 	}
493 
494 	g_value_take_object (pixbuf_value, pixbuf);
495 	g_free (path);
496 	return;
497 
498 failed:
499 	*load_failed = TRUE;
500 }
501 
502 static void
terminal_profile_reset_property_internal(TerminalProfile * profile,GParamSpec * pspec,gboolean notify)503 terminal_profile_reset_property_internal (TerminalProfile *profile,
504         GParamSpec *pspec,
505         gboolean notify)
506 {
507 	TerminalProfilePrivate *priv = profile->priv;
508 	GValue value_ = { 0, };
509 	GValue *value;
510 
511 	if (notify)
512 	{
513 		value = &value_;
514 		g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
515 	}
516 	else
517 		value = g_value_array_get_nth (priv->properties, pspec->param_id);
518 	g_assert (value != NULL);
519 
520 	/* A few properties don't have defaults via the param spec; set them explicitly */
521 	switch (pspec->param_id)
522 	{
523 	case PROP_FOREGROUND_COLOR:
524 	case PROP_BOLD_COLOR:
525 		g_value_set_boxed (value, &DEFAULT_FOREGROUND_COLOR);
526 		break;
527 
528 	case PROP_BACKGROUND_COLOR:
529 		g_value_set_boxed (value, &DEFAULT_BACKGROUND_COLOR);
530 		break;
531 
532 	case PROP_FONT:
533 		g_value_take_boxed (value, pango_font_description_from_string (DEFAULT_FONT));
534 		break;
535 
536 	case PROP_PALETTE:
537 		set_value_from_palette (value, DEFAULT_PALETTE, TERMINAL_PALETTE_SIZE);
538 		break;
539 
540 	default:
541 		g_param_value_set_default (pspec, value);
542 		break;
543 	}
544 
545 	if (notify)
546 	{
547 		g_object_set_property (G_OBJECT (profile), pspec->name, value);
548 		g_value_unset (value);
549 	}
550 }
551 
552 static void
terminal_profile_gsettings_notify_cb(GSettings * settings,gchar * key,gpointer user_data)553 terminal_profile_gsettings_notify_cb (GSettings *settings,
554                                       gchar *key,
555                                       gpointer     user_data)
556 {
557 	TerminalProfile *profile = TERMINAL_PROFILE (user_data);
558 	TerminalProfilePrivate *priv = profile->priv;
559 	TerminalProfileClass *klass;
560 	GVariant *settings_value;
561 	GParamSpec *pspec;
562 	GValue value = { 0, };
563 	gboolean equal;
564 	gboolean force_set = FALSE;
565 
566 	if (!key) return;
567 
568 	_terminal_debug_print (TERMINAL_DEBUG_PROFILE,
569 	                       "GSettings notification for key %s [%s]\n",
570 	                       key,
571 	                       g_settings_is_writable (settings, key) ? "writable" : "LOCKED");
572 
573 	klass = TERMINAL_PROFILE_GET_CLASS (profile);
574 	pspec = g_hash_table_lookup (klass->gsettings_keys, key);
575 	if (!pspec)
576 		return; /* ignore unknown keys, for future extensibility */
577 
578 	priv->locked[pspec->param_id] = !g_settings_is_writable (settings, key);
579 
580 	settings_value = g_settings_get_value (settings, key);
581 	if (!settings_value)
582 		return;
583 
584 	g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
585 
586 	if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
587 	{
588 		if (!g_variant_is_of_type (settings_value, G_VARIANT_TYPE_BOOLEAN))
589 			goto out;
590 
591 		g_value_set_boolean (&value, g_variant_get_boolean (settings_value));
592 	}
593 	else if (G_IS_PARAM_SPEC_STRING (pspec))
594 	{
595 		if (!g_variant_is_of_type (settings_value, G_VARIANT_TYPE_STRING))
596 			goto out;
597 
598 		g_value_set_string (&value, g_variant_get_string (settings_value, NULL));
599 	}
600 	else if (G_IS_PARAM_SPEC_ENUM (pspec))
601 	{
602 
603 		if (!g_variant_is_of_type (settings_value, G_VARIANT_TYPE_STRING))
604 			goto out;
605 
606 		g_value_set_enum (&value, g_settings_get_enum (settings, key));
607 	}
608 	else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
609 	{
610 		GdkRGBA color;
611 
612 		if (!g_variant_is_of_type (settings_value, G_VARIANT_TYPE_STRING))
613 			goto out;
614 
615 		if (!gdk_rgba_parse (&color, g_variant_get_string (settings_value, NULL)))
616 			goto out;
617 
618 		g_value_set_boxed (&value, &color);
619 	}
620 	else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == PANGO_TYPE_FONT_DESCRIPTION)
621 	{
622 		if (!g_variant_is_of_type (settings_value, G_VARIANT_TYPE_STRING))
623 			goto out;
624 
625 		g_value_take_boxed (&value, pango_font_description_from_string (g_variant_get_string (settings_value, NULL)));
626 	}
627 	else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
628 	{
629 		if (!g_variant_is_of_type (settings_value, G_VARIANT_TYPE_DOUBLE))
630 			goto out;
631 
632 		g_value_set_double (&value, g_variant_get_double (settings_value));
633 	}
634 	else if (G_IS_PARAM_SPEC_INT (pspec))
635 	{
636 		if (!g_variant_is_of_type (settings_value, G_VARIANT_TYPE_INT16) &&
637 		    !g_variant_is_of_type (settings_value, G_VARIANT_TYPE_INT32) &&
638 		    !g_variant_is_of_type (settings_value, G_VARIANT_TYPE_INT64))
639 			goto out;
640 
641 		g_value_set_int (&value, g_settings_get_int(settings, key));
642 	}
643 	else if (G_IS_PARAM_SPEC_VALUE_ARRAY (pspec) &&
644 	         G_PARAM_SPEC_VALUE_TYPE (G_PARAM_SPEC_VALUE_ARRAY (pspec)->element_spec) == GDK_TYPE_RGBA)
645 	{
646 		char **color_strings;
647 		GdkRGBA *colors;
648 		int n_colors, i;
649 
650 		if (!g_variant_is_of_type (settings_value, G_VARIANT_TYPE_STRING))
651 			goto out;
652 
653 		color_strings = g_strsplit (g_variant_get_string (settings_value, NULL), ":", -1);
654 		if (!color_strings)
655 			goto out;
656 
657 		n_colors = g_strv_length (color_strings);
658 		colors = g_new0 (GdkRGBA, n_colors);
659 		for (i = 0; i < n_colors; ++i)
660 		{
661 			if (!gdk_rgba_parse (&colors[i], color_strings[i]))
662 				continue; /* ignore errors */
663 		}
664 		g_strfreev (color_strings);
665 
666 		/* We continue even with a palette size != TERMINAL_PALETTE_SIZE,
667 		 * so we can change the palette size in future versions without
668 		 * causing too many issues.
669 		 */
670 		set_value_from_palette (&value, colors, n_colors);
671 		g_free (colors);
672 	}
673 	else
674 	{
675 		g_printerr ("Unhandled value type %s of pspec %s\n", g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), pspec->name);
676 		goto out;
677 	}
678 
679 	if (g_param_value_validate (pspec, &value))
680 	{
681 		_terminal_debug_print (TERMINAL_DEBUG_PROFILE,
682 		                       "Invalid value in GSettings for key %s was changed to comply with pspec %s\n",
683 		                       key, pspec->name);
684 
685 		force_set = TRUE;
686 	}
687 
688 	/* Only set the property if the value is different than our current value,
689 	 * so we don't go into an infinite loop.
690 	 */
691 	equal = values_equal (pspec, &value, g_value_array_get_nth (priv->properties, pspec->param_id));
692 #ifdef MATE_ENABLE_DEBUG
693 	_TERMINAL_DEBUG_IF (TERMINAL_DEBUG_PROFILE)
694 	{
695 		if (!equal)
696 			_terminal_debug_print (TERMINAL_DEBUG_PROFILE,
697 			                       "Setting property %s to a different value\n"
698 			                       "  now: %s\n"
699 			                       "  new: %s\n",
700 			                       pspec->name,
701 			                       g_strdup_value_contents (g_value_array_get_nth (priv->properties, pspec->param_id)),
702 			                       g_strdup_value_contents (&value));
703 	}
704 #endif
705 
706 	if (!equal || force_set)
707 	{
708 		priv->gsettings_notification_pspec = pspec;
709 		g_object_set_property (G_OBJECT (profile), pspec->name, &value);
710 		priv->gsettings_notification_pspec = NULL;
711 	}
712 
713 out:
714 	/* FIXME: if we arrive here through goto in the error cases,
715 	 * should we maybe reset the property to its default value?
716 	 */
717 
718 	g_value_unset (&value);
719 	g_variant_unref (settings_value);
720 }
721 
722 static void
terminal_profile_gsettings_changeset_add(TerminalProfile * profile,GSettings * changeset,GParamSpec * pspec)723 terminal_profile_gsettings_changeset_add (TerminalProfile *profile,
724         GSettings *changeset,
725         GParamSpec *pspec)
726 {
727 	TerminalProfilePrivate *priv = profile->priv;
728 	char *key;
729 	const GValue *value;
730 
731 	/* FIXME: do this? */
732 #if 0
733 	if (priv->locked[pspec->param_id])
734 		return;
735 
736 	if (!g_settings_is_writable (priv->settings, gsettings_key, NULL))
737 		return;
738 #endif
739 
740 	key = g_param_spec_get_qdata (pspec, gsettings_key_quark);
741 	if (!key)
742 		return;
743 
744 	value = g_value_array_get_nth (priv->properties, pspec->param_id);
745 
746 	_terminal_debug_print (TERMINAL_DEBUG_PROFILE,
747 	                       "Adding pspec %s with value %s to the GSettings changeset\n",
748 	                       pspec->name, g_strdup_value_contents (value));
749 
750 	if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
751 		g_settings_set_boolean (changeset, key, g_value_get_boolean (value));
752 	else if (G_IS_PARAM_SPEC_STRING (pspec))
753 	{
754 		const char *str;
755 
756 		str = g_value_get_string (value);
757 		g_settings_set_string (changeset, key, str ? str : "");
758 	}
759 	else if (G_IS_PARAM_SPEC_ENUM (pspec))
760 	{
761 		const GEnumValue *eval;
762 
763 		eval = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, g_value_get_enum (value));
764 
765 		g_settings_set_enum (changeset, key, eval->value);
766 	}
767 	else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
768 	{
769 		GdkRGBA *color;
770 		char str[16];
771 
772 		color = g_value_get_boxed (value);
773 		if (!color)
774 			goto cleanup;
775 
776 		g_snprintf (str, sizeof (str),
777 		            "#%04X%04X%04X",
778 		            (guint) (color->red * 65535),
779 		            (guint) (color->green * 65535),
780 		            (guint) (color->blue * 65535));
781 
782 		g_settings_set_string (changeset, key, str);
783 	}
784 	else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == PANGO_TYPE_FONT_DESCRIPTION)
785 	{
786 		PangoFontDescription *font_desc;
787 		char *font;
788 
789 		font_desc = g_value_get_boxed (value);
790 		if (!font_desc)
791 			goto cleanup;
792 
793 		font = pango_font_description_to_string (font_desc);
794 		g_settings_set_string (changeset, key, font);
795 		g_free (font);
796 	}
797 	else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
798 		g_settings_set_double (changeset, key, g_value_get_double (value));
799 	else if (G_IS_PARAM_SPEC_INT (pspec))
800 		g_settings_set_int (changeset, key, g_value_get_int (value));
801 	else if (G_IS_PARAM_SPEC_VALUE_ARRAY (pspec) &&
802 	         G_PARAM_SPEC_VALUE_TYPE (G_PARAM_SPEC_VALUE_ARRAY (pspec)->element_spec) == GDK_TYPE_RGBA)
803 	{
804 		GValueArray *array;
805 		GString *string;
806 		guint n_colors, i;
807 
808 		/* We need to do this ourselves, because the gtk_color_selection_palette_to_string
809 		 * does not carry all the bytes, and xterm's palette is messed up...
810 		 */
811 
812 		array = g_value_get_boxed (value);
813 		if (!array)
814 			goto cleanup;
815 
816 		n_colors = array->n_values;
817 		string = g_string_sized_new (n_colors * (1 /* # */ + 3 * 4) + n_colors /* : separators and terminating \0 */);
818 		for (i = 0; i < n_colors; ++i)
819 		{
820 			GdkRGBA *color;
821 
822 			if (i > 0)
823 				g_string_append_c (string, ':');
824 
825 			color = g_value_get_boxed (g_value_array_get_nth (array, i));
826 			if (!color)
827 				continue;
828 
829 			g_string_append_printf (string,
830 			                        "#%04X%04X%04X",
831 			                        (guint) (color->red * 65535),
832 			                        (guint) (color->green * 65535),
833 			                        (guint) (color->blue * 65535));
834 		}
835 
836 		g_settings_set_string (changeset, key, string->str);
837 		g_string_free (string, TRUE);
838 	}
839 	else
840 		g_printerr ("Unhandled value type %s of pspec %s\n", g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), pspec->name);
841 
842 cleanup:
843 	return;
844 }
845 
846 static void
terminal_profile_save(TerminalProfile * profile)847 terminal_profile_save (TerminalProfile *profile)
848 {
849 	TerminalProfilePrivate *priv = profile->priv;
850 	GSettings *changeset;
851 	GSList *l;
852 	gchar *concat;
853 
854 	priv->save_idle_id = 0;
855 	concat = g_strconcat (CONF_PROFILE_PREFIX, priv->profile_dir,"/", NULL);
856 	changeset = g_settings_new_with_path (CONF_PROFILE_SCHEMA, concat);
857 	g_free (concat);
858 	g_settings_delay (changeset);
859 
860 	for (l = priv->dirty_pspecs; l != NULL; l = l->next)
861 	{
862 		GParamSpec *pspec = (GParamSpec *) l->data;
863 
864 		if (pspec->owner_type != TERMINAL_TYPE_PROFILE)
865 			continue;
866 
867 		if ((pspec->flags & G_PARAM_WRITABLE) == 0)
868 			continue;
869 
870 		terminal_profile_gsettings_changeset_add (profile, changeset, pspec);
871 	}
872 
873 	g_slist_free (priv->dirty_pspecs);
874 	priv->dirty_pspecs = NULL;
875 
876 	g_settings_apply (changeset);
877 	g_object_unref (changeset);
878 }
879 
880 static gboolean
terminal_profile_save_idle_cb(TerminalProfile * profile)881 terminal_profile_save_idle_cb (TerminalProfile *profile)
882 {
883 	terminal_profile_save (profile);
884 
885 	/* don't run again */
886 	return FALSE;
887 }
888 
889 static void
terminal_profile_schedule_save(TerminalProfile * profile,GParamSpec * pspec)890 terminal_profile_schedule_save (TerminalProfile *profile,
891                                 GParamSpec *pspec)
892 {
893 	TerminalProfilePrivate *priv = profile->priv;
894 
895 	g_assert (pspec != NULL);
896 
897 	if (!g_slist_find (priv->dirty_pspecs, pspec))
898 		priv->dirty_pspecs = g_slist_prepend (priv->dirty_pspecs, pspec);
899 
900 	if (priv->save_idle_id != 0)
901 		return;
902 
903 	priv->save_idle_id = g_idle_add ((GSourceFunc) terminal_profile_save_idle_cb, profile);
904 }
905 
906 static void
terminal_profile_init(TerminalProfile * profile)907 terminal_profile_init (TerminalProfile *profile)
908 {
909 	TerminalProfilePrivate *priv;
910 	GObjectClass *object_class;
911 	GParamSpec **pspecs;
912 	guint n_pspecs, i;
913 
914 	priv = profile->priv = terminal_profile_get_instance_private (profile);
915 
916 	priv->gsettings_notification_pspec = NULL;
917 	priv->locked = g_new0 (gboolean, LAST_PROP);
918 
919 	priv->properties = g_value_array_new (LAST_PROP);
920 	for (i = 0; i < LAST_PROP; ++i)
921 		g_value_array_append (priv->properties, NULL);
922 
923 	pspecs = g_object_class_list_properties (G_OBJECT_CLASS (TERMINAL_PROFILE_GET_CLASS (profile)), &n_pspecs);
924 	for (i = 0; i < n_pspecs; ++i)
925 	{
926 		GParamSpec *pspec = pspecs[i];
927 		GValue *value;
928 
929 		if (pspec->owner_type != TERMINAL_TYPE_PROFILE)
930 			continue;
931 
932 		g_assert (pspec->param_id < LAST_PROP);
933 		value = g_value_array_get_nth (priv->properties, pspec->param_id);
934 		g_value_init (value, pspec->value_type);
935 		g_param_value_set_default (pspec, value);
936 	}
937 
938 	g_free (pspecs);
939 
940 	/* A few properties don't have defaults via the param spec; set them explicitly */
941 	object_class = G_OBJECT_CLASS (TERMINAL_PROFILE_GET_CLASS (profile));
942 	terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_FOREGROUND_COLOR), FALSE);
943 	terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_BOLD_COLOR), FALSE);
944 	terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_BACKGROUND_COLOR), FALSE);
945 	terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_FONT), FALSE);
946 	terminal_profile_reset_property_internal (profile, g_object_class_find_property (object_class, TERMINAL_PROFILE_PALETTE), FALSE);
947 }
948 
949 static GObject *
terminal_profile_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)950 terminal_profile_constructor (GType type,
951                               guint n_construct_properties,
952                               GObjectConstructParam *construct_params)
953 {
954 	GObject *object;
955 	TerminalProfile *profile;
956 	TerminalProfilePrivate *priv;
957 	const char *name;
958 	GParamSpec **pspecs;
959 	guint n_pspecs, i;
960 	gchar *concat;
961 
962 	object = G_OBJECT_CLASS (terminal_profile_parent_class)->constructor
963 	         (type, n_construct_properties, construct_params);
964 
965 	profile = TERMINAL_PROFILE (object);
966 	priv = profile->priv;
967 
968 	name = g_value_get_string (g_value_array_get_nth (priv->properties, PROP_NAME));
969 	g_assert (name != NULL);
970 
971 	concat = g_strconcat (CONF_PROFILE_PREFIX, name, "/", NULL);
972 	priv->settings = g_settings_new_with_path (CONF_PROFILE_SCHEMA, concat);
973 	g_assert (priv->settings != NULL);
974 	g_free (concat);
975 	concat = g_strconcat("changed::", priv->profile_dir, "/", NULL);
976 	g_signal_connect (priv->settings,
977 			  concat,
978 			  G_CALLBACK(terminal_profile_gsettings_notify_cb),
979 			  profile);
980 
981 	g_free (concat);
982 
983 	/* Now load those properties from GSettings that were not set as construction params */
984 	pspecs = g_object_class_list_properties (G_OBJECT_CLASS (TERMINAL_PROFILE_GET_CLASS (profile)), &n_pspecs);
985 	for (i = 0; i < n_pspecs; ++i)
986 	{
987 		GParamSpec *pspec = pspecs[i];
988 		guint j;
989 		gboolean is_construct = FALSE;
990 		char *key;
991 
992 		if (pspec->owner_type != TERMINAL_TYPE_PROFILE)
993 			continue;
994 
995 		if ((pspec->flags & G_PARAM_WRITABLE) == 0 ||
996 		        (pspec->flags & G_PARAM_CONSTRUCT_ONLY) != 0)
997 			continue;
998 
999 		for (j = 0; j < n_construct_properties; ++j)
1000 			if (pspec == construct_params[j].pspec)
1001 			{
1002 				is_construct = TRUE;
1003 				break;
1004 			}
1005 
1006 		if (is_construct)
1007 			continue;
1008 
1009 		key = g_param_spec_get_qdata (pspec, gsettings_key_quark);
1010 		if (!key)
1011 			continue;
1012 
1013 		terminal_profile_gsettings_notify_cb (priv->settings, key,  profile);
1014 	}
1015 
1016 	g_free (pspecs);
1017 
1018 	return object;
1019 }
1020 
1021 static void
terminal_profile_finalize(GObject * object)1022 terminal_profile_finalize (GObject *object)
1023 {
1024 	TerminalProfile *profile = TERMINAL_PROFILE (object);
1025 	TerminalProfilePrivate *priv = profile->priv;
1026 
1027 	g_signal_handlers_disconnect_by_func (priv->settings,
1028 		G_CALLBACK(terminal_profile_gsettings_notify_cb),
1029 		profile);
1030 
1031 	if (priv->save_idle_id)
1032 	{
1033 		g_source_remove (priv->save_idle_id);
1034 
1035 		/* Save now */
1036 		terminal_profile_save (profile);
1037 	}
1038 
1039 	_terminal_profile_forget (profile);
1040 
1041 	g_object_unref (priv->settings);
1042 
1043 	g_free (priv->profile_dir);
1044 	g_free (priv->locked);
1045 	g_value_array_free (priv->properties);
1046 
1047 	G_OBJECT_CLASS (terminal_profile_parent_class)->finalize (object);
1048 }
1049 
1050 static void
terminal_profile_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1051 terminal_profile_get_property (GObject *object,
1052                                guint prop_id,
1053                                GValue *value,
1054                                GParamSpec *pspec)
1055 {
1056 	TerminalProfile *profile = TERMINAL_PROFILE (object);
1057 	TerminalProfilePrivate *priv = profile->priv;
1058 
1059 	if (prop_id == 0 || prop_id >= LAST_PROP)
1060 	{
1061 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1062 		return;
1063 	}
1064 
1065 	/* Note: When adding things here, do the same in get_prop_value_from_prop_name! */
1066 	switch (prop_id)
1067 	{
1068 	case PROP_BACKGROUND_IMAGE:
1069 		ensure_pixbuf_property (profile, PROP_BACKGROUND_IMAGE_FILE, PROP_BACKGROUND_IMAGE, &priv->background_load_failed);
1070 		break;
1071 	default:
1072 		break;
1073 	}
1074 
1075 	g_value_copy (g_value_array_get_nth (priv->properties, prop_id), value);
1076 }
1077 
1078 static void
terminal_profile_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1079 terminal_profile_set_property (GObject *object,
1080                                guint prop_id,
1081                                const GValue *value,
1082                                GParamSpec *pspec)
1083 {
1084 	TerminalProfile *profile = TERMINAL_PROFILE (object);
1085 	TerminalProfilePrivate *priv = profile->priv;
1086 	GValue *prop_value;
1087 
1088 	if (prop_id == 0 || prop_id >= LAST_PROP)
1089 	{
1090 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1091 		return;
1092 	}
1093 
1094 	prop_value = g_value_array_get_nth (priv->properties, prop_id);
1095 
1096 	/* Preprocessing */
1097 	switch (prop_id)
1098 	{
1099 #if 0
1100 	case PROP_FONT:
1101 	{
1102 		PangoFontDescription *font_desc, *new_font_desc;
1103 
1104 		font_desc = g_value_get_boxed (prop_value);
1105 		new_font_desc = g_value_get_boxed (value);
1106 
1107 		if (font_desc && new_font_desc)
1108 		{
1109 			/* Merge in case the new string isn't complete enough to load a font */
1110 			pango_font_description_merge (font_desc, new_font_desc, TRUE);
1111 			pango_font_description_free (new_font_desc);
1112 			break;
1113 		}
1114 
1115 		/* fall-through */
1116 	}
1117 #endif
1118 	default:
1119 		g_value_copy (value, prop_value);
1120 		break;
1121 	}
1122 
1123 	/* Postprocessing */
1124 	switch (prop_id)
1125 	{
1126 	case PROP_NAME:
1127 	{
1128 		const char *name = g_value_get_string (value);
1129 
1130 		g_assert (name != NULL);
1131 		priv->profile_dir = g_strdup (name);
1132 		if (priv->settings != NULL) {
1133 			gchar *concat;
1134 			g_signal_handlers_disconnect_by_func (priv->settings,
1135 							      G_CALLBACK(terminal_profile_gsettings_notify_cb),
1136 							      profile);
1137 			g_object_unref (priv->settings);
1138 			concat=  g_strconcat (CONF_PROFILE_PREFIX, priv->profile_dir, "/", NULL);
1139 			priv->settings = g_settings_new_with_path (CONF_PROFILE_SCHEMA, concat);
1140 			g_free (concat);
1141 			concat = g_strconcat("changed::", priv->profile_dir, "/", NULL);
1142 			g_signal_connect (priv->settings,
1143 					  concat,
1144 					  G_CALLBACK(terminal_profile_gsettings_notify_cb),
1145 					  profile);
1146 			g_free (concat);
1147 		}
1148 		break;
1149 	}
1150 
1151 	case PROP_BACKGROUND_IMAGE_FILE:
1152 		/* Clear the cached image */
1153 		g_value_set_object (g_value_array_get_nth (priv->properties, PROP_BACKGROUND_IMAGE), NULL);
1154 		priv->background_load_failed = FALSE;
1155 		g_object_notify (object, TERMINAL_PROFILE_BACKGROUND_IMAGE);
1156 		break;
1157 
1158 	default:
1159 		break;
1160 	}
1161 }
1162 
1163 static void
terminal_profile_notify(GObject * object,GParamSpec * pspec)1164 terminal_profile_notify (GObject *object,
1165                          GParamSpec *pspec)
1166 {
1167 	TerminalProfilePrivate *priv = TERMINAL_PROFILE (object)->priv;
1168 	void (* notify) (GObject *, GParamSpec *) = G_OBJECT_CLASS (terminal_profile_parent_class)->notify;
1169 
1170 	_terminal_debug_print (TERMINAL_DEBUG_PROFILE,
1171 	                       "Property notification for prop %s\n",
1172 	                       pspec->name);
1173 
1174 	if (notify)
1175 		notify (object, pspec);
1176 
1177 	if (pspec->owner_type == TERMINAL_TYPE_PROFILE &&
1178 	        (pspec->flags & G_PARAM_WRITABLE) &&
1179 	        g_param_spec_get_qdata (pspec, gsettings_key_quark) != NULL &&
1180 	        pspec != priv->gsettings_notification_pspec)
1181 		terminal_profile_schedule_save (TERMINAL_PROFILE (object), pspec);
1182 }
1183 
1184 static void
terminal_profile_class_init(TerminalProfileClass * klass)1185 terminal_profile_class_init (TerminalProfileClass *klass)
1186 {
1187 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
1188 
1189 	gsettings_key_quark = g_quark_from_static_string ("GT::GSettingsKey");
1190 
1191 	object_class->constructor = terminal_profile_constructor;
1192 	object_class->finalize = terminal_profile_finalize;
1193 	object_class->get_property = terminal_profile_get_property;
1194 	object_class->set_property = terminal_profile_set_property;
1195 	object_class->notify = terminal_profile_notify;
1196 
1197 	signals[FORGOTTEN] =
1198 	    g_signal_new ("forgotten",
1199 	                  G_OBJECT_CLASS_TYPE (object_class),
1200 	                  G_SIGNAL_RUN_LAST,
1201 	                  G_STRUCT_OFFSET (TerminalProfileClass, forgotten),
1202 	                  NULL, NULL,
1203 	                  g_cclosure_marshal_VOID__VOID,
1204 	                  G_TYPE_NONE, 0);
1205 
1206 	/* gsettings_key -> pspec hash */
1207 	klass->gsettings_keys = g_hash_table_new (g_str_hash, g_str_equal);
1208 
1209 #define TERMINAL_PROFILE_PSPEC_STATIC (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)
1210 
1211 #define TERMINAL_PROFILE_PROPERTY(propId, propSpec, propGSettings) \
1212 {\
1213   GParamSpec *pspec = propSpec;\
1214   g_object_class_install_property (object_class, propId, pspec);\
1215 \
1216   if (propGSettings)\
1217     {\
1218       g_param_spec_set_qdata (pspec, gsettings_key_quark, (gpointer) propGSettings);\
1219       g_hash_table_insert (klass->gsettings_keys, (gpointer) propGSettings, pspec);\
1220     }\
1221 }
1222 
1223 #define TERMINAL_PROFILE_PROPERTY_BOOLEAN(prop, propDefault, propGSettings) \
1224   TERMINAL_PROFILE_PROPERTY (PROP_##prop,\
1225     g_param_spec_boolean (TERMINAL_PROFILE_##prop, NULL, NULL,\
1226                           propDefault,\
1227                           G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\
1228     propGSettings)
1229 
1230 #define TERMINAL_PROFILE_PROPERTY_BOXED(prop, propType, propGSettings)\
1231   TERMINAL_PROFILE_PROPERTY (PROP_##prop,\
1232     g_param_spec_boxed (TERMINAL_PROFILE_##prop, NULL, NULL,\
1233                         propType,\
1234                         G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\
1235     propGSettings)
1236 
1237 #define TERMINAL_PROFILE_PROPERTY_DOUBLE(prop, propMin, propMax, propDefault, propGSettings)\
1238   TERMINAL_PROFILE_PROPERTY (PROP_##prop,\
1239     g_param_spec_double (TERMINAL_PROFILE_##prop, NULL, NULL,\
1240                          propMin, propMax, propDefault,\
1241                          G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\
1242     propGSettings)
1243 
1244 #define TERMINAL_PROFILE_PROPERTY_ENUM(prop, propType, propDefault, propGSettings)\
1245   TERMINAL_PROFILE_PROPERTY (PROP_##prop,\
1246     g_param_spec_enum (TERMINAL_PROFILE_##prop, NULL, NULL,\
1247                        propType, propDefault,\
1248                        G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\
1249     propGSettings)
1250 
1251 #define TERMINAL_PROFILE_PROPERTY_INT(prop, propMin, propMax, propDefault, propGSettings)\
1252   TERMINAL_PROFILE_PROPERTY (PROP_##prop,\
1253     g_param_spec_int (TERMINAL_PROFILE_##prop, NULL, NULL,\
1254                       propMin, propMax, propDefault,\
1255                       G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\
1256     propGSettings)
1257 
1258 	/* these are all read-only */
1259 #define TERMINAL_PROFILE_PROPERTY_OBJECT(prop, propType, propGSettings)\
1260   TERMINAL_PROFILE_PROPERTY (PROP_##prop,\
1261     g_param_spec_object (TERMINAL_PROFILE_##prop, NULL, NULL,\
1262                          propType,\
1263                          G_PARAM_READABLE | TERMINAL_PROFILE_PSPEC_STATIC),\
1264     propGSettings)
1265 
1266 #define TERMINAL_PROFILE_PROPERTY_STRING(prop, propDefault, propGSettings)\
1267   TERMINAL_PROFILE_PROPERTY (PROP_##prop,\
1268     g_param_spec_string (TERMINAL_PROFILE_##prop, NULL, NULL,\
1269                          propDefault,\
1270                          G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\
1271     propGSettings)
1272 
1273 #define TERMINAL_PROFILE_PROPERTY_STRING_CO(prop, propDefault, propGSettings)\
1274   TERMINAL_PROFILE_PROPERTY (PROP_##prop,\
1275     g_param_spec_string (TERMINAL_PROFILE_##prop, NULL, NULL,\
1276                          propDefault,\
1277                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | TERMINAL_PROFILE_PSPEC_STATIC),\
1278     propGSettings)
1279 
1280 #define TERMINAL_PROFILE_PROPERTY_VALUE_ARRAY_BOXED(prop, propElementName, propElementType, propGSettings)\
1281   TERMINAL_PROFILE_PROPERTY (PROP_##prop,\
1282     g_param_spec_value_array (TERMINAL_PROFILE_##prop, NULL, NULL,\
1283                               g_param_spec_boxed (propElementName, NULL, NULL,\
1284                                                   propElementType, \
1285                                                   G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\
1286                               G_PARAM_READWRITE | TERMINAL_PROFILE_PSPEC_STATIC),\
1287     propGSettings)
1288 
1289 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (ALLOW_BOLD, DEFAULT_ALLOW_BOLD, KEY_ALLOW_BOLD);
1290 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (BOLD_COLOR_SAME_AS_FG, DEFAULT_BOLD_COLOR_SAME_AS_FG, KEY_BOLD_COLOR_SAME_AS_FG);
1291 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (DEFAULT_SHOW_MENUBAR, DEFAULT_DEFAULT_SHOW_MENUBAR, KEY_DEFAULT_SHOW_MENUBAR);
1292 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (LOGIN_SHELL, DEFAULT_LOGIN_SHELL, KEY_LOGIN_SHELL);
1293 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (SCROLL_BACKGROUND, DEFAULT_SCROLL_BACKGROUND, KEY_SCROLL_BACKGROUND);
1294 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (SCROLLBACK_UNLIMITED, DEFAULT_SCROLLBACK_UNLIMITED, KEY_SCROLLBACK_UNLIMITED);
1295 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (SCROLL_ON_KEYSTROKE, DEFAULT_SCROLL_ON_KEYSTROKE, KEY_SCROLL_ON_KEYSTROKE);
1296 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (SCROLL_ON_OUTPUT, DEFAULT_SCROLL_ON_OUTPUT, KEY_SCROLL_ON_OUTPUT);
1297 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (SILENT_BELL, DEFAULT_SILENT_BELL, KEY_SILENT_BELL);
1298 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (COPY_SELECTION, DEFAULT_COPY_SELECTION, KEY_COPY_SELECTION);
1299 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_CUSTOM_COMMAND, DEFAULT_USE_CUSTOM_COMMAND, KEY_USE_CUSTOM_COMMAND);
1300 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_CUSTOM_DEFAULT_SIZE, DEFAULT_USE_CUSTOM_DEFAULT_SIZE, KEY_USE_CUSTOM_DEFAULT_SIZE);
1301 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_SKEY, DEFAULT_USE_SKEY, KEY_USE_SKEY);
1302 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_URLS, DEFAULT_USE_URLS, KEY_USE_URLS);
1303 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_SYSTEM_FONT, DEFAULT_USE_SYSTEM_FONT, KEY_USE_SYSTEM_FONT);
1304 	TERMINAL_PROFILE_PROPERTY_BOOLEAN (USE_THEME_COLORS, DEFAULT_USE_THEME_COLORS, KEY_USE_THEME_COLORS);
1305 
1306 	TERMINAL_PROFILE_PROPERTY_BOXED (BACKGROUND_COLOR, GDK_TYPE_RGBA, KEY_BACKGROUND_COLOR);
1307 	TERMINAL_PROFILE_PROPERTY_BOXED (BOLD_COLOR, GDK_TYPE_RGBA, KEY_BOLD_COLOR);
1308 	TERMINAL_PROFILE_PROPERTY_BOXED (FONT, PANGO_TYPE_FONT_DESCRIPTION, KEY_FONT);
1309 	TERMINAL_PROFILE_PROPERTY_BOXED (FOREGROUND_COLOR, GDK_TYPE_RGBA, KEY_FOREGROUND_COLOR);
1310 
1311 	/* 0.0 = normal bg, 1.0 = all black bg, 0.5 = half darkened */
1312 	TERMINAL_PROFILE_PROPERTY_DOUBLE (BACKGROUND_DARKNESS, 0.0, 1.0, DEFAULT_BACKGROUND_DARKNESS, KEY_BACKGROUND_DARKNESS);
1313 
1314 	TERMINAL_PROFILE_PROPERTY_ENUM (BACKGROUND_TYPE, TERMINAL_TYPE_BACKGROUND_TYPE, DEFAULT_BACKGROUND_TYPE, KEY_BACKGROUND_TYPE);
1315 	TERMINAL_PROFILE_PROPERTY_ENUM (BACKSPACE_BINDING,  VTE_TYPE_ERASE_BINDING, DEFAULT_BACKSPACE_BINDING, KEY_BACKSPACE_BINDING);
1316 	TERMINAL_PROFILE_PROPERTY_ENUM (CURSOR_BLINK_MODE, VTE_TYPE_CURSOR_BLINK_MODE, DEFAULT_CURSOR_BLINK_MODE, KEY_CURSOR_BLINK_MODE);
1317 	TERMINAL_PROFILE_PROPERTY_ENUM (CURSOR_SHAPE, VTE_TYPE_CURSOR_SHAPE, DEFAULT_CURSOR_SHAPE, KEY_CURSOR_SHAPE);
1318 	TERMINAL_PROFILE_PROPERTY_ENUM (DELETE_BINDING, VTE_TYPE_ERASE_BINDING, DEFAULT_DELETE_BINDING, KEY_DELETE_BINDING);
1319 	TERMINAL_PROFILE_PROPERTY_ENUM (EXIT_ACTION, TERMINAL_TYPE_EXIT_ACTION, DEFAULT_EXIT_ACTION, KEY_EXIT_ACTION);
1320 	TERMINAL_PROFILE_PROPERTY_ENUM (SCROLLBAR_POSITION, TERMINAL_TYPE_SCROLLBAR_POSITION, DEFAULT_SCROLLBAR_POSITION, KEY_SCROLLBAR_POSITION);
1321 	TERMINAL_PROFILE_PROPERTY_ENUM (TITLE_MODE, TERMINAL_TYPE_TITLE_MODE, DEFAULT_TITLE_MODE, KEY_TITLE_MODE);
1322 
1323 	TERMINAL_PROFILE_PROPERTY_INT (DEFAULT_SIZE_COLUMNS, 1, 1024, DEFAULT_DEFAULT_SIZE_COLUMNS, KEY_DEFAULT_SIZE_COLUMNS);
1324 	TERMINAL_PROFILE_PROPERTY_INT (DEFAULT_SIZE_ROWS, 1, 1024, DEFAULT_DEFAULT_SIZE_ROWS, KEY_DEFAULT_SIZE_ROWS);
1325 	TERMINAL_PROFILE_PROPERTY_INT (SCROLLBACK_LINES, 1, G_MAXINT, DEFAULT_SCROLLBACK_LINES, KEY_SCROLLBACK_LINES);
1326 
1327 	TERMINAL_PROFILE_PROPERTY_OBJECT (BACKGROUND_IMAGE, GDK_TYPE_PIXBUF, NULL);
1328 
1329 	TERMINAL_PROFILE_PROPERTY_STRING_CO (NAME, DEFAULT_NAME, NULL);
1330 	TERMINAL_PROFILE_PROPERTY_STRING (BACKGROUND_IMAGE_FILE, DEFAULT_BACKGROUND_IMAGE_FILE, KEY_BACKGROUND_IMAGE_FILE);
1331 	TERMINAL_PROFILE_PROPERTY_STRING (CUSTOM_COMMAND, DEFAULT_CUSTOM_COMMAND, KEY_CUSTOM_COMMAND);
1332 	TERMINAL_PROFILE_PROPERTY_STRING (TITLE, _(DEFAULT_TITLE), KEY_TITLE);
1333 	TERMINAL_PROFILE_PROPERTY_STRING (VISIBLE_NAME, _(DEFAULT_VISIBLE_NAME), KEY_VISIBLE_NAME);
1334 	TERMINAL_PROFILE_PROPERTY_STRING (WORD_CHARS, DEFAULT_WORD_CHARS, KEY_WORD_CHARS);
1335 
1336 	TERMINAL_PROFILE_PROPERTY_VALUE_ARRAY_BOXED (PALETTE, "palette-color", GDK_TYPE_RGBA, KEY_PALETTE);
1337 }
1338 
1339 /* Semi-Public API */
1340 
1341 TerminalProfile*
_terminal_profile_new(const char * name)1342 _terminal_profile_new (const char *name)
1343 {
1344 	return g_object_new (TERMINAL_TYPE_PROFILE,
1345 	                     "name", name,
1346 	                     NULL);
1347 }
1348 
1349 void
_terminal_profile_forget(TerminalProfile * profile)1350 _terminal_profile_forget (TerminalProfile *profile)
1351 {
1352 	TerminalProfilePrivate *priv = profile->priv;
1353 
1354 	if (!priv->forgotten)
1355 	{
1356 		priv->forgotten = TRUE;
1357 
1358 		g_signal_emit (G_OBJECT (profile), signals[FORGOTTEN], 0);
1359 	}
1360 }
1361 
1362 gboolean
_terminal_profile_get_forgotten(TerminalProfile * profile)1363 _terminal_profile_get_forgotten (TerminalProfile *profile)
1364 {
1365 	return profile->priv->forgotten;
1366 }
1367 
1368 TerminalProfile *
_terminal_profile_clone(TerminalProfile * base_profile,const char * visible_name)1369 _terminal_profile_clone (TerminalProfile *base_profile,
1370                          const char      *visible_name)
1371 {
1372 	TerminalApp *app = terminal_app_get ();
1373 	GObject *base_object = G_OBJECT (base_profile);
1374 	TerminalProfilePrivate *new_priv;
1375 	char profile_name[32];
1376 	GParameter *params;
1377 	GParamSpec **pspecs;
1378 	guint n_pspecs, i, n_params, profile_num;
1379 	TerminalProfile *new_profile;
1380 
1381 	g_object_ref (base_profile);
1382 
1383 	profile_num = 0;
1384 	do
1385 	{
1386 		g_snprintf (profile_name, sizeof (profile_name), "profile%u", profile_num++);
1387 	}
1388 	while (terminal_app_get_profile_by_name (app, profile_name) != NULL);
1389 
1390 	/* Now we have an unused profile name */
1391 	pspecs = g_object_class_list_properties (G_OBJECT_CLASS (TERMINAL_PROFILE_GET_CLASS (base_profile)), &n_pspecs);
1392 
1393 	params = g_newa (GParameter, n_pspecs);
1394 	n_params = 0;
1395 
1396 	for (i = 0; i < n_pspecs; ++i)
1397 	{
1398 		GParamSpec *pspec = pspecs[i];
1399 		GValue *value;
1400 
1401 		if (pspec->owner_type != TERMINAL_TYPE_PROFILE ||
1402 		        (pspec->flags & G_PARAM_WRITABLE) == 0)
1403 			continue;
1404 
1405 		params[n_params].name = pspec->name;
1406 
1407 		value = &params[n_params].value;
1408 		G_VALUE_TYPE (value) = 0;
1409 		g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1410 
1411 		if (pspec->name == I_(TERMINAL_PROFILE_NAME))
1412 			g_value_set_static_string (value, profile_name);
1413 		else if (pspec->name == I_(TERMINAL_PROFILE_VISIBLE_NAME))
1414 			g_value_set_static_string (value, visible_name);
1415 		else
1416 			g_object_get_property (base_object, pspec->name, value);
1417 
1418 		++n_params;
1419 	}
1420 
1421 	new_profile = g_object_newv (TERMINAL_TYPE_PROFILE, n_params, params);
1422 
1423 	g_object_unref (base_profile);
1424 
1425 	for (i = 0; i < n_params; ++i)
1426 		g_value_unset (&params[i].value);
1427 
1428 	/* Flush the new profile to GSettings */
1429 	new_priv = new_profile->priv;
1430 
1431 	g_slist_free (new_priv->dirty_pspecs);
1432 	new_priv->dirty_pspecs = NULL;
1433 	if (new_priv->save_idle_id != 0)
1434 	{
1435 		g_source_remove (new_priv->save_idle_id);
1436 		new_priv->save_idle_id = 0;
1437 	}
1438 
1439 	for (i = 0; i < n_pspecs; ++i)
1440 	{
1441 		GParamSpec *pspec = pspecs[i];
1442 
1443 		if (pspec->owner_type != TERMINAL_TYPE_PROFILE ||
1444 		        (pspec->flags & G_PARAM_WRITABLE) == 0)
1445 			continue;
1446 
1447 		new_priv->dirty_pspecs = g_slist_prepend (new_priv->dirty_pspecs, pspec);
1448 	}
1449 	g_free (pspecs);
1450 
1451 	terminal_profile_save (new_profile);
1452 
1453 	return new_profile;
1454 }
1455 
1456 /* Public API */
1457 
1458 gboolean
terminal_profile_get_property_boolean(TerminalProfile * profile,const char * prop_name)1459 terminal_profile_get_property_boolean (TerminalProfile *profile,
1460                                        const char *prop_name)
1461 {
1462 	const GValue *value;
1463 
1464 	value = get_prop_value_from_prop_name (profile, prop_name);
1465 	g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_BOOLEAN (value), FALSE);
1466 	if (!value || !G_VALUE_HOLDS_BOOLEAN (value))
1467 		return FALSE;
1468 
1469 	return g_value_get_boolean (value);
1470 }
1471 
1472 gconstpointer
terminal_profile_get_property_boxed(TerminalProfile * profile,const char * prop_name)1473 terminal_profile_get_property_boxed (TerminalProfile *profile,
1474                                      const char *prop_name)
1475 {
1476 	const GValue *value;
1477 
1478 	value = get_prop_value_from_prop_name (profile, prop_name);
1479 	g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_BOXED (value), NULL);
1480 	if (!value || !G_VALUE_HOLDS_BOXED (value))
1481 		return NULL;
1482 
1483 	return g_value_get_boxed (value);
1484 }
1485 
1486 double
terminal_profile_get_property_double(TerminalProfile * profile,const char * prop_name)1487 terminal_profile_get_property_double (TerminalProfile *profile,
1488                                       const char *prop_name)
1489 {
1490 	const GValue *value;
1491 
1492 	value = get_prop_value_from_prop_name (profile, prop_name);
1493 	g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_DOUBLE (value), 0.0);
1494 	if (!value || !G_VALUE_HOLDS_DOUBLE (value))
1495 		return 0.0;
1496 
1497 	return g_value_get_double (value);
1498 }
1499 
1500 int
terminal_profile_get_property_enum(TerminalProfile * profile,const char * prop_name)1501 terminal_profile_get_property_enum (TerminalProfile *profile,
1502                                     const char *prop_name)
1503 {
1504 	const GValue *value;
1505 
1506 	value = get_prop_value_from_prop_name (profile, prop_name);
1507 	g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_ENUM (value), 0);
1508 	if (!value || !G_VALUE_HOLDS_ENUM (value))
1509 		return 0;
1510 
1511 	return g_value_get_enum (value);
1512 }
1513 
1514 int
terminal_profile_get_property_int(TerminalProfile * profile,const char * prop_name)1515 terminal_profile_get_property_int (TerminalProfile *profile,
1516                                    const char *prop_name)
1517 {
1518 	const GValue *value;
1519 
1520 	value = get_prop_value_from_prop_name (profile, prop_name);
1521 	g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_INT (value), 0);
1522 	if (!value || !G_VALUE_HOLDS_INT (value))
1523 		return 0;
1524 
1525 	return g_value_get_int (value);
1526 }
1527 
1528 gpointer
terminal_profile_get_property_object(TerminalProfile * profile,const char * prop_name)1529 terminal_profile_get_property_object (TerminalProfile *profile,
1530                                       const char *prop_name)
1531 {
1532 	const GValue *value;
1533 
1534 	value = get_prop_value_from_prop_name (profile, prop_name);
1535 	g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_OBJECT (value), NULL);
1536 	if (!value || !G_VALUE_HOLDS_OBJECT (value))
1537 		return NULL;
1538 
1539 	return g_value_get_object (value);
1540 }
1541 
1542 const char*
terminal_profile_get_property_string(TerminalProfile * profile,const char * prop_name)1543 terminal_profile_get_property_string (TerminalProfile *profile,
1544                                       const char *prop_name)
1545 {
1546 	const GValue *value;
1547 
1548 	value = get_prop_value_from_prop_name (profile, prop_name);
1549 	g_return_val_if_fail (value != NULL && G_VALUE_HOLDS_STRING (value), NULL);
1550 	if (!value || !G_VALUE_HOLDS_STRING (value))
1551 		return NULL;
1552 
1553 	return g_value_get_string (value);
1554 }
1555 
1556 gboolean
terminal_profile_property_locked(TerminalProfile * profile,const char * prop_name)1557 terminal_profile_property_locked (TerminalProfile *profile,
1558                                   const char *prop_name)
1559 {
1560 	TerminalProfilePrivate *priv = profile->priv;
1561 	GParamSpec *pspec;
1562 
1563 	pspec = get_pspec_from_name (profile, prop_name);
1564 	g_return_val_if_fail (pspec != NULL, FALSE);
1565 	if (!pspec)
1566 		return FALSE;
1567 
1568 	return priv->locked[pspec->param_id];
1569 }
1570 
1571 void
terminal_profile_reset_property(TerminalProfile * profile,const char * prop_name)1572 terminal_profile_reset_property (TerminalProfile *profile,
1573                                  const char *prop_name)
1574 {
1575 	GParamSpec *pspec;
1576 
1577 	pspec = get_pspec_from_name (profile, prop_name);
1578 	g_return_if_fail (pspec != NULL);
1579 	if (!pspec ||
1580 	        (pspec->flags & G_PARAM_WRITABLE) == 0)
1581 		return;
1582 
1583 	terminal_profile_reset_property_internal (profile, pspec, TRUE);
1584 }
1585 
1586 gboolean
terminal_profile_get_palette(TerminalProfile * profile,GdkRGBA * colors,guint * n_colors)1587 terminal_profile_get_palette (TerminalProfile *profile,
1588                               GdkRGBA *colors,
1589                               guint *n_colors)
1590 {
1591 	TerminalProfilePrivate *priv;
1592 	GValueArray *array;
1593 	guint i, n;
1594 
1595 	g_return_val_if_fail (TERMINAL_IS_PROFILE (profile), FALSE);
1596 	g_return_val_if_fail (colors != NULL && n_colors != NULL, FALSE);
1597 
1598 	priv = profile->priv;
1599 	array = g_value_get_boxed (g_value_array_get_nth (priv->properties, PROP_PALETTE));
1600 	if (!array)
1601 		return FALSE;
1602 
1603 	n = MIN (array->n_values, *n_colors);
1604 	for (i = 0; i < n; ++i)
1605 	{
1606 		GdkRGBA *color = g_value_get_boxed (g_value_array_get_nth (array, i));
1607 		if (!color)
1608 			continue; /* shouldn't happen!! */
1609 
1610 		colors[i] = *color;
1611 	}
1612 
1613 	*n_colors = n;
1614 	return TRUE;
1615 }
1616 
1617 gboolean
terminal_profile_get_palette_is_builtin(TerminalProfile * profile,guint * n)1618 terminal_profile_get_palette_is_builtin (TerminalProfile *profile,
1619         guint *n)
1620 {
1621 	GdkRGBA colors[TERMINAL_PALETTE_SIZE];
1622 	guint n_colors;
1623 	guint i;
1624 
1625 	n_colors = G_N_ELEMENTS (colors);
1626 	if (!terminal_profile_get_palette (profile, colors, &n_colors) ||
1627 	        n_colors != TERMINAL_PALETTE_SIZE)
1628 		return FALSE;
1629 
1630 	for (i = 0; i < TERMINAL_PALETTE_N_BUILTINS; ++i)
1631 		if (palette_cmp (colors, terminal_palettes[i]))
1632 		{
1633 			*n = i;
1634 			return TRUE;
1635 		}
1636 
1637 	return FALSE;
1638 }
1639 
1640 void
terminal_profile_set_palette_builtin(TerminalProfile * profile,guint n)1641 terminal_profile_set_palette_builtin (TerminalProfile *profile,
1642                                       guint n)
1643 {
1644 	GValue value = { 0, };
1645 
1646 	g_return_if_fail (n < TERMINAL_PALETTE_N_BUILTINS);
1647 
1648 	g_value_init (&value, G_TYPE_VALUE_ARRAY);
1649 	set_value_from_palette (&value, terminal_palettes[n], TERMINAL_PALETTE_SIZE);
1650 	g_object_set_property (G_OBJECT (profile), TERMINAL_PROFILE_PALETTE, &value);
1651 	g_value_unset (&value);
1652 }
1653 
1654 gboolean
terminal_profile_modify_palette_entry(TerminalProfile * profile,guint i,const GdkRGBA * color)1655 terminal_profile_modify_palette_entry (TerminalProfile *profile,
1656                                        guint            i,
1657                                        const GdkRGBA   *color)
1658 {
1659 	TerminalProfilePrivate *priv = profile->priv;
1660 	GValueArray *array;
1661 	GValue *value;
1662 	GdkRGBA *old_color;
1663 
1664 	array = g_value_get_boxed (g_value_array_get_nth (priv->properties, PROP_PALETTE));
1665 	if (!array ||
1666 	        i >= array->n_values)
1667 		return FALSE;
1668 
1669 	value = g_value_array_get_nth (array, i);
1670 	old_color = g_value_get_boxed (value);
1671 	if (!old_color ||
1672 	        !rgba_equal (old_color, color))
1673 	{
1674 		g_value_set_boxed (value, color);
1675 		g_object_notify (G_OBJECT (profile), TERMINAL_PROFILE_PALETTE);
1676 	}
1677 
1678 	return TRUE;
1679 }
1680