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