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