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_MARK("Text"), 77 B_TRANSLATE_MARK("Background"), 78 B_TRANSLATE_MARK("Cursor"), 79 B_TRANSLATE_MARK("Text under cursor"), 80 B_TRANSLATE_MARK("Selected text"), 81 B_TRANSLATE_MARK("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 108 fTabTitle = new BTextControl("tabTitle", B_TRANSLATE("Tab title:"), "", 109 NULL); 110 fTabTitle->SetModificationMessage( 111 new BMessage(MSG_TAB_TITLE_SETTING_CHANGED)); 112 fTabTitle->SetToolTip(BString(B_TRANSLATE( 113 "The pattern specifying the tab titles. The following placeholders\n" 114 "can be used:\n")) << kTooTipSetTabTitlePlaceholders); 115 116 fWindowTitle = new BTextControl("windowTitle", B_TRANSLATE("Window title:"), 117 "", NULL); 118 fWindowTitle->SetModificationMessage( 119 new BMessage(MSG_WINDOW_TITLE_SETTING_CHANGED)); 120 fWindowTitle->SetToolTip(BString(B_TRANSLATE( 121 "The pattern specifying the window titles. The following placeholders\n" 122 "can be used:\n")) << kTooTipSetWindowTitlePlaceholders); 123 124 BLayoutBuilder::Group<>(this) 125 .SetInsets(5, 5, 5, 5) 126 .AddGrid(5, 5) 127 .Add(fTabTitle->CreateLabelLayoutItem(), 0, 0) 128 .Add(fTabTitle->CreateTextViewLayoutItem(), 1, 0) 129 .Add(fWindowTitle->CreateLabelLayoutItem(), 0, 1) 130 .Add(fWindowTitle->CreateTextViewLayoutItem(), 1, 1) 131 .Add(fFontField->CreateLabelLayoutItem(), 0, 2) 132 .Add(fFontField->CreateMenuBarLayoutItem(), 1, 2) 133 .Add(fColorSchemeField->CreateLabelLayoutItem(), 0, 3) 134 .Add(fColorSchemeField->CreateMenuBarLayoutItem(), 1, 3) 135 .Add(fColorField->CreateLabelLayoutItem(), 0, 4) 136 .Add(fColorField->CreateMenuBarLayoutItem(), 1, 4) 137 .End() 138 .AddGlue() 139 .Add(fColorControl = new BColorControl(BPoint(10, 10), 140 B_CELLS_32x8, 8.0, "", new BMessage(MSG_COLOR_CHANGED))) 141 .Add(fBlinkCursor) 142 .Add(fWarnOnExit); 143 144 fTabTitle->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 145 fWindowTitle->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 146 fFontField->SetAlignment(B_ALIGN_RIGHT); 147 fColorField->SetAlignment(B_ALIGN_RIGHT); 148 fColorSchemeField->SetAlignment(B_ALIGN_RIGHT); 149 150 fTabTitle->SetText(PrefHandler::Default()->getString(PREF_TAB_TITLE)); 151 fWindowTitle->SetText(PrefHandler::Default()->getString(PREF_WINDOW_TITLE)); 152 153 fColorControl->SetValue( 154 PrefHandler::Default()->getRGB(PREF_TEXT_FORE_COLOR)); 155 156 fBlinkCursor->SetValue(PrefHandler::Default()->getBool(PREF_BLINK_CURSOR)); 157 fWarnOnExit->SetValue(PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT)); 158 159 BTextControl* redInput = (BTextControl*)fColorControl->ChildAt(0); 160 BTextControl* greenInput = (BTextControl*)fColorControl->ChildAt(1); 161 BTextControl* blueInput = (BTextControl*)fColorControl->ChildAt(2); 162 163 redInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 164 greenInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 165 blueInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 166 } 167 168 169 void 170 AppearancePrefView::GetPreferredSize(float* _width, float* _height) 171 { 172 if (_width) 173 *_width = Bounds().Width(); 174 175 if (*_height) 176 *_height = fColorControl->Frame().bottom; 177 } 178 179 180 void 181 AppearancePrefView::Revert() 182 { 183 PrefHandler* pref = PrefHandler::Default(); 184 185 fTabTitle->SetText(pref->getString(PREF_TAB_TITLE)); 186 fWindowTitle->SetText(pref->getString(PREF_WINDOW_TITLE)); 187 188 fWarnOnExit->SetValue(pref->getBool( 189 PREF_WARN_ON_EXIT)); 190 191 fColorSchemeField->Menu()->ItemAt(0)->SetMarked(true); 192 fColorControl->SetValue(pref-> 193 getRGB(PREF_TEXT_FORE_COLOR)); 194 195 const char* family = pref->getString(PREF_HALF_FONT_FAMILY); 196 const char* style = pref->getString(PREF_HALF_FONT_STYLE); 197 const char* size = pref->getString(PREF_HALF_FONT_SIZE); 198 199 _MarkSelectedFont(family, style, size); 200 } 201 202 203 void 204 AppearancePrefView::AttachedToWindow() 205 { 206 fTabTitle->SetTarget(this); 207 fWindowTitle->SetTarget(this); 208 fBlinkCursor->SetTarget(this); 209 fWarnOnExit->SetTarget(this); 210 211 fFontField->Menu()->SetTargetForItems(this); 212 for (int32 i = 0; i < fFontField->Menu()->CountItems(); i++) { 213 BMenu* fontSizeMenu = fFontField->Menu()->SubmenuAt(i); 214 if (fontSizeMenu == NULL) 215 continue; 216 217 fontSizeMenu->SetTargetForItems(this); 218 } 219 220 fColorControl->SetTarget(this); 221 fColorField->Menu()->SetTargetForItems(this); 222 fColorSchemeField->Menu()->SetTargetForItems(this); 223 224 _SetCurrentColorScheme(); 225 } 226 227 228 void 229 230 AppearancePrefView::MessageReceived(BMessage* msg) 231 { 232 bool modified = false; 233 234 switch (msg->what) { 235 case MSG_HALF_FONT_CHANGED: 236 { 237 const char* family = NULL; 238 const char* style = NULL; 239 const char* size = NULL; 240 if (msg->FindString("font_family", &family) != B_OK 241 || msg->FindString("font_style", &style) != B_OK 242 || msg->FindString("font_size", &size) != B_OK) { 243 break; 244 } 245 246 PrefHandler* pref = PrefHandler::Default(); 247 const char* currentFamily 248 = pref->getString(PREF_HALF_FONT_FAMILY); 249 const char* currentStyle 250 = pref->getString(PREF_HALF_FONT_STYLE); 251 const char* currentSize 252 = pref->getString(PREF_HALF_FONT_SIZE); 253 254 if (currentFamily == NULL || strcmp(currentFamily, family) != 0 255 || currentStyle == NULL || strcmp(currentStyle, style) != 0 256 || currentSize == NULL || strcmp(currentSize, size) != 0) { 257 pref->setString(PREF_HALF_FONT_FAMILY, family); 258 pref->setString(PREF_HALF_FONT_STYLE, style); 259 pref->setString(PREF_HALF_FONT_SIZE, size); 260 _MarkSelectedFont(family, style, size); 261 modified = true; 262 } 263 break; 264 } 265 266 case MSG_COLOR_CHANGED: 267 { 268 const BMessage* itemMessage 269 = fColorField->Menu()->FindMarked()->Message(); 270 const char* label = NULL; 271 if (itemMessage->FindString("label", &label) != B_OK) 272 break; 273 rgb_color oldColor = PrefHandler::Default()->getRGB(label); 274 if (oldColor != fColorControl->ValueAsColor()) { 275 BMenuItem* item = fColorSchemeField->Menu()->FindMarked(); 276 if (strcmp(item->Label(), gCustomColorScheme.name) != 0) { 277 item->SetMarked(false); 278 item = fColorSchemeField->Menu()->FindItem( 279 gCustomColorScheme.name); 280 if (item) 281 item->SetMarked(true); 282 } 283 284 PrefHandler::Default()->setRGB(label, 285 fColorControl->ValueAsColor()); 286 modified = true; 287 } 288 break; 289 } 290 291 case MSG_COLOR_SCHEME_CHANGED: 292 { 293 color_scheme* newScheme = NULL; 294 if (msg->FindPointer("color_scheme", 295 (void**)&newScheme) == B_OK) { 296 _ChangeColorScheme(newScheme); 297 const char* label = NULL; 298 if (fColorField->Menu()->FindMarked()->Message()->FindString( 299 "label", &label) == B_OK) 300 fColorControl->SetValue( 301 PrefHandler::Default()->getRGB(label)); 302 modified = true; 303 } 304 break; 305 } 306 307 case MSG_COLOR_FIELD_CHANGED: 308 { 309 const char* label = NULL; 310 if (msg->FindString("label", &label) == B_OK) 311 fColorControl->SetValue(PrefHandler::Default()->getRGB(label)); 312 break; 313 } 314 315 case MSG_BLINK_CURSOR_CHANGED: 316 if (PrefHandler::Default()->getBool(PREF_BLINK_CURSOR) 317 != fBlinkCursor->Value()) { 318 PrefHandler::Default()->setBool(PREF_BLINK_CURSOR, 319 fBlinkCursor->Value()); 320 modified = true; 321 } 322 break; 323 324 case MSG_WARN_ON_EXIT_CHANGED: 325 if (PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT) 326 != fWarnOnExit->Value()) { 327 PrefHandler::Default()->setBool(PREF_WARN_ON_EXIT, 328 fWarnOnExit->Value()); 329 modified = true; 330 } 331 break; 332 333 case MSG_TAB_TITLE_SETTING_CHANGED: 334 { 335 BString oldValue(PrefHandler::Default()->getString(PREF_TAB_TITLE)); 336 if (oldValue != fTabTitle->Text()) { 337 PrefHandler::Default()->setString(PREF_TAB_TITLE, 338 fTabTitle->Text()); 339 modified = true; 340 } 341 break; 342 } 343 344 case MSG_WINDOW_TITLE_SETTING_CHANGED: 345 { 346 BString oldValue(PrefHandler::Default()->getString( 347 PREF_WINDOW_TITLE)); 348 if (oldValue != fWindowTitle->Text()) { 349 PrefHandler::Default()->setString(PREF_WINDOW_TITLE, 350 fWindowTitle->Text()); 351 modified = true; 352 } 353 break; 354 } 355 356 default: 357 BView::MessageReceived(msg); 358 return; 359 } 360 361 if (modified) { 362 fTerminalMessenger.SendMessage(msg); 363 364 BMessenger messenger(this); 365 messenger.SendMessage(MSG_PREF_MODIFIED); 366 } 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() 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 if (currentSchemeName == NULL) 408 break; 409 BMenuItem* item = fColorSchemeField->Menu()->ItemAt(i); 410 if (strcmp(item->Label(), currentSchemeName) == 0) { 411 item->SetMarked(true); 412 break; 413 } 414 } 415 } 416 417 418 /*static*/ BMenu* 419 AppearancePrefView::_MakeFontMenu(uint32 command, 420 const char* defaultFamily, const char* defaultStyle) 421 { 422 BPopUpMenu* menu = new BPopUpMenu(""); 423 int32 numFamilies = count_font_families(); 424 uint32 flags; 425 426 for (int32 i = 0; i < numFamilies; i++) { 427 font_family family; 428 if (get_font_family(i, &family, &flags) == B_OK) { 429 BFont font; 430 font_style style; 431 int32 numStyles = count_font_styles(family); 432 for (int32 j = 0; j < numStyles; j++) { 433 if (get_font_style(family, j, &style) == B_OK) { 434 font.SetFamilyAndStyle(family, style); 435 if (IsFontUsable(font)) { 436 BMessage* message = new BMessage(command); 437 const char* size 438 = PrefHandler::Default()->getString(PREF_HALF_FONT_SIZE); 439 message->AddString("font_family", family); 440 message->AddString("font_style", style); 441 message->AddString("font_size", size); 442 char fontMenuLabel[134]; 443 snprintf(fontMenuLabel, sizeof(fontMenuLabel), 444 "%s - %s", family, style); 445 BMenu* fontSizeMenu = _MakeFontSizeMenu(fontMenuLabel, 446 MSG_HALF_FONT_CHANGED, family, style, size); 447 BMenuItem* item = new BMenuItem(fontSizeMenu, message); 448 menu->AddItem(item); 449 if (strcmp(defaultFamily, family) == 0 450 && strcmp(defaultStyle, style) == 0) 451 item->SetMarked(true); 452 } 453 } 454 } 455 } 456 } 457 458 if (menu->FindMarked() == NULL) 459 menu->ItemAt(0)->SetMarked(true); 460 461 return menu; 462 } 463 464 465 /*static*/ BMenu* 466 AppearancePrefView::_MakeFontSizeMenu(const char* label, uint32 command, 467 const char* family, const char* style, const char* size) 468 { 469 BMenu* menu = new BMenu(label); 470 menu->SetRadioMode(true); 471 menu->SetLabelFromMarked(false); 472 473 int32 sizes[] = { 474 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 0 475 }; 476 477 bool found = false; 478 479 for (uint32 i = 0; sizes[i]; i++) { 480 BString fontSize; 481 fontSize << sizes[i]; 482 BMessage* message = new BMessage(command); 483 message->AddString("font_family", family); 484 message->AddString("font_style", style); 485 message->AddString("font_size", fontSize.String()); 486 BMenuItem* item = new BMenuItem(fontSize.String(), message); 487 menu->AddItem(item); 488 if (sizes[i] == atoi(size)) { 489 item->SetMarked(true); 490 found = true; 491 } 492 } 493 494 if (!found) { 495 for (uint32 i = 0; sizes[i]; i++) { 496 if (sizes[i] > atoi(size)) { 497 BMessage* message = new BMessage(command); 498 message->AddString("font_family", family); 499 message->AddString("font_style", style); 500 message->AddString("font_size", size); 501 BMenuItem* item = new BMenuItem(size, message); 502 item->SetMarked(true); 503 menu->AddItem(item, i); 504 break; 505 } 506 } 507 } 508 509 return menu; 510 } 511 512 513 /*static*/ BPopUpMenu* 514 AppearancePrefView::_MakeMenu(uint32 msg, const char** items, 515 const char* defaultItemName) 516 { 517 BPopUpMenu* menu = new BPopUpMenu(""); 518 519 while (*items) { 520 if (strcmp((*items), "") == 0) 521 menu->AddSeparatorItem(); 522 else { 523 BMessage* message = new BMessage(msg); 524 message->AddString("label", *items); 525 BMenuItem* item = new BMenuItem(B_TRANSLATE(*items), message); 526 menu->AddItem(item); 527 if (strcmp(*items, defaultItemName) == 0) 528 item->SetMarked(true); 529 } 530 531 items++; 532 } 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