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