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 */ 46static 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 102PrefHandler *PrefHandler::sPrefHandler = NULL; 103 104 105PrefHandler::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 150PrefHandler::PrefHandler(const PrefHandler* p) 151{ 152 fContainer = p->fContainer; 153} 154 155 156PrefHandler::~PrefHandler() 157{ 158} 159 160 161/* static */ 162PrefHandler * 163PrefHandler::Default() 164{ 165 if (sPrefHandler == NULL) 166 sPrefHandler = new PrefHandler(); 167 return sPrefHandler; 168} 169 170 171/* static */ 172void 173PrefHandler::DeleteDefault() 174{ 175 delete sPrefHandler; 176 sPrefHandler = NULL; 177} 178 179 180/* static */ 181void 182PrefHandler::SetDefault(PrefHandler *prefHandler) 183{ 184 DeleteDefault(); 185 sPrefHandler = prefHandler; 186} 187 188 189/* static */ 190status_t 191PrefHandler::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 211status_t 212PrefHandler::OpenText(const char *path) 213{ 214 return _LoadFromTextFile(path); 215} 216 217 218void 219PrefHandler::SaveDefaultAsText() 220{ 221 BPath path; 222 if (GetDefaultPath(path) == B_OK) 223 SaveAsText(path.Path(), PREFFILE_MIMETYPE); 224} 225 226 227void 228PrefHandler::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 263static int 264SortByName(const color_scheme *lhs, const color_scheme *rhs) 265{ 266 return strcmp(lhs->name, rhs->name); 267} 268 269 270void 271PrefHandler::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 289void 290PrefHandler::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 317int32 318PrefHandler::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 328float 329PrefHandler::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 342const char* 343PrefHandler::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 354bool 355PrefHandler::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 365int 366PrefHandler::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 384rgb_color 385PrefHandler::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 408void 409PrefHandler::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 419void 420PrefHandler::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 430void 431PrefHandler::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 442void 443PrefHandler::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 453void 454PrefHandler::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 464bool 465PrefHandler::IsEmpty() const 466{ 467 return fContainer.IsEmpty(); 468} 469 470 471void 472PrefHandler::_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 504status_t 505PrefHandler::_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 523status_t 524PrefHandler::_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 549bool 550PrefHandler::_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