1 /* 2 * Copyright 2001-2015, 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 <CharacterSet.h> 17 #include <CharacterSetRoster.h> 18 #include <CheckBox.h> 19 #include <ColorControl.h> 20 #include <LayoutBuilder.h> 21 #include <Locale.h> 22 #include <Menu.h> 23 #include <MenuField.h> 24 #include <MenuItem.h> 25 #include <PopUpMenu.h> 26 #include <TextControl.h> 27 #include <View.h> 28 29 #include "Colors.h" 30 #include "Globals.h" 31 #include "PrefHandler.h" 32 #include "TermConst.h" 33 #include "TermWindow.h" 34 35 36 #undef B_TRANSLATION_CONTEXT 37 #define B_TRANSLATION_CONTEXT "Terminal AppearancePrefView" 38 39 40 // #pragma mark - 41 42 43 AppearancePrefView::AppearancePrefView(const char* name, 44 const BMessenger& messenger) 45 : 46 BGroupView(name, B_VERTICAL, 5), 47 fTerminalMessenger(messenger) 48 { 49 fBlinkCursor = new BCheckBox( 50 B_TRANSLATE("Blinking cursor"), 51 new BMessage(MSG_BLINK_CURSOR_CHANGED)); 52 53 fAllowBold = new BCheckBox( 54 B_TRANSLATE("Allow bold text"), 55 new BMessage(MSG_ALLOW_BOLD_CHANGED)); 56 57 fUseOptionAsMetaKey = new BCheckBox( 58 B_TRANSLATE("Use left Option as Meta key"), 59 new BMessage(MSG_USE_OPTION_AS_META_CHANGED)); 60 61 fWarnOnExit = new BCheckBox( 62 B_TRANSLATE("Confirm exit if active programs exist"), 63 new BMessage(MSG_WARN_ON_EXIT_CHANGED)); 64 65 BMenu* fontMenu = _MakeFontMenu(MSG_HALF_FONT_CHANGED, 66 PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY), 67 PrefHandler::Default()->getString(PREF_HALF_FONT_STYLE)); 68 fFontField = new BMenuField(B_TRANSLATE("Font:"), fontMenu); 69 70 BMenu* sizeMenu = new (std::nothrow) BPopUpMenu( 71 B_TRANSLATE_COMMENT("Custom", "Window size")); 72 if (sizeMenu != NULL) { 73 TermWindow::MakeWindowSizeMenu(sizeMenu); 74 sizeMenu->SetLabelFromMarked(true); 75 } 76 fWindowSizeField = new BMenuField(B_TRANSLATE("Window size:"), sizeMenu); 77 78 BMenu* encodingMenu = new (std::nothrow) BPopUpMenu("Text encoding"); 79 if (encodingMenu != NULL) { 80 TermWindow::MakeEncodingMenu(encodingMenu); 81 encodingMenu->SetLabelFromMarked(true); 82 } 83 fEncodingField = new BMenuField(B_TRANSLATE("Encoding:"), encodingMenu); 84 85 fTabTitle = new BTextControl("tabTitle", B_TRANSLATE("Tab title:"), "", 86 NULL); 87 fTabTitle->SetModificationMessage( 88 new BMessage(MSG_TAB_TITLE_SETTING_CHANGED)); 89 fTabTitle->SetToolTip(BString(B_TRANSLATE( 90 "The pattern specifying the tab titles. The following placeholders\n" 91 "can be used:")) << "\n" << kToolTipSetTabTitlePlaceholders 92 << "\n" << kToolTipCommonTitlePlaceholders); 93 94 fWindowTitle = new BTextControl("windowTitle", B_TRANSLATE("Window title:"), 95 "", NULL); 96 fWindowTitle->SetModificationMessage( 97 new BMessage(MSG_WINDOW_TITLE_SETTING_CHANGED)); 98 fWindowTitle->SetToolTip(BString(B_TRANSLATE( 99 "The pattern specifying the window titles. The following placeholders\n" 100 "can be used:")) << "\n" << kToolTipSetWindowTitlePlaceholders 101 << "\n" << kToolTipCommonTitlePlaceholders); 102 103 BLayoutBuilder::Group<>(this) 104 .SetInsets(5, 5, 5, 5) 105 .AddGrid(5, 5) 106 .Add(fTabTitle->CreateLabelLayoutItem(), 0, 0) 107 .Add(fTabTitle->CreateTextViewLayoutItem(), 1, 0) 108 .Add(fWindowTitle->CreateLabelLayoutItem(), 0, 1) 109 .Add(fWindowTitle->CreateTextViewLayoutItem(), 1, 1) 110 .Add(fWindowSizeField->CreateLabelLayoutItem(), 0, 2) 111 .Add(fWindowSizeField->CreateMenuBarLayoutItem(), 1, 2) 112 .Add(fFontField->CreateLabelLayoutItem(), 0, 3) 113 .Add(fFontField->CreateMenuBarLayoutItem(), 1, 3) 114 .Add(fEncodingField->CreateLabelLayoutItem(), 0, 4) 115 .Add(fEncodingField->CreateMenuBarLayoutItem(), 1, 4) 116 .End() 117 .AddGlue() 118 .Add(fBlinkCursor) 119 .Add(fAllowBold) 120 .Add(fUseOptionAsMetaKey) 121 .Add(fWarnOnExit); 122 123 fTabTitle->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 124 fWindowTitle->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 125 fFontField->SetAlignment(B_ALIGN_RIGHT); 126 fWindowSizeField->SetAlignment(B_ALIGN_RIGHT); 127 fEncodingField->SetAlignment(B_ALIGN_RIGHT); 128 129 Revert(); 130 } 131 132 133 void 134 AppearancePrefView::Revert() 135 { 136 PrefHandler* pref = PrefHandler::Default(); 137 138 fTabTitle->SetText(pref->getString(PREF_TAB_TITLE)); 139 fWindowTitle->SetText(pref->getString(PREF_WINDOW_TITLE)); 140 141 fBlinkCursor->SetValue(pref->getBool(PREF_BLINK_CURSOR)); 142 fAllowBold->SetValue(pref->getBool(PREF_ALLOW_BOLD)); 143 fUseOptionAsMetaKey->SetValue(pref->getBool(PREF_USE_OPTION_AS_META)); 144 fWarnOnExit->SetValue(pref->getBool(PREF_WARN_ON_EXIT)); 145 146 _SetEncoding(pref->getString(PREF_TEXT_ENCODING)); 147 _SetWindowSize(pref->getInt32(PREF_ROWS), pref->getInt32(PREF_COLS)); 148 149 const char* family = pref->getString(PREF_HALF_FONT_FAMILY); 150 const char* style = pref->getString(PREF_HALF_FONT_STYLE); 151 const char* size = pref->getString(PREF_HALF_FONT_SIZE); 152 153 _MarkSelectedFont(family, style, size); 154 } 155 156 157 void 158 AppearancePrefView::AttachedToWindow() 159 { 160 fTabTitle->SetTarget(this); 161 fWindowTitle->SetTarget(this); 162 fBlinkCursor->SetTarget(this); 163 fAllowBold->SetTarget(this); 164 fUseOptionAsMetaKey->SetTarget(this); 165 fWarnOnExit->SetTarget(this); 166 167 fFontField->Menu()->SetTargetForItems(this); 168 for (int32 i = 0; i < fFontField->Menu()->CountItems(); i++) { 169 BMenu* fontSizeMenu = fFontField->Menu()->SubmenuAt(i); 170 if (fontSizeMenu == NULL) 171 continue; 172 173 fontSizeMenu->SetTargetForItems(this); 174 } 175 176 fWindowSizeField->Menu()->SetTargetForItems(this); 177 fEncodingField->Menu()->SetTargetForItems(this); 178 } 179 180 181 void 182 183 AppearancePrefView::MessageReceived(BMessage* msg) 184 { 185 bool modified = false; 186 187 switch (msg->what) { 188 case MSG_HALF_FONT_CHANGED: 189 { 190 const char* family = NULL; 191 const char* style = NULL; 192 const char* size = NULL; 193 if (msg->FindString("font_family", &family) != B_OK 194 || msg->FindString("font_style", &style) != B_OK 195 || msg->FindString("font_size", &size) != B_OK) { 196 break; 197 } 198 199 PrefHandler* pref = PrefHandler::Default(); 200 const char* currentFamily 201 = pref->getString(PREF_HALF_FONT_FAMILY); 202 const char* currentStyle 203 = pref->getString(PREF_HALF_FONT_STYLE); 204 const char* currentSize 205 = pref->getString(PREF_HALF_FONT_SIZE); 206 207 if (currentFamily == NULL || strcmp(currentFamily, family) != 0 208 || currentStyle == NULL || strcmp(currentStyle, style) != 0 209 || currentSize == NULL || strcmp(currentSize, size) != 0) { 210 pref->setString(PREF_HALF_FONT_FAMILY, family); 211 pref->setString(PREF_HALF_FONT_STYLE, style); 212 pref->setString(PREF_HALF_FONT_SIZE, size); 213 _MarkSelectedFont(family, style, size); 214 modified = true; 215 } 216 break; 217 } 218 219 case MSG_COLS_CHANGED: 220 { 221 int rows = msg->FindInt32("rows"); 222 int columns = msg->FindInt32("columns"); 223 _SetWindowSize(rows, columns); 224 PrefHandler* handler = PrefHandler::Default(); 225 if (handler->getInt32(PREF_ROWS) != rows) { 226 PrefHandler::Default()->setInt32(PREF_ROWS, rows); 227 modified = true; 228 } 229 if (handler->getInt32(PREF_COLS) != columns) { 230 PrefHandler::Default()->setInt32(PREF_COLS, columns); 231 modified = true; 232 } 233 234 break; 235 } 236 237 case MSG_BLINK_CURSOR_CHANGED: 238 if (PrefHandler::Default()->getBool(PREF_BLINK_CURSOR) 239 != fBlinkCursor->Value()) { 240 PrefHandler::Default()->setBool(PREF_BLINK_CURSOR, 241 fBlinkCursor->Value()); 242 modified = true; 243 } 244 break; 245 246 case MSG_ALLOW_BOLD_CHANGED: 247 if (PrefHandler::Default()->getBool(PREF_ALLOW_BOLD) 248 != fAllowBold->Value()) { 249 PrefHandler::Default()->setBool(PREF_ALLOW_BOLD, 250 fAllowBold->Value()); 251 modified = true; 252 } 253 break; 254 255 case MSG_USE_OPTION_AS_META_CHANGED: 256 if (PrefHandler::Default()->getBool(PREF_USE_OPTION_AS_META) 257 != fUseOptionAsMetaKey->Value()) { 258 PrefHandler::Default()->setBool(PREF_USE_OPTION_AS_META, 259 fUseOptionAsMetaKey->Value()); 260 modified = true; 261 } 262 break; 263 264 case MSG_WARN_ON_EXIT_CHANGED: 265 if (PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT) 266 != fWarnOnExit->Value()) { 267 PrefHandler::Default()->setBool(PREF_WARN_ON_EXIT, 268 fWarnOnExit->Value()); 269 modified = true; 270 } 271 break; 272 273 case MSG_TAB_TITLE_SETTING_CHANGED: 274 { 275 BString oldValue(PrefHandler::Default()->getString(PREF_TAB_TITLE)); 276 if (oldValue != fTabTitle->Text()) { 277 PrefHandler::Default()->setString(PREF_TAB_TITLE, 278 fTabTitle->Text()); 279 modified = true; 280 } 281 break; 282 } 283 284 case MSG_WINDOW_TITLE_SETTING_CHANGED: 285 { 286 BString oldValue(PrefHandler::Default()->getString( 287 PREF_WINDOW_TITLE)); 288 if (oldValue != fWindowTitle->Text()) { 289 PrefHandler::Default()->setString(PREF_WINDOW_TITLE, 290 fWindowTitle->Text()); 291 modified = true; 292 } 293 break; 294 } 295 296 default: 297 BView::MessageReceived(msg); 298 return; 299 } 300 301 if (modified) { 302 fTerminalMessenger.SendMessage(msg); 303 304 BMessenger messenger(this); 305 messenger.SendMessage(MSG_PREF_MODIFIED); 306 } 307 } 308 309 310 void 311 AppearancePrefView::_SetEncoding(const char* name) 312 { 313 const BPrivate::BCharacterSet* charset 314 = BPrivate::BCharacterSetRoster::FindCharacterSetByName(name); 315 if (charset == NULL) 316 return; 317 int code = charset->GetConversionID(); 318 for (int32 i = 0; i < fEncodingField->Menu()->CountItems(); i++) { 319 BMenuItem* item = fEncodingField->Menu()->ItemAt(i); 320 BMessage* msg = item->Message(); 321 if (msg->FindInt32("op") == code) { 322 item->SetMarked(true); 323 break; 324 } 325 } 326 } 327 328 329 void 330 AppearancePrefView::_SetWindowSize(int rows, int cols) 331 { 332 for (int32 i = 0; i < fWindowSizeField->Menu()->CountItems(); i++) { 333 BMenuItem* item = fWindowSizeField->Menu()->ItemAt(i); 334 BMessage* msg = item->Message(); 335 if (msg->FindInt32("rows") == rows && msg->FindInt32("columns") == cols) { 336 item->SetMarked(true); 337 break; 338 } 339 } 340 } 341 342 343 /*static*/ BMenu* 344 AppearancePrefView::_MakeFontMenu(uint32 command, 345 const char* defaultFamily, const char* defaultStyle) 346 { 347 BPopUpMenu* menu = new BPopUpMenu(""); 348 int32 numFamilies = count_font_families(); 349 for (int32 i = 0; i < numFamilies; i++) { 350 font_family family; 351 uint32 flags; 352 if (get_font_family(i, &family, &flags) == B_OK) { 353 BFont font; 354 font_style style; 355 int32 numStyles = count_font_styles(family); 356 for (int32 j = 0; j < numStyles; j++) { 357 if (get_font_style(family, j, &style) == B_OK) { 358 font.SetFamilyAndStyle(family, style); 359 if (IsFontUsable(font)) { 360 BMessage* message = new BMessage(command); 361 const char* size 362 = PrefHandler::Default()->getString(PREF_HALF_FONT_SIZE); 363 message->AddString("font_family", family); 364 message->AddString("font_style", style); 365 message->AddString("font_size", size); 366 char fontMenuLabel[134]; 367 snprintf(fontMenuLabel, sizeof(fontMenuLabel), 368 "%s - %s", family, style); 369 BMenu* fontSizeMenu = _MakeFontSizeMenu(fontMenuLabel, 370 MSG_HALF_FONT_CHANGED, family, style, size); 371 BMenuItem* item = new BMenuItem(fontSizeMenu, message); 372 menu->AddItem(item); 373 if (strcmp(defaultFamily, family) == 0 374 && strcmp(defaultStyle, style) == 0) 375 item->SetMarked(true); 376 } 377 } 378 } 379 } 380 } 381 382 if (menu->FindMarked() == NULL) 383 menu->ItemAt(0)->SetMarked(true); 384 385 return menu; 386 } 387 388 389 /*static*/ BMenu* 390 AppearancePrefView::_MakeFontSizeMenu(const char* label, uint32 command, 391 const char* family, const char* style, const char* size) 392 { 393 BMenu* menu = new BMenu(label); 394 menu->SetRadioMode(true); 395 menu->SetLabelFromMarked(false); 396 397 int32 sizes[] = { 398 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 28, 32, 36, 0 399 }; 400 401 bool found = false; 402 403 for (uint32 i = 0; sizes[i]; i++) { 404 BString fontSize; 405 fontSize << sizes[i]; 406 BMessage* message = new BMessage(command); 407 message->AddString("font_family", family); 408 message->AddString("font_style", style); 409 message->AddString("font_size", fontSize.String()); 410 BMenuItem* item = new BMenuItem(fontSize.String(), message); 411 menu->AddItem(item); 412 if (sizes[i] == atoi(size)) { 413 item->SetMarked(true); 414 found = true; 415 } 416 } 417 418 if (!found) { 419 for (uint32 i = 0; sizes[i]; i++) { 420 if (sizes[i] > atoi(size)) { 421 BMessage* message = new BMessage(command); 422 message->AddString("font_family", family); 423 message->AddString("font_style", style); 424 message->AddString("font_size", size); 425 BMenuItem* item = new BMenuItem(size, message); 426 item->SetMarked(true); 427 menu->AddItem(item, i); 428 break; 429 } 430 } 431 } 432 433 return menu; 434 } 435 436 437 /*static*/ BPopUpMenu* 438 AppearancePrefView::_MakeMenu(uint32 msg, const char** items, 439 const char* defaultItemName) 440 { 441 BPopUpMenu* menu = new BPopUpMenu(""); 442 443 while (*items) { 444 if (strcmp((*items), "") == 0) 445 menu->AddSeparatorItem(); 446 else { 447 BMessage* message = new BMessage(msg); 448 message->AddString("label", *items); 449 BMenuItem* item = new BMenuItem(B_TRANSLATE(*items), message); 450 menu->AddItem(item); 451 if (strcmp(*items, defaultItemName) == 0) 452 item->SetMarked(true); 453 } 454 455 items++; 456 } 457 458 return menu; 459 } 460 461 462 void 463 AppearancePrefView::_MarkSelectedFont(const char* family, const char* style, 464 const char* size) 465 { 466 char fontMenuLabel[134]; 467 snprintf(fontMenuLabel, sizeof(fontMenuLabel), "%s - %s", family, style); 468 469 // mark the selected font 470 BMenuItem* selectedFont = fFontField->Menu()->FindItem(fontMenuLabel); 471 if (selectedFont != NULL) 472 selectedFont->SetMarked(true); 473 474 // mark the selected font size on all font menus 475 for (int32 i = 0; i < fFontField->Menu()->CountItems(); i++) { 476 BMenu* fontSizeMenu = fFontField->Menu()->SubmenuAt(i); 477 if (fontSizeMenu == NULL) 478 continue; 479 480 BMenuItem* item = fontSizeMenu->FindItem(size); 481 if (item != NULL) 482 item->SetMarked(true); 483 } 484 } 485