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
PrefHandler(bool loadSettings)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
PrefHandler(const PrefHandler * p)150 PrefHandler::PrefHandler(const PrefHandler* p)
151 {
152 fContainer = p->fContainer;
153 }
154
155
~PrefHandler()156 PrefHandler::~PrefHandler()
157 {
158 }
159
160
161 /* static */
162 PrefHandler *
Default()163 PrefHandler::Default()
164 {
165 if (sPrefHandler == NULL)
166 sPrefHandler = new PrefHandler();
167 return sPrefHandler;
168 }
169
170
171 /* static */
172 void
DeleteDefault()173 PrefHandler::DeleteDefault()
174 {
175 delete sPrefHandler;
176 sPrefHandler = NULL;
177 }
178
179
180 /* static */
181 void
SetDefault(PrefHandler * prefHandler)182 PrefHandler::SetDefault(PrefHandler *prefHandler)
183 {
184 DeleteDefault();
185 sPrefHandler = prefHandler;
186 }
187
188
189 /* static */
190 status_t
GetDefaultPath(BPath & path)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
OpenText(const char * path)212 PrefHandler::OpenText(const char *path)
213 {
214 return _LoadFromTextFile(path);
215 }
216
217
218 void
SaveDefaultAsText()219 PrefHandler::SaveDefaultAsText()
220 {
221 BPath path;
222 if (GetDefaultPath(path) == B_OK)
223 SaveAsText(path.Path(), PREFFILE_MIMETYPE);
224 }
225
226
227 void
SaveAsText(const char * path,const char * mimetype,const char * signature)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
SortByName(const color_scheme * lhs,const color_scheme * rhs)264 SortByName(const color_scheme *lhs, const color_scheme *rhs)
265 {
266 return strcmp(lhs->name, rhs->name);
267 }
268
269
270 void
LoadThemes()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
LoadColorScheme(color_scheme * scheme)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
getInt32(const char * key)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
getFloat(const char * key)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*
getString(const char * key)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
getBool(const char * key)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
getCursor(const char * key)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
getRGB(const char * key)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
setInt32(const char * key,int32 data)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
setFloat(const char * key,float data)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
setBool(const char * key,bool data)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
setString(const char * key,const char * data)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
setRGB(const char * key,const rgb_color data)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
IsEmpty() const465 PrefHandler::IsEmpty() const
466 {
467 return fContainer.IsEmpty();
468 }
469
470
471 void
_ConfirmFont(const BFont * fallbackFont)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
_LoadFromDefault(const pref_defaults * defaults)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
_LoadFromTextFile(const char * path)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
_LoadThemesFromDirectory(const BString & directory)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