1 /* -*- Mode: C; c-set-style: linux indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* mate-desktop-utils.c - Utilities for the MATE Desktop
3 
4    Copyright (C) 1998 Tom Tromey
5    All rights reserved.
6 
7    This file is part of the Mate Library.
8 
9    The Mate Library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Library General Public License as
11    published by the Free Software Foundation; either version 2 of the
12    License, or (at your option) any later version.
13 
14    The Mate Library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Library General Public License for more details.
18 
19    You should have received a copy of the GNU Library General Public
20    License along with the Mate Library; see the file COPYING.LIB.  If not,
21    write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22    Boston, MA 02110-1301, USA.  */
23 /*
24   @NOTATION@
25  */
26 
27 #include <config.h>
28 #include <glib.h>
29 #include <gio/gio.h>
30 #include <glib/gi18n-lib.h>
31 #include <gdk/gdk.h>
32 #include <gtk/gtk.h>
33 
34 #define MATE_DESKTOP_USE_UNSTABLE_API
35 #include <mate-desktop-utils.h>
36 
37 #include "private.h"
38 
39 static void
40 gtk_style_shade (GdkRGBA *a,
41                  GdkRGBA *b,
42                  gdouble  k);
43 
44 static void
45 rgb_to_hls (gdouble *r,
46             gdouble *g,
47             gdouble *b);
48 
49 static void
50 hls_to_rgb (gdouble *h,
51             gdouble *l,
52             gdouble *s);
53 
54 /**
55  * mate_desktop_prepend_terminal_to_vector:
56  * @argc: a pointer to the vector size
57  * @argv: a pointer to the vector
58  *
59  * Prepends a terminal (either the one configured as default in the user's
60  * MATE setup, or one of the common xterm emulators) to the passed in vector,
61  * modifying it in the process.  The vector should be allocated with #g_malloc,
62  * as this will #g_free the original vector.  Also all elements must have been
63  * allocated separately.  That is the standard glib/MATE way of doing vectors
64  * however.  If the integer that @argc points to is negative, the size will
65  * first be computed.  Also note that passing in pointers to a vector that is
66  * empty, will just create a new vector for you.
67  **/
68 void
mate_desktop_prepend_terminal_to_vector(int * argc,char *** argv)69 mate_desktop_prepend_terminal_to_vector (int *argc, char ***argv)
70 {
71         char **real_argv;
72         int real_argc;
73         int i, j;
74 	char **term_argv = NULL;
75 	int term_argc = 0;
76 	GSettings *settings;
77 
78 	gchar *terminal;
79 
80 	char **the_argv;
81 
82         g_return_if_fail (argc != NULL);
83         g_return_if_fail (argv != NULL);
84 
85         _mate_desktop_init_i18n ();
86 
87 	/* sanity */
88         if(*argv == NULL)
89                 *argc = 0;
90 
91 	the_argv = *argv;
92 
93 	/* compute size if not given */
94 	if (*argc < 0) {
95 		for (i = 0; the_argv[i] != NULL; i++)
96 			;
97 		*argc = i;
98 	}
99 
100 	settings = g_settings_new ("org.mate.applications-terminal");
101 	terminal = g_settings_get_string (settings, "exec");
102 
103 	if (terminal && *terminal != '\0') {
104 		gchar *command_line;
105 		gchar *exec_flag;
106 
107 		exec_flag = g_settings_get_string (settings, "exec-arg");
108 
109 		if (!exec_flag || *exec_flag == '\0')
110 			command_line = g_strdup (terminal);
111 		else
112 			command_line = g_strdup_printf ("%s %s", terminal,
113 							exec_flag);
114 
115 		g_shell_parse_argv (command_line,
116 				    &term_argc,
117 				    &term_argv,
118 				    NULL /* error */);
119 
120 		g_free (command_line);
121 		g_free (exec_flag);
122 	}
123 	g_free (terminal);
124 	g_object_unref (settings);
125 
126 	if (term_argv == NULL) {
127 		char *check;
128 
129 		term_argc = 2;
130 		term_argv = g_new0 (char *, 3);
131 
132 		check = g_find_program_in_path ("mate-terminal");
133 		if (check != NULL) {
134 			term_argv[0] = check;
135 			/* Note that mate-terminal takes -x and
136 			 * as -e in mate-terminal is broken we use that. */
137 			term_argv[1] = g_strdup ("-x");
138 		} else {
139 			if (check == NULL)
140 				check = g_find_program_in_path ("nxterm");
141 			if (check == NULL)
142 				check = g_find_program_in_path ("color-xterm");
143 			if (check == NULL)
144 				check = g_find_program_in_path ("rxvt");
145 			if (check == NULL)
146 				check = g_find_program_in_path ("xterm");
147 			if (check == NULL)
148 				check = g_find_program_in_path ("dtterm");
149 			if (check == NULL) {
150 				g_warning (_("Cannot find a terminal, using "
151 					     "xterm, even if it may not work"));
152 				check = g_strdup ("xterm");
153 			}
154 			term_argv[0] = check;
155 			term_argv[1] = g_strdup ("-e");
156 		}
157 	}
158 
159         real_argc = term_argc + *argc;
160         real_argv = g_new (char *, real_argc + 1);
161 
162         for (i = 0; i < term_argc; i++)
163                 real_argv[i] = term_argv[i];
164 
165         for (j = 0; j < *argc; j++, i++)
166                 real_argv[i] = (char *)the_argv[j];
167 
168 	real_argv[i] = NULL;
169 
170 	g_free (*argv);
171 	*argv = real_argv;
172 	*argc = real_argc;
173 
174 	/* we use g_free here as we sucked all the inner strings
175 	 * out from it into real_argv */
176 	g_free (term_argv);
177 }
178 
179 /**
180  * mate_gdk_spawn_command_line_on_screen:
181  * @screen: a GdkScreen
182  * @command: a command line
183  * @error: return location for errors
184  *
185  * This is a replacement for gdk_spawn_command_line_on_screen, deprecated
186  * in GDK 2.24 and removed in GDK 3.0.
187  *
188  * gdk_spawn_command_line_on_screen is like g_spawn_command_line_async(),
189  * except the child process is spawned in such an environment that on
190  * calling gdk_display_open() it would be returned a GdkDisplay with
191  * screen as the default screen.
192  *
193  * This is useful for applications which wish to launch an application
194  * on a specific screen.
195  *
196  * Returns: TRUE on success, FALSE if error is set.
197  *
198  * Since: 1.7.1
199  **/
200 gboolean
mate_gdk_spawn_command_line_on_screen(GdkScreen * screen,const gchar * command,GError ** error)201 mate_gdk_spawn_command_line_on_screen (GdkScreen *screen, const gchar *command, GError **error)
202 {
203 	GAppInfo *appinfo = NULL;
204 	GdkAppLaunchContext *context = NULL;
205 	gboolean res = FALSE;
206 
207 	appinfo = g_app_info_create_from_commandline (command, NULL, G_APP_INFO_CREATE_NONE, error);
208 
209 	if (appinfo) {
210 		context = gdk_display_get_app_launch_context (gdk_screen_get_display (screen));
211 		res = g_app_info_launch (appinfo, NULL, G_APP_LAUNCH_CONTEXT (context), error);
212 		g_object_unref (context);
213 		g_object_unref (appinfo);
214 	}
215 
216 	return res;
217 }
218 
219 void
_mate_desktop_init_i18n(void)220 _mate_desktop_init_i18n (void) {
221 	static gboolean initialized = FALSE;
222 
223 	if (!initialized) {
224 		bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
225 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
226 		bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
227 #endif
228 		initialized = TRUE;
229 	}
230 }
231 
232 /**
233  * gtk_style_shade:
234  * @a:  the starting colour
235  * @b:  [out] the resulting colour
236  * @k:  amount to scale lightness and saturation by
237  *
238  * Takes a colour "a", scales the lightness and saturation by a certain amount,
239  * and sets "b" to the resulting colour.
240  * gtkstyle.c cut-and-pastage.
241  */
242 static void
gtk_style_shade(GdkRGBA * a,GdkRGBA * b,gdouble k)243 gtk_style_shade (GdkRGBA *a,
244                  GdkRGBA *b,
245                  gdouble  k)
246 {
247 	gdouble red;
248 	gdouble green;
249 	gdouble blue;
250 
251 	red = a->red;
252 	green = a->green;
253 	blue = a->blue;
254 
255 	rgb_to_hls (&red, &green, &blue);
256 
257 	green *= k;
258 	if (green > 1.0)
259 		green = 1.0;
260 	else if (green < 0.0)
261 		green = 0.0;
262 
263 	blue *= k;
264 	if (blue > 1.0)
265 		blue = 1.0;
266 	else if (blue < 0.0)
267 		blue = 0.0;
268 
269 	hls_to_rgb (&red, &green, &blue);
270 
271 	b->red = red;
272 	b->green = green;
273 	b->blue = blue;
274 }
275 
276 /**
277  * rgb_to_hls:
278  * @r:  on input, red; on output, hue
279  * @g:  on input, green; on output, lightness
280  * @b:  on input, blue; on output, saturation
281  *
282  * Converts a red/green/blue triplet to a hue/lightness/saturation triplet.
283  */
284 static void
rgb_to_hls(gdouble * r,gdouble * g,gdouble * b)285 rgb_to_hls (gdouble *r,
286             gdouble *g,
287             gdouble *b)
288 {
289 	gdouble min;
290 	gdouble max;
291 	gdouble red;
292 	gdouble green;
293 	gdouble blue;
294 	gdouble h, l, s;
295 	gdouble delta;
296 
297 	red = *r;
298 	green = *g;
299 	blue = *b;
300 
301 	if (red > green)
302 	{
303 		if (red > blue)
304 			max = red;
305 		else
306 			max = blue;
307 
308 		if (green < blue)
309 			min = green;
310 		else
311 			min = blue;
312 	}
313 	else
314 	{
315 		if (green > blue)
316 			max = green;
317 		else
318 			max = blue;
319 
320 		if (red < blue)
321 			min = red;
322 		else
323 			min = blue;
324 	}
325 
326 	l = (max + min) / 2;
327 	s = 0;
328 	h = 0;
329 
330 	if (max != min)
331 	{
332 		if (l <= 0.5)
333 			s = (max - min) / (max + min);
334 		else
335 			s = (max - min) / (2 - max - min);
336 
337 		delta = max -min;
338 		if (red == max)
339 			h = (green - blue) / delta;
340 		else if (green == max)
341 			h = 2 + (blue - red) / delta;
342 		else if (blue == max)
343 			h = 4 + (red - green) / delta;
344 
345 		h *= 60;
346 		if (h < 0.0)
347 			h += 360;
348 	}
349 
350 	*r = h;
351 	*g = l;
352 	*b = s;
353 }
354 
355 /**
356  * hls_to_rgb:
357  * @h: on input, hue; on output, red
358  * @l: on input, lightness; on output, green
359  * @s  on input, saturation; on output, blue
360  *
361  * Converts a hue/lightness/saturation triplet to a red/green/blue triplet.
362  */
363 static void
hls_to_rgb(gdouble * h,gdouble * l,gdouble * s)364 hls_to_rgb (gdouble *h,
365             gdouble *l,
366             gdouble *s)
367 {
368 	gdouble hue;
369 	gdouble lightness;
370 	gdouble saturation;
371 	gdouble m1, m2;
372 	gdouble r, g, b;
373 
374 	lightness = *l;
375 	saturation = *s;
376 
377 	if (lightness <= 0.5)
378 		m2 = lightness * (1 + saturation);
379 	else
380 		m2 = lightness + saturation - lightness * saturation;
381 	m1 = 2 * lightness - m2;
382 
383 	if (saturation == 0)
384 	{
385 		*h = lightness;
386 		*l = lightness;
387 		*s = lightness;
388 	}
389 	else
390 	{
391 		hue = *h + 120;
392 		while (hue > 360)
393 			hue -= 360;
394 		while (hue < 0)
395 			hue += 360;
396 
397 		if (hue < 60)
398 			r = m1 + (m2 - m1) * hue / 60;
399 		else if (hue < 180)
400 			r = m2;
401 		else if (hue < 240)
402 			r = m1 + (m2 - m1) * (240 - hue) / 60;
403 		else
404 			r = m1;
405 
406 		hue = *h;
407 		while (hue > 360)
408 			hue -= 360;
409 		while (hue < 0)
410 			hue += 360;
411 
412 		if (hue < 60)
413 			g = m1 + (m2 - m1) * hue / 60;
414 		else if (hue < 180)
415 			g = m2;
416 		else if (hue < 240)
417 			g = m1 + (m2 - m1) * (240 - hue) / 60;
418 		else
419 			g = m1;
420 
421 		hue = *h - 120;
422 		while (hue > 360)
423 			hue -= 360;
424 		while (hue < 0)
425 			hue += 360;
426 
427 		if (hue < 60)
428 			b = m1 + (m2 - m1) * hue / 60;
429 		else if (hue < 180)
430 			b = m2;
431 		else if (hue < 240)
432 			b = m1 + (m2 - m1) * (240 - hue) / 60;
433 		else
434 			b = m1;
435 
436 		*h = r;
437 		*l = g;
438 		*s = b;
439 	}
440 }
441 
442 /* Based on set_color() in gtkstyle.c */
443 #define LIGHTNESS_MULT 1.3
444 #define DARKNESS_MULT  0.7
445 void
mate_desktop_gtk_style_get_light_color(GtkStyleContext * style,GtkStateFlags state,GdkRGBA * color)446 mate_desktop_gtk_style_get_light_color (GtkStyleContext *style,
447                                         GtkStateFlags    state,
448                                         GdkRGBA         *color)
449 {
450 	gtk_style_context_get_background_color (style, state, color);
451 	gtk_style_shade (color, color, LIGHTNESS_MULT);
452 }
453 
454 void
mate_desktop_gtk_style_get_dark_color(GtkStyleContext * style,GtkStateFlags state,GdkRGBA * color)455 mate_desktop_gtk_style_get_dark_color (GtkStyleContext *style,
456                                        GtkStateFlags    state,
457                                        GdkRGBA         *color)
458 {
459 	gtk_style_context_get_background_color (style, state, color);
460 	gtk_style_shade (color, color, DARKNESS_MULT);
461 }
462