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