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