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