xref: /haiku/src/apps/terminal/AppearPrefView.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
1 /*
2  * Copyright 2001-2015, 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 <CharacterSet.h>
17 #include <CharacterSetRoster.h>
18 #include <CheckBox.h>
19 #include <ColorControl.h>
20 #include <LayoutBuilder.h>
21 #include <Locale.h>
22 #include <Menu.h>
23 #include <MenuField.h>
24 #include <MenuItem.h>
25 #include <PopUpMenu.h>
26 #include <TextControl.h>
27 #include <View.h>
28 
29 #include "Colors.h"
30 #include "Globals.h"
31 #include "PrefHandler.h"
32 #include "TermConst.h"
33 #include "TermWindow.h"
34 
35 
36 #undef B_TRANSLATION_CONTEXT
37 #define B_TRANSLATION_CONTEXT "Terminal AppearancePrefView"
38 
39 
40 // #pragma mark -
41 
42 
43 AppearancePrefView::AppearancePrefView(const char* name,
44 		const BMessenger& messenger)
45 	:
46 	BGroupView(name, B_VERTICAL, 5),
47 	fTerminalMessenger(messenger)
48 {
49 	fBlinkCursor = new BCheckBox(
50 		B_TRANSLATE("Blinking cursor"),
51 			new BMessage(MSG_BLINK_CURSOR_CHANGED));
52 
53 	fAllowBold = new BCheckBox(
54 		B_TRANSLATE("Allow bold text"),
55 			new BMessage(MSG_ALLOW_BOLD_CHANGED));
56 
57 	fUseOptionAsMetaKey = new BCheckBox(
58 		B_TRANSLATE("Use left Option as Meta key"),
59 			new BMessage(MSG_USE_OPTION_AS_META_CHANGED));
60 
61 	fWarnOnExit = new BCheckBox(
62 		B_TRANSLATE("Confirm exit if active programs exist"),
63 			new BMessage(MSG_WARN_ON_EXIT_CHANGED));
64 
65 	BMenu* fontMenu = _MakeFontMenu(MSG_HALF_FONT_CHANGED,
66 		PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY),
67 		PrefHandler::Default()->getString(PREF_HALF_FONT_STYLE));
68 	fFontField = new BMenuField(B_TRANSLATE("Font:"), fontMenu);
69 
70 	BMenu* sizeMenu = new (std::nothrow) BPopUpMenu(
71 		B_TRANSLATE_COMMENT("Custom", "Window size"));
72 	if (sizeMenu != NULL) {
73 		TermWindow::MakeWindowSizeMenu(sizeMenu);
74 		sizeMenu->SetLabelFromMarked(true);
75 	}
76 	fWindowSizeField = new BMenuField(B_TRANSLATE("Window size:"), sizeMenu);
77 
78 	BMenu* encodingMenu = new (std::nothrow) BPopUpMenu("Text encoding");
79 	if (encodingMenu != NULL) {
80 		TermWindow::MakeEncodingMenu(encodingMenu);
81 		encodingMenu->SetLabelFromMarked(true);
82 	}
83 	fEncodingField = new BMenuField(B_TRANSLATE("Encoding:"), encodingMenu);
84 
85 	fTabTitle = new BTextControl("tabTitle", B_TRANSLATE("Tab title:"), "",
86 		NULL);
87 	fTabTitle->SetModificationMessage(
88 		new BMessage(MSG_TAB_TITLE_SETTING_CHANGED));
89 	fTabTitle->SetToolTip(BString(B_TRANSLATE(
90 		"The pattern specifying the tab titles. The following placeholders\n"
91 		"can be used:")) << "\n" << kToolTipSetTabTitlePlaceholders
92 		<< "\n" << kToolTipCommonTitlePlaceholders);
93 
94 	fWindowTitle = new BTextControl("windowTitle", B_TRANSLATE("Window title:"),
95 		"", NULL);
96 	fWindowTitle->SetModificationMessage(
97 		new BMessage(MSG_WINDOW_TITLE_SETTING_CHANGED));
98 	fWindowTitle->SetToolTip(BString(B_TRANSLATE(
99 		"The pattern specifying the window titles. The following placeholders\n"
100 		"can be used:")) << "\n" << kToolTipSetWindowTitlePlaceholders
101 		<< "\n" << kToolTipCommonTitlePlaceholders);
102 
103 	BLayoutBuilder::Group<>(this)
104 		.SetInsets(5, 5, 5, 5)
105 		.AddGrid(5, 5)
106 			.Add(fTabTitle->CreateLabelLayoutItem(), 0, 0)
107 			.Add(fTabTitle->CreateTextViewLayoutItem(), 1, 0)
108 			.Add(fWindowTitle->CreateLabelLayoutItem(), 0, 1)
109 			.Add(fWindowTitle->CreateTextViewLayoutItem(), 1, 1)
110 			.Add(fWindowSizeField->CreateLabelLayoutItem(), 0, 2)
111 			.Add(fWindowSizeField->CreateMenuBarLayoutItem(), 1, 2)
112 			.Add(fFontField->CreateLabelLayoutItem(), 0, 3)
113 			.Add(fFontField->CreateMenuBarLayoutItem(), 1, 3)
114 			.Add(fEncodingField->CreateLabelLayoutItem(), 0, 4)
115 			.Add(fEncodingField->CreateMenuBarLayoutItem(), 1, 4)
116 			.End()
117 		.AddGlue()
118 		.Add(fBlinkCursor)
119 		.Add(fAllowBold)
120 		.Add(fUseOptionAsMetaKey)
121 		.Add(fWarnOnExit);
122 
123 	fTabTitle->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
124 	fWindowTitle->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
125 	fFontField->SetAlignment(B_ALIGN_RIGHT);
126 	fWindowSizeField->SetAlignment(B_ALIGN_RIGHT);
127 	fEncodingField->SetAlignment(B_ALIGN_RIGHT);
128 
129 	Revert();
130 }
131 
132 
133 void
134 AppearancePrefView::Revert()
135 {
136 	PrefHandler* pref = PrefHandler::Default();
137 
138 	fTabTitle->SetText(pref->getString(PREF_TAB_TITLE));
139 	fWindowTitle->SetText(pref->getString(PREF_WINDOW_TITLE));
140 
141 	fBlinkCursor->SetValue(pref->getBool(PREF_BLINK_CURSOR));
142 	fAllowBold->SetValue(pref->getBool(PREF_ALLOW_BOLD));
143 	fUseOptionAsMetaKey->SetValue(pref->getBool(PREF_USE_OPTION_AS_META));
144 	fWarnOnExit->SetValue(pref->getBool(PREF_WARN_ON_EXIT));
145 
146 	_SetEncoding(pref->getString(PREF_TEXT_ENCODING));
147 	_SetWindowSize(pref->getInt32(PREF_ROWS), pref->getInt32(PREF_COLS));
148 
149 	const char* family = pref->getString(PREF_HALF_FONT_FAMILY);
150 	const char* style = pref->getString(PREF_HALF_FONT_STYLE);
151 	const char* size = pref->getString(PREF_HALF_FONT_SIZE);
152 
153 	_MarkSelectedFont(family, style, size);
154 }
155 
156 
157 void
158 AppearancePrefView::AttachedToWindow()
159 {
160 	fTabTitle->SetTarget(this);
161 	fWindowTitle->SetTarget(this);
162 	fBlinkCursor->SetTarget(this);
163 	fAllowBold->SetTarget(this);
164 	fUseOptionAsMetaKey->SetTarget(this);
165 	fWarnOnExit->SetTarget(this);
166 
167 	fFontField->Menu()->SetTargetForItems(this);
168 	for (int32 i = 0; i < fFontField->Menu()->CountItems(); i++) {
169 		BMenu* fontSizeMenu = fFontField->Menu()->SubmenuAt(i);
170 		if (fontSizeMenu == NULL)
171 			continue;
172 
173 		fontSizeMenu->SetTargetForItems(this);
174 	}
175 
176 	fWindowSizeField->Menu()->SetTargetForItems(this);
177 	fEncodingField->Menu()->SetTargetForItems(this);
178 }
179 
180 
181 void
182 
183 AppearancePrefView::MessageReceived(BMessage* msg)
184 {
185 	bool modified = false;
186 
187 	switch (msg->what) {
188 		case MSG_HALF_FONT_CHANGED:
189 		{
190 			const char* family = NULL;
191 			const char* style = NULL;
192 			const char* size = NULL;
193 			if (msg->FindString("font_family", &family) != B_OK
194 				|| msg->FindString("font_style", &style) != B_OK
195 				|| msg->FindString("font_size", &size) != B_OK) {
196 				break;
197 			}
198 
199 			PrefHandler* pref = PrefHandler::Default();
200 			const char* currentFamily
201 				= pref->getString(PREF_HALF_FONT_FAMILY);
202 			const char* currentStyle
203 				= pref->getString(PREF_HALF_FONT_STYLE);
204 			const char* currentSize
205 				= pref->getString(PREF_HALF_FONT_SIZE);
206 
207 			if (currentFamily == NULL || strcmp(currentFamily, family) != 0
208 				|| currentStyle == NULL || strcmp(currentStyle, style) != 0
209 				|| currentSize == NULL || strcmp(currentSize, size) != 0) {
210 				pref->setString(PREF_HALF_FONT_FAMILY, family);
211 				pref->setString(PREF_HALF_FONT_STYLE, style);
212 				pref->setString(PREF_HALF_FONT_SIZE, size);
213 				_MarkSelectedFont(family, style, size);
214 				modified = true;
215 			}
216 			break;
217 		}
218 
219 		case MSG_COLS_CHANGED:
220 		{
221 			int rows = msg->FindInt32("rows");
222 			int columns = msg->FindInt32("columns");
223 			_SetWindowSize(rows, columns);
224 			PrefHandler* handler = PrefHandler::Default();
225 			if (handler->getInt32(PREF_ROWS) != rows) {
226 				PrefHandler::Default()->setInt32(PREF_ROWS, rows);
227 				modified = true;
228 			}
229 			if (handler->getInt32(PREF_COLS) != columns) {
230 				PrefHandler::Default()->setInt32(PREF_COLS, columns);
231 				modified = true;
232 			}
233 
234 			break;
235 		}
236 
237 		case MSG_BLINK_CURSOR_CHANGED:
238 			if (PrefHandler::Default()->getBool(PREF_BLINK_CURSOR)
239 				!= fBlinkCursor->Value()) {
240 					PrefHandler::Default()->setBool(PREF_BLINK_CURSOR,
241 						fBlinkCursor->Value());
242 					modified = true;
243 			}
244 			break;
245 
246 		case MSG_ALLOW_BOLD_CHANGED:
247 			if (PrefHandler::Default()->getBool(PREF_ALLOW_BOLD)
248 				!= fAllowBold->Value()) {
249 					PrefHandler::Default()->setBool(PREF_ALLOW_BOLD,
250 						fAllowBold->Value());
251 					modified = true;
252 			}
253 			break;
254 
255 		case MSG_USE_OPTION_AS_META_CHANGED:
256 			if (PrefHandler::Default()->getBool(PREF_USE_OPTION_AS_META)
257 				!= fUseOptionAsMetaKey->Value()) {
258 					PrefHandler::Default()->setBool(PREF_USE_OPTION_AS_META,
259 						fUseOptionAsMetaKey->Value());
260 					modified = true;
261 			}
262 			break;
263 
264 		case MSG_WARN_ON_EXIT_CHANGED:
265 			if (PrefHandler::Default()->getBool(PREF_WARN_ON_EXIT)
266 				!= fWarnOnExit->Value()) {
267 					PrefHandler::Default()->setBool(PREF_WARN_ON_EXIT,
268 						fWarnOnExit->Value());
269 					modified = true;
270 			}
271 			break;
272 
273 		case MSG_TAB_TITLE_SETTING_CHANGED:
274 		{
275 			BString oldValue(PrefHandler::Default()->getString(PREF_TAB_TITLE));
276 			if (oldValue != fTabTitle->Text()) {
277 				PrefHandler::Default()->setString(PREF_TAB_TITLE,
278 					fTabTitle->Text());
279 				modified = true;
280 			}
281 			break;
282 		}
283 
284 		case MSG_WINDOW_TITLE_SETTING_CHANGED:
285 		{
286 			BString oldValue(PrefHandler::Default()->getString(
287 				PREF_WINDOW_TITLE));
288 			if (oldValue != fWindowTitle->Text()) {
289 				PrefHandler::Default()->setString(PREF_WINDOW_TITLE,
290 					fWindowTitle->Text());
291 				modified = true;
292 			}
293 			break;
294 		}
295 
296 		default:
297 			BView::MessageReceived(msg);
298 			return;
299 	}
300 
301 	if (modified) {
302 		fTerminalMessenger.SendMessage(msg);
303 
304 		BMessenger messenger(this);
305 		messenger.SendMessage(MSG_PREF_MODIFIED);
306 	}
307 }
308 
309 
310 void
311 AppearancePrefView::_SetEncoding(const char* name)
312 {
313 	const BPrivate::BCharacterSet* charset
314 		= BPrivate::BCharacterSetRoster::FindCharacterSetByName(name);
315 	if (charset == NULL)
316 		return;
317 	int code = charset->GetConversionID();
318 	for (int32 i = 0; i < fEncodingField->Menu()->CountItems(); i++) {
319 		BMenuItem* item = fEncodingField->Menu()->ItemAt(i);
320 		BMessage* msg = item->Message();
321 		if (msg->FindInt32("op") == code) {
322 			item->SetMarked(true);
323 			break;
324 		}
325 	}
326 }
327 
328 
329 void
330 AppearancePrefView::_SetWindowSize(int rows, int cols)
331 {
332 	for (int32 i = 0; i < fWindowSizeField->Menu()->CountItems(); i++) {
333 		BMenuItem* item = fWindowSizeField->Menu()->ItemAt(i);
334 		BMessage* msg = item->Message();
335 		if (msg->FindInt32("rows") == rows && msg->FindInt32("columns") == cols) {
336 			item->SetMarked(true);
337 			break;
338 		}
339 	}
340 }
341 
342 
343 /*static*/ BMenu*
344 AppearancePrefView::_MakeFontMenu(uint32 command,
345 	const char* defaultFamily, const char* defaultStyle)
346 {
347 	BPopUpMenu* menu = new BPopUpMenu("");
348 	int32 numFamilies = count_font_families();
349 	for (int32 i = 0; i < numFamilies; i++) {
350 		font_family family;
351 		uint32 flags;
352 		if (get_font_family(i, &family, &flags) == B_OK) {
353 			BFont font;
354 			font_style style;
355 			int32 numStyles = count_font_styles(family);
356 			for (int32 j = 0; j < numStyles; j++) {
357 				if (get_font_style(family, j, &style) == B_OK) {
358 					font.SetFamilyAndStyle(family, style);
359 					if (IsFontUsable(font)) {
360 						BMessage* message = new BMessage(command);
361 						const char* size
362 							= PrefHandler::Default()->getString(PREF_HALF_FONT_SIZE);
363 						message->AddString("font_family", family);
364 						message->AddString("font_style", style);
365 						message->AddString("font_size", size);
366 						char fontMenuLabel[134];
367 						snprintf(fontMenuLabel, sizeof(fontMenuLabel),
368 							"%s - %s", family, style);
369 						BMenu* fontSizeMenu = _MakeFontSizeMenu(fontMenuLabel,
370 							MSG_HALF_FONT_CHANGED, family, style, size);
371 						BMenuItem* item = new BMenuItem(fontSizeMenu, message);
372 						menu->AddItem(item);
373 						if (strcmp(defaultFamily, family) == 0
374 							&& strcmp(defaultStyle, style) == 0)
375 							item->SetMarked(true);
376 					}
377 				}
378 			}
379 		}
380 	}
381 
382 	if (menu->FindMarked() == NULL)
383 		menu->ItemAt(0)->SetMarked(true);
384 
385 	return menu;
386 }
387 
388 
389 /*static*/ BMenu*
390 AppearancePrefView::_MakeFontSizeMenu(const char* label, uint32 command,
391 	const char* family, const char* style, const char* size)
392 {
393 	BMenu* menu = new BMenu(label);
394 	menu->SetRadioMode(true);
395 	menu->SetLabelFromMarked(false);
396 
397 	int32 sizes[] = {
398 		8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 28, 32, 36, 0
399 	};
400 
401 	bool found = false;
402 
403 	for (uint32 i = 0; sizes[i]; i++) {
404 		BString fontSize;
405 		fontSize << sizes[i];
406 		BMessage* message = new BMessage(command);
407 		message->AddString("font_family", family);
408 		message->AddString("font_style", style);
409 		message->AddString("font_size", fontSize.String());
410 		BMenuItem* item = new BMenuItem(fontSize.String(), message);
411 		menu->AddItem(item);
412 		if (sizes[i] == atoi(size)) {
413 			item->SetMarked(true);
414 			found = true;
415 		}
416 	}
417 
418 	if (!found) {
419 		for (uint32 i = 0; sizes[i]; i++) {
420 			if (sizes[i] > atoi(size)) {
421 				BMessage* message = new BMessage(command);
422 				message->AddString("font_family", family);
423 				message->AddString("font_style", style);
424 				message->AddString("font_size", size);
425 				BMenuItem* item = new BMenuItem(size, message);
426 				item->SetMarked(true);
427 				menu->AddItem(item, i);
428 				break;
429 			}
430 		}
431 	}
432 
433 	return menu;
434 }
435 
436 
437 /*static*/ BPopUpMenu*
438 AppearancePrefView::_MakeMenu(uint32 msg, const char** items,
439 	const char* defaultItemName)
440 {
441 	BPopUpMenu* menu = new BPopUpMenu("");
442 
443 	while (*items) {
444 		if (strcmp((*items), "") == 0)
445 			menu->AddSeparatorItem();
446 		else {
447 			BMessage* message = new BMessage(msg);
448 			message->AddString("label", *items);
449 			BMenuItem* item = new BMenuItem(B_TRANSLATE(*items), message);
450 			menu->AddItem(item);
451 			if (strcmp(*items, defaultItemName) == 0)
452 				item->SetMarked(true);
453 		}
454 
455 		items++;
456 	}
457 
458 	return menu;
459 }
460 
461 
462 void
463 AppearancePrefView::_MarkSelectedFont(const char* family, const char* style,
464 	const char* size)
465 {
466 	char fontMenuLabel[134];
467 	snprintf(fontMenuLabel, sizeof(fontMenuLabel), "%s - %s", family, style);
468 
469 	// mark the selected font
470 	BMenuItem* selectedFont = fFontField->Menu()->FindItem(fontMenuLabel);
471 	if (selectedFont != NULL)
472 		selectedFont->SetMarked(true);
473 
474 	// mark the selected font size on all font menus
475 	for (int32 i = 0; i < fFontField->Menu()->CountItems(); i++) {
476 		BMenu* fontSizeMenu = fFontField->Menu()->SubmenuAt(i);
477 		if (fontSizeMenu == NULL)
478 			continue;
479 
480 		BMenuItem* item = fontSizeMenu->FindItem(size);
481 		if (item != NULL)
482 			item->SetMarked(true);
483 	}
484 }
485