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