xref: /haiku/src/apps/terminal/PrefHandler.cpp (revision db6fcb750a1afb5fdc752322972adf6044d3b4c4)
1 /*
2  * Copyright 2003-2015, Haiku, Inc. All Rights Reserved.
3  * Copyright (c) 2004 Daniel Furrer <assimil8or@users.sourceforge.net>
4  * Copyright (c) 2003-4 Kian Duffy <myob@users.sourceforge.net>
5  * Copyright (c) 1998,99 Kazuho Okui and Takashi Murai.
6  *
7  * Distributed unter the terms of the MIT License.
8  *
9  * Authors:
10  *		Kian Duffy, myob@users.sourceforge.net
11  *		Daniel Furrer, assimil8or@users.sourceforge.net
12  *		Simon South, simon@simonsouth.net
13  *		Siarzhuk Zharski, zharik@gmx.li
14  */
15 
16 
17 #include "PrefHandler.h"
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 
24 #include <Catalog.h>
25 #include <Directory.h>
26 #include <Entry.h>
27 #include <File.h>
28 #include <FindDirectory.h>
29 #include <Font.h>
30 #include <GraphicsDefs.h>
31 #include <Locale.h>
32 #include <Message.h>
33 #include <NodeInfo.h>
34 #include <Path.h>
35 
36 #include "Globals.h"
37 #include "TermConst.h"
38 
39 #include <iostream>
40 
41 /*
42  * Startup preference settings.
43  */
44 static const pref_defaults kTermDefaults[] = {
45 	{ PREF_COLS,				"80" },
46 	{ PREF_ROWS,				"25" },
47 
48 //	No need for PREF_HALF_FONT_FAMILY/_STYLE defaults here,
49 //	these entries will be filled with corresponding params
50 //	of the current system fixed font if they are not
51 //	available in the settings file
52 
53 	{ PREF_HALF_FONT_SIZE,		"12" },
54 
55 	{ PREF_TEXT_FORE_COLOR,		"  0,   0,   0" },
56 	{ PREF_TEXT_BACK_COLOR,		"255, 255, 255" },
57 	{ PREF_CURSOR_FORE_COLOR,	"255, 255, 255" },
58 	{ PREF_CURSOR_BACK_COLOR,	"  0,   0,   0" },
59 	{ PREF_SELECT_FORE_COLOR,	"255, 255, 255" },
60 	{ PREF_SELECT_BACK_COLOR,	"  0,   0,   0" },
61 
62 	{ PREF_IM_FORE_COLOR,		"  0,   0,   0" },
63 	{ PREF_IM_BACK_COLOR,		"152, 203, 255" },
64 	{ PREF_IM_SELECT_COLOR,		"255, 152, 152" },
65 
66 	{ PREF_ANSI_BLACK_COLOR,	" 40,  40,  40" },
67 	{ PREF_ANSI_RED_COLOR,		"204,   0,   0" },
68 	{ PREF_ANSI_GREEN_COLOR,	" 78, 154,   6" },
69 	{ PREF_ANSI_YELLOW_COLOR,	"218, 168,   0" },
70 	{ PREF_ANSI_BLUE_COLOR,		" 51, 102, 152" },
71 	{ PREF_ANSI_MAGENTA_COLOR,	"115,  68, 123" },
72 	{ PREF_ANSI_CYAN_COLOR,		"  6, 152, 154" },
73 	{ PREF_ANSI_WHITE_COLOR,	"245, 245, 245" },
74 
75 	{ PREF_ANSI_BLACK_HCOLOR,	"128, 128, 128" },
76 	{ PREF_ANSI_RED_HCOLOR,		"255,   0,   0" },
77 	{ PREF_ANSI_GREEN_HCOLOR,	"  0, 255,   0" },
78 	{ PREF_ANSI_YELLOW_HCOLOR,	"255, 255,   0" },
79 	{ PREF_ANSI_BLUE_HCOLOR,	"  0,   0, 255" },
80 	{ PREF_ANSI_MAGENTA_HCOLOR,	"255,   0, 255" },
81 	{ PREF_ANSI_CYAN_HCOLOR,	"  0, 255, 255" },
82 	{ PREF_ANSI_WHITE_HCOLOR,	"255, 255, 255" },
83 
84 	{ PREF_HISTORY_SIZE,		"10000" },
85 
86 	{ PREF_TEXT_ENCODING,		"UTF-8" },
87 
88 	{ PREF_IM_AWARE,			"0"},
89 
90 	{ PREF_TAB_TITLE,			"%1d: %p%e" },
91 	{ PREF_WINDOW_TITLE,		"%T% i: %t" },
92 	{ PREF_BLINK_CURSOR,		PREF_TRUE },
93 	{ PREF_USE_OPTION_AS_META,	PREF_FALSE },
94 	{ PREF_WARN_ON_EXIT,		PREF_TRUE },
95 	{ PREF_CURSOR_STYLE,		PREF_BLOCK_CURSOR },
96 	{ PREF_EMULATE_BOLD,		PREF_FALSE },
97 
98 	{ NULL, NULL},
99 };
100 
101 
102 PrefHandler *PrefHandler::sPrefHandler = NULL;
103 
104 
105 PrefHandler::PrefHandler(bool loadSettings)
106 	:
107 	fContainer('Pref')
108 {
109 	_LoadFromDefault(kTermDefaults);
110 
111 	if (loadSettings) {
112 		BPath path;
113 		GetDefaultPath(path);
114 		OpenText(path.Path());
115 	}
116 
117 	// TODO: If no fixed font is available, be_fixed_font
118 	// points to a proportional font.
119 	if (IsFontUsable(be_fixed_font))
120 		_ConfirmFont(be_fixed_font);
121 	else {
122 		int32 numFamilies = count_font_families();
123 		for (int32 i = 0; i < numFamilies; i++) {
124 			font_family family;
125 			uint32 flags;
126 			if (get_font_family(i, &family, &flags) == B_OK) {
127 				font_style style;
128 				int32 numStyles = count_font_styles(family);
129 				for (int32 j = 0; j < numStyles; j++) {
130 					if (get_font_style(family, j, &style) == B_OK) {
131 						BFont fallBackFont;
132 						fallBackFont.SetFamilyAndStyle(family, style);
133 						if (IsFontUsable(fallBackFont)) {
134 							_ConfirmFont(&fallBackFont);
135 							return;
136 						}
137 					}
138 				}
139 			}
140 		}
141 	}
142 }
143 
144 
145 PrefHandler::PrefHandler(const PrefHandler* p)
146 {
147 	fContainer = p->fContainer;
148 }
149 
150 
151 PrefHandler::~PrefHandler()
152 {
153 }
154 
155 
156 /* static */
157 PrefHandler *
158 PrefHandler::Default()
159 {
160 	if (sPrefHandler == NULL)
161 		sPrefHandler = new PrefHandler();
162 	return sPrefHandler;
163 }
164 
165 
166 /* static */
167 void
168 PrefHandler::DeleteDefault()
169 {
170 	delete sPrefHandler;
171 	sPrefHandler = NULL;
172 }
173 
174 
175 /* static */
176 void
177 PrefHandler::SetDefault(PrefHandler *prefHandler)
178 {
179 	DeleteDefault();
180 	sPrefHandler = prefHandler;
181 }
182 
183 
184 /* static */
185 status_t
186 PrefHandler::GetDefaultPath(BPath& path)
187 {
188 	status_t status;
189 	status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
190 	if (status != B_OK)
191 		return status;
192 
193 	status = path.Append("Terminal");
194 	if (status != B_OK)
195 		return status;
196 
197 	// Just create the directory. Harmless if already there
198 	status = create_directory(path.Path(), 0755);
199 	if (status != B_OK)
200 		return status;
201 
202 	return path.Append("Default");
203 }
204 
205 
206 status_t
207 PrefHandler::OpenText(const char *path)
208 {
209 	return _LoadFromTextFile(path);
210 }
211 
212 
213 void
214 PrefHandler::SaveDefaultAsText()
215 {
216 	BPath path;
217 	if (GetDefaultPath(path) == B_OK)
218 		SaveAsText(path.Path(), PREFFILE_MIMETYPE);
219 }
220 
221 
222 void
223 PrefHandler::SaveAsText(const char *path, const char *mimetype,
224 	const char *signature)
225 {
226 	// make sure the target path exists
227 #if 0
228 	BPath directoryPath(path);
229 	if (directoryPath.GetParent(&directoryPath) == B_OK)
230 		create_directory(directoryPath.Path(), 0755);
231 #endif
232 
233 	BFile file(path, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
234 	char buffer[512];
235 	type_code type;
236 	const char *key;
237 
238 	for (int32 i = 0;
239 #ifdef B_BEOS_VERSION_DANO
240 			fContainer.GetInfo(B_STRING_TYPE, i, &key, &type) == B_OK;
241 #else
242 			fContainer.GetInfo(B_STRING_TYPE, i, (char**)&key, &type) == B_OK;
243 #endif
244 			i++) {
245 		int len = snprintf(buffer, sizeof(buffer), "\"%s\" , \"%s\"\n",
246 				key, getString(key));
247 		file.Write(buffer, len);
248 	}
249 
250 	if (mimetype != NULL) {
251 		BNodeInfo info(&file);
252 		info.SetType(mimetype);
253 		info.SetPreferredApp(signature);
254 	}
255 }
256 
257 
258 int32
259 PrefHandler::getInt32(const char *key)
260 {
261 	const char *value = fContainer.FindString(key);
262 	if (value == NULL)
263 		return 0;
264 
265 	return atoi(value);
266 }
267 
268 
269 float
270 PrefHandler::getFloat(const char *key)
271 {
272 	const char *value = fContainer.FindString(key);
273 	if (value == NULL)
274 		return 0;
275 
276 	return atof(value);
277 }
278 
279 
280 #undef B_TRANSLATION_CONTEXT
281 #define B_TRANSLATION_CONTEXT "Terminal getString"
282 
283 const char*
284 PrefHandler::getString(const char *key)
285 {
286 	const char *buffer;
287 	if (fContainer.FindString(key, &buffer) != B_OK)
288 		buffer = B_TRANSLATE("Error!");
289 
290 	//printf("%x GET %s: %s\n", this, key, buf);
291 	return buffer;
292 }
293 
294 
295 bool
296 PrefHandler::getBool(const char *key)
297 {
298 	const char *value = fContainer.FindString(key);
299 	if (value == NULL)
300 		return false;
301 
302 	return strcmp(value, PREF_TRUE) == 0;
303 }
304 
305 
306 int
307 PrefHandler::getCursor(const char *key)
308 {
309 	const char *value = fContainer.FindString(key);
310 	if (value != NULL && strcmp(value, PREF_BLOCK_CURSOR) != 0) {
311 		if (strcmp(value, PREF_UNDERLINE_CURSOR) == 0)
312 			return UNDERLINE_CURSOR;
313 		if (strcmp(value, PREF_IBEAM_CURSOR) == 0)
314 			return IBEAM_CURSOR;
315 	}
316 	return BLOCK_CURSOR;
317 }
318 
319 
320 #undef B_TRANSLATION_CONTEXT
321 #define B_TRANSLATION_CONTEXT "Terminal getRGB"
322 
323 /** Returns RGB data from given key. */
324 
325 rgb_color
326 PrefHandler::getRGB(const char *key)
327 {
328 	rgb_color col;
329 	int r, g, b;
330 
331 	if (const char *s = fContainer.FindString(key)) {
332 		sscanf(s, "%d, %d, %d", &r, &g, &b);
333 	} else {
334 		fprintf(stderr,
335 			"PrefHandler::getRGB(%s) - key not found\n", key);
336 		r = g = b = 0;
337 	}
338 
339 	col.red = r;
340 	col.green = g;
341 	col.blue = b;
342 	col.alpha = 255;
343 	return col;
344 }
345 
346 
347 /** Setting Int32 data with key. */
348 
349 void
350 PrefHandler::setInt32(const char *key, int32 data)
351 {
352 	char buffer[32];
353 	snprintf(buffer, sizeof(buffer), "%d", (int)data);
354 	setString(key, buffer);
355 }
356 
357 
358 /** Setting Float data with key */
359 
360 void
361 PrefHandler::setFloat(const char *key, float data)
362 {
363 	char buffer[32];
364 	snprintf(buffer, sizeof(buffer), "%g", data);
365 	setString(key, buffer);
366 }
367 
368 
369 /** Setting Bool data with key */
370 
371 void
372 PrefHandler::setBool(const char *key, bool data)
373 {
374 	if (data)
375 		setString(key, PREF_TRUE);
376 	else
377 		setString(key, PREF_FALSE);
378 }
379 
380 
381 /** Setting CString data with key */
382 
383 void
384 PrefHandler::setString(const char *key, const char *data)
385 {
386 	//printf("%x SET %s: %s\n", this, key, data);
387 	fContainer.RemoveName(key);
388 	fContainer.AddString(key, data);
389 }
390 
391 
392 /** Setting RGB data with key */
393 
394 void
395 PrefHandler::setRGB(const char *key, const rgb_color data)
396 {
397 	char buffer[32];
398 	snprintf(buffer, sizeof(buffer), "%d, %d, %d", data.red, data.green, data.blue);
399 	setString(key, buffer);
400 }
401 
402 
403 /** Check any peference stored or not. */
404 
405 bool
406 PrefHandler::IsEmpty() const
407 {
408 	return fContainer.IsEmpty();
409 }
410 
411 
412 void
413 PrefHandler::_ConfirmFont(const BFont *fallbackFont)
414 {
415 	font_family family;
416 	font_style style;
417 
418 	const char *prefFamily = getString(PREF_HALF_FONT_FAMILY);
419 	int32 familiesCount = (prefFamily != NULL) ? count_font_families() : 0;
420 
421 	for (int32 i = 0; i < familiesCount; i++) {
422 		if (get_font_family(i, &family) != B_OK
423 			|| strcmp(family, prefFamily) != 0)
424 			continue;
425 
426 		const char *prefStyle = getString(PREF_HALF_FONT_STYLE);
427 		int32 stylesCount = (prefStyle != NULL) ? count_font_styles(family) : 0;
428 
429 		for (int32 j = 0; j < stylesCount; j++) {
430 			// check style if we can safely use this font
431 			if (get_font_style(family, j, &style) == B_OK
432 				&& strcmp(style, prefStyle) == 0)
433 				return;
434 		}
435 	}
436 
437 	// use fall-back font
438 	fallbackFont->GetFamilyAndStyle(&family, &style);
439 	setString(PREF_HALF_FONT_FAMILY, family);
440 	setString(PREF_HALF_FONT_STYLE, style);
441 }
442 
443 
444 status_t
445 PrefHandler::_LoadFromDefault(const pref_defaults* defaults)
446 {
447 	if (defaults == NULL)
448 		return B_ERROR;
449 
450 	while (defaults->key) {
451 		setString(defaults->key, defaults->item);
452 		++defaults;
453 	}
454 
455 	return B_OK;
456 }
457 
458 
459 /**	Text is "key","Content"
460  *	Comment : Start with '#'
461  */
462 
463 status_t
464 PrefHandler::_LoadFromTextFile(const char * path)
465 {
466 	char buffer[1024];
467 	char key[B_FIELD_NAME_LENGTH], data[512];
468 	int n;
469 	FILE *file;
470 
471 	file = fopen(path, "r");
472 	if (file == NULL)
473 		return B_ENTRY_NOT_FOUND;
474 
475 	while (fgets(buffer, sizeof(buffer), file) != NULL) {
476 		if (*buffer == '#')
477 			continue;
478 
479 		n = sscanf(buffer, "%*[\"]%[^\"]%*[\"]%*[^\"]%*[\"]%[^\"]", key, data);
480 		if (n == 2)
481 			setString(key, data);
482 	}
483 
484 	fclose(file);
485 	return B_OK;
486 }
487