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