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