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