xref: /haiku/src/apps/terminal/PrefHandler.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
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 <AutoDeleter.h>
25 #include <Catalog.h>
26 #include <Directory.h>
27 #include <Entry.h>
28 #include <File.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 #include <PathFinder.h>
36 
37 #include "Colors.h"
38 #include "Globals.h"
39 #include "TermConst.h"
40 
41 #include <iostream>
42 
43 /*
44  * Startup preference settings.
45  */
46 static const pref_defaults kTermDefaults[] = {
47 	{ PREF_COLS,				"80" },
48 	{ PREF_ROWS,				"25" },
49 
50 //	No need for PREF_HALF_FONT_FAMILY/_STYLE/_SIZE defaults here,
51 //	these entries will be filled with corresponding params
52 //	of the current system fixed font if they are not
53 //	available in the settings file
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 		// Add the builtin schemes
117 		if (gColorSchemes == NULL) {
118 			LoadThemes();
119 		}
120 	}
121 
122 	// TODO: If no fixed font is available, be_fixed_font
123 	// points to a proportional font.
124 	if (IsFontUsable(be_fixed_font))
125 		_ConfirmFont(be_fixed_font);
126 	else {
127 		int32 numFamilies = count_font_families();
128 		for (int32 i = 0; i < numFamilies; i++) {
129 			font_family family;
130 			uint32 flags;
131 			if (get_font_family(i, &family, &flags) == B_OK) {
132 				font_style style;
133 				int32 numStyles = count_font_styles(family);
134 				for (int32 j = 0; j < numStyles; j++) {
135 					if (get_font_style(family, j, &style) == B_OK) {
136 						BFont fallBackFont;
137 						fallBackFont.SetFamilyAndStyle(family, style);
138 						if (IsFontUsable(fallBackFont)) {
139 							_ConfirmFont(&fallBackFont);
140 							return;
141 						}
142 					}
143 				}
144 			}
145 		}
146 	}
147 }
148 
149 
150 PrefHandler::PrefHandler(const PrefHandler* p)
151 {
152 	fContainer = p->fContainer;
153 }
154 
155 
156 PrefHandler::~PrefHandler()
157 {
158 }
159 
160 
161 /* static */
162 PrefHandler *
163 PrefHandler::Default()
164 {
165 	if (sPrefHandler == NULL)
166 		sPrefHandler = new PrefHandler();
167 	return sPrefHandler;
168 }
169 
170 
171 /* static */
172 void
173 PrefHandler::DeleteDefault()
174 {
175 	delete sPrefHandler;
176 	sPrefHandler = NULL;
177 }
178 
179 
180 /* static */
181 void
182 PrefHandler::SetDefault(PrefHandler *prefHandler)
183 {
184 	DeleteDefault();
185 	sPrefHandler = prefHandler;
186 }
187 
188 
189 /* static */
190 status_t
191 PrefHandler::GetDefaultPath(BPath& path)
192 {
193 	status_t status;
194 	status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
195 	if (status != B_OK)
196 		return status;
197 
198 	status = path.Append("Terminal");
199 	if (status != B_OK)
200 		return status;
201 
202 	// Just create the directory. Harmless if already there
203 	status = create_directory(path.Path(), 0755);
204 	if (status != B_OK)
205 		return status;
206 
207 	return path.Append("Default");
208 }
209 
210 
211 status_t
212 PrefHandler::OpenText(const char *path)
213 {
214 	return _LoadFromTextFile(path);
215 }
216 
217 
218 void
219 PrefHandler::SaveDefaultAsText()
220 {
221 	BPath path;
222 	if (GetDefaultPath(path) == B_OK)
223 		SaveAsText(path.Path(), PREFFILE_MIMETYPE);
224 }
225 
226 
227 void
228 PrefHandler::SaveAsText(const char *path, const char *mimetype,
229 	const char *signature)
230 {
231 	// make sure the target path exists
232 #if 0
233 	BPath directoryPath(path);
234 	if (directoryPath.GetParent(&directoryPath) == B_OK)
235 		create_directory(directoryPath.Path(), 0755);
236 #endif
237 
238 	BFile file(path, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
239 	char buffer[512];
240 	type_code type;
241 	const char *key;
242 
243 	for (int32 i = 0;
244 #ifdef B_BEOS_VERSION_DANO
245 			fContainer.GetInfo(B_STRING_TYPE, i, &key, &type) == B_OK;
246 #else
247 			fContainer.GetInfo(B_STRING_TYPE, i, (char**)&key, &type) == B_OK;
248 #endif
249 			i++) {
250 		int len = snprintf(buffer, sizeof(buffer), "\"%s\" , \"%s\"\n",
251 				key, getString(key));
252 		file.Write(buffer, len);
253 	}
254 
255 	if (mimetype != NULL) {
256 		BNodeInfo info(&file);
257 		info.SetType(mimetype);
258 		info.SetPreferredApp(signature);
259 	}
260 }
261 
262 
263 static int
264 SortByName(const color_scheme *lhs, const color_scheme *rhs)
265 {
266 	return strcmp(lhs->name, rhs->name);
267 }
268 
269 
270 void
271 PrefHandler::LoadThemes()
272 {
273 	gColorSchemes = new BObjectList<const color_scheme>(10, true);
274 
275 	BStringList paths;
276 
277 	if (BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY,
278 			"Terminal/Themes/", B_FIND_PATH_EXISTING_ONLY, paths) == B_OK)
279 		paths.DoForEach(PrefHandler::_LoadThemesFromDirectory);
280 
281 	if (BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY,
282 			"Terminal/Themes/", B_FIND_PATH_EXISTING_ONLY, paths) == B_OK)
283 		paths.DoForEach(PrefHandler::_LoadThemesFromDirectory);
284 
285 	gColorSchemes->SortItems(SortByName);
286 }
287 
288 
289 void
290 PrefHandler::LoadColorScheme(color_scheme* scheme)
291 {
292 	scheme->text_fore_color = getRGB(PREF_TEXT_FORE_COLOR);
293 	scheme->text_back_color = getRGB(PREF_TEXT_BACK_COLOR);
294 	scheme->select_fore_color = getRGB(PREF_SELECT_FORE_COLOR);
295 	scheme->select_back_color = getRGB(PREF_SELECT_BACK_COLOR);
296 	scheme->cursor_fore_color = getRGB(PREF_CURSOR_FORE_COLOR);
297 	scheme->cursor_back_color = getRGB(PREF_CURSOR_BACK_COLOR);
298 	scheme->ansi_colors.black = getRGB(PREF_ANSI_BLACK_COLOR);
299 	scheme->ansi_colors.red = getRGB(PREF_ANSI_RED_COLOR);
300 	scheme->ansi_colors.green = getRGB(PREF_ANSI_GREEN_COLOR);
301 	scheme->ansi_colors.yellow = getRGB(PREF_ANSI_YELLOW_COLOR);
302 	scheme->ansi_colors.blue = getRGB(PREF_ANSI_BLUE_COLOR);
303 	scheme->ansi_colors.magenta = getRGB(PREF_ANSI_MAGENTA_COLOR);
304 	scheme->ansi_colors.cyan = getRGB(PREF_ANSI_CYAN_COLOR);
305 	scheme->ansi_colors.white = getRGB(PREF_ANSI_WHITE_COLOR);
306 	scheme->ansi_colors_h.black = getRGB(PREF_ANSI_BLACK_HCOLOR);
307 	scheme->ansi_colors_h.red = getRGB(PREF_ANSI_RED_HCOLOR);
308 	scheme->ansi_colors_h.green = getRGB(PREF_ANSI_GREEN_HCOLOR);
309 	scheme->ansi_colors_h.yellow = getRGB(PREF_ANSI_YELLOW_HCOLOR);
310 	scheme->ansi_colors_h.blue = getRGB(PREF_ANSI_BLUE_HCOLOR);
311 	scheme->ansi_colors_h.magenta = getRGB(PREF_ANSI_MAGENTA_HCOLOR);
312 	scheme->ansi_colors_h.cyan = getRGB(PREF_ANSI_CYAN_HCOLOR);
313 	scheme->ansi_colors_h.white = getRGB(PREF_ANSI_WHITE_HCOLOR);
314 }
315 
316 
317 int32
318 PrefHandler::getInt32(const char *key)
319 {
320 	const char *value = fContainer.FindString(key);
321 	if (value == NULL)
322 		return 0;
323 
324 	return atoi(value);
325 }
326 
327 
328 float
329 PrefHandler::getFloat(const char *key)
330 {
331 	const char *value = fContainer.FindString(key);
332 	if (value == NULL)
333 		return 0;
334 
335 	return atof(value);
336 }
337 
338 
339 #undef B_TRANSLATION_CONTEXT
340 #define B_TRANSLATION_CONTEXT "Terminal getString"
341 
342 const char*
343 PrefHandler::getString(const char *key)
344 {
345 	const char *buffer;
346 	if (fContainer.FindString(key, &buffer) != B_OK)
347 		buffer = B_TRANSLATE("Error!");
348 
349 	//printf("%x GET %s: %s\n", this, key, buf);
350 	return buffer;
351 }
352 
353 
354 bool
355 PrefHandler::getBool(const char *key)
356 {
357 	const char *value = fContainer.FindString(key);
358 	if (value == NULL)
359 		return false;
360 
361 	return strcmp(value, PREF_TRUE) == 0;
362 }
363 
364 
365 int
366 PrefHandler::getCursor(const char *key)
367 {
368 	const char *value = fContainer.FindString(key);
369 	if (value != NULL && strcmp(value, PREF_BLOCK_CURSOR) != 0) {
370 		if (strcmp(value, PREF_UNDERLINE_CURSOR) == 0)
371 			return UNDERLINE_CURSOR;
372 		if (strcmp(value, PREF_IBEAM_CURSOR) == 0)
373 			return IBEAM_CURSOR;
374 	}
375 	return BLOCK_CURSOR;
376 }
377 
378 
379 #undef B_TRANSLATION_CONTEXT
380 #define B_TRANSLATION_CONTEXT "Terminal getRGB"
381 
382 /** Returns RGB data from given key. */
383 
384 rgb_color
385 PrefHandler::getRGB(const char *key)
386 {
387 	rgb_color col;
388 	int r, g, b;
389 
390 	if (const char *s = fContainer.FindString(key)) {
391 		sscanf(s, "%d, %d, %d", &r, &g, &b);
392 	} else {
393 		fprintf(stderr,
394 			"PrefHandler::getRGB(%s) - key not found\n", key);
395 		r = g = b = 0;
396 	}
397 
398 	col.red = r;
399 	col.green = g;
400 	col.blue = b;
401 	col.alpha = 255;
402 	return col;
403 }
404 
405 
406 /** Setting Int32 data with key. */
407 
408 void
409 PrefHandler::setInt32(const char *key, int32 data)
410 {
411 	char buffer[32];
412 	snprintf(buffer, sizeof(buffer), "%d", (int)data);
413 	setString(key, buffer);
414 }
415 
416 
417 /** Setting Float data with key */
418 
419 void
420 PrefHandler::setFloat(const char *key, float data)
421 {
422 	char buffer[32];
423 	snprintf(buffer, sizeof(buffer), "%g", data);
424 	setString(key, buffer);
425 }
426 
427 
428 /** Setting Bool data with key */
429 
430 void
431 PrefHandler::setBool(const char *key, bool data)
432 {
433 	if (data)
434 		setString(key, PREF_TRUE);
435 	else
436 		setString(key, PREF_FALSE);
437 }
438 
439 
440 /** Setting CString data with key */
441 
442 void
443 PrefHandler::setString(const char *key, const char *data)
444 {
445 	//printf("%x SET %s: %s\n", this, key, data);
446 	fContainer.RemoveName(key);
447 	fContainer.AddString(key, data);
448 }
449 
450 
451 /** Setting RGB data with key */
452 
453 void
454 PrefHandler::setRGB(const char *key, const rgb_color data)
455 {
456 	char buffer[32];
457 	snprintf(buffer, sizeof(buffer), "%d, %d, %d", data.red, data.green, data.blue);
458 	setString(key, buffer);
459 }
460 
461 
462 /** Check any peference stored or not. */
463 
464 bool
465 PrefHandler::IsEmpty() const
466 {
467 	return fContainer.IsEmpty();
468 }
469 
470 
471 void
472 PrefHandler::_ConfirmFont(const BFont *fallbackFont)
473 {
474 	font_family family;
475 	font_style style;
476 
477 	const char *prefFamily = getString(PREF_HALF_FONT_FAMILY);
478 	int32 familiesCount = (prefFamily != NULL) ? count_font_families() : 0;
479 
480 	for (int32 i = 0; i < familiesCount; i++) {
481 		if (get_font_family(i, &family) != B_OK
482 			|| strcmp(family, prefFamily) != 0)
483 			continue;
484 
485 		const char *prefStyle = getString(PREF_HALF_FONT_STYLE);
486 		int32 stylesCount = (prefStyle != NULL) ? count_font_styles(family) : 0;
487 
488 		for (int32 j = 0; j < stylesCount; j++) {
489 			// check style if we can safely use this font
490 			if (get_font_style(family, j, &style) == B_OK
491 				&& strcmp(style, prefStyle) == 0)
492 				return;
493 		}
494 	}
495 
496 	// use fall-back font
497 	fallbackFont->GetFamilyAndStyle(&family, &style);
498 	setString(PREF_HALF_FONT_FAMILY, family);
499 	setString(PREF_HALF_FONT_STYLE, style);
500 	setInt32(PREF_HALF_FONT_SIZE, fallbackFont->Size());
501 }
502 
503 
504 status_t
505 PrefHandler::_LoadFromDefault(const pref_defaults* defaults)
506 {
507 	if (defaults == NULL)
508 		return B_ERROR;
509 
510 	while (defaults->key) {
511 		setString(defaults->key, defaults->item);
512 		++defaults;
513 	}
514 
515 	return B_OK;
516 }
517 
518 
519 /**	Text is "key","Content"
520  *	Comment : Start with '#'
521  */
522 
523 status_t
524 PrefHandler::_LoadFromTextFile(const char * path)
525 {
526 	char buffer[1024];
527 	char key[B_FIELD_NAME_LENGTH], data[512];
528 	int n;
529 	FILE *file;
530 
531 	file = fopen(path, "r");
532 	if (file == NULL)
533 		return B_ENTRY_NOT_FOUND;
534 
535 	while (fgets(buffer, sizeof(buffer), file) != NULL) {
536 		if (*buffer == '#')
537 			continue;
538 
539 		n = sscanf(buffer, "%*[\"]%[^\"]%*[\"]%*[^\"]%*[\"]%[^\"]", key, data);
540 		if (n == 2)
541 			setString(key, data);
542 	}
543 
544 	fclose(file);
545 	return B_OK;
546 }
547 
548 
549 bool
550 PrefHandler::_LoadThemesFromDirectory(const BString &directory)
551 {
552 	BDirectory *themes = new BDirectory(directory.String());
553 	if (themes == NULL)
554 		return false;
555 
556 	BEntry entry;
557 	BPath path;
558 	FindColorSchemeByName comparator;
559 	while (themes->GetNextEntry(&entry) == B_OK)
560 	{
561 		if (entry.GetPath(&path) != B_OK)
562 			continue;
563 
564 		PrefHandler *themeHandler = new PrefHandler(false);
565 		ObjectDeleter<PrefHandler> themeHandlerDeleter(themeHandler);
566 		themeHandler->_LoadFromTextFile(path.Path());
567 
568 		const char *name = themeHandler->fContainer.GetString(PREF_THEME_NAME, NULL);
569 
570 		if (name == NULL || strlen(name) == 0)
571 			continue;
572 
573 		comparator.scheme_name = name;
574 
575 		const color_scheme *scheme = gColorSchemes->FindIf(comparator);
576 
577 		if (scheme != NULL) {
578 			// Scheme with this name exists, replace with this version instead
579 			gColorSchemes->RemoveItem(scheme);
580 		}
581 
582 		color_scheme *newScheme = new color_scheme();
583 		newScheme->name = strdup(name);
584 		themeHandler->LoadColorScheme(newScheme);
585 		gColorSchemes->AddItem(newScheme);
586 	}
587 
588 	return false;
589 }
590