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