xref: /haiku/src/apps/webpositive/support/FontSelectionView.cpp (revision f657ee8e466a7a275c1b21a3d3011282727271ed)
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(&currentFamily, &currentStyle);
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