xref: /haiku/src/preferences/screen/ScreenWindow.cpp (revision aed35104852941f0f6f3d1dcc5338b5f337d0a3c)
1 /*
2  * Copyright 2001-2012, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Rafael Romo
7  *		Stefano Ceccherini (burton666@libero.it)
8  *		Andrew Bachmann
9  *		Rene Gollent, rene@gollent.com
10  *		Thomas Kurschel
11  *		Axel Dörfler, axeld@pinc-software.de
12  *		Stephan Aßmus <superstippi@gmx.de>
13  *		Alexandre Deckner, alex@zappotek.com
14  */
15 
16 
17 #include "ScreenWindow.h"
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <Alert.h>
24 #include <Application.h>
25 #include <Box.h>
26 #include <Button.h>
27 #include <Catalog.h>
28 #include <Directory.h>
29 #include <File.h>
30 #include <FindDirectory.h>
31 #include <InterfaceDefs.h>
32 #include <LayoutBuilder.h>
33 #include <MenuBar.h>
34 #include <MenuItem.h>
35 #include <MenuField.h>
36 #include <Messenger.h>
37 #include <Path.h>
38 #include <PopUpMenu.h>
39 #include <Screen.h>
40 #include <String.h>
41 #include <StringView.h>
42 #include <Roster.h>
43 #include <Window.h>
44 
45 #include <InterfacePrivate.h>
46 
47 #include "AlertWindow.h"
48 #include "Constants.h"
49 #include "RefreshWindow.h"
50 #include "MonitorView.h"
51 #include "ScreenSettings.h"
52 #include "Utility.h"
53 
54 /* Note, this headers defines a *private* interface to the Radeon accelerant.
55  * It's a solution that works with the current BeOS interface that Haiku
56  * adopted.
57  * However, it's not a nice and clean solution. Don't use this header in any
58  * application if you can avoid it. No other driver is using this, or should
59  * be using this.
60  * It will be replaced as soon as we introduce an updated accelerant interface
61  * which may even happen before R1 hits the streets.
62  */
63 #include "multimon.h"	// the usual: DANGER WILL, ROBINSON!
64 
65 
66 #undef B_TRANSLATION_CONTEXT
67 #define B_TRANSLATION_CONTEXT "Screen"
68 
69 
70 const char* kBackgroundsSignature = "application/x-vnd.Haiku-Backgrounds";
71 
72 // list of officially supported colour spaces
73 static const struct {
74 	color_space	space;
75 	int32		bits_per_pixel;
76 	const char*	label;
77 } kColorSpaces[] = {
78 	{ B_CMAP8, 8, B_TRANSLATE("8 bits/pixel, 256 colors") },
79 	{ B_RGB15, 15, B_TRANSLATE("15 bits/pixel, 32768 colors") },
80 	{ B_RGB16, 16, B_TRANSLATE("16 bits/pixel, 65536 colors") },
81 	{ B_RGB24, 24, B_TRANSLATE("24 bits/pixel, 16 Million colors") },
82 	{ B_RGB32, 32, B_TRANSLATE("32 bits/pixel, 16 Million colors") }
83 };
84 static const int32 kColorSpaceCount
85 	= sizeof(kColorSpaces) / sizeof(kColorSpaces[0]);
86 
87 // list of standard refresh rates
88 static const int32 kRefreshRates[] = { 60, 70, 72, 75, 80, 85, 95, 100 };
89 static const int32 kRefreshRateCount
90 	= sizeof(kRefreshRates) / sizeof(kRefreshRates[0]);
91 
92 // list of combine modes
93 static const struct {
94 	combine_mode	mode;
95 	const char		*name;
96 } kCombineModes[] = {
97 	{ kCombineDisable, B_TRANSLATE("disable") },
98 	{ kCombineHorizontally, B_TRANSLATE("horizontally") },
99 	{ kCombineVertically, B_TRANSLATE("vertically") }
100 };
101 static const int32 kCombineModeCount
102 	= sizeof(kCombineModes) / sizeof(kCombineModes[0]);
103 
104 
105 static BString
106 tv_standard_to_string(uint32 mode)
107 {
108 	switch (mode) {
109 		case 0:		return "disabled";
110 		case 1:		return "NTSC";
111 		case 2:		return "NTSC Japan";
112 		case 3:		return "PAL BDGHI";
113 		case 4:		return "PAL M";
114 		case 5:		return "PAL N";
115 		case 6:		return "SECAM";
116 		case 101:	return "NTSC 443";
117 		case 102:	return "PAL 60";
118 		case 103:	return "PAL NC";
119 		default:
120 		{
121 			BString name;
122 			name << "??? (" << mode << ")";
123 
124 			return name;
125 		}
126 	}
127 }
128 
129 
130 static void
131 resolution_to_string(screen_mode& mode, BString &string)
132 {
133 	string << mode.width << " x " << mode.height;
134 }
135 
136 
137 static void
138 refresh_rate_to_string(float refresh, BString &string,
139 	bool appendUnit = true, bool alwaysWithFraction = false)
140 {
141 	snprintf(string.LockBuffer(32), 32, "%.*g", refresh >= 100.0 ? 4 : 3,
142 		refresh);
143 	string.UnlockBuffer();
144 
145 	if (appendUnit)
146 		string << " " << B_TRANSLATE("Hz");
147 }
148 
149 
150 static const char*
151 screen_errors(status_t status)
152 {
153 	switch (status) {
154 		case B_ENTRY_NOT_FOUND:
155 			return B_TRANSLATE("Unknown mode");
156 		// TODO: add more?
157 
158 		default:
159 			return strerror(status);
160 	}
161 }
162 
163 
164 //	#pragma mark -
165 
166 
167 ScreenWindow::ScreenWindow(ScreenSettings* settings)
168 	:
169 	BWindow(settings->WindowFrame(), B_TRANSLATE_SYSTEM_NAME("Screen"),
170 		B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE
171 		| B_AUTO_UPDATE_SIZE_LIMITS, B_ALL_WORKSPACES),
172 	fIsVesa(false),
173 	fBootWorkspaceApplied(false),
174 	fOtherRefresh(NULL),
175 	fScreenMode(this),
176 	fUndoScreenMode(this),
177 	fModified(false)
178 {
179 	BScreen screen(this);
180 
181 	accelerant_device_info info;
182 	if (screen.GetDeviceInfo(&info) == B_OK
183 		&& !strcasecmp(info.chipset, "VESA"))
184 		fIsVesa = true;
185 
186 	_UpdateOriginal();
187 	_BuildSupportedColorSpaces();
188 	fActive = fSelected = fOriginal;
189 
190 	fSettings = settings;
191 
192 	// we need the "Current Workspace" first to get its height
193 
194 	BPopUpMenu *popUpMenu = new BPopUpMenu(B_TRANSLATE("Current workspace"),
195 		true, true);
196 	fAllWorkspacesItem = new BMenuItem(B_TRANSLATE("All workspaces"),
197 		new BMessage(WORKSPACE_CHECK_MSG));
198 	popUpMenu->AddItem(fAllWorkspacesItem);
199 	BMenuItem *item = new BMenuItem(B_TRANSLATE("Current workspace"),
200 		new BMessage(WORKSPACE_CHECK_MSG));
201 
202 	popUpMenu->AddItem(item);
203 	fAllWorkspacesItem->SetMarked(true);
204 
205 	BMenuField* workspaceMenuField = new BMenuField("WorkspaceMenu", NULL,
206 		popUpMenu);
207 	workspaceMenuField->ResizeToPreferred();
208 
209 	// box on the left with workspace count and monitor view
210 
211 	BBox* screenBox = new BBox("screen box");
212 	BGroupLayout* layout = new BGroupLayout(B_VERTICAL, 5.0);
213 	layout->SetInsets(10, 10, 10, 10);
214 	screenBox->SetLayout(layout);
215 
216 	fMonitorInfo = new BStringView("monitor info", "");
217 	screenBox->AddChild(fMonitorInfo);
218 
219 	fMonitorView = new MonitorView(BRect(0.0, 0.0, 80.0, 80.0),
220 		"monitor", screen.Frame().IntegerWidth() + 1,
221 		screen.Frame().IntegerHeight() + 1);
222 	screenBox->AddChild(fMonitorView);
223 
224 	fColumnsControl = new BTextControl(B_TRANSLATE("Columns:"), "0",
225 		new BMessage(kMsgWorkspaceColumnsChanged));
226 	fRowsControl = new BTextControl(B_TRANSLATE("Rows:"), "0",
227 		new BMessage(kMsgWorkspaceRowsChanged));
228 
229 	screenBox->AddChild(BLayoutBuilder::Grid<>(5.0, 5.0)
230 		.Add(new BStringView("", B_TRANSLATE("Workspaces")), 0, 0, 3)
231 		.AddTextControl(fColumnsControl, 0, 1, B_ALIGN_RIGHT)
232 		.AddGroup(B_HORIZONTAL, 0, 2, 1)
233 			.Add(_CreateColumnRowButton(true, false))
234 			.Add(_CreateColumnRowButton(true, true))
235 			.End()
236 		.AddTextControl(fRowsControl, 0, 2, B_ALIGN_RIGHT)
237 		.AddGroup(B_HORIZONTAL, 0, 2, 2)
238 			.Add(_CreateColumnRowButton(false, false))
239 			.Add(_CreateColumnRowButton(false, true))
240 			.End()
241 		.View());
242 
243 	fBackgroundsButton = new BButton("BackgroundsButton",
244 		B_TRANSLATE("Set background" B_UTF8_ELLIPSIS),
245 		new BMessage(BUTTON_LAUNCH_BACKGROUNDS_MSG));
246 	fBackgroundsButton->SetFontSize(be_plain_font->Size() * 0.9);
247 	screenBox->AddChild(fBackgroundsButton);
248 
249 	// box on the right with screen resolution, etc.
250 
251 	BBox* controlsBox = new BBox("controls box");
252 	controlsBox->SetLabel(workspaceMenuField);
253 	BGroupView* outerControlsView = new BGroupView(B_VERTICAL, 10.0);
254 	outerControlsView->GroupLayout()->SetInsets(10, 10, 10, 10);
255 	controlsBox->AddChild(outerControlsView);
256 
257 	fResolutionMenu = new BPopUpMenu("resolution", true, true);
258 
259 	uint16 maxWidth = 0;
260 	uint16 maxHeight = 0;
261 	uint16 previousWidth = 0;
262 	uint16 previousHeight = 0;
263 	for (int32 i = 0; i < fScreenMode.CountModes(); i++) {
264 		screen_mode mode = fScreenMode.ModeAt(i);
265 
266 		if (mode.width == previousWidth && mode.height == previousHeight)
267 			continue;
268 
269 		previousWidth = mode.width;
270 		previousHeight = mode.height;
271 		if (maxWidth < mode.width)
272 			maxWidth = mode.width;
273 		if (maxHeight < mode.height)
274 			maxHeight = mode.height;
275 
276 		BMessage* message = new BMessage(POP_RESOLUTION_MSG);
277 		message->AddInt32("width", mode.width);
278 		message->AddInt32("height", mode.height);
279 
280 		BString name;
281 		name << mode.width << " x " << mode.height;
282 
283 		fResolutionMenu->AddItem(new BMenuItem(name.String(), message));
284 	}
285 
286 	fMonitorView->SetMaxResolution(maxWidth, maxHeight);
287 
288 	fResolutionField = new BMenuField("ResolutionMenu",
289 		B_TRANSLATE("Resolution:"), fResolutionMenu);
290 
291 	fColorsMenu = new BPopUpMenu("colors", true, false);
292 
293 	for (int32 i = 0; i < kColorSpaceCount; i++) {
294 		if ((fSupportedColorSpaces & (1 << i)) == 0)
295 			continue;
296 
297 		BMessage* message = new BMessage(POP_COLORS_MSG);
298 		message->AddInt32("bits_per_pixel", kColorSpaces[i].bits_per_pixel);
299 		message->AddInt32("space", kColorSpaces[i].space);
300 
301 		BMenuItem* item = new BMenuItem(kColorSpaces[i].label, message);
302 		if (kColorSpaces[i].space == screen.ColorSpace())
303 			fUserSelectedColorSpace = item;
304 
305 		fColorsMenu->AddItem(item);
306 	}
307 
308 	fColorsField = new BMenuField("ColorsMenu", B_TRANSLATE("Colors:"),
309 		fColorsMenu);
310 
311 	fRefreshMenu = new BPopUpMenu("refresh rate", true, true);
312 
313 	float min, max;
314 	if (fScreenMode.GetRefreshLimits(fActive, min, max) != B_OK) {
315 		// if we couldn't obtain the refresh limits, reset to the default
316 		// range. Constraints from detected monitors will fine-tune this
317 		// later.
318 		min = kRefreshRates[0];
319 		max = kRefreshRates[kRefreshRateCount - 1];
320 	}
321 
322 	if (min == max) {
323 		// This is a special case for drivers that only support a single
324 		// frequency, like the VESA driver
325 		BString name;
326 		refresh_rate_to_string(min, name);
327 		BMessage *message = new BMessage(POP_REFRESH_MSG);
328 		message->AddFloat("refresh", min);
329 		BMenuItem *item = new BMenuItem(name.String(), message);
330 		fRefreshMenu->AddItem(item);
331 		item->SetEnabled(false);
332 	} else {
333 		monitor_info info;
334 		if (fScreenMode.GetMonitorInfo(info) == B_OK) {
335 			min = max_c(info.min_vertical_frequency, min);
336 			max = min_c(info.max_vertical_frequency, max);
337 		}
338 
339 		for (int32 i = 0; i < kRefreshRateCount; ++i) {
340 			if (kRefreshRates[i] < min || kRefreshRates[i] > max)
341 				continue;
342 
343 			BString name;
344 			name << kRefreshRates[i] << " " << B_TRANSLATE("Hz");
345 
346 			BMessage *message = new BMessage(POP_REFRESH_MSG);
347 			message->AddFloat("refresh", kRefreshRates[i]);
348 
349 			fRefreshMenu->AddItem(new BMenuItem(name.String(), message));
350 		}
351 
352 		fOtherRefresh = new BMenuItem(B_TRANSLATE("Other" B_UTF8_ELLIPSIS),
353 			new BMessage(POP_OTHER_REFRESH_MSG));
354 		fRefreshMenu->AddItem(fOtherRefresh);
355 	}
356 
357 	fRefreshField = new BMenuField("RefreshMenu", B_TRANSLATE("Refresh rate:"),
358 		fRefreshMenu);
359 
360 	if (_IsVesa())
361 		fRefreshField->Hide();
362 
363 	// enlarged area for multi-monitor settings
364 	{
365 		bool dummy;
366 		uint32 dummy32;
367 		bool multiMonSupport;
368 		bool useLaptopPanelSupport;
369 		bool tvStandardSupport;
370 
371 		multiMonSupport = TestMultiMonSupport(&screen) == B_OK;
372 		useLaptopPanelSupport = GetUseLaptopPanel(&screen, &dummy) == B_OK;
373 		tvStandardSupport = GetTVStandard(&screen, &dummy32) == B_OK;
374 
375 		// even if there is no support, we still create all controls
376 		// to make sure we don't access NULL pointers later on
377 
378 		fCombineMenu = new BPopUpMenu("CombineDisplays",
379 			true, true);
380 
381 		for (int32 i = 0; i < kCombineModeCount; i++) {
382 			BMessage *message = new BMessage(POP_COMBINE_DISPLAYS_MSG);
383 			message->AddInt32("mode", kCombineModes[i].mode);
384 
385 			fCombineMenu->AddItem(new BMenuItem(kCombineModes[i].name,
386 				message));
387 		}
388 
389 		fCombineField = new BMenuField("CombineMenu",
390 			B_TRANSLATE("Combine displays:"), fCombineMenu);
391 
392 		if (!multiMonSupport)
393 			fCombineField->Hide();
394 
395 		fSwapDisplaysMenu = new BPopUpMenu("SwapDisplays",
396 			true, true);
397 
398 		// !order is important - we rely that boolean value == idx
399 		BMessage *message = new BMessage(POP_SWAP_DISPLAYS_MSG);
400 		message->AddBool("swap", false);
401 		fSwapDisplaysMenu->AddItem(new BMenuItem(B_TRANSLATE("no"), message));
402 
403 		message = new BMessage(POP_SWAP_DISPLAYS_MSG);
404 		message->AddBool("swap", true);
405 		fSwapDisplaysMenu->AddItem(new BMenuItem(B_TRANSLATE("yes"), message));
406 
407 		fSwapDisplaysField = new BMenuField("SwapMenu",
408 			B_TRANSLATE("Swap displays:"), fSwapDisplaysMenu);
409 
410 		if (!multiMonSupport)
411 			fSwapDisplaysField->Hide();
412 
413 		fUseLaptopPanelMenu = new BPopUpMenu("UseLaptopPanel",
414 			true, true);
415 
416 		// !order is important - we rely that boolean value == idx
417 		message = new BMessage(POP_USE_LAPTOP_PANEL_MSG);
418 		message->AddBool("use", false);
419 		fUseLaptopPanelMenu->AddItem(new BMenuItem(B_TRANSLATE("if needed"),
420 			message));
421 
422 		message = new BMessage(POP_USE_LAPTOP_PANEL_MSG);
423 		message->AddBool("use", true);
424 		fUseLaptopPanelMenu->AddItem(new BMenuItem(B_TRANSLATE("always"),
425 			message));
426 
427 		fUseLaptopPanelField = new BMenuField("UseLaptopPanel",
428 			B_TRANSLATE("Use laptop panel:"), fUseLaptopPanelMenu);
429 
430 		if (!useLaptopPanelSupport)
431 			fUseLaptopPanelField->Hide();
432 
433 		fTVStandardMenu = new BPopUpMenu("TVStandard", true, true);
434 
435 		// arbitrary limit
436 		uint32 i;
437 		for (i = 0; i < 100; ++i) {
438 			uint32 mode;
439 			if (GetNthSupportedTVStandard(&screen, i, &mode) != B_OK)
440 				break;
441 
442 			BString name = tv_standard_to_string(mode);
443 
444 			message = new BMessage(POP_TV_STANDARD_MSG);
445 			message->AddInt32("tv_standard", mode);
446 
447 			fTVStandardMenu->AddItem(new BMenuItem(name.String(), message));
448 		}
449 
450 		fTVStandardField = new BMenuField("tv standard",
451 			B_TRANSLATE("Video format:"), fTVStandardMenu);
452 		fTVStandardField->SetAlignment(B_ALIGN_RIGHT);
453 
454 		if (!tvStandardSupport || i == 0)
455 			fTVStandardField->Hide();
456 	}
457 
458 	BLayoutBuilder::Group<>(outerControlsView)
459 		.AddGrid(5.0, 5.0)
460 			.AddMenuField(fResolutionField, 0, 0, B_ALIGN_RIGHT)
461 			.AddMenuField(fColorsField, 0, 1, B_ALIGN_RIGHT)
462 			.AddMenuField(fRefreshField, 0, 2, B_ALIGN_RIGHT)
463 			.AddMenuField(fCombineField, 0, 3, B_ALIGN_RIGHT)
464 			.AddMenuField(fSwapDisplaysField, 0, 4, B_ALIGN_RIGHT)
465 			.AddMenuField(fUseLaptopPanelField, 0, 5, B_ALIGN_RIGHT)
466 			.AddMenuField(fTVStandardField, 0, 6, B_ALIGN_RIGHT)
467 		.End();
468 
469 	// TODO: we don't support getting the screen's preferred settings
470 	/* fDefaultsButton = new BButton(buttonRect, "DefaultsButton", "Defaults",
471 		new BMessage(BUTTON_DEFAULTS_MSG));*/
472 
473 	fApplyButton = new BButton("ApplyButton", B_TRANSLATE("Apply"),
474 		new BMessage(BUTTON_APPLY_MSG));
475 	fApplyButton->SetEnabled(false);
476 	BLayoutBuilder::Group<>(outerControlsView)
477 		.AddGlue()
478 			.AddGroup(B_HORIZONTAL)
479 			.AddGlue()
480 			.Add(fApplyButton);
481 
482 	fRevertButton = new BButton("RevertButton", B_TRANSLATE("Revert"),
483 		new BMessage(BUTTON_REVERT_MSG));
484 	fRevertButton->SetEnabled(false);
485 
486 	BLayoutBuilder::Group<>(this, B_VERTICAL, 10.0)
487 		.SetInsets(10, 10, 10, 10)
488 		.AddGroup(B_HORIZONTAL, 10.0)
489 			.AddGroup(B_VERTICAL)
490 				.AddStrut(floor(controlsBox->TopBorderOffset() / 16) - 1)
491 				.Add(screenBox)
492 			.End()
493 			.Add(controlsBox)
494 		.End()
495 		.AddGroup(B_HORIZONTAL, 10.0)
496 			.Add(fRevertButton)
497 			.AddGlue();
498 
499 	_UpdateControls();
500 	_UpdateMonitor();
501 }
502 
503 
504 ScreenWindow::~ScreenWindow()
505 {
506 	delete fSettings;
507 }
508 
509 
510 bool
511 ScreenWindow::QuitRequested()
512 {
513 	fSettings->SetWindowFrame(Frame());
514 
515 	// Write mode of workspace 0 (the boot workspace) to the vesa settings file
516 	screen_mode vesaMode;
517 	if (fBootWorkspaceApplied && fScreenMode.Get(vesaMode, 0) == B_OK) {
518 		status_t status = _WriteVesaModeFile(vesaMode);
519 		if (status < B_OK) {
520 			BString warning = B_TRANSLATE("Could not write VESA mode settings"
521 				" file:\n\t");
522 			warning << strerror(status);
523 			BAlert* alert = new BAlert(B_TRANSLATE("Warning"),
524 				warning.String(), B_TRANSLATE("OK"), NULL,
525 				NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
526 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
527 			alert->Go();
528 		}
529 	}
530 
531 	be_app->PostMessage(B_QUIT_REQUESTED);
532 
533 	return BWindow::QuitRequested();
534 }
535 
536 
537 /*!	Update resolution list according to combine mode
538 	(some resolutions may not be combinable due to memory restrictions).
539 */
540 void
541 ScreenWindow::_CheckResolutionMenu()
542 {
543 	for (int32 i = 0; i < fResolutionMenu->CountItems(); i++)
544 		fResolutionMenu->ItemAt(i)->SetEnabled(false);
545 
546 	for (int32 i = 0; i < fScreenMode.CountModes(); i++) {
547 		screen_mode mode = fScreenMode.ModeAt(i);
548 		if (mode.combine != fSelected.combine)
549 			continue;
550 
551 		BString name;
552 		name << mode.width << " x " << mode.height;
553 
554 		BMenuItem *item = fResolutionMenu->FindItem(name.String());
555 		if (item != NULL)
556 			item->SetEnabled(true);
557 	}
558 }
559 
560 
561 /*!	Update color and refresh options according to current mode
562 	(a color space is made active if there is any mode with
563 	given resolution and this colour space; same applies for
564 	refresh rate, though "Other…" is always possible)
565 */
566 void
567 ScreenWindow::_CheckColorMenu()
568 {
569 	int32 supportsAnything = false;
570 	int32 index = 0;
571 
572 	for (int32 i = 0; i < kColorSpaceCount; i++) {
573 		if ((fSupportedColorSpaces & (1 << i)) == 0)
574 			continue;
575 
576 		bool supported = false;
577 
578 		for (int32 j = 0; j < fScreenMode.CountModes(); j++) {
579 			screen_mode mode = fScreenMode.ModeAt(j);
580 
581 			if (fSelected.width == mode.width
582 				&& fSelected.height == mode.height
583 				&& kColorSpaces[i].space == mode.space
584 				&& fSelected.combine == mode.combine) {
585 				supportsAnything = true;
586 				supported = true;
587 				break;
588 			}
589 		}
590 
591 		BMenuItem* item = fColorsMenu->ItemAt(index++);
592 		if (item)
593 			item->SetEnabled(supported);
594 	}
595 
596 	fColorsField->SetEnabled(supportsAnything);
597 
598 	if (!supportsAnything)
599 		return;
600 
601 	// Make sure a valid item is selected
602 
603 	BMenuItem* item = fColorsMenu->FindMarked();
604 	bool changed = false;
605 
606 	if (item != fUserSelectedColorSpace) {
607 		if (fUserSelectedColorSpace != NULL
608 			&& fUserSelectedColorSpace->IsEnabled()) {
609 			fUserSelectedColorSpace->SetMarked(true);
610 			item = fUserSelectedColorSpace;
611 			changed = true;
612 		}
613 	}
614 	if (item != NULL && !item->IsEnabled()) {
615 		// find the next best item
616 		int32 index = fColorsMenu->IndexOf(item);
617 		bool found = false;
618 
619 		for (int32 i = index + 1; i < fColorsMenu->CountItems(); i++) {
620 			item = fColorsMenu->ItemAt(i);
621 			if (item->IsEnabled()) {
622 				found = true;
623 				break;
624 			}
625 		}
626 		if (!found) {
627 			// search backwards as well
628 			for (int32 i = index - 1; i >= 0; i--) {
629 				item = fColorsMenu->ItemAt(i);
630 				if (item->IsEnabled())
631 					break;
632 			}
633 		}
634 
635 		item->SetMarked(true);
636 		changed = true;
637 	}
638 
639 	if (changed) {
640 		// Update selected space
641 
642 		BMessage* message = item->Message();
643 		int32 space;
644 		if (message->FindInt32("space", &space) == B_OK) {
645 			fSelected.space = (color_space)space;
646 			_UpdateColorLabel();
647 		}
648 	}
649 }
650 
651 
652 /*!	Enable/disable refresh options according to current mode. */
653 void
654 ScreenWindow::_CheckRefreshMenu()
655 {
656 	float min, max;
657 	if (fScreenMode.GetRefreshLimits(fSelected, min, max) != B_OK || min == max)
658 		return;
659 
660 	for (int32 i = fRefreshMenu->CountItems(); i-- > 0;) {
661 		BMenuItem* item = fRefreshMenu->ItemAt(i);
662 		BMessage* message = item->Message();
663 		float refresh;
664 		if (message != NULL && message->FindFloat("refresh", &refresh) == B_OK)
665 			item->SetEnabled(refresh >= min && refresh <= max);
666 	}
667 }
668 
669 
670 /*!	Activate appropriate menu item according to selected refresh rate */
671 void
672 ScreenWindow::_UpdateRefreshControl()
673 {
674 	for (int32 i = 0; i < fRefreshMenu->CountItems(); i++) {
675 		BMenuItem* item = fRefreshMenu->ItemAt(i);
676 		if (item->Message()->FindFloat("refresh") == fSelected.refresh) {
677 			item->SetMarked(true);
678 			// "Other" items only contains a refresh rate when active
679 			fOtherRefresh->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS));
680 			return;
681 		}
682 	}
683 
684 	// this is a non-standard refresh rate
685 	if (fOtherRefresh != NULL) {
686 		fOtherRefresh->Message()->ReplaceFloat("refresh", fSelected.refresh);
687 		fOtherRefresh->SetMarked(true);
688 
689 		BString string;
690 		refresh_rate_to_string(fSelected.refresh, string);
691 		fRefreshMenu->Superitem()->SetLabel(string.String());
692 
693 		string.Append(B_TRANSLATE("/other" B_UTF8_ELLIPSIS));
694 		fOtherRefresh->SetLabel(string.String());
695 	}
696 }
697 
698 
699 void
700 ScreenWindow::_UpdateMonitorView()
701 {
702 	BMessage updateMessage(UPDATE_DESKTOP_MSG);
703 	updateMessage.AddInt32("width", fSelected.width);
704 	updateMessage.AddInt32("height", fSelected.height);
705 
706 	PostMessage(&updateMessage, fMonitorView);
707 }
708 
709 
710 void
711 ScreenWindow::_UpdateControls()
712 {
713 	_UpdateWorkspaceButtons();
714 
715 	BMenuItem* item = fSwapDisplaysMenu->ItemAt((int32)fSelected.swap_displays);
716 	if (item && !item->IsMarked())
717 		item->SetMarked(true);
718 
719 	item = fUseLaptopPanelMenu->ItemAt((int32)fSelected.use_laptop_panel);
720 	if (item && !item->IsMarked())
721 		item->SetMarked(true);
722 
723 	for (int32 i = 0; i < fTVStandardMenu->CountItems(); i++) {
724 		item = fTVStandardMenu->ItemAt(i);
725 
726 		uint32 tvStandard;
727 		item->Message()->FindInt32("tv_standard", (int32 *)&tvStandard);
728 		if (tvStandard == fSelected.tv_standard) {
729 			if (!item->IsMarked())
730 				item->SetMarked(true);
731 			break;
732 		}
733 	}
734 
735 	_CheckResolutionMenu();
736 	_CheckColorMenu();
737 	_CheckRefreshMenu();
738 
739 	BString string;
740 	resolution_to_string(fSelected, string);
741 	item = fResolutionMenu->FindItem(string.String());
742 
743 	if (item != NULL) {
744 		if (!item->IsMarked())
745 			item->SetMarked(true);
746 	} else {
747 		// this is bad luck - if mode has been set via screen references,
748 		// this case cannot occur; there are three possible solutions:
749 		// 1. add a new resolution to list
750 		//    - we had to remove it as soon as a "valid" one is selected
751 		//    - we don't know which frequencies/bit depths are supported
752 		//    - as long as we haven't the GMT formula to create
753 		//      parameters for any resolution given, we cannot
754 		//      really set current mode - it's just not in the list
755 		// 2. choose nearest resolution
756 		//    - probably a good idea, but implies coding and testing
757 		// 3. choose lowest resolution
758 		//    - do you really think we are so lazy? yes, we are
759 		item = fResolutionMenu->ItemAt(0);
760 		if (item)
761 			item->SetMarked(true);
762 
763 		// okay - at least we set menu label to active resolution
764 		fResolutionMenu->Superitem()->SetLabel(string.String());
765 	}
766 
767 	// mark active combine mode
768 	for (int32 i = 0; i < kCombineModeCount; i++) {
769 		if (kCombineModes[i].mode == fSelected.combine) {
770 			item = fCombineMenu->ItemAt(i);
771 			if (item && !item->IsMarked())
772 				item->SetMarked(true);
773 			break;
774 		}
775 	}
776 
777 	item = fColorsMenu->ItemAt(0);
778 
779 	for (int32 i = 0, index = 0; i <  kColorSpaceCount; i++) {
780 		if ((fSupportedColorSpaces & (1 << i)) == 0)
781 			continue;
782 
783 		if (kColorSpaces[i].space == fSelected.space) {
784 			item = fColorsMenu->ItemAt(index);
785 			break;
786 		}
787 
788 		index++;
789 	}
790 
791 	if (item && !item->IsMarked())
792 		item->SetMarked(true);
793 
794 	_UpdateColorLabel();
795 	_UpdateMonitorView();
796 	_UpdateRefreshControl();
797 
798 	_CheckApplyEnabled();
799 }
800 
801 
802 /*! Reflect active mode in chosen settings */
803 void
804 ScreenWindow::_UpdateActiveMode()
805 {
806 	// Usually, this function gets called after a mode
807 	// has been set manually; still, as the graphics driver
808 	// is free to fiddle with mode passed, we better ask
809 	// what kind of mode we actually got
810 	fScreenMode.Get(fActive);
811 	fSelected = fActive;
812 
813 	_UpdateMonitor();
814 	_UpdateControls();
815 }
816 
817 
818 void
819 ScreenWindow::_UpdateWorkspaceButtons()
820 {
821 	uint32 columns;
822 	uint32 rows;
823 	BPrivate::get_workspaces_layout(&columns, &rows);
824 
825 	char text[32];
826 	snprintf(text, sizeof(text), "%ld", columns);
827 	fColumnsControl->SetText(text);
828 
829 	snprintf(text, sizeof(text), "%ld", rows);
830 	fRowsControl->SetText(text);
831 
832 	_GetColumnRowButton(true, false)->SetEnabled(columns != 1 && rows != 32);
833 	_GetColumnRowButton(true, true)->SetEnabled((columns + 1) * rows < 32);
834 	_GetColumnRowButton(false, false)->SetEnabled(rows != 1 && columns != 32);
835 	_GetColumnRowButton(false, true)->SetEnabled(columns * (rows + 1) < 32);
836 }
837 
838 
839 void
840 ScreenWindow::ScreenChanged(BRect frame, color_space mode)
841 {
842 	// move window on screen, if necessary
843 	if (frame.right <= Frame().right
844 		&& frame.bottom <= Frame().bottom) {
845 		MoveTo((frame.Width() - Frame().Width()) / 2,
846 			(frame.Height() - Frame().Height()) / 2);
847 	}
848 }
849 
850 
851 void
852 ScreenWindow::WorkspaceActivated(int32 workspace, bool state)
853 {
854 	fScreenMode.GetOriginalMode(fOriginal, workspace);
855 	_UpdateActiveMode();
856 
857 	BMessage message(UPDATE_DESKTOP_COLOR_MSG);
858 	PostMessage(&message, fMonitorView);
859 }
860 
861 
862 void
863 ScreenWindow::MessageReceived(BMessage* message)
864 {
865 	switch (message->what) {
866 		case WORKSPACE_CHECK_MSG:
867 			_CheckApplyEnabled();
868 			break;
869 
870 		case kMsgWorkspaceLayoutChanged:
871 		{
872 			int32 deltaX = 0;
873 			int32 deltaY = 0;
874 			message->FindInt32("delta_x", &deltaX);
875 			message->FindInt32("delta_y", &deltaY);
876 
877 			if (deltaX == 0 && deltaY == 0)
878 				break;
879 
880 			uint32 newColumns;
881 			uint32 newRows;
882 			BPrivate::get_workspaces_layout(&newColumns, &newRows);
883 
884 			newColumns += deltaX;
885 			newRows += deltaY;
886 			BPrivate::set_workspaces_layout(newColumns, newRows);
887 
888 			_UpdateWorkspaceButtons();
889 			_CheckApplyEnabled();
890 			break;
891 		}
892 
893 		case kMsgWorkspaceColumnsChanged:
894 		{
895 			uint32 newColumns = strtoul(fColumnsControl->Text(), NULL, 10);
896 
897 			uint32 rows;
898 			BPrivate::get_workspaces_layout(NULL, &rows);
899 			BPrivate::set_workspaces_layout(newColumns, rows);
900 
901 			_UpdateWorkspaceButtons();
902 			_CheckApplyEnabled();
903 			break;
904 		}
905 
906 		case kMsgWorkspaceRowsChanged:
907 		{
908 			uint32 newRows = strtoul(fRowsControl->Text(), NULL, 10);
909 
910 			uint32 columns;
911 			BPrivate::get_workspaces_layout(&columns, NULL);
912 			BPrivate::set_workspaces_layout(columns, newRows);
913 
914 			_UpdateWorkspaceButtons();
915 			_CheckApplyEnabled();
916 			break;
917 		}
918 
919 		case POP_RESOLUTION_MSG:
920 		{
921 			message->FindInt32("width", &fSelected.width);
922 			message->FindInt32("height", &fSelected.height);
923 
924 			_CheckColorMenu();
925 			_CheckRefreshMenu();
926 
927 			_UpdateMonitorView();
928 			_UpdateRefreshControl();
929 
930 			_CheckApplyEnabled();
931 			break;
932 		}
933 
934 		case POP_COLORS_MSG:
935 		{
936 			int32 space;
937 			if (message->FindInt32("space", &space) != B_OK)
938 				break;
939 
940 			int32 index;
941 			if (message->FindInt32("index", &index) == B_OK
942 				&& fColorsMenu->ItemAt(index) != NULL)
943 				fUserSelectedColorSpace = fColorsMenu->ItemAt(index);
944 
945 			fSelected.space = (color_space)space;
946 			_UpdateColorLabel();
947 
948 			_CheckApplyEnabled();
949 			break;
950 		}
951 
952 		case POP_REFRESH_MSG:
953 		{
954 			message->FindFloat("refresh", &fSelected.refresh);
955 			fOtherRefresh->SetLabel(B_TRANSLATE("Other" B_UTF8_ELLIPSIS));
956 				// revert "Other…" label - it might have a refresh rate prefix
957 
958 			_CheckApplyEnabled();
959 			break;
960 		}
961 
962 		case POP_OTHER_REFRESH_MSG:
963 		{
964 			// make sure menu shows something useful
965 			_UpdateRefreshControl();
966 
967 			float min = 0, max = 999;
968 			fScreenMode.GetRefreshLimits(fSelected, min, max);
969 			if (min < gMinRefresh)
970 				min = gMinRefresh;
971 			if (max > gMaxRefresh)
972 				max = gMaxRefresh;
973 
974 			monitor_info info;
975 			if (fScreenMode.GetMonitorInfo(info) == B_OK) {
976 				min = max_c(info.min_vertical_frequency, min);
977 				max = min_c(info.max_vertical_frequency, max);
978 			}
979 
980 			RefreshWindow *fRefreshWindow = new RefreshWindow(
981 				fRefreshField->ConvertToScreen(B_ORIGIN), fSelected.refresh,
982 				min, max);
983 			fRefreshWindow->Show();
984 			break;
985 		}
986 
987 		case SET_CUSTOM_REFRESH_MSG:
988 		{
989 			// user pressed "done" in "Other…" refresh dialog;
990 			// select the refresh rate chosen
991 			message->FindFloat("refresh", &fSelected.refresh);
992 
993 			_UpdateRefreshControl();
994 			_CheckApplyEnabled();
995 			break;
996 		}
997 
998 		case POP_COMBINE_DISPLAYS_MSG:
999 		{
1000 			// new combine mode has bee chosen
1001 			int32 mode;
1002 			if (message->FindInt32("mode", &mode) == B_OK)
1003 				fSelected.combine = (combine_mode)mode;
1004 
1005 			_CheckResolutionMenu();
1006 			_CheckApplyEnabled();
1007 			break;
1008 		}
1009 
1010 		case POP_SWAP_DISPLAYS_MSG:
1011 			message->FindBool("swap", &fSelected.swap_displays);
1012 			_CheckApplyEnabled();
1013 			break;
1014 
1015 		case POP_USE_LAPTOP_PANEL_MSG:
1016 			message->FindBool("use", &fSelected.use_laptop_panel);
1017 			_CheckApplyEnabled();
1018 			break;
1019 
1020 		case POP_TV_STANDARD_MSG:
1021 			message->FindInt32("tv_standard", (int32 *)&fSelected.tv_standard);
1022 			_CheckApplyEnabled();
1023 			break;
1024 
1025 		case BUTTON_LAUNCH_BACKGROUNDS_MSG:
1026 			if (be_roster->Launch(kBackgroundsSignature) == B_ALREADY_RUNNING) {
1027 				app_info info;
1028 				be_roster->GetAppInfo(kBackgroundsSignature, &info);
1029 				be_roster->ActivateApp(info.team);
1030 			}
1031 			break;
1032 
1033 		case BUTTON_DEFAULTS_MSG:
1034 		{
1035 			// TODO: get preferred settings of screen
1036 			fSelected.width = 640;
1037 			fSelected.height = 480;
1038 			fSelected.space = B_CMAP8;
1039 			fSelected.refresh = 60.0;
1040 			fSelected.combine = kCombineDisable;
1041 			fSelected.swap_displays = false;
1042 			fSelected.use_laptop_panel = false;
1043 			fSelected.tv_standard = 0;
1044 
1045 			// TODO: workspace defaults
1046 
1047 			_UpdateControls();
1048 			break;
1049 		}
1050 
1051 		case BUTTON_UNDO_MSG:
1052 			fUndoScreenMode.Revert();
1053 			_UpdateActiveMode();
1054 			break;
1055 
1056 		case BUTTON_REVERT_MSG:
1057 		{
1058 			fModified = false;
1059 			fBootWorkspaceApplied = false;
1060 
1061 			// ScreenMode::Revert() assumes that we first set the correct
1062 			// number of workspaces
1063 
1064 			BPrivate::set_workspaces_layout(fOriginalWorkspacesColumns,
1065 				fOriginalWorkspacesRows);
1066 			_UpdateWorkspaceButtons();
1067 
1068 			fScreenMode.Revert();
1069 			_UpdateActiveMode();
1070 			break;
1071 		}
1072 
1073 		case BUTTON_APPLY_MSG:
1074 			_Apply();
1075 			break;
1076 
1077 		case MAKE_INITIAL_MSG:
1078 			// user pressed "keep" in confirmation dialog
1079 			fModified = true;
1080 			_UpdateActiveMode();
1081 			break;
1082 
1083 		default:
1084 			BWindow::MessageReceived(message);
1085 			break;
1086 	}
1087 }
1088 
1089 
1090 status_t
1091 ScreenWindow::_WriteVesaModeFile(const screen_mode& mode) const
1092 {
1093 	BPath path;
1094 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
1095 	if (status < B_OK)
1096 		return status;
1097 
1098 	path.Append("kernel/drivers");
1099 	status = create_directory(path.Path(), 0755);
1100 	if (status < B_OK)
1101 		return status;
1102 
1103 	path.Append("vesa");
1104 	BFile file;
1105 	status = file.SetTo(path.Path(), B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE);
1106 	if (status < B_OK)
1107 		return status;
1108 
1109 	char buffer[256];
1110 	snprintf(buffer, sizeof(buffer), "mode %ld %ld %ld\n",
1111 		mode.width, mode.height, mode.BitsPerPixel());
1112 
1113 	ssize_t bytesWritten = file.Write(buffer, strlen(buffer));
1114 	if (bytesWritten < B_OK)
1115 		return bytesWritten;
1116 
1117 	return B_OK;
1118 }
1119 
1120 
1121 BButton*
1122 ScreenWindow::_CreateColumnRowButton(bool columns, bool plus)
1123 {
1124 	BMessage* message = new BMessage(kMsgWorkspaceLayoutChanged);
1125 	message->AddInt32("delta_x", columns ? (plus ? 1 : -1) : 0);
1126 	message->AddInt32("delta_y", !columns ? (plus ? 1 : -1) : 0);
1127 
1128 	BButton* button = new BButton(plus ? "+" : "-", message);
1129 	button->SetFontSize(be_plain_font->Size() * 0.9);
1130 
1131 	BSize size = button->MinSize();
1132 	size.width = button->StringWidth("+") + 16;
1133 	button->SetExplicitMinSize(size);
1134 	button->SetExplicitMaxSize(size);
1135 
1136 	fWorkspacesButtons[(columns ? 0 : 2) + (plus ? 1 : 0)] = button;
1137 	return button;
1138 }
1139 
1140 
1141 BButton*
1142 ScreenWindow::_GetColumnRowButton(bool columns, bool plus)
1143 {
1144 	return fWorkspacesButtons[(columns ? 0 : 2) + (plus ? 1 : 0)];
1145 }
1146 
1147 
1148 void
1149 ScreenWindow::_BuildSupportedColorSpaces()
1150 {
1151 	fSupportedColorSpaces = 0;
1152 
1153 	for (int32 i = 0; i < kColorSpaceCount; i++) {
1154 		for (int32 j = 0; j < fScreenMode.CountModes(); j++) {
1155 			if (fScreenMode.ModeAt(j).space == kColorSpaces[i].space) {
1156 				fSupportedColorSpaces |= 1 << i;
1157 				break;
1158 			}
1159 		}
1160 	}
1161 }
1162 
1163 
1164 void
1165 ScreenWindow::_CheckApplyEnabled()
1166 {
1167 	fApplyButton->SetEnabled(fSelected != fActive
1168 		|| fAllWorkspacesItem->IsMarked());
1169 
1170 	uint32 columns;
1171 	uint32 rows;
1172 	BPrivate::get_workspaces_layout(&columns, &rows);
1173 
1174 	fRevertButton->SetEnabled(columns != fOriginalWorkspacesColumns
1175 		|| rows != fOriginalWorkspacesRows
1176 		|| fSelected != fOriginal);
1177 }
1178 
1179 
1180 void
1181 ScreenWindow::_UpdateOriginal()
1182 {
1183 	BPrivate::get_workspaces_layout(&fOriginalWorkspacesColumns,
1184 		&fOriginalWorkspacesRows);
1185 
1186 	fScreenMode.Get(fOriginal);
1187 	fScreenMode.UpdateOriginalModes();
1188 }
1189 
1190 
1191 void
1192 ScreenWindow::_UpdateMonitor()
1193 {
1194 	monitor_info info;
1195 	float diagonalInches;
1196 	status_t status = fScreenMode.GetMonitorInfo(info, &diagonalInches);
1197 	if (status == B_OK) {
1198 		char text[512];
1199 		snprintf(text, sizeof(text), "%s%s%s %g\"", info.vendor,
1200 			info.name[0] ? " " : "", info.name, diagonalInches);
1201 
1202 		fMonitorInfo->SetText(text);
1203 
1204 		if (fMonitorInfo->IsHidden())
1205 			fMonitorInfo->Show();
1206 	} else {
1207 		if (!fMonitorInfo->IsHidden())
1208 			fMonitorInfo->Hide();
1209 	}
1210 
1211 	char text[512];
1212 	size_t length = 0;
1213 	text[0] = 0;
1214 
1215 	if (status == B_OK) {
1216 		if (info.min_horizontal_frequency != 0
1217 			&& info.min_vertical_frequency != 0
1218 			&& info.max_pixel_clock != 0) {
1219 			length = snprintf(text, sizeof(text),
1220 				B_TRANSLATE("Horizonal frequency:\t%lu - %lu kHz\n"
1221 				"Vertical frequency:\t%lu - %lu Hz\n\n"
1222 				"Maximum pixel clock:\t%g MHz"),
1223 				info.min_horizontal_frequency, info.max_horizontal_frequency,
1224 				info.min_vertical_frequency, info.max_vertical_frequency,
1225 				info.max_pixel_clock / 1000.0);
1226 		}
1227 		if (info.serial_number[0] && length < sizeof(text)) {
1228 			length += snprintf(text + length, sizeof(text) - length,
1229 				B_TRANSLATE("%sSerial no.: %s"), length ? "\n\n" : "",
1230 				info.serial_number);
1231 			if (info.produced.week != 0 && info.produced.year != 0
1232 				&& length < sizeof(text)) {
1233 				length += snprintf(text + length, sizeof(text) - length,
1234 					" (%u/%u)", info.produced.week, info.produced.year);
1235 	 		}
1236 		}
1237 	}
1238 
1239 	// Add info about the graphics device
1240 
1241 	accelerant_device_info deviceInfo;
1242 	if (fScreenMode.GetDeviceInfo(deviceInfo) == B_OK
1243 		&& length < sizeof(text)) {
1244 		if (deviceInfo.name[0] && deviceInfo.chipset[0]) {
1245 			length += snprintf(text + length, sizeof(text) - length,
1246 				"%s%s (%s)", length != 0 ? "\n\n" : "", deviceInfo.name,
1247 				deviceInfo.chipset);
1248 		} else if (deviceInfo.name[0] || deviceInfo.chipset[0]) {
1249 			length += snprintf(text + length, sizeof(text) - length,
1250 				"%s%s", length != 0 ? "\n\n" : "", deviceInfo.name[0]
1251 					? deviceInfo.name : deviceInfo.chipset);
1252 		}
1253 	}
1254 
1255 	if (text[0])
1256 		fMonitorView->SetToolTip(text);
1257 }
1258 
1259 
1260 void
1261 ScreenWindow::_UpdateColorLabel()
1262 {
1263 	BString string;
1264 	string << fSelected.BitsPerPixel() << " " << B_TRANSLATE("bits/pixel");
1265 	fColorsMenu->Superitem()->SetLabel(string.String());
1266 }
1267 
1268 
1269 void
1270 ScreenWindow::_Apply()
1271 {
1272 	// make checkpoint, so we can undo these changes
1273 	fUndoScreenMode.UpdateOriginalModes();
1274 
1275 	status_t status = fScreenMode.Set(fSelected);
1276 	if (status == B_OK) {
1277 		// use the mode that has eventually been set and
1278 		// thus we know to be working; it can differ from
1279 		// the mode selected by user due to hardware limitation
1280 		display_mode newMode;
1281 		BScreen screen(this);
1282 		screen.GetMode(&newMode);
1283 
1284 		if (fAllWorkspacesItem->IsMarked()) {
1285 			int32 originatingWorkspace = current_workspace();
1286 			for (int32 i = 0; i < count_workspaces(); i++) {
1287 				if (i != originatingWorkspace)
1288 					screen.SetMode(i, &newMode, true);
1289 			}
1290 			fBootWorkspaceApplied = true;
1291 		} else {
1292 			if (current_workspace() == 0)
1293 				fBootWorkspaceApplied = true;
1294 		}
1295 
1296 		fActive = fSelected;
1297 
1298 		// TODO: only show alert when this is an unknown mode
1299 		BWindow* window = new AlertWindow(this);
1300 		window->Show();
1301 	} else {
1302 		char message[256];
1303 		snprintf(message, sizeof(message),
1304 			B_TRANSLATE("The screen mode could not be set:\n\t%s\n"),
1305 			screen_errors(status));
1306 		BAlert* alert = new BAlert(B_TRANSLATE("Warning"), message,
1307 			B_TRANSLATE("OK"), NULL, NULL,
1308 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1309 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1310 		alert->Go();
1311 	}
1312 }
1313 
1314