xref: /haiku/src/apps/terminal/AppearPrefView.cpp (revision 125183f9e5c136781f71c879faaeab43fdc3ea7b)
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 <ColorControl.h>
17 #include <GridLayoutBuilder.h>
18 #include <GroupLayoutBuilder.h>
19 #include <LayoutBuilder.h>
20 #include <Locale.h>
21 #include <Menu.h>
22 #include <MenuField.h>
23 #include <MenuItem.h>
24 #include <PopUpMenu.h>
25 #include <TextControl.h>
26 #include <View.h>
27 
28 #include "PrefHandler.h"
29 #include "TermConst.h"
30 
31 
32 #undef TR_CONTEXT
33 #define TR_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 #if 0
83 		"",
84 		PREF_IM_FORE_COLOR,
85 		PREF_IM_BACK_COLOR,
86 		PREF_IM_SELECT_COLOR,
87 		"",
88 		PREF_ANSI_BLACK_COLOR,
89 		PREF_ANSI_RED_COLOR,
90 		PREF_ANSI_GREEN_COLOR,
91 		PREF_ANSI_YELLOW_COLOR,
92 		PREF_ANSI_BLUE_COLOR,
93 		PREF_ANSI_MAGENTA_COLOR,
94 		PREF_ANSI_CYAN_COLOR,
95 		PREF_ANSI_WHITE_COLOR,
96 #endif
97 		NULL
98   	};
99 
100 	SetLayout(new BGroupLayout(B_HORIZONTAL));
101 
102 	BMenu* fontMenu = _MakeFontMenu(MSG_HALF_FONT_CHANGED,
103 		PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY),
104 		PrefHandler::Default()->getString(PREF_HALF_FONT_STYLE));
105 
106 	BMenu* sizeMenu = _MakeSizeMenu(MSG_HALF_SIZE_CHANGED,
107 		PrefHandler::Default()->getInt32(PREF_HALF_FONT_SIZE));
108 
109 	fFont = new BMenuField(B_TRANSLATE("Font:"), fontMenu);
110 	fFontSize = new BMenuField(B_TRANSLATE("Size:"), sizeMenu);
111 	fColorField = new BMenuField(B_TRANSLATE("Color:"),
112 		_MakeMenu(MSG_COLOR_FIELD_CHANGED, kColorTable,
113 		kColorTable[0]));
114 
115 	BView* layoutView = BLayoutBuilder::Group<>()
116 		.SetInsets(5, 5, 5, 5)
117 		.AddGroup(B_VERTICAL, 5)
118 			.Add(BGridLayoutBuilder(5, 5)
119 				.Add(fFont->CreateLabelLayoutItem(), 0, 0)
120 				.Add(fFont->CreateMenuBarLayoutItem(), 1, 0)
121 				.Add(fFontSize->CreateLabelLayoutItem(), 0, 1)
122 				.Add(fFontSize->CreateMenuBarLayoutItem(), 1, 1)
123 				.Add(fColorField->CreateLabelLayoutItem(), 0, 2)
124 				.Add(fColorField->CreateMenuBarLayoutItem(), 1, 2)
125 				)
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 			.End()
131 		.End();
132 
133 	AddChild(layoutView);
134 
135 	fFont->SetAlignment(B_ALIGN_RIGHT);
136 	fFontSize->SetAlignment(B_ALIGN_RIGHT);
137 	fColorField->SetAlignment(B_ALIGN_RIGHT);
138 
139 	fColorControl->SetValue(PrefHandler::Default()->getRGB(PREF_TEXT_FORE_COLOR));
140 
141 	BTextControl* redInput = (BTextControl*)fColorControl->ChildAt(0);
142 	BTextControl* greenInput = (BTextControl*)fColorControl->ChildAt(1);
143 	BTextControl* blueInput = (BTextControl*)fColorControl->ChildAt(2);
144 
145 	redInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
146 	greenInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
147 	blueInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
148 }
149 
150 
151 void
152 AppearancePrefView::GetPreferredSize(float* _width, float* _height)
153 {
154 	if (_width)
155 		*_width = Bounds().Width();
156 
157 	if (*_height)
158 		*_height = fColorControl->Frame().bottom;
159 }
160 
161 
162 void
163 AppearancePrefView::Revert()
164 {
165 	fColorField->Menu()->ItemAt(0)->SetMarked(true);
166 	fColorControl->SetValue(PrefHandler::Default()->
167 		getRGB(PREF_TEXT_FORE_COLOR));
168 
169 	fFont->Menu()->FindItem(PrefHandler::Default()->getString(
170 		PREF_HALF_FONT_FAMILY))->SetMarked(true);
171 	fFontSize->Menu()->FindItem(PrefHandler::Default()->getString(
172 		PREF_HALF_FONT_FAMILY))->SetMarked(true);
173 }
174 
175 
176 void
177 AppearancePrefView::AttachedToWindow()
178 {
179 	fFontSize->Menu()->SetTargetForItems(this);
180 	fFont->Menu()->SetTargetForItems(this);
181 
182   	fColorControl->SetTarget(this);
183   	fColorField->Menu()->SetTargetForItems(this);
184 }
185 
186 
187 void
188 AppearancePrefView::MessageReceived(BMessage* msg)
189 {
190 	bool modified = false;
191 
192 	switch (msg->what) {
193 		case MSG_HALF_FONT_CHANGED:
194 		{
195 			const char* family = NULL;
196 			const char* style = NULL;
197 			msg->FindString("font_family", &family);
198 			msg->FindString("font_style", &style);
199 
200 			PrefHandler* pref = PrefHandler::Default();
201 			const char* currentFamily
202 				= pref->getString(PREF_HALF_FONT_FAMILY);
203 			const char* currentStyle
204 				= pref->getString(PREF_HALF_FONT_STYLE);
205 			if (currentFamily == NULL || strcmp(currentFamily, family)
206 				|| currentStyle == NULL || strcmp(currentStyle, style)) {
207 				pref->setString(PREF_HALF_FONT_FAMILY, family);
208 				pref->setString(PREF_HALF_FONT_STYLE, style);
209 				modified = true;
210 			}
211 			break;
212 		}
213 		case MSG_HALF_SIZE_CHANGED:
214 			if (strcmp(PrefHandler::Default()->getString(PREF_HALF_FONT_SIZE),
215 					fFontSize->Menu()->FindMarked()->Label())) {
216 
217 				PrefHandler::Default()->setString(PREF_HALF_FONT_SIZE,
218 					fFontSize->Menu()->FindMarked()->Label());
219 				modified = true;
220 			}
221 			break;
222 
223 		case MSG_COLOR_CHANGED: {
224 			rgb_color oldColor = PrefHandler::Default()->getRGB(
225 				fColorField->Menu()->FindMarked()->Label());
226 				if (oldColor != fColorControl->ValueAsColor()) {
227 					PrefHandler::Default()->setRGB(
228 						fColorField->Menu()->FindMarked()->Label(),
229 						fColorControl->ValueAsColor());
230 					modified = true;
231 				}
232 			}
233 			break;
234 
235 		case MSG_COLOR_FIELD_CHANGED:
236 			fColorControl->SetValue(PrefHandler::Default()->getRGB(
237 				fColorField->Menu()->FindMarked()->Label()));
238 			break;
239 
240 		default:
241 			BView::MessageReceived(msg);
242 			return;
243 	}
244 
245 	if (modified) {
246 		fTerminalMessenger.SendMessage(msg);
247 
248 		BMessenger messenger(this);
249 		messenger.SendMessage(MSG_PREF_MODIFIED);
250 	}
251 }
252 
253 
254 /*static*/ BMenu*
255 AppearancePrefView::_MakeFontMenu(uint32 command,
256 	const char* defaultFamily, const char* defaultStyle)
257 {
258 	BPopUpMenu* menu = new BPopUpMenu("");
259 	int32 numFamilies = count_font_families();
260 	uint32 flags;
261 
262 	for (int32 i = 0; i < numFamilies; i++) {
263 		font_family family;
264 		if (get_font_family(i, &family, &flags) == B_OK) {
265 			BFont font;
266 			font_style style;
267 			int32 numStyles = count_font_styles(family);
268 			for (int32 j = 0; j < numStyles; j++) {
269 				if (get_font_style(family, j, &style) == B_OK) {
270 					font.SetFamilyAndStyle(family, style);
271 					if (IsFontUsable(font)) {
272 						BMessage* message = new BMessage(command);
273 						message->AddString("font_family", family);
274 						message->AddString("font_style", style);
275 						char itemLabel[134];
276 						snprintf(itemLabel, sizeof(itemLabel),
277 							"%s - %s", family, style);
278 						BMenuItem* item = new BMenuItem(itemLabel,
279 							message);
280 						menu->AddItem(item);
281 						if (!strcmp(defaultFamily, family)
282 							&& !strcmp(defaultStyle, style))
283 							item->SetMarked(true);
284 					}
285 				}
286 			}
287 		}
288 	}
289 
290 	if (menu->FindMarked() == NULL)
291 		menu->ItemAt(0)->SetMarked(true);
292 
293 	return menu;
294 }
295 
296 
297 /*static*/ BMenu*
298 AppearancePrefView::_MakeSizeMenu(uint32 command, uint8 defaultSize)
299 {
300 	BPopUpMenu* menu = new BPopUpMenu("size");
301 	int32 sizes[] = {9, 10, 11, 12, 14, 16, 18, 0};
302 
303 	bool found = false;
304 
305 	for (uint32 i = 0; sizes[i]; i++) {
306 		BString string;
307 		string << sizes[i];
308 
309 		BMenuItem* item = new BMenuItem(string.String(), new BMessage(command));
310 		menu->AddItem(item);
311 
312 		if (sizes[i] == defaultSize) {
313 			item->SetMarked(true);
314 			found = true;
315 		}
316 	}
317 	if (!found) {
318 		for (uint32 i = 0; sizes[i]; i++) {
319 			if (sizes[i] > defaultSize) {
320 				BString string;
321 				string << defaultSize;
322 				BMenuItem* item = new BMenuItem(string.String(), new BMessage(command));
323 				item->SetMarked(true);
324 				menu->AddItem(item, i);
325 				break;
326 			}
327 		}
328 	}
329 
330 	return menu;
331 }
332 
333 
334 /*static*/ BPopUpMenu*
335 AppearancePrefView::_MakeMenu(uint32 msg, const char** items,
336 	const char* defaultItemName)
337 {
338 	BPopUpMenu* menu = new BPopUpMenu("");
339 
340 	int32 i = 0;
341 	while (*items) {
342 		if (!strcmp(*items, ""))
343 			menu->AddSeparatorItem();
344 		else
345 			menu->AddItem(new BMenuItem(*items, new BMessage(msg)));
346 		if (!strcmp(*items, defaultItemName))
347 			menu->ItemAt(i)->SetMarked(true);
348 
349 		items++;
350 		i++;
351 	}
352 	return menu;
353 }
354