xref: /haiku/src/apps/terminal/AppearPrefView.cpp (revision 1345706a9ff6ad0dc041339a02d4259998b0765d)
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 	BView(name, B_WILL_DRAW),
73 	fTerminalMessenger(messenger)
74 {
75 	const char* kColorTable[] = {
76 		PREF_TEXT_FORE_COLOR,
77 		PREF_TEXT_BACK_COLOR,
78 		PREF_CURSOR_FORE_COLOR,
79 		PREF_CURSOR_BACK_COLOR,
80 		PREF_SELECT_FORE_COLOR,
81 		PREF_SELECT_BACK_COLOR,
82 		NULL
83 	};
84 
85 	fWarnOnExit = new BCheckBox(
86 		B_TRANSLATE("Confirm exit if active programs exist"),
87 			new BMessage(MSG_WARN_ON_EXIT_CHANGED));
88 
89 	BMenu* fontMenu = _MakeFontMenu(MSG_HALF_FONT_CHANGED,
90 		PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY),
91 		PrefHandler::Default()->getString(PREF_HALF_FONT_STYLE));
92 
93 	BMenu* sizeMenu = _MakeSizeMenu(MSG_HALF_SIZE_CHANGED,
94 		PrefHandler::Default()->getInt32(PREF_HALF_FONT_SIZE));
95 
96 	fFont = new BMenuField(B_TRANSLATE("Font:"), fontMenu);
97 	fFontSize = new BMenuField(B_TRANSLATE("Size:"), sizeMenu);
98 
99 	BPopUpMenu* schemasPopUp =_MakeColorSchemaMenu(MSG_COLOR_SCHEMA_CHANGED,
100 		gPredefinedSchemas, gPredefinedSchemas[0]);
101 	fColorSchemaField = new BMenuField(B_TRANSLATE("Color schema:"),
102 		schemasPopUp);
103 
104 	BPopUpMenu* colorsPopUp =_MakeMenu(MSG_COLOR_FIELD_CHANGED, kColorTable,
105 		kColorTable[0]);
106 
107 	fColorField = new BMenuField(B_TRANSLATE("Color:"),
108 			colorsPopUp);
109 	fColorField->SetEnabled(false);
110 
111 	BGroupLayout* layout = new BGroupLayout(B_HORIZONTAL);
112 	SetLayout(layout);
113 	BLayoutBuilder::Group<>(layout)
114 		.SetInsets(5, 5, 5, 5)
115 		.AddGroup(B_VERTICAL, 5)
116 			.AddGrid(5, 5)
117 				.Add(fFont->CreateLabelLayoutItem(), 0, 0)
118 				.Add(fFont->CreateMenuBarLayoutItem(), 1, 0)
119 				.Add(fFontSize->CreateLabelLayoutItem(), 0, 1)
120 				.Add(fFontSize->CreateMenuBarLayoutItem(), 1, 1)
121 				.Add(fColorSchemaField->CreateLabelLayoutItem(), 0, 2)
122 				.Add(fColorSchemaField->CreateMenuBarLayoutItem(), 1, 2)
123 				.Add(fColorField->CreateLabelLayoutItem(), 0, 3)
124 				.Add(fColorField->CreateMenuBarLayoutItem(), 1, 3)
125 			.	End()
126 			.AddGroup(B_VERTICAL, 5)
127 				.AddGlue()
128 				.Add(fColorControl = new BColorControl(BPoint(10, 10),
129 					B_CELLS_32x8, 8.0, "", new BMessage(MSG_COLOR_CHANGED)))
130 				.Add(fWarnOnExit);
131 
132 	fFont->SetAlignment(B_ALIGN_RIGHT);
133 	fFontSize->SetAlignment(B_ALIGN_RIGHT);
134 	fColorField->SetAlignment(B_ALIGN_RIGHT);
135 	fColorSchemaField->SetAlignment(B_ALIGN_RIGHT);
136 
137 	fColorControl->SetEnabled(false);
138 	fColorControl->SetValue(PrefHandler::Default()->getRGB(PREF_TEXT_FORE_COLOR));
139 
140 	fWarnOnExit->SetValue(PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT));
141 
142 	BTextControl* redInput = (BTextControl*)fColorControl->ChildAt(0);
143 	BTextControl* greenInput = (BTextControl*)fColorControl->ChildAt(1);
144 	BTextControl* blueInput = (BTextControl*)fColorControl->ChildAt(2);
145 
146 	redInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
147 	greenInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
148 	blueInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
149 }
150 
151 
152 void
153 AppearancePrefView::GetPreferredSize(float* _width, float* _height)
154 {
155 	if (_width)
156 		*_width = Bounds().Width();
157 
158 	if (*_height)
159 		*_height = fColorControl->Frame().bottom;
160 }
161 
162 
163 void
164 AppearancePrefView::Revert()
165 {
166 	fWarnOnExit->SetValue(PrefHandler::Default()->getBool(
167 		PREF_WARN_ON_EXIT));
168 
169 	fColorSchemaField->Menu()->ItemAt(0)->SetMarked(true);
170 	fColorControl->SetValue(PrefHandler::Default()->
171 		getRGB(PREF_TEXT_FORE_COLOR));
172 
173 	fFont->Menu()->FindItem(PrefHandler::Default()->getString(
174 		PREF_HALF_FONT_FAMILY))->SetMarked(true);
175 	fFontSize->Menu()->FindItem(PrefHandler::Default()->getString(
176 		PREF_HALF_FONT_FAMILY))->SetMarked(true);
177 }
178 
179 
180 void
181 AppearancePrefView::AttachedToWindow()
182 {
183 	fWarnOnExit->SetTarget(this);
184 
185 	fFontSize->Menu()->SetTargetForItems(this);
186 	fFont->Menu()->SetTargetForItems(this);
187 
188   	fColorControl->SetTarget(this);
189   	fColorField->Menu()->SetTargetForItems(this);
190   	fColorSchemaField->Menu()->SetTargetForItems(this);
191 
192   	_SetCurrentColorSchema(fColorSchemaField);
193   	bool enableCustomColors =
194 		!strcmp(fColorSchemaField->Menu()->FindMarked()->Label(),
195 			"Custom");
196 
197   	_EnableCustomColors(enableCustomColors);
198 }
199 
200 
201 void
202 AppearancePrefView::MessageReceived(BMessage* msg)
203 {
204 	bool modified = false;
205 
206 	switch (msg->what) {
207 		case MSG_HALF_FONT_CHANGED:
208 		{
209 			const char* family = NULL;
210 			const char* style = NULL;
211 			msg->FindString("font_family", &family);
212 			msg->FindString("font_style", &style);
213 
214 			PrefHandler* pref = PrefHandler::Default();
215 			const char* currentFamily
216 				= pref->getString(PREF_HALF_FONT_FAMILY);
217 			const char* currentStyle
218 				= pref->getString(PREF_HALF_FONT_STYLE);
219 			if (currentFamily == NULL || strcmp(currentFamily, family)
220 				|| currentStyle == NULL || strcmp(currentStyle, style)) {
221 				pref->setString(PREF_HALF_FONT_FAMILY, family);
222 				pref->setString(PREF_HALF_FONT_STYLE, style);
223 				modified = true;
224 			}
225 			break;
226 		}
227 		case MSG_HALF_SIZE_CHANGED:
228 			if (strcmp(PrefHandler::Default()->getString(PREF_HALF_FONT_SIZE),
229 					fFontSize->Menu()->FindMarked()->Label())) {
230 
231 				PrefHandler::Default()->setString(PREF_HALF_FONT_SIZE,
232 					fFontSize->Menu()->FindMarked()->Label());
233 				modified = true;
234 			}
235 			break;
236 
237 		case MSG_COLOR_CHANGED:
238 		{
239 			rgb_color oldColor = PrefHandler::Default()->getRGB(
240 				fColorField->Menu()->FindMarked()->Label());
241 				if (oldColor != fColorControl->ValueAsColor()) {
242 					PrefHandler::Default()->setRGB(
243 						fColorField->Menu()->FindMarked()->Label(),
244 						fColorControl->ValueAsColor());
245 					modified = true;
246 				}
247 			}
248 			break;
249 
250 		case MSG_COLOR_SCHEMA_CHANGED:
251 		{
252 			color_schema* newSchema = NULL;
253 			if (msg->FindPointer("color_schema",
254 				(void**)&newSchema) == B_OK) {
255 
256 				if (newSchema == &gCustomSchema)
257 					_EnableCustomColors(true);
258 				else
259 					_EnableCustomColors(false);
260 				_ChangeColorSchema(newSchema);
261 				modified = true;
262 			}
263 			break;
264 		}
265 
266 		case MSG_COLOR_FIELD_CHANGED:
267 			fColorControl->SetValue(PrefHandler::Default()->getRGB(
268 				fColorField->Menu()->FindMarked()->Label()));
269 			break;
270 
271 		case MSG_WARN_ON_EXIT_CHANGED:
272 			if (PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT)
273 				!= fWarnOnExit->Value()) {
274 					PrefHandler::Default()->setBool(PREF_WARN_ON_EXIT,
275 						fWarnOnExit->Value());
276 					modified = true;
277 			}
278 			break;
279 		default:
280 			BView::MessageReceived(msg);
281 			return;
282 	}
283 
284 	if (modified) {
285 		fTerminalMessenger.SendMessage(msg);
286 
287 		BMessenger messenger(this);
288 		messenger.SendMessage(MSG_PREF_MODIFIED);
289 	}
290 }
291 
292 
293 void
294 AppearancePrefView::_EnableCustomColors(bool enable)
295 {
296 	fColorField->SetEnabled(enable);
297 	fColorControl->SetEnabled(enable);
298 }
299 
300 
301 void
302 AppearancePrefView::_ChangeColorSchema(color_schema* schema)
303 {
304 	PrefHandler* pref = PrefHandler::Default();
305 
306 	pref->setRGB(PREF_TEXT_FORE_COLOR, schema->text_fore_color);
307 	pref->setRGB(PREF_TEXT_BACK_COLOR, schema->text_back_color);
308 	pref->setRGB(PREF_CURSOR_FORE_COLOR, schema->cursor_fore_color);
309 	pref->setRGB(PREF_CURSOR_BACK_COLOR, schema->cursor_back_color);
310 	pref->setRGB(PREF_SELECT_FORE_COLOR, schema->select_fore_color);
311 	pref->setRGB(PREF_SELECT_BACK_COLOR, schema->select_back_color);
312 }
313 
314 
315 void
316 AppearancePrefView::_SetCurrentColorSchema(BMenuField* field)
317 {
318 	PrefHandler* pref = PrefHandler::Default();
319 
320 	gCustomSchema.text_fore_color = pref->getRGB(PREF_TEXT_FORE_COLOR);
321 	gCustomSchema.text_back_color = pref->getRGB(PREF_TEXT_BACK_COLOR);
322 	gCustomSchema.cursor_fore_color = pref->getRGB(PREF_CURSOR_FORE_COLOR);
323 	gCustomSchema.cursor_back_color = pref->getRGB(PREF_CURSOR_BACK_COLOR);
324 	gCustomSchema.select_fore_color = pref->getRGB(PREF_SELECT_FORE_COLOR);
325 	gCustomSchema.select_back_color = pref->getRGB(PREF_SELECT_BACK_COLOR);
326 
327 	const char* currentSchemaName = NULL;
328 
329 	color_schema** schemas = const_cast<color_schema**>(gPredefinedSchemas);
330 	while (*schemas) {
331 		if (gCustomSchema == **schemas) {
332 			currentSchemaName = (*schemas)->name;
333 			break;
334 		}
335 		schemas++;
336 	}
337 
338 	bool found = false;
339 	for (int32 i = 0; i < fColorSchemaField->Menu()->CountItems(); i++) {
340 		BMenuItem* item = fColorSchemaField->Menu()->ItemAt(i);
341 		if (!strcmp(item->Label(), currentSchemaName)) {
342 			item->SetMarked(true);
343 			found = true;
344 			break;
345 		}
346 	}
347 }
348 
349 
350 /*static*/ BMenu*
351 AppearancePrefView::_MakeFontMenu(uint32 command,
352 	const char* defaultFamily, const char* defaultStyle)
353 {
354 	BPopUpMenu* menu = new BPopUpMenu("");
355 	int32 numFamilies = count_font_families();
356 	uint32 flags;
357 
358 	for (int32 i = 0; i < numFamilies; i++) {
359 		font_family family;
360 		if (get_font_family(i, &family, &flags) == B_OK) {
361 			BFont font;
362 			font_style style;
363 			int32 numStyles = count_font_styles(family);
364 			for (int32 j = 0; j < numStyles; j++) {
365 				if (get_font_style(family, j, &style) == B_OK) {
366 					font.SetFamilyAndStyle(family, style);
367 					if (IsFontUsable(font)) {
368 						BMessage* message = new BMessage(command);
369 						message->AddString("font_family", family);
370 						message->AddString("font_style", style);
371 						char itemLabel[134];
372 						snprintf(itemLabel, sizeof(itemLabel),
373 							"%s - %s", family, style);
374 						BMenuItem* item = new BMenuItem(itemLabel,
375 							message);
376 						menu->AddItem(item);
377 						if (!strcmp(defaultFamily, family)
378 							&& !strcmp(defaultStyle, style))
379 							item->SetMarked(true);
380 					}
381 				}
382 			}
383 		}
384 	}
385 
386 	if (menu->FindMarked() == NULL)
387 		menu->ItemAt(0)->SetMarked(true);
388 
389 	return menu;
390 }
391 
392 
393 /*static*/ BMenu*
394 AppearancePrefView::_MakeSizeMenu(uint32 command, uint8 defaultSize)
395 {
396 	BPopUpMenu* menu = new BPopUpMenu("size");
397 	int32 sizes[] = {9, 10, 11, 12, 14, 16, 18, 0};
398 
399 	bool found = false;
400 
401 	for (uint32 i = 0; sizes[i]; i++) {
402 		BString string;
403 		string << sizes[i];
404 
405 		BMenuItem* item = new BMenuItem(string.String(), new BMessage(command));
406 		menu->AddItem(item);
407 
408 		if (sizes[i] == defaultSize) {
409 			item->SetMarked(true);
410 			found = true;
411 		}
412 	}
413 	if (!found) {
414 		for (uint32 i = 0; sizes[i]; i++) {
415 			if (sizes[i] > defaultSize) {
416 				BString string;
417 				string << defaultSize;
418 				BMenuItem* item = new BMenuItem(string.String(),
419 					new BMessage(command));
420 				item->SetMarked(true);
421 				menu->AddItem(item, i);
422 				break;
423 			}
424 		}
425 	}
426 
427 	return menu;
428 }
429 
430 
431 /*static*/ BPopUpMenu*
432 AppearancePrefView::_MakeMenu(uint32 msg, const char **items,
433 	const char* defaultItemName)
434 {
435 	BPopUpMenu* menu = new BPopUpMenu("");
436 
437 	int32 i = 0;
438 	while (*items) {
439 		if (!strcmp((*items), ""))
440 			menu->AddSeparatorItem();
441 		else {
442 			BMessage *message = new BMessage(msg);
443 			menu->AddItem(new BMenuItem((*items), message));
444 		}
445 
446 		items++;
447 		i++;
448 	}
449 
450 	menu->FindItem(defaultItemName)->SetMarked(true);
451 
452 	return menu;
453 }
454 
455 
456 /*static*/ BPopUpMenu*
457 AppearancePrefView::_MakeColorSchemaMenu(uint32 msg, const color_schema **items,
458 	const color_schema* defaultItemName)
459 {
460 	BPopUpMenu* menu = new BPopUpMenu("");
461 
462 	int32 i = 0;
463 	while (*items) {
464 		if (!strcmp((*items)->name, ""))
465 			menu->AddSeparatorItem();
466 		else {
467 			BMessage *message = new BMessage(msg);
468 			message->AddPointer("color_schema", (const void*)*items);
469 			menu->AddItem(new BMenuItem((*items)->name, message));
470 		}
471 
472 		items++;
473 		i++;
474 	}
475 	return menu;
476 }
477