1 /*
2 * Copyright 2001-2010, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Mark Hogben
7 * DarkWyrm <bpmagic@columbus.rr.com>
8 * Axel Dörfler, axeld@pinc-software.de
9 * Philippe Saint-Pierre, stpere@gmail.com
10 * Stephan Aßmus <superstippi@gmx.de>
11 */
12
13 #include "FontSelectionView.h"
14
15 #include "private/interface/FontPrivate.h"
16
17 #include <Box.h>
18 #include <Catalog.h>
19 #include <ControlLook.h>
20 #include <GroupLayoutBuilder.h>
21 #include <LayoutItem.h>
22 #include <Locale.h>
23 #include <Looper.h>
24 #include <MenuField.h>
25 #include <MenuItem.h>
26 #include <PopUpMenu.h>
27 #include <String.h>
28 #include <TextView.h>
29
30 #include <stdio.h>
31
32 #undef B_TRANSLATION_CONTEXT
33 #define B_TRANSLATION_CONTEXT "Font Selection view"
34
35
36 static const float kMinSize = 8.0;
37 static const float kMaxSize = 18.0;
38
39 static const char* kPreviewText = B_TRANSLATE_COMMENT(
40 "The quick brown fox jumps over the lazy dog.",
41 "Don't translate this literally ! Use a phrase showing all "
42 "chars from A to Z.");
43
44 static const int32 kMsgSetFamily = 'fmly';
45 static const int32 kMsgSetStyle = 'styl';
46 static const int32 kMsgSetSize = 'size';
47
48
49 // #pragma mark -
50
51
FontSelectionView(const char * name,const char * label,bool separateStyles,const BFont * currentFont)52 FontSelectionView::FontSelectionView(const char* name, const char* label,
53 bool separateStyles, const BFont* currentFont)
54 :
55 BHandler(name),
56 fMessage(NULL),
57 fTarget(NULL)
58 {
59 if (currentFont == NULL)
60 fCurrentFont = _DefaultFont();
61 else
62 fCurrentFont = *currentFont;
63
64 fSavedFont = fCurrentFont;
65
66 fSizesMenu = new BPopUpMenu("size menu");
67 fFontsMenu = new BPopUpMenu("font menu");
68
69 // font menu
70 fFontsMenuField = new BMenuField("fonts", label, fFontsMenu, B_WILL_DRAW);
71 fFontsMenuField->SetAlignment(B_ALIGN_RIGHT);
72
73 // styles menu, if desired
74 if (separateStyles) {
75 fStylesMenu = new BPopUpMenu("styles menu");
76 fStylesMenuField = new BMenuField("styles", B_TRANSLATE("Style:"),
77 fStylesMenu, B_WILL_DRAW);
78 } else {
79 fStylesMenu = NULL;
80 fStylesMenuField = NULL;
81 }
82
83 // size menu
84 fSizesMenuField = new BMenuField("size", B_TRANSLATE("Size:"), fSizesMenu,
85 B_WILL_DRAW);
86 fSizesMenuField->SetAlignment(B_ALIGN_RIGHT);
87
88 // preview
89 // A string view would be enough if only it handled word-wrap.
90 fPreviewTextView = new BTextView("preview text");
91 fPreviewTextView->SetFontAndColor(&fCurrentFont);
92 fPreviewTextView->SetText(kPreviewText);
93 fPreviewTextView->MakeResizable(false);
94 fPreviewTextView->SetWordWrap(true);
95 fPreviewTextView->MakeEditable(false);
96 fPreviewTextView->MakeSelectable(false);
97 fPreviewTextView->SetInsets(0, 0, 0, 0);
98 fPreviewTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
99 fPreviewTextView->SetHighUIColor(B_PANEL_TEXT_COLOR);
100
101 // determine initial line count using fCurrentFont
102 fPreviewTextWidth = be_control_look->DefaultLabelSpacing() * 58.0f;
103 float lineCount = ceilf(fCurrentFont.StringWidth(kPreviewText) / fPreviewTextWidth);
104 fPreviewTextView->SetExplicitSize(
105 BSize(fPreviewTextWidth, fPreviewTextView->LineHeight(0) * lineCount));
106
107 // box around preview
108 fPreviewBox = new BBox("preview box", B_WILL_DRAW | B_FRAME_EVENTS);
109 fPreviewBox->AddChild(BGroupLayoutBuilder(B_VERTICAL)
110 .AddGroup(B_HORIZONTAL, 0)
111 .Add(fPreviewTextView)
112 .AddGlue()
113 .End()
114 .SetInsets(B_USE_SMALL_SPACING, B_USE_SMALL_SPACING,
115 B_USE_SMALL_SPACING, B_USE_SMALL_SPACING)
116 .TopView()
117 );
118 _UpdateFontPreview();
119 }
120
121
~FontSelectionView()122 FontSelectionView::~FontSelectionView()
123 {
124 // Some controls may not have been attached...
125 if (!fPreviewTextView->Window())
126 delete fPreviewTextView;
127 if (!fSizesMenuField->Window())
128 delete fSizesMenuField;
129 if (fStylesMenuField && !fStylesMenuField->Window())
130 delete fStylesMenuField;
131 if (!fFontsMenuField->Window())
132 delete fFontsMenuField;
133
134 delete fMessage;
135 }
136
137
138 void
AttachedToLooper()139 FontSelectionView::AttachedToLooper()
140 {
141 _BuildSizesMenu();
142 UpdateFontsMenu();
143 }
144
145
146 void
MessageReceived(BMessage * message)147 FontSelectionView::MessageReceived(BMessage* message)
148 {
149 switch (message->what) {
150 case B_COLORS_UPDATED:
151 {
152 if (message->HasColor(ui_color_name(B_PANEL_TEXT_COLOR))) {
153 rgb_color textColor;
154 if (message->FindColor(ui_color_name(B_PANEL_TEXT_COLOR), &textColor) == B_OK)
155 fPreviewTextView->SetFontAndColor(&fCurrentFont, B_FONT_ALL, &textColor);
156 }
157 break;
158 }
159
160 case kMsgSetSize:
161 {
162 int32 size;
163 if (message->FindInt32("size", &size) != B_OK
164 || size == fCurrentFont.Size())
165 break;
166
167 fCurrentFont.SetSize(size);
168 _UpdateFontPreview();
169 _Invoke();
170 break;
171 }
172
173 case kMsgSetFamily:
174 {
175 const char* family;
176 if (message->FindString("family", &family) != B_OK)
177 break;
178
179 font_style style;
180 fCurrentFont.GetFamilyAndStyle(NULL, &style);
181
182 BMenuItem* familyItem = fFontsMenu->FindItem(family);
183 if (familyItem != NULL) {
184 _SelectCurrentFont(false);
185
186 BMenuItem* styleItem;
187 if (fStylesMenuField != NULL)
188 styleItem = fStylesMenuField->Menu()->FindMarked();
189 else {
190 styleItem = familyItem->Submenu()->FindItem(style);
191 if (styleItem == NULL)
192 styleItem = familyItem->Submenu()->ItemAt(0);
193 }
194
195 if (styleItem != NULL) {
196 styleItem->SetMarked(true);
197 fCurrentFont.SetFamilyAndStyle(family, styleItem->Label());
198 _UpdateFontPreview();
199 }
200 if (fStylesMenuField != NULL)
201 _AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu());
202 }
203
204 _Invoke();
205 break;
206 }
207
208 case kMsgSetStyle:
209 {
210 const char* family;
211 const char* style;
212 if (message->FindString("family", &family) != B_OK
213 || message->FindString("style", &style) != B_OK)
214 break;
215
216 BMenuItem *familyItem = fFontsMenu->FindItem(family);
217 if (!familyItem)
218 break;
219
220 _SelectCurrentFont(false);
221 familyItem->SetMarked(true);
222
223 fCurrentFont.SetFamilyAndStyle(family, style);
224 _UpdateFontPreview();
225 _Invoke();
226 break;
227 }
228
229 default:
230 BHandler::MessageReceived(message);
231 }
232 }
233
234
235 void
SetMessage(BMessage * message)236 FontSelectionView::SetMessage(BMessage* message)
237 {
238 delete fMessage;
239 fMessage = message;
240 }
241
242
243 void
SetTarget(BHandler * target)244 FontSelectionView::SetTarget(BHandler* target)
245 {
246 fTarget = target;
247 }
248
249
250 // #pragma mark -
251
252
253 void
SetFont(const BFont & font,float size)254 FontSelectionView::SetFont(const BFont& font, float size)
255 {
256 BFont resizedFont(font);
257 resizedFont.SetSize(size);
258 SetFont(resizedFont);
259 }
260
261
262 void
SetFont(const BFont & font)263 FontSelectionView::SetFont(const BFont& font)
264 {
265 if (font == fCurrentFont && font == fSavedFont)
266 return;
267
268 _SelectCurrentFont(false);
269 fSavedFont = fCurrentFont = font;
270 _UpdateFontPreview();
271
272 _SelectCurrentFont(true);
273 _SelectCurrentSize(true);
274 }
275
276
277 void
SetSize(float size)278 FontSelectionView::SetSize(float size)
279 {
280 SetFont(fCurrentFont, size);
281 }
282
283
284 const BFont&
Font() const285 FontSelectionView::Font() const
286 {
287 return fCurrentFont;
288 }
289
290
291 void
SetDefaults()292 FontSelectionView::SetDefaults()
293 {
294 BFont defaultFont = _DefaultFont();
295 if (defaultFont == fCurrentFont)
296 return;
297
298 _SelectCurrentFont(false);
299
300 fCurrentFont = defaultFont;
301 _UpdateFontPreview();
302
303 _SelectCurrentFont(true);
304 _SelectCurrentSize(true);
305 }
306
307
308 void
Revert()309 FontSelectionView::Revert()
310 {
311 if (!IsRevertable())
312 return;
313
314 _SelectCurrentFont(false);
315
316 fCurrentFont = fSavedFont;
317 _UpdateFontPreview();
318
319 _SelectCurrentFont(true);
320 _SelectCurrentSize(true);
321 }
322
323
324 bool
IsDefaultable()325 FontSelectionView::IsDefaultable()
326 {
327 return fCurrentFont != _DefaultFont();
328 }
329
330
331 bool
IsRevertable()332 FontSelectionView::IsRevertable()
333 {
334 return fCurrentFont != fSavedFont;
335 }
336
337
338 void
UpdateFontsMenu()339 FontSelectionView::UpdateFontsMenu()
340 {
341 int32 numFamilies = count_font_families();
342
343 fFontsMenu->RemoveItems(0, fFontsMenu->CountItems(), true);
344
345 BFont font = fCurrentFont;
346
347 font_family currentFamily;
348 font_style currentStyle;
349 font.GetFamilyAndStyle(¤tFamily, ¤tStyle);
350
351 for (int32 i = 0; i < numFamilies; i++) {
352 font_family family;
353 uint32 flags;
354 if (get_font_family(i, &family, &flags) != B_OK)
355 continue;
356
357 // if we're setting the fixed font, we only want to show fixed and
358 // full-and-half-fixed fonts
359 if (strcmp(Name(), "fixed") == 0
360 && (flags & (B_IS_FIXED | B_PRIVATE_FONT_IS_FULL_AND_HALF_FIXED)) == 0) {
361 continue;
362 }
363
364 font.SetFamilyAndFace(family, B_REGULAR_FACE);
365
366 BMessage* message = new BMessage(kMsgSetFamily);
367 message->AddString("family", family);
368 message->AddString("name", Name());
369
370 BMenuItem* familyItem;
371 if (fStylesMenuField != NULL) {
372 familyItem = new BMenuItem(family, message);
373 } else {
374 // Each family item has a submenu with all styles for that font.
375 BMenu* stylesMenu = new BMenu(family);
376 _AddStylesToMenu(font, stylesMenu);
377 familyItem = new BMenuItem(stylesMenu, message);
378 }
379
380 familyItem->SetMarked(strcmp(family, currentFamily) == 0);
381 fFontsMenu->AddItem(familyItem);
382 familyItem->SetTarget(this);
383 }
384
385 // Separate styles menu for only the current font.
386 if (fStylesMenuField != NULL)
387 _AddStylesToMenu(fCurrentFont, fStylesMenuField->Menu());
388 }
389
390
391 // #pragma mark - private
392
393
394 BLayoutItem*
CreateSizesLabelLayoutItem()395 FontSelectionView::CreateSizesLabelLayoutItem()
396 {
397 return fSizesMenuField->CreateLabelLayoutItem();
398 }
399
400
401 BLayoutItem*
CreateSizesMenuBarLayoutItem()402 FontSelectionView::CreateSizesMenuBarLayoutItem()
403 {
404 return fSizesMenuField->CreateMenuBarLayoutItem();
405 }
406
407
408 BLayoutItem*
CreateFontsLabelLayoutItem()409 FontSelectionView::CreateFontsLabelLayoutItem()
410 {
411 return fFontsMenuField->CreateLabelLayoutItem();
412 }
413
414
415 BLayoutItem*
CreateFontsMenuBarLayoutItem()416 FontSelectionView::CreateFontsMenuBarLayoutItem()
417 {
418 return fFontsMenuField->CreateMenuBarLayoutItem();
419 }
420
421
422 BLayoutItem*
CreateStylesLabelLayoutItem()423 FontSelectionView::CreateStylesLabelLayoutItem()
424 {
425 if (fStylesMenuField)
426 return fStylesMenuField->CreateLabelLayoutItem();
427 return NULL;
428 }
429
430
431 BLayoutItem*
CreateStylesMenuBarLayoutItem()432 FontSelectionView::CreateStylesMenuBarLayoutItem()
433 {
434 if (fStylesMenuField)
435 return fStylesMenuField->CreateMenuBarLayoutItem();
436 return NULL;
437 }
438
439
440 BView*
PreviewBox() const441 FontSelectionView::PreviewBox() const
442 {
443 return fPreviewBox;
444 }
445
446
447 // #pragma mark - private
448
449
450 void
_Invoke()451 FontSelectionView::_Invoke()
452 {
453 if (fTarget != NULL && fTarget->Looper() != NULL && fMessage != NULL) {
454 BMessage message(*fMessage);
455 fTarget->Looper()->PostMessage(&message, fTarget);
456 }
457 }
458
459
460 BFont
_DefaultFont() const461 FontSelectionView::_DefaultFont() const
462 {
463 if (strcmp(Name(), "bold") == 0)
464 return *be_bold_font;
465 if (strcmp(Name(), "fixed") == 0)
466 return *be_fixed_font;
467 else
468 return *be_plain_font;
469 }
470
471
472 void
_SelectCurrentFont(bool select)473 FontSelectionView::_SelectCurrentFont(bool select)
474 {
475 font_family family;
476 font_style style;
477 fCurrentFont.GetFamilyAndStyle(&family, &style);
478
479 BMenuItem *item = fFontsMenu->FindItem(family);
480 if (item != NULL) {
481 item->SetMarked(select);
482
483 if (item->Submenu() != NULL) {
484 item = item->Submenu()->FindItem(style);
485 if (item != NULL)
486 item->SetMarked(select);
487 }
488 }
489 }
490
491
492 void
_SelectCurrentSize(bool select)493 FontSelectionView::_SelectCurrentSize(bool select)
494 {
495 char label[16];
496 snprintf(label, sizeof(label), "%" B_PRId32, (int32)fCurrentFont.Size());
497
498 BMenuItem* item = fSizesMenu->FindItem(label);
499 if (item != NULL)
500 item->SetMarked(select);
501 }
502
503
504 void
_UpdateFontPreview()505 FontSelectionView::_UpdateFontPreview()
506 {
507 fPreviewTextView->SetFontAndColor(&fCurrentFont);
508 fPreviewTextView->SetExplicitSize(
509 BSize(fPreviewTextWidth, fPreviewTextView->LineHeight(0) * fPreviewTextView->CountLines()));
510 }
511
512
513 void
_BuildSizesMenu()514 FontSelectionView::_BuildSizesMenu()
515 {
516 const int32 sizes[] = {7, 8, 9, 10, 11, 12, 13, 14, 18, 21, 24, 0};
517
518 // build size menu
519 for (int32 i = 0; sizes[i]; i++) {
520 int32 size = sizes[i];
521 if (size < kMinSize || size > kMaxSize)
522 continue;
523
524 char label[32];
525 snprintf(label, sizeof(label), "%" B_PRId32, size);
526
527 BMessage* message = new BMessage(kMsgSetSize);
528 message->AddInt32("size", size);
529 message->AddString("name", Name());
530
531 BMenuItem* item = new BMenuItem(label, message);
532 if (size == fCurrentFont.Size())
533 item->SetMarked(true);
534
535 fSizesMenu->AddItem(item);
536 item->SetTarget(this);
537 }
538 }
539
540
541 void
_AddStylesToMenu(const BFont & font,BMenu * stylesMenu) const542 FontSelectionView::_AddStylesToMenu(const BFont& font, BMenu* stylesMenu) const
543 {
544 stylesMenu->RemoveItems(0, stylesMenu->CountItems(), true);
545 stylesMenu->SetRadioMode(true);
546
547 font_family family;
548 font_style style;
549 font.GetFamilyAndStyle(&family, &style);
550 BString currentStyle(style);
551
552 int32 numStyles = count_font_styles(family);
553
554 for (int32 j = 0; j < numStyles; j++) {
555 if (get_font_style(family, j, &style) != B_OK)
556 continue;
557
558 BMessage* message = new BMessage(kMsgSetStyle);
559 message->AddString("family", (char*)family);
560 message->AddString("style", (char*)style);
561
562 BMenuItem* item = new BMenuItem(style, message);
563 item->SetMarked(currentStyle == style);
564
565 stylesMenu->AddItem(item);
566 item->SetTarget(this);
567 }
568 }
569
570