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