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