xref: /haiku/src/apps/terminal/AppearPrefView.cpp (revision de18d919e7c940b402a6e213f7deb21e2f187e22)
1 /*
2  * Copyright 2001-2010, Haiku, Inc.
3  * Copyright 2003-2004 Kian Duffy, myob@users.sourceforge.net
4  * Parts Copyright 1998-1999 Kazuho Okui and Takashi Murai.
5  * All rights reserved. Distributed under the terms of the MIT license.
6  */
7 
8 
9 #include "AppearPrefView.h"
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 
14 #include <Button.h>
15 #include <Catalog.h>
16 #include <CheckBox.h>
17 #include <ColorControl.h>
18 #include <LayoutBuilder.h>
19 #include <Locale.h>
20 #include <Menu.h>
21 #include <MenuField.h>
22 #include <MenuItem.h>
23 #include <PopUpMenu.h>
24 #include <TextControl.h>
25 #include <View.h>
26 
27 #include "Colors.h"
28 #include "PrefHandler.h"
29 #include "TermConst.h"
30 
31 
32 #undef B_TRANSLATE_CONTEXT
33 #define B_TRANSLATE_CONTEXT "Terminal AppearancePrefView"
34 
35 
36 static bool
37 IsFontUsable(const BFont& font)
38 {
39 	// TODO: If BFont::IsFullAndHalfFixed() was implemented, we could
40 	// use that. But I don't think it's easily implementable using
41 	// Freetype.
42 
43 	if (font.IsFixed())
44 		return true;
45 
46 	// manually check if all applicable chars are the same width
47 	char buffer[2] = { ' ', 0 };
48 	int firstWidth = (int)ceilf(font.StringWidth(buffer));
49 
50 	// TODO: Workaround for broken fonts/font_subsystem
51 	if (firstWidth <= 0)
52 		return false;
53 
54 	for (int c = ' ' + 1; c <= 0x7e; c++) {
55 		buffer[0] = c;
56 		int width = (int)ceilf(font.StringWidth(buffer));
57 
58 		if (width != firstWidth)
59 			return false;
60 	}
61 
62 	return true;
63 }
64 
65 
66 // #pragma mark -
67 
68 
69 AppearancePrefView::AppearancePrefView(const char* name,
70 		const BMessenger& messenger)
71 	:
72 	BGroupView(name, B_VERTICAL, 5),
73 	fTerminalMessenger(messenger)
74 {
75 	const char* kColorTable[] = {
76 		PREF_TEXT_FORE_COLOR,
77 		PREF_TEXT_BACK_COLOR,
78 		PREF_SELECT_FORE_COLOR,
79 		PREF_SELECT_BACK_COLOR,
80 		NULL
81 	};
82 
83 	fWarnOnExit = new BCheckBox(
84 		B_TRANSLATE("Confirm exit if active programs exist"),
85 			new BMessage(MSG_WARN_ON_EXIT_CHANGED));
86 
87 	BMenu* fontMenu = _MakeFontMenu(MSG_HALF_FONT_CHANGED,
88 		PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY),
89 		PrefHandler::Default()->getString(PREF_HALF_FONT_STYLE));
90 
91 	BMenu* sizeMenu = _MakeSizeMenu(MSG_HALF_SIZE_CHANGED,
92 		PrefHandler::Default()->getInt32(PREF_HALF_FONT_SIZE));
93 
94 	fFont = new BMenuField(B_TRANSLATE("Font:"), fontMenu);
95 	fFontSize = new BMenuField(B_TRANSLATE("Size:"), sizeMenu);
96 
97 	BPopUpMenu* schemasPopUp =_MakeColorSchemaMenu(MSG_COLOR_SCHEMA_CHANGED,
98 		gPredefinedSchemas, gPredefinedSchemas[0]);
99 	fColorSchemaField = new BMenuField(B_TRANSLATE("Color schema:"),
100 		schemasPopUp);
101 
102 	BPopUpMenu* colorsPopUp =_MakeMenu(MSG_COLOR_FIELD_CHANGED, kColorTable,
103 		kColorTable[0]);
104 
105 	fColorField = new BMenuField(B_TRANSLATE("Color:"),
106 			colorsPopUp);
107 	fColorField->SetEnabled(false);
108 
109 	BLayoutBuilder::Group<>(this)
110 		.SetInsets(5, 5, 5, 5)
111 		.AddGrid(5, 5)
112 			.Add(fFont->CreateLabelLayoutItem(), 0, 0)
113 			.Add(fFont->CreateMenuBarLayoutItem(), 1, 0)
114 			.Add(fFontSize->CreateLabelLayoutItem(), 0, 1)
115 			.Add(fFontSize->CreateMenuBarLayoutItem(), 1, 1)
116 			.Add(fColorSchemaField->CreateLabelLayoutItem(), 0, 2)
117 			.Add(fColorSchemaField->CreateMenuBarLayoutItem(), 1, 2)
118 			.Add(fColorField->CreateLabelLayoutItem(), 0, 3)
119 			.Add(fColorField->CreateMenuBarLayoutItem(), 1, 3)
120 			.End()
121 		.AddGlue()
122 		.Add(fColorControl = new BColorControl(BPoint(10, 10),
123 			B_CELLS_32x8, 8.0, "", new BMessage(MSG_COLOR_CHANGED)))
124 		.Add(fWarnOnExit);
125 
126 	fFont->SetAlignment(B_ALIGN_RIGHT);
127 	fFontSize->SetAlignment(B_ALIGN_RIGHT);
128 	fColorField->SetAlignment(B_ALIGN_RIGHT);
129 	fColorSchemaField->SetAlignment(B_ALIGN_RIGHT);
130 
131 	fColorControl->SetEnabled(false);
132 	fColorControl->SetValue(PrefHandler::Default()->getRGB(PREF_TEXT_FORE_COLOR));
133 
134 	fWarnOnExit->SetValue(PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT));
135 
136 	BTextControl* redInput = (BTextControl*)fColorControl->ChildAt(0);
137 	BTextControl* greenInput = (BTextControl*)fColorControl->ChildAt(1);
138 	BTextControl* blueInput = (BTextControl*)fColorControl->ChildAt(2);
139 
140 	redInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
141 	greenInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
142 	blueInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
143 }
144 
145 
146 void
147 AppearancePrefView::GetPreferredSize(float* _width, float* _height)
148 {
149 	if (_width)
150 		*_width = Bounds().Width();
151 
152 	if (*_height)
153 		*_height = fColorControl->Frame().bottom;
154 }
155 
156 
157 void
158 AppearancePrefView::Revert()
159 {
160 	fWarnOnExit->SetValue(PrefHandler::Default()->getBool(
161 		PREF_WARN_ON_EXIT));
162 
163 	fColorSchemaField->Menu()->ItemAt(0)->SetMarked(true);
164 	fColorControl->SetValue(PrefHandler::Default()->
165 		getRGB(PREF_TEXT_FORE_COLOR));
166 
167 	fFont->Menu()->FindItem(PrefHandler::Default()->getString(
168 		PREF_HALF_FONT_FAMILY))->SetMarked(true);
169 	fFontSize->Menu()->FindItem(PrefHandler::Default()->getString(
170 		PREF_HALF_FONT_FAMILY))->SetMarked(true);
171 }
172 
173 
174 void
175 AppearancePrefView::AttachedToWindow()
176 {
177 	fWarnOnExit->SetTarget(this);
178 
179 	fFontSize->Menu()->SetTargetForItems(this);
180 	fFont->Menu()->SetTargetForItems(this);
181 
182   	fColorControl->SetTarget(this);
183   	fColorField->Menu()->SetTargetForItems(this);
184   	fColorSchemaField->Menu()->SetTargetForItems(this);
185 
186   	_SetCurrentColorSchema(fColorSchemaField);
187   	bool enableCustomColors =
188 		!strcmp(fColorSchemaField->Menu()->FindMarked()->Label(),
189 			"Custom");
190 
191   	_EnableCustomColors(enableCustomColors);
192 }
193 
194 
195 void
196 AppearancePrefView::MessageReceived(BMessage* msg)
197 {
198 	bool modified = false;
199 
200 	switch (msg->what) {
201 		case MSG_HALF_FONT_CHANGED:
202 		{
203 			const char* family = NULL;
204 			const char* style = NULL;
205 			msg->FindString("font_family", &family);
206 			msg->FindString("font_style", &style);
207 
208 			PrefHandler* pref = PrefHandler::Default();
209 			const char* currentFamily
210 				= pref->getString(PREF_HALF_FONT_FAMILY);
211 			const char* currentStyle
212 				= pref->getString(PREF_HALF_FONT_STYLE);
213 			if (currentFamily == NULL || strcmp(currentFamily, family)
214 				|| currentStyle == NULL || strcmp(currentStyle, style)) {
215 				pref->setString(PREF_HALF_FONT_FAMILY, family);
216 				pref->setString(PREF_HALF_FONT_STYLE, style);
217 				modified = true;
218 			}
219 			break;
220 		}
221 		case MSG_HALF_SIZE_CHANGED:
222 			if (strcmp(PrefHandler::Default()->getString(PREF_HALF_FONT_SIZE),
223 					fFontSize->Menu()->FindMarked()->Label())) {
224 
225 				PrefHandler::Default()->setString(PREF_HALF_FONT_SIZE,
226 					fFontSize->Menu()->FindMarked()->Label());
227 				modified = true;
228 			}
229 			break;
230 
231 		case MSG_COLOR_CHANGED:
232 		{
233 			rgb_color oldColor = PrefHandler::Default()->getRGB(
234 				fColorField->Menu()->FindMarked()->Label());
235 				if (oldColor != fColorControl->ValueAsColor()) {
236 					PrefHandler::Default()->setRGB(
237 						fColorField->Menu()->FindMarked()->Label(),
238 						fColorControl->ValueAsColor());
239 					modified = true;
240 				}
241 			}
242 			break;
243 
244 		case MSG_COLOR_SCHEMA_CHANGED:
245 		{
246 			color_schema* newSchema = NULL;
247 			if (msg->FindPointer("color_schema",
248 				(void**)&newSchema) == B_OK) {
249 
250 				if (newSchema == &gCustomSchema)
251 					_EnableCustomColors(true);
252 				else
253 					_EnableCustomColors(false);
254 				_ChangeColorSchema(newSchema);
255 				modified = true;
256 			}
257 			break;
258 		}
259 
260 		case MSG_COLOR_FIELD_CHANGED:
261 			fColorControl->SetValue(PrefHandler::Default()->getRGB(
262 				fColorField->Menu()->FindMarked()->Label()));
263 			break;
264 
265 		case MSG_WARN_ON_EXIT_CHANGED:
266 			if (PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT)
267 				!= fWarnOnExit->Value()) {
268 					PrefHandler::Default()->setBool(PREF_WARN_ON_EXIT,
269 						fWarnOnExit->Value());
270 					modified = true;
271 			}
272 			break;
273 		default:
274 			BView::MessageReceived(msg);
275 			return;
276 	}
277 
278 	if (modified) {
279 		fTerminalMessenger.SendMessage(msg);
280 
281 		BMessenger messenger(this);
282 		messenger.SendMessage(MSG_PREF_MODIFIED);
283 	}
284 }
285 
286 
287 void
288 AppearancePrefView::_EnableCustomColors(bool enable)
289 {
290 	fColorField->SetEnabled(enable);
291 	fColorControl->SetEnabled(enable);
292 }
293 
294 
295 void
296 AppearancePrefView::_ChangeColorSchema(color_schema* schema)
297 {
298 	PrefHandler* pref = PrefHandler::Default();
299 
300 	pref->setRGB(PREF_TEXT_FORE_COLOR, schema->text_fore_color);
301 	pref->setRGB(PREF_TEXT_BACK_COLOR, schema->text_back_color);
302 	pref->setRGB(PREF_SELECT_FORE_COLOR, schema->select_fore_color);
303 	pref->setRGB(PREF_SELECT_BACK_COLOR, schema->select_back_color);
304 }
305 
306 
307 void
308 AppearancePrefView::_SetCurrentColorSchema(BMenuField* field)
309 {
310 	PrefHandler* pref = PrefHandler::Default();
311 
312 	gCustomSchema.text_fore_color = pref->getRGB(PREF_TEXT_FORE_COLOR);
313 	gCustomSchema.text_back_color = pref->getRGB(PREF_TEXT_BACK_COLOR);
314 	gCustomSchema.select_fore_color = pref->getRGB(PREF_SELECT_FORE_COLOR);
315 	gCustomSchema.select_back_color = pref->getRGB(PREF_SELECT_BACK_COLOR);
316 
317 	const char* currentSchemaName = NULL;
318 
319 	color_schema** schemas = const_cast<color_schema**>(gPredefinedSchemas);
320 	while (*schemas) {
321 		if (gCustomSchema == **schemas) {
322 			currentSchemaName = (*schemas)->name;
323 			break;
324 		}
325 		schemas++;
326 	}
327 
328 	bool found = false;
329 	for (int32 i = 0; i < fColorSchemaField->Menu()->CountItems(); i++) {
330 		BMenuItem* item = fColorSchemaField->Menu()->ItemAt(i);
331 		if (!strcmp(item->Label(), currentSchemaName)) {
332 			item->SetMarked(true);
333 			found = true;
334 			break;
335 		}
336 	}
337 }
338 
339 
340 /*static*/ BMenu*
341 AppearancePrefView::_MakeFontMenu(uint32 command,
342 	const char* defaultFamily, const char* defaultStyle)
343 {
344 	BPopUpMenu* menu = new BPopUpMenu("");
345 	int32 numFamilies = count_font_families();
346 	uint32 flags;
347 
348 	for (int32 i = 0; i < numFamilies; i++) {
349 		font_family family;
350 		if (get_font_family(i, &family, &flags) == B_OK) {
351 			BFont font;
352 			font_style style;
353 			int32 numStyles = count_font_styles(family);
354 			for (int32 j = 0; j < numStyles; j++) {
355 				if (get_font_style(family, j, &style) == B_OK) {
356 					font.SetFamilyAndStyle(family, style);
357 					if (IsFontUsable(font)) {
358 						BMessage* message = new BMessage(command);
359 						message->AddString("font_family", family);
360 						message->AddString("font_style", style);
361 						char itemLabel[134];
362 						snprintf(itemLabel, sizeof(itemLabel),
363 							"%s - %s", family, style);
364 						BMenuItem* item = new BMenuItem(itemLabel,
365 							message);
366 						menu->AddItem(item);
367 						if (!strcmp(defaultFamily, family)
368 							&& !strcmp(defaultStyle, style))
369 							item->SetMarked(true);
370 					}
371 				}
372 			}
373 		}
374 	}
375 
376 	if (menu->FindMarked() == NULL)
377 		menu->ItemAt(0)->SetMarked(true);
378 
379 	return menu;
380 }
381 
382 
383 /*static*/ BMenu*
384 AppearancePrefView::_MakeSizeMenu(uint32 command, uint8 defaultSize)
385 {
386 	BPopUpMenu* menu = new BPopUpMenu("size");
387 	int32 sizes[] = {9, 10, 11, 12, 14, 16, 18, 0};
388 
389 	bool found = false;
390 
391 	for (uint32 i = 0; sizes[i]; i++) {
392 		BString string;
393 		string << sizes[i];
394 
395 		BMenuItem* item = new BMenuItem(string.String(), new BMessage(command));
396 		menu->AddItem(item);
397 
398 		if (sizes[i] == defaultSize) {
399 			item->SetMarked(true);
400 			found = true;
401 		}
402 	}
403 	if (!found) {
404 		for (uint32 i = 0; sizes[i]; i++) {
405 			if (sizes[i] > defaultSize) {
406 				BString string;
407 				string << defaultSize;
408 				BMenuItem* item = new BMenuItem(string.String(),
409 					new BMessage(command));
410 				item->SetMarked(true);
411 				menu->AddItem(item, i);
412 				break;
413 			}
414 		}
415 	}
416 
417 	return menu;
418 }
419 
420 
421 /*static*/ BPopUpMenu*
422 AppearancePrefView::_MakeMenu(uint32 msg, const char **items,
423 	const char* defaultItemName)
424 {
425 	BPopUpMenu* menu = new BPopUpMenu("");
426 
427 	int32 i = 0;
428 	while (*items) {
429 		if (!strcmp((*items), ""))
430 			menu->AddSeparatorItem();
431 		else {
432 			BMessage *message = new BMessage(msg);
433 			menu->AddItem(new BMenuItem((*items), message));
434 		}
435 
436 		items++;
437 		i++;
438 	}
439 
440 	menu->FindItem(defaultItemName)->SetMarked(true);
441 
442 	return menu;
443 }
444 
445 
446 /*static*/ BPopUpMenu*
447 AppearancePrefView::_MakeColorSchemaMenu(uint32 msg, const color_schema **items,
448 	const color_schema* defaultItemName)
449 {
450 	BPopUpMenu* menu = new BPopUpMenu("");
451 
452 	int32 i = 0;
453 	while (*items) {
454 		if (!strcmp((*items)->name, ""))
455 			menu->AddSeparatorItem();
456 		else {
457 			BMessage *message = new BMessage(msg);
458 			message->AddPointer("color_schema", (const void*)*items);
459 			menu->AddItem(new BMenuItem((*items)->name, message));
460 		}
461 
462 		items++;
463 		i++;
464 	}
465 	return menu;
466 }
467