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