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 <ColorControl.h> 17 #include <GridLayoutBuilder.h> 18 #include <GroupLayoutBuilder.h> 19 #include <LayoutBuilder.h> 20 #include <Locale.h> 21 #include <Menu.h> 22 #include <MenuField.h> 23 #include <MenuItem.h> 24 #include <PopUpMenu.h> 25 #include <TextControl.h> 26 #include <View.h> 27 28 #include "PrefHandler.h" 29 #include "TermConst.h" 30 31 32 #undef TR_CONTEXT 33 #define TR_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 #if 0 83 "", 84 PREF_IM_FORE_COLOR, 85 PREF_IM_BACK_COLOR, 86 PREF_IM_SELECT_COLOR, 87 "", 88 PREF_ANSI_BLACK_COLOR, 89 PREF_ANSI_RED_COLOR, 90 PREF_ANSI_GREEN_COLOR, 91 PREF_ANSI_YELLOW_COLOR, 92 PREF_ANSI_BLUE_COLOR, 93 PREF_ANSI_MAGENTA_COLOR, 94 PREF_ANSI_CYAN_COLOR, 95 PREF_ANSI_WHITE_COLOR, 96 #endif 97 NULL 98 }; 99 100 SetLayout(new BGroupLayout(B_HORIZONTAL)); 101 102 BMenu* fontMenu = _MakeFontMenu(MSG_HALF_FONT_CHANGED, 103 PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY), 104 PrefHandler::Default()->getString(PREF_HALF_FONT_STYLE)); 105 106 BMenu* sizeMenu = _MakeSizeMenu(MSG_HALF_SIZE_CHANGED, 107 PrefHandler::Default()->getInt32(PREF_HALF_FONT_SIZE)); 108 109 fFont = new BMenuField(B_TRANSLATE("Font:"), fontMenu); 110 fFontSize = new BMenuField(B_TRANSLATE("Size:"), sizeMenu); 111 fColorField = new BMenuField(B_TRANSLATE("Color:"), 112 _MakeMenu(MSG_COLOR_FIELD_CHANGED, kColorTable, 113 kColorTable[0])); 114 115 BView* layoutView = BLayoutBuilder::Group<>() 116 .SetInsets(5, 5, 5, 5) 117 .AddGroup(B_VERTICAL, 5) 118 .Add(BGridLayoutBuilder(5, 5) 119 .Add(fFont->CreateLabelLayoutItem(), 0, 0) 120 .Add(fFont->CreateMenuBarLayoutItem(), 1, 0) 121 .Add(fFontSize->CreateLabelLayoutItem(), 0, 1) 122 .Add(fFontSize->CreateMenuBarLayoutItem(), 1, 1) 123 .Add(fColorField->CreateLabelLayoutItem(), 0, 2) 124 .Add(fColorField->CreateMenuBarLayoutItem(), 1, 2) 125 ) 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 .End() 131 .End(); 132 133 AddChild(layoutView); 134 135 fFont->SetAlignment(B_ALIGN_RIGHT); 136 fFontSize->SetAlignment(B_ALIGN_RIGHT); 137 fColorField->SetAlignment(B_ALIGN_RIGHT); 138 139 fColorControl->SetValue(PrefHandler::Default()->getRGB(PREF_TEXT_FORE_COLOR)); 140 141 BTextControl* redInput = (BTextControl*)fColorControl->ChildAt(0); 142 BTextControl* greenInput = (BTextControl*)fColorControl->ChildAt(1); 143 BTextControl* blueInput = (BTextControl*)fColorControl->ChildAt(2); 144 145 redInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 146 greenInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 147 blueInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 148 } 149 150 151 void 152 AppearancePrefView::GetPreferredSize(float* _width, float* _height) 153 { 154 if (_width) 155 *_width = Bounds().Width(); 156 157 if (*_height) 158 *_height = fColorControl->Frame().bottom; 159 } 160 161 162 void 163 AppearancePrefView::Revert() 164 { 165 fColorField->Menu()->ItemAt(0)->SetMarked(true); 166 fColorControl->SetValue(PrefHandler::Default()-> 167 getRGB(PREF_TEXT_FORE_COLOR)); 168 169 fFont->Menu()->FindItem(PrefHandler::Default()->getString( 170 PREF_HALF_FONT_FAMILY))->SetMarked(true); 171 fFontSize->Menu()->FindItem(PrefHandler::Default()->getString( 172 PREF_HALF_FONT_FAMILY))->SetMarked(true); 173 } 174 175 176 void 177 AppearancePrefView::AttachedToWindow() 178 { 179 fFontSize->Menu()->SetTargetForItems(this); 180 fFont->Menu()->SetTargetForItems(this); 181 182 fColorControl->SetTarget(this); 183 fColorField->Menu()->SetTargetForItems(this); 184 } 185 186 187 void 188 AppearancePrefView::MessageReceived(BMessage* msg) 189 { 190 bool modified = false; 191 192 switch (msg->what) { 193 case MSG_HALF_FONT_CHANGED: 194 { 195 const char* family = NULL; 196 const char* style = NULL; 197 msg->FindString("font_family", &family); 198 msg->FindString("font_style", &style); 199 200 PrefHandler* pref = PrefHandler::Default(); 201 const char* currentFamily 202 = pref->getString(PREF_HALF_FONT_FAMILY); 203 const char* currentStyle 204 = pref->getString(PREF_HALF_FONT_STYLE); 205 if (currentFamily == NULL || strcmp(currentFamily, family) 206 || currentStyle == NULL || strcmp(currentStyle, style)) { 207 pref->setString(PREF_HALF_FONT_FAMILY, family); 208 pref->setString(PREF_HALF_FONT_STYLE, style); 209 modified = true; 210 } 211 break; 212 } 213 case MSG_HALF_SIZE_CHANGED: 214 if (strcmp(PrefHandler::Default()->getString(PREF_HALF_FONT_SIZE), 215 fFontSize->Menu()->FindMarked()->Label())) { 216 217 PrefHandler::Default()->setString(PREF_HALF_FONT_SIZE, 218 fFontSize->Menu()->FindMarked()->Label()); 219 modified = true; 220 } 221 break; 222 223 case MSG_COLOR_CHANGED: { 224 rgb_color oldColor = PrefHandler::Default()->getRGB( 225 fColorField->Menu()->FindMarked()->Label()); 226 if (oldColor != fColorControl->ValueAsColor()) { 227 PrefHandler::Default()->setRGB( 228 fColorField->Menu()->FindMarked()->Label(), 229 fColorControl->ValueAsColor()); 230 modified = true; 231 } 232 } 233 break; 234 235 case MSG_COLOR_FIELD_CHANGED: 236 fColorControl->SetValue(PrefHandler::Default()->getRGB( 237 fColorField->Menu()->FindMarked()->Label())); 238 break; 239 240 default: 241 BView::MessageReceived(msg); 242 return; 243 } 244 245 if (modified) { 246 fTerminalMessenger.SendMessage(msg); 247 248 BMessenger messenger(this); 249 messenger.SendMessage(MSG_PREF_MODIFIED); 250 } 251 } 252 253 254 /*static*/ BMenu* 255 AppearancePrefView::_MakeFontMenu(uint32 command, 256 const char* defaultFamily, const char* defaultStyle) 257 { 258 BPopUpMenu* menu = new BPopUpMenu(""); 259 int32 numFamilies = count_font_families(); 260 uint32 flags; 261 262 for (int32 i = 0; i < numFamilies; i++) { 263 font_family family; 264 if (get_font_family(i, &family, &flags) == B_OK) { 265 BFont font; 266 font_style style; 267 int32 numStyles = count_font_styles(family); 268 for (int32 j = 0; j < numStyles; j++) { 269 if (get_font_style(family, j, &style) == B_OK) { 270 font.SetFamilyAndStyle(family, style); 271 if (IsFontUsable(font)) { 272 BMessage* message = new BMessage(command); 273 message->AddString("font_family", family); 274 message->AddString("font_style", style); 275 char itemLabel[134]; 276 snprintf(itemLabel, sizeof(itemLabel), 277 "%s - %s", family, style); 278 BMenuItem* item = new BMenuItem(itemLabel, 279 message); 280 menu->AddItem(item); 281 if (!strcmp(defaultFamily, family) 282 && !strcmp(defaultStyle, style)) 283 item->SetMarked(true); 284 } 285 } 286 } 287 } 288 } 289 290 if (menu->FindMarked() == NULL) 291 menu->ItemAt(0)->SetMarked(true); 292 293 return menu; 294 } 295 296 297 /*static*/ BMenu* 298 AppearancePrefView::_MakeSizeMenu(uint32 command, uint8 defaultSize) 299 { 300 BPopUpMenu* menu = new BPopUpMenu("size"); 301 int32 sizes[] = {9, 10, 11, 12, 14, 16, 18, 0}; 302 303 bool found = false; 304 305 for (uint32 i = 0; sizes[i]; i++) { 306 BString string; 307 string << sizes[i]; 308 309 BMenuItem* item = new BMenuItem(string.String(), new BMessage(command)); 310 menu->AddItem(item); 311 312 if (sizes[i] == defaultSize) { 313 item->SetMarked(true); 314 found = true; 315 } 316 } 317 if (!found) { 318 for (uint32 i = 0; sizes[i]; i++) { 319 if (sizes[i] > defaultSize) { 320 BString string; 321 string << defaultSize; 322 BMenuItem* item = new BMenuItem(string.String(), new BMessage(command)); 323 item->SetMarked(true); 324 menu->AddItem(item, i); 325 break; 326 } 327 } 328 } 329 330 return menu; 331 } 332 333 334 /*static*/ BPopUpMenu* 335 AppearancePrefView::_MakeMenu(uint32 msg, const char** items, 336 const char* defaultItemName) 337 { 338 BPopUpMenu* menu = new BPopUpMenu(""); 339 340 int32 i = 0; 341 while (*items) { 342 if (!strcmp(*items, "")) 343 menu->AddSeparatorItem(); 344 else 345 menu->AddItem(new BMenuItem(*items, new BMessage(msg))); 346 if (!strcmp(*items, defaultItemName)) 347 menu->ItemAt(i)->SetMarked(true); 348 349 items++; 350 i++; 351 } 352 return menu; 353 } 354