1 /* 2 * Copyright 2001-2010, Haiku, Inc. 3 * Copyright 2003-2004 Kian Duffy, myob@users.sourceforge.net 4 * Parts Copyright 1998-1999 Kazuho Okui and Takashi Murai. 5 * All rights reserved. Distributed under the terms of the MIT license. 6 */ 7 8 9 #include "AppearPrefView.h" 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 14 #include <Button.h> 15 #include <Catalog.h> 16 #include <CheckBox.h> 17 #include <ColorControl.h> 18 #include <LayoutBuilder.h> 19 #include <Locale.h> 20 #include <Menu.h> 21 #include <MenuField.h> 22 #include <MenuItem.h> 23 #include <PopUpMenu.h> 24 #include <TextControl.h> 25 #include <View.h> 26 27 #include "Colors.h" 28 #include "PrefHandler.h" 29 #include "TermConst.h" 30 31 32 #undef B_TRANSLATION_CONTEXT 33 #define B_TRANSLATION_CONTEXT "Terminal AppearancePrefView" 34 35 36 static bool 37 IsFontUsable(const BFont& font) 38 { 39 // TODO: If BFont::IsFullAndHalfFixed() was implemented, we could 40 // use that. But I don't think it's easily implementable using 41 // Freetype. 42 43 if (font.IsFixed()) 44 return true; 45 46 // manually check if all applicable chars are the same width 47 char buffer[2] = { ' ', 0 }; 48 int firstWidth = (int)ceilf(font.StringWidth(buffer)); 49 50 // TODO: Workaround for broken fonts/font_subsystem 51 if (firstWidth <= 0) 52 return false; 53 54 for (int c = ' ' + 1; c <= 0x7e; c++) { 55 buffer[0] = c; 56 int width = (int)ceilf(font.StringWidth(buffer)); 57 58 if (width != firstWidth) 59 return false; 60 } 61 62 return true; 63 } 64 65 66 // #pragma mark - 67 68 69 AppearancePrefView::AppearancePrefView(const char* name, 70 const BMessenger& messenger) 71 : 72 BGroupView(name, B_VERTICAL, 5), 73 fTerminalMessenger(messenger) 74 { 75 const char* kColorTable[] = { 76 B_TRANSLATE("Text"), 77 B_TRANSLATE("Background"), 78 B_TRANSLATE("Cursor"), 79 B_TRANSLATE("Text under cursor"), 80 B_TRANSLATE("Selected text"), 81 B_TRANSLATE("Selected background"), 82 NULL 83 }; 84 85 fBlinkCursor = new BCheckBox( 86 B_TRANSLATE("Blinking cursor"), 87 new BMessage(MSG_BLINK_CURSOR_CHANGED)); 88 89 fWarnOnExit = new BCheckBox( 90 B_TRANSLATE("Confirm exit if active programs exist"), 91 new BMessage(MSG_WARN_ON_EXIT_CHANGED)); 92 93 BMenu* fontMenu = _MakeFontMenu(MSG_HALF_FONT_CHANGED, 94 PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY), 95 PrefHandler::Default()->getString(PREF_HALF_FONT_STYLE)); 96 fFontField = new BMenuField(B_TRANSLATE("Font:"), fontMenu); 97 98 BPopUpMenu* schemesPopUp = _MakeColorSchemeMenu(MSG_COLOR_SCHEME_CHANGED, 99 gPredefinedColorSchemes, gPredefinedColorSchemes[0]); 100 fColorSchemeField = new BMenuField(B_TRANSLATE("Color scheme:"), 101 schemesPopUp); 102 103 BPopUpMenu* colorsPopUp = _MakeMenu(MSG_COLOR_FIELD_CHANGED, kColorTable, 104 kColorTable[0]); 105 106 fColorField = new BMenuField(B_TRANSLATE("Color:"), colorsPopUp); 107 fColorField->SetEnabled(false); 108 109 fTabTitle = new BTextControl("tabTitle", B_TRANSLATE("Tab title:"), "", 110 NULL); 111 fTabTitle->SetModificationMessage( 112 new BMessage(MSG_TAB_TITLE_SETTING_CHANGED)); 113 fTabTitle->SetToolTip(BString(B_TRANSLATE( 114 "The pattern specifying the tab titles. The following placeholders\n" 115 "can be used:\n")) << kTooTipSetTabTitlePlaceholders); 116 117 fWindowTitle = new BTextControl("windowTitle", B_TRANSLATE("Window title:"), 118 "", NULL); 119 fWindowTitle->SetModificationMessage( 120 new BMessage(MSG_WINDOW_TITLE_SETTING_CHANGED)); 121 fWindowTitle->SetToolTip(BString(B_TRANSLATE( 122 "The pattern specifying the window titles. The following placeholders\n" 123 "can be used:\n")) << kTooTipSetWindowTitlePlaceholders); 124 125 BLayoutBuilder::Group<>(this) 126 .SetInsets(5, 5, 5, 5) 127 .AddGrid(5, 5) 128 .Add(fTabTitle->CreateLabelLayoutItem(), 0, 0) 129 .Add(fTabTitle->CreateTextViewLayoutItem(), 1, 0) 130 .Add(fWindowTitle->CreateLabelLayoutItem(), 0, 1) 131 .Add(fWindowTitle->CreateTextViewLayoutItem(), 1, 1) 132 .Add(fFontField->CreateLabelLayoutItem(), 0, 2) 133 .Add(fFontField->CreateMenuBarLayoutItem(), 1, 2) 134 .Add(fColorSchemeField->CreateLabelLayoutItem(), 0, 3) 135 .Add(fColorSchemeField->CreateMenuBarLayoutItem(), 1, 3) 136 .Add(fColorField->CreateLabelLayoutItem(), 0, 4) 137 .Add(fColorField->CreateMenuBarLayoutItem(), 1, 4) 138 .End() 139 .AddGlue() 140 .Add(fColorControl = new BColorControl(BPoint(10, 10), 141 B_CELLS_32x8, 8.0, "", new BMessage(MSG_COLOR_CHANGED))) 142 .Add(fBlinkCursor) 143 .Add(fWarnOnExit); 144 145 fTabTitle->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 146 fWindowTitle->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 147 fFontField->SetAlignment(B_ALIGN_RIGHT); 148 fColorField->SetAlignment(B_ALIGN_RIGHT); 149 fColorSchemeField->SetAlignment(B_ALIGN_RIGHT); 150 151 fTabTitle->SetText(PrefHandler::Default()->getString(PREF_TAB_TITLE)); 152 fWindowTitle->SetText(PrefHandler::Default()->getString(PREF_WINDOW_TITLE)); 153 154 fColorControl->SetEnabled(false); 155 fColorControl->SetValue( 156 PrefHandler::Default()->getRGB(PREF_TEXT_FORE_COLOR)); 157 158 fBlinkCursor->SetValue(PrefHandler::Default()->getBool(PREF_BLINK_CURSOR)); 159 fWarnOnExit->SetValue(PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT)); 160 161 BTextControl* redInput = (BTextControl*)fColorControl->ChildAt(0); 162 BTextControl* greenInput = (BTextControl*)fColorControl->ChildAt(1); 163 BTextControl* blueInput = (BTextControl*)fColorControl->ChildAt(2); 164 165 redInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 166 greenInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 167 blueInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 168 } 169 170 171 void 172 AppearancePrefView::GetPreferredSize(float* _width, float* _height) 173 { 174 if (_width) 175 *_width = Bounds().Width(); 176 177 if (*_height) 178 *_height = fColorControl->Frame().bottom; 179 } 180 181 182 void 183 AppearancePrefView::Revert() 184 { 185 PrefHandler* pref = PrefHandler::Default(); 186 187 fTabTitle->SetText(pref->getString(PREF_TAB_TITLE)); 188 fWindowTitle->SetText(pref->getString(PREF_WINDOW_TITLE)); 189 190 fWarnOnExit->SetValue(pref->getBool( 191 PREF_WARN_ON_EXIT)); 192 193 fColorSchemeField->Menu()->ItemAt(0)->SetMarked(true); 194 fColorControl->SetValue(pref-> 195 getRGB(PREF_TEXT_FORE_COLOR)); 196 197 const char* family = pref->getString(PREF_HALF_FONT_FAMILY); 198 const char* style = pref->getString(PREF_HALF_FONT_STYLE); 199 const char* size = pref->getString(PREF_HALF_FONT_SIZE); 200 201 _MarkSelectedFont(family, style, size); 202 } 203 204 205 void 206 AppearancePrefView::AttachedToWindow() 207 { 208 fTabTitle->SetTarget(this); 209 fWindowTitle->SetTarget(this); 210 fBlinkCursor->SetTarget(this); 211 fWarnOnExit->SetTarget(this); 212 213 fFontField->Menu()->SetTargetForItems(this); 214 for (int32 i = 0; i < fFontField->Menu()->CountItems(); i++) { 215 BMenu* fontSizeMenu = fFontField->Menu()->SubmenuAt(i); 216 if (fontSizeMenu == NULL) 217 continue; 218 219 fontSizeMenu->SetTargetForItems(this); 220 } 221 222 fColorControl->SetTarget(this); 223 fColorField->Menu()->SetTargetForItems(this); 224 fColorSchemeField->Menu()->SetTargetForItems(this); 225 226 _SetCurrentColorScheme(fColorSchemeField); 227 bool enableCustomColors = 228 strcmp(fColorSchemeField->Menu()->FindMarked()->Label(), 229 gCustomColorScheme.name) == 0; 230 231 _EnableCustomColors(enableCustomColors); 232 } 233 234 235 void 236 237 AppearancePrefView::MessageReceived(BMessage* msg) 238 { 239 bool modified = false; 240 241 switch (msg->what) { 242 case MSG_HALF_FONT_CHANGED: 243 { 244 const char* family = NULL; 245 const char* style = NULL; 246 const char* size = NULL; 247 if (msg->FindString("font_family", &family) != B_OK 248 || msg->FindString("font_style", &style) != B_OK 249 || msg->FindString("font_size", &size) != B_OK) { 250 break; 251 } 252 253 PrefHandler* pref = PrefHandler::Default(); 254 const char* currentFamily 255 = pref->getString(PREF_HALF_FONT_FAMILY); 256 const char* currentStyle 257 = pref->getString(PREF_HALF_FONT_STYLE); 258 const char* currentSize 259 = pref->getString(PREF_HALF_FONT_SIZE); 260 261 if (currentFamily == NULL || strcmp(currentFamily, family) != 0 262 || currentStyle == NULL || strcmp(currentStyle, style) != 0 263 || currentSize == NULL || strcmp(currentSize, size) != 0) { 264 pref->setString(PREF_HALF_FONT_FAMILY, family); 265 pref->setString(PREF_HALF_FONT_STYLE, style); 266 pref->setString(PREF_HALF_FONT_SIZE, size); 267 _MarkSelectedFont(family, style, size); 268 modified = true; 269 } 270 break; 271 } 272 273 case MSG_COLOR_CHANGED: 274 { 275 rgb_color oldColor = PrefHandler::Default()->getRGB( 276 fColorField->Menu()->FindMarked()->Label()); 277 if (oldColor != fColorControl->ValueAsColor()) { 278 PrefHandler::Default()->setRGB( 279 fColorField->Menu()->FindMarked()->Label(), 280 fColorControl->ValueAsColor()); 281 modified = true; 282 } 283 break; 284 } 285 286 case MSG_COLOR_SCHEME_CHANGED: 287 { 288 color_scheme* newScheme = NULL; 289 if (msg->FindPointer("color_scheme", 290 (void**)&newScheme) == B_OK) { 291 if (newScheme == &gCustomColorScheme) 292 _EnableCustomColors(true); 293 else 294 _EnableCustomColors(false); 295 296 _ChangeColorScheme(newScheme); 297 modified = true; 298 } 299 break; 300 } 301 302 case MSG_COLOR_FIELD_CHANGED: 303 fColorControl->SetValue(PrefHandler::Default()->getRGB( 304 fColorField->Menu()->FindMarked()->Label())); 305 break; 306 307 case MSG_BLINK_CURSOR_CHANGED: 308 if (PrefHandler::Default()->getBool(PREF_BLINK_CURSOR) 309 != fBlinkCursor->Value()) { 310 PrefHandler::Default()->setBool(PREF_BLINK_CURSOR, 311 fBlinkCursor->Value()); 312 modified = true; 313 } 314 break; 315 316 case MSG_WARN_ON_EXIT_CHANGED: 317 if (PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT) 318 != fWarnOnExit->Value()) { 319 PrefHandler::Default()->setBool(PREF_WARN_ON_EXIT, 320 fWarnOnExit->Value()); 321 modified = true; 322 } 323 break; 324 325 case MSG_TAB_TITLE_SETTING_CHANGED: 326 { 327 BString oldValue(PrefHandler::Default()->getString(PREF_TAB_TITLE)); 328 if (oldValue != fTabTitle->Text()) { 329 PrefHandler::Default()->setString(PREF_TAB_TITLE, 330 fTabTitle->Text()); 331 modified = true; 332 } 333 break; 334 } 335 336 case MSG_WINDOW_TITLE_SETTING_CHANGED: 337 { 338 BString oldValue(PrefHandler::Default()->getString( 339 PREF_WINDOW_TITLE)); 340 if (oldValue != fWindowTitle->Text()) { 341 PrefHandler::Default()->setString(PREF_WINDOW_TITLE, 342 fWindowTitle->Text()); 343 modified = true; 344 } 345 break; 346 } 347 348 default: 349 BView::MessageReceived(msg); 350 return; 351 } 352 353 if (modified) { 354 fTerminalMessenger.SendMessage(msg); 355 356 BMessenger messenger(this); 357 messenger.SendMessage(MSG_PREF_MODIFIED); 358 } 359 } 360 361 362 void 363 AppearancePrefView::_EnableCustomColors(bool enable) 364 { 365 fColorField->SetEnabled(enable); 366 fColorControl->SetEnabled(enable); 367 } 368 369 370 void 371 AppearancePrefView::_ChangeColorScheme(color_scheme* scheme) 372 { 373 PrefHandler* pref = PrefHandler::Default(); 374 375 pref->setRGB(PREF_TEXT_FORE_COLOR, scheme->text_fore_color); 376 pref->setRGB(PREF_TEXT_BACK_COLOR, scheme->text_back_color); 377 pref->setRGB(PREF_SELECT_FORE_COLOR, scheme->select_fore_color); 378 pref->setRGB(PREF_SELECT_BACK_COLOR, scheme->select_back_color); 379 pref->setRGB(PREF_CURSOR_FORE_COLOR, scheme->cursor_fore_color); 380 pref->setRGB(PREF_CURSOR_BACK_COLOR, scheme->cursor_back_color); 381 } 382 383 384 void 385 AppearancePrefView::_SetCurrentColorScheme(BMenuField* field) 386 { 387 PrefHandler* pref = PrefHandler::Default(); 388 389 gCustomColorScheme.text_fore_color = pref->getRGB(PREF_TEXT_FORE_COLOR); 390 gCustomColorScheme.text_back_color = pref->getRGB(PREF_TEXT_BACK_COLOR); 391 gCustomColorScheme.select_fore_color = pref->getRGB(PREF_SELECT_FORE_COLOR); 392 gCustomColorScheme.select_back_color = pref->getRGB(PREF_SELECT_BACK_COLOR); 393 gCustomColorScheme.cursor_fore_color = pref->getRGB(PREF_CURSOR_FORE_COLOR); 394 gCustomColorScheme.cursor_back_color = pref->getRGB(PREF_CURSOR_BACK_COLOR); 395 396 const char* currentSchemeName = NULL; 397 398 for (const color_scheme** schemes = gPredefinedColorSchemes; 399 *schemes != NULL; schemes++) { 400 if (gCustomColorScheme == **schemes) { 401 currentSchemeName = (*schemes)->name; 402 break; 403 } 404 } 405 406 for (int32 i = 0; i < fColorSchemeField->Menu()->CountItems(); i++) { 407 BMenuItem* item = fColorSchemeField->Menu()->ItemAt(i); 408 if (strcmp(item->Label(), currentSchemeName) == 0) { 409 item->SetMarked(true); 410 break; 411 } 412 } 413 } 414 415 416 /*static*/ BMenu* 417 AppearancePrefView::_MakeFontMenu(uint32 command, 418 const char* defaultFamily, const char* defaultStyle) 419 { 420 BPopUpMenu* menu = new BPopUpMenu(""); 421 int32 numFamilies = count_font_families(); 422 uint32 flags; 423 424 for (int32 i = 0; i < numFamilies; i++) { 425 font_family family; 426 if (get_font_family(i, &family, &flags) == B_OK) { 427 BFont font; 428 font_style style; 429 int32 numStyles = count_font_styles(family); 430 for (int32 j = 0; j < numStyles; j++) { 431 if (get_font_style(family, j, &style) == B_OK) { 432 font.SetFamilyAndStyle(family, style); 433 if (IsFontUsable(font)) { 434 BMessage* message = new BMessage(command); 435 const char* size 436 = PrefHandler::Default()->getString(PREF_HALF_FONT_SIZE); 437 message->AddString("font_family", family); 438 message->AddString("font_style", style); 439 message->AddString("font_size", size); 440 char fontMenuLabel[134]; 441 snprintf(fontMenuLabel, sizeof(fontMenuLabel), 442 "%s - %s", family, style); 443 BMenu* fontSizeMenu = _MakeFontSizeMenu(fontMenuLabel, 444 MSG_HALF_FONT_CHANGED, family, style, size); 445 BMenuItem* item = new BMenuItem(fontSizeMenu, message); 446 menu->AddItem(item); 447 if (strcmp(defaultFamily, family) == 0 448 && strcmp(defaultStyle, style) == 0) 449 item->SetMarked(true); 450 } 451 } 452 } 453 } 454 } 455 456 if (menu->FindMarked() == NULL) 457 menu->ItemAt(0)->SetMarked(true); 458 459 return menu; 460 } 461 462 463 /*static*/ BMenu* 464 AppearancePrefView::_MakeFontSizeMenu(const char* label, uint32 command, 465 const char* family, const char* style, const char* size) 466 { 467 BMenu* menu = new BMenu(label); 468 menu->SetRadioMode(true); 469 menu->SetLabelFromMarked(false); 470 471 int32 sizes[] = { 472 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 0 473 }; 474 475 bool found = false; 476 477 for (uint32 i = 0; sizes[i]; i++) { 478 BString fontSize; 479 fontSize << sizes[i]; 480 BMessage* message = new BMessage(command); 481 message->AddString("font_family", family); 482 message->AddString("font_style", style); 483 message->AddString("font_size", fontSize.String()); 484 BMenuItem* item = new BMenuItem(fontSize.String(), message); 485 menu->AddItem(item); 486 if (sizes[i] == atoi(size)) { 487 item->SetMarked(true); 488 found = true; 489 } 490 } 491 492 if (!found) { 493 for (uint32 i = 0; sizes[i]; i++) { 494 if (sizes[i] > atoi(size)) { 495 BMessage* message = new BMessage(command); 496 message->AddString("font_family", family); 497 message->AddString("font_style", style); 498 message->AddString("font_size", size); 499 BMenuItem* item = new BMenuItem(size, message); 500 item->SetMarked(true); 501 menu->AddItem(item, i); 502 break; 503 } 504 } 505 } 506 507 return menu; 508 } 509 510 511 /*static*/ BPopUpMenu* 512 AppearancePrefView::_MakeMenu(uint32 msg, const char** items, 513 const char* defaultItemName) 514 { 515 BPopUpMenu* menu = new BPopUpMenu(""); 516 517 int32 i = 0; 518 while (*items) { 519 if (strcmp((*items), "") == 0) 520 menu->AddSeparatorItem(); 521 else { 522 BMessage* message = new BMessage(msg); 523 menu->AddItem(new BMenuItem((*items), message)); 524 } 525 526 items++; 527 i++; 528 } 529 530 BMenuItem* defaultItem = menu->FindItem(defaultItemName); 531 if (defaultItem) 532 defaultItem->SetMarked(true); 533 534 return menu; 535 } 536 537 538 /*static*/ BPopUpMenu* 539 AppearancePrefView::_MakeColorSchemeMenu(uint32 msg, const color_scheme** items, 540 const color_scheme* defaultItemName) 541 { 542 BPopUpMenu* menu = new BPopUpMenu(""); 543 544 int32 i = 0; 545 while (*items) { 546 if (strcmp((*items)->name, "") == 0) 547 menu->AddSeparatorItem(); 548 else { 549 BMessage* message = new BMessage(msg); 550 message->AddPointer("color_scheme", (const void*)*items); 551 menu->AddItem(new BMenuItem((*items)->name, message)); 552 } 553 554 items++; 555 i++; 556 } 557 return menu; 558 } 559 560 561 void 562 AppearancePrefView::_MarkSelectedFont(const char* family, const char* style, 563 const char* size) 564 { 565 char fontMenuLabel[134]; 566 snprintf(fontMenuLabel, sizeof(fontMenuLabel), "%s - %s", family, style); 567 568 // mark the selected font 569 BMenuItem* selectedFont = fFontField->Menu()->FindItem(fontMenuLabel); 570 if (selectedFont != NULL) 571 selectedFont->SetMarked(true); 572 573 // mark the selected font size on all font menus 574 for (int32 i = 0; i < fFontField->Menu()->CountItems(); i++) { 575 BMenu* fontSizeMenu = fFontField->Menu()->SubmenuAt(i); 576 if (fontSizeMenu == NULL) 577 continue; 578 579 BMenuItem* item = fontSizeMenu->FindItem(size); 580 if (item != NULL) 581 item->SetMarked(true); 582 } 583 } 584