xref: /haiku/src/preferences/screensaver/ScreenSaverWindow.cpp (revision 8a990d5228b2d1099e3062180532ba709dfeef6d)
1 /*
2  * Copyright 2003-2010, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Phipps
7  *		Jérôme Duval, jerome.duval@free.fr
8  *		Axel Dörfler, axeld@pinc-software.de
9  */
10 
11 
12 #include "ScreenSaverWindow.h"
13 
14 #include <stdio.h>
15 
16 #include <Application.h>
17 #include <Box.h>
18 #include <Button.h>
19 #include <Catalog.h>
20 #include <Directory.h>
21 #include <DurationFormat.h>
22 #include <Entry.h>
23 #include <File.h>
24 #include <FindDirectory.h>
25 #include <Font.h>
26 #include <ListView.h>
27 #include <Path.h>
28 #include <Roster.h>
29 #include <Screen.h>
30 #include <ScreenSaver.h>
31 #include <ScrollView.h>
32 #include <Slider.h>
33 #include <StringView.h>
34 #include <TabView.h>
35 
36 #include <BuildScreenSaverDefaultSettingsView.h>
37 
38 #include "PreviewView.h"
39 #include "ScreenCornerSelector.h"
40 #include "ScreenSaverItem.h"
41 
42 #undef B_TRANSLATE_CONTEXT
43 #define B_TRANSLATE_CONTEXT "ScreenSaver"
44 
45 const uint32 kPreviewMonitorGap = 16;
46 const uint32 kMinSettingsWidth = 230;
47 const uint32 kMinSettingsHeight = 120;
48 
49 const int32 kMsgSaverSelected = 'SSEL';
50 const int32 kMsgTestSaver = 'TEST';
51 const int32 kMsgAddSaver = 'ADD ';
52 const int32 kMsgPasswordCheckBox = 'PWCB';
53 const int32 kMsgRunSliderChanged = 'RSch';
54 const int32 kMsgRunSliderUpdate = 'RSup';
55 const int32 kMsgPasswordSliderChanged = 'PWch';
56 const int32 kMsgPasswordSliderUpdate = 'PWup';
57 const int32 kMsgChangePassword = 'PWBT';
58 const int32 kMsgEnableScreenSaverBox = 'ESCH';
59 
60 const int32 kMsgTurnOffCheckBox = 'TUOF';
61 const int32 kMsgTurnOffSliderChanged = 'TUch';
62 const int32 kMsgTurnOffSliderUpdate = 'TUup';
63 
64 const int32 kMsgFadeCornerChanged = 'fdcc';
65 const int32 kMsgNeverFadeCornerChanged = 'nfcc';
66 
67 
68 class TimeSlider : public BSlider {
69 public:
70 								TimeSlider(BRect frame, const char* name,
71 									uint32 changedMessage,
72 									uint32 updateMessage);
73 	virtual						~TimeSlider();
74 
75 	virtual	void				AttachedToWindow();
76 	virtual	void				SetValue(int32 value);
77 
78 			void				SetTime(bigtime_t useconds);
79 			bigtime_t			Time() const;
80 
81 private:
82 			void				_TimeToString(bigtime_t useconds,
83 									BString& string);
84 };
85 
86 
87 class FadeView : public BView {
88 public:
89 								FadeView(BRect frame, const char* name,
90 									ScreenSaverSettings& settings);
91 
92 	virtual	void				AttachedToWindow();
93 };
94 
95 
96 class ModulesView : public BView {
97 public:
98 								ModulesView(BRect frame, const char* name,
99 									ScreenSaverSettings& settings);
100 	virtual						~ModulesView();
101 
102 	virtual	void				DetachedFromWindow();
103 	virtual	void				AttachedToWindow();
104 	virtual	void				AllAttached();
105 	virtual	void				MessageReceived(BMessage* message);
106 
107 			void				PopulateScreenSaverList();
108 			void				SaveState();
109 
110 private:
111 	static	int					_CompareScreenSaverItems(const void* left,
112 									const void* right);
113 
114 			BScreenSaver*		_ScreenSaver();
115 			void				_CloseSaver();
116 			void				_OpenSaver();
117 
118 private:
119 			BFilePanel*			fFilePanel;
120 			BListView*			fListView;
121 			BButton*			fTestButton;
122 			BButton*			fAddButton;
123 
124 			ScreenSaverSettings& fSettings;
125 			ScreenSaverRunner*	fSaverRunner;
126 			BString				fCurrentName;
127 
128 			BBox*				fSettingsBox;
129 			BView*				fSettingsView;
130 
131 			PreviewView*		fPreviewView;
132 
133 			team_id				fScreenSaverTestTeam;
134 };
135 
136 
137 static const int32 kTimeInUnits[] = {
138 	30,    60,   90,
139 	120,   150,  180,
140 	240,   300,  360,
141 	420,   480,  540,
142 	600,   900,  1200,
143 	1500,  1800, 2400,
144 	3000,  3600, 5400,
145 	7200,  9000, 10800,
146 	14400, 18000
147 };
148 static const int32 kTimeUnitCount
149 	= sizeof(kTimeInUnits) / sizeof(kTimeInUnits[0]);
150 
151 
152 TimeSlider::TimeSlider(BRect frame, const char* name, uint32 changedMessage,
153 		uint32 updateMessage)
154 	:
155 	BSlider(frame, name, B_TRANSLATE("30 seconds"),
156 		new BMessage(changedMessage),
157 		0, kTimeUnitCount - 1, B_TRIANGLE_THUMB, B_FOLLOW_LEFT_RIGHT)
158 {
159 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
160 	SetModificationMessage(new BMessage(updateMessage));
161 	SetBarThickness(10);
162 }
163 
164 
165 TimeSlider::~TimeSlider()
166 {
167 }
168 
169 
170 void
171 TimeSlider::AttachedToWindow()
172 {
173 	BSlider::AttachedToWindow();
174 	SetTarget(Window());
175 }
176 
177 
178 void
179 TimeSlider::SetValue(int32 value)
180 {
181 	int32 oldValue = Value();
182 	BSlider::SetValue(value);
183 
184 	if (oldValue != Value()) {
185 		BString label;
186 		_TimeToString(kTimeInUnits[Value()] * 1000000LL, label);
187 		SetLabel(label.String());
188 	}
189 }
190 
191 
192 void
193 TimeSlider::SetTime(bigtime_t useconds)
194 {
195 	for (int t = 0; t < kTimeUnitCount; t++) {
196 		if (kTimeInUnits[t] * 1000000LL == useconds) {
197 			SetValue(t);
198 			break;
199 		}
200 	}
201 }
202 
203 
204 bigtime_t
205 TimeSlider::Time() const
206 {
207 	return 1000000LL * kTimeInUnits[Value()];
208 }
209 
210 
211 void
212 TimeSlider::_TimeToString(bigtime_t useconds, BString& string)
213 {
214 	BDurationFormat formatter;
215 	formatter.Format(0, useconds, &string);
216 }
217 
218 
219 //	#pragma mark -
220 
221 
222 FadeView::FadeView(BRect rect, const char* name, ScreenSaverSettings& settings)
223 	:
224 	BView(rect, name, B_FOLLOW_ALL, B_WILL_DRAW)
225 {
226 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
227 }
228 
229 
230 void
231 FadeView::AttachedToWindow()
232 {
233 	if (Parent() != NULL) {
234 		// We adopt the size of our parent view (in case the window
235 		// was resized during our absence (BTabView...)
236 		ResizeTo(Parent()->Bounds().Width(), Parent()->Bounds().Height());
237 	}
238 }
239 
240 
241 //	#pragma mark -
242 
243 
244 ModulesView::ModulesView(BRect rect, const char* name,
245 		ScreenSaverSettings& settings)
246 	:
247 	BView(rect, name, B_FOLLOW_ALL, B_WILL_DRAW),
248 	fSettings(settings),
249 	fSaverRunner(NULL),
250 	fSettingsView(NULL),
251 	fScreenSaverTestTeam(-1)
252 {
253 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
254 
255 	fTestButton = new BButton(rect, "TestButton", B_TRANSLATE("Test"),
256 		new BMessage(kMsgTestSaver), B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
257 	float width, height;
258 	fTestButton->GetPreferredSize(&width, &height);
259 	fTestButton->ResizeTo(width + 16, height);
260 	fTestButton->MoveTo(8, rect.bottom - 8 - height);
261 	AddChild(fTestButton);
262 
263 	rect = fTestButton->Frame();
264 	rect.OffsetBy(fTestButton->Bounds().Width() + 8, 0);
265 	fAddButton = new BButton(rect, "AddButton",
266 		B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddSaver),
267 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
268 	AddChild(fAddButton);
269 
270 	rect = Bounds().InsetByCopy(8 + kPreviewMonitorGap, 12);
271 	rect.right = fAddButton->Frame().right - kPreviewMonitorGap;
272 	rect.bottom = rect.top + 3 * rect.Width() / 4;
273 		// 4:3 monitor
274 
275 	fPreviewView = new PreviewView(rect, "preview");
276 	AddChild(fPreviewView);
277 
278 	rect.left = 8;
279 	rect.right -= B_V_SCROLL_BAR_WIDTH + 2 - kPreviewMonitorGap;
280 		// scroll view border
281 	rect.top = rect.bottom + 14;
282 	rect.bottom = fTestButton->Frame().top - 10;
283 	fListView = new BListView(rect, "SaversListView", B_SINGLE_SELECTION_LIST,
284 		B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM);
285 	fListView->SetSelectionMessage(new BMessage(kMsgSaverSelected));
286 	AddChild(new BScrollView("scroll_list", fListView,
287 		B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, 0, false, true));
288 
289 	rect = Bounds().InsetByCopy(8, 8);
290 	rect.left = fAddButton->Frame().right + 8;
291 	AddChild(fSettingsBox = new BBox(rect, "SettingsBox", B_FOLLOW_ALL,
292 		B_WILL_DRAW));
293 	fSettingsBox->SetLabel(B_TRANSLATE("Module settings"));
294 
295 	PopulateScreenSaverList();
296 	fFilePanel = new BFilePanel();
297 }
298 
299 
300 ModulesView::~ModulesView()
301 {
302 	delete fFilePanel;
303 }
304 
305 
306 void
307 ModulesView::DetachedFromWindow()
308 {
309 	SaveState();
310 	_CloseSaver();
311 }
312 
313 
314 void
315 ModulesView::AttachedToWindow()
316 {
317 	if (Parent() != NULL) {
318 		// We adopt the size of our parent view (in case the window
319 		// was resized during our absence (BTabView...)
320 		ResizeTo(Parent()->Bounds().Width(), Parent()->Bounds().Height());
321 	}
322 
323 	_OpenSaver();
324 
325 	fListView->SetTarget(this);
326 	fTestButton->SetTarget(this);
327 	fAddButton->SetTarget(this);
328 }
329 
330 
331 void
332 ModulesView::AllAttached()
333 {
334 	// This only works after the view has been attached
335 	fListView->ScrollToSelection();
336 }
337 
338 
339 void
340 ModulesView::MessageReceived(BMessage* message)
341 {
342 	switch (message->what) {
343 		case kMsgSaverSelected:
344 		{
345 			int selection = fListView->CurrentSelection();
346 			if (selection < 0)
347 				break;
348 
349 			ScreenSaverItem* item
350 				= (ScreenSaverItem*)fListView->ItemAt(selection);
351 			if (item == NULL)
352 				break;
353 
354 			if (!strcmp(item->Text(), B_TRANSLATE("Blackness")))
355 				fSettings.SetModuleName("");
356 			else
357 				fSettings.SetModuleName(item->Text());
358 
359 			SaveState();
360 			_CloseSaver();
361 			_OpenSaver();
362 			fSettings.Save();
363 			break;
364 		}
365 
366 		case kMsgTestSaver:
367 		{
368 			SaveState();
369 			fSettings.Save();
370 
371 			_CloseSaver();
372 
373 			be_roster->StartWatching(BMessenger(this, Looper()),
374 				B_REQUEST_QUIT);
375 			if (be_roster->Launch(SCREEN_BLANKER_SIG, &fSettings.Message(),
376 					&fScreenSaverTestTeam) == B_OK)
377 				break;
378 
379 			// Try really hard to launch it. It's very likely that this fails
380 			// when we run from the CD, and there is only an incomplete mime
381 			// database for example...
382 			BPath path;
383 			if (find_directory(B_SYSTEM_BIN_DIRECTORY, &path) != B_OK
384 				|| path.Append("screen_blanker") != B_OK)
385 				path.SetTo("/boot/system/bin/screen_blanker");
386 
387 			BEntry entry(path.Path());
388 			entry_ref ref;
389 			if (entry.GetRef(&ref) == B_OK) {
390 				be_roster->Launch(&ref, &fSettings.Message(),
391 					&fScreenSaverTestTeam);
392 			}
393 			break;
394 		}
395 		case kMsgAddSaver:
396 			fFilePanel->Show();
397 			break;
398 
399 		case B_SOME_APP_QUIT:
400 		{
401 			team_id team;
402 			if (message->FindInt32("be:team", &team) == B_OK
403 				&& team == fScreenSaverTestTeam) {
404 				be_roster->StopWatching(this);
405 				_OpenSaver();
406 			}
407 			break;
408 		}
409 
410 		default:
411 			BView::MessageReceived(message);
412 	}
413 }
414 
415 
416 void
417 ModulesView::SaveState()
418 {
419 	BScreenSaver* saver = _ScreenSaver();
420 	if (saver == NULL)
421 		return;
422 
423 	BMessage state;
424 	if (saver->SaveState(&state) == B_OK)
425 		fSettings.SetModuleState(fCurrentName.String(), &state);
426 }
427 
428 
429 void
430 ModulesView::PopulateScreenSaverList()
431 {
432  	fListView->DeselectAll();
433 	while (ScreenSaverItem* item
434 			= (ScreenSaverItem*)fListView->RemoveItem((int32)0)) {
435 		delete item;
436 	}
437 
438 	// Blackness is a built-in screen saver
439 	fListView->AddItem(new ScreenSaverItem(B_TRANSLATE("Blackness"), ""));
440 
441 	// Iterate over add-on directories, and add their files to the list view
442 
443 	directory_which which[] = {
444 		B_BEOS_ADDONS_DIRECTORY, B_USER_ADDONS_DIRECTORY};
445 	ScreenSaverItem* selectItem = NULL;
446 
447 	for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
448 		BPath basePath;
449 		find_directory(which[i], &basePath);
450 		basePath.Append("Screen Savers", true);
451 
452 		BDirectory dir(basePath.Path());
453 		BEntry entry;
454 		while (dir.GetNextEntry(&entry, true) == B_OK) {
455 			char name[B_FILE_NAME_LENGTH];
456 			if (entry.GetName(name) != B_OK)
457 				continue;
458 
459 			BPath path = basePath;
460 			path.Append(name);
461 
462 			ScreenSaverItem* item = new ScreenSaverItem(name, path.Path());
463 			fListView->AddItem(item);
464 
465 			if (!strcmp(fSettings.ModuleName(), item->Text())
466 				|| (!strcmp(fSettings.ModuleName(), "")
467 					&& !strcmp(item->Text(), B_TRANSLATE("Blackness"))))
468 				selectItem = item;
469 		}
470 	}
471 
472 	fListView->SortItems(_CompareScreenSaverItems);
473 
474 	fListView->Select(fListView->IndexOf(selectItem));
475 	fListView->ScrollToSelection();
476 }
477 
478 
479 //! Sorting function for ScreenSaverItems
480 int
481 ModulesView::_CompareScreenSaverItems(const void* left, const void* right)
482 {
483 	ScreenSaverItem* leftItem  = *(ScreenSaverItem **)left;
484 	ScreenSaverItem* rightItem = *(ScreenSaverItem **)right;
485 
486 	return strcasecmp(leftItem->Text(), rightItem->Text());
487 }
488 
489 
490 BScreenSaver*
491 ModulesView::_ScreenSaver()
492 {
493 	if (fSaverRunner != NULL)
494 		return fSaverRunner->ScreenSaver();
495 
496 	return NULL;
497 }
498 
499 
500 void
501 ModulesView::_CloseSaver()
502 {
503 	// remove old screen saver preview & config
504 
505 	BScreenSaver* saver = _ScreenSaver();
506 	BView* view = fPreviewView->RemovePreview();
507 	if (fSettingsView != NULL)
508 		fSettingsBox->RemoveChild(fSettingsView);
509 
510 	if (fSaverRunner != NULL)
511 		fSaverRunner->Quit();
512 	if (saver != NULL)
513 		saver->StopConfig();
514 
515 	delete view;
516 	delete fSettingsView;
517 	delete fSaverRunner;
518 		// the saver runner also unloads the add-on, so it must
519 		// be deleted last
520 
521 	fSettingsView = NULL;
522 	fSaverRunner = NULL;
523 }
524 
525 
526 void
527 ModulesView::_OpenSaver()
528 {
529 	// create new screen saver preview & config
530 
531 	BView* view = fPreviewView->AddPreview();
532 	fCurrentName = fSettings.ModuleName();
533 	fSaverRunner = new ScreenSaverRunner(Window(), view, true, fSettings);
534 	BScreenSaver* saver = _ScreenSaver();
535 
536 #ifdef __HAIKU__
537 	BRect rect = fSettingsBox->InnerFrame().InsetByCopy(4, 4);
538 #else
539 	BRect rect = fSettingsBox->Bounds().InsetByCopy(4, 4);
540 	rect.top += 14;
541 #endif
542 	fSettingsView = new BView(rect, "SettingsView", B_FOLLOW_ALL, B_WILL_DRAW);
543 	fSettingsView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
544 	fSettingsBox->AddChild(fSettingsView);
545 
546 	if (saver != NULL) {
547 		fSaverRunner->Run();
548 		saver->StartConfig(fSettingsView);
549 	}
550 
551 	if (fSettingsView->ChildAt(0) == NULL) {
552 		// There are no settings at all, we add the module name here to
553 		// let it look a bit better at least.
554 		BPrivate::BuildScreenSaverDefaultSettingsView(fSettingsView,
555 			fSettings.ModuleName()[0] ? fSettings.ModuleName() :
556 			B_TRANSLATE("Blackness"), saver || !fSettings.ModuleName()[0]
557 				? B_TRANSLATE("No options available") :
558 				B_TRANSLATE("Could not load screen saver"));
559 	}
560 
561 	ScreenSaverWindow* window = dynamic_cast<ScreenSaverWindow*>(Window());
562 	if (window == NULL)
563 		return;
564 
565 	// find the minimal size of the settings view
566 
567 	float right = 0, bottom = 0;
568 	int32 i = 0;
569 	while ((view = fSettingsView->ChildAt(i++)) != NULL) {
570 		// very simple heuristic...
571 		float viewRight = view->Frame().right;
572 		if ((view->ResizingMode() & _rule_(0, 0xf, 0, 0xf))
573 				== B_FOLLOW_LEFT_RIGHT) {
574 			float width, height;
575 			view->GetPreferredSize(&width, &height);
576 			viewRight = view->Frame().left + width / 2;
577 		} else if ((view->ResizingMode() & _rule_(0, 0xf, 0, 0xf))
578 				== B_FOLLOW_RIGHT)
579 			viewRight = 8 + view->Frame().Width();
580 
581 		float viewBottom = view->Frame().bottom;
582 		if ((view->ResizingMode() & _rule_(0xf, 0, 0xf, 0))
583 				== B_FOLLOW_TOP_BOTTOM) {
584 			float width, height;
585 			view->GetPreferredSize(&width, &height);
586 			viewBottom = view->Frame().top + height;
587 		} else if ((view->ResizingMode() & _rule_(0xf, 0, 0xf, 0))
588 				== B_FOLLOW_BOTTOM)
589 			viewBottom = 8 + view->Frame().Height();
590 
591 		if (viewRight > right)
592 			right = viewRight;
593 		if (viewBottom > bottom)
594 			bottom = viewBottom;
595 	}
596 
597 	if (right < kMinSettingsWidth)
598 		right = kMinSettingsWidth;
599 	if (bottom < kMinSettingsHeight)
600 		bottom = kMinSettingsHeight;
601 
602 	BPoint leftTop = fSettingsView->LeftTop();
603 	fSettingsView->ConvertToScreen(&leftTop);
604 	window->ConvertFromScreen(&leftTop);
605 	window->SetMinimalSizeLimit(leftTop.x + right + 16,
606 		leftTop.y + bottom + 16);
607 }
608 
609 
610 //	#pragma mark -
611 
612 
613 ScreenSaverWindow::ScreenSaverWindow()
614 	:
615 	BWindow(BRect(50, 50, 496, 375), B_TRANSLATE("ScreenSaver"),
616 		B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS)
617 {
618 	fSettings.Load();
619 
620 	BRect rect = Bounds();
621 	fMinWidth = 430;
622 	fMinHeight = 325;
623 
624 	// Create a background view
625 	BView *background = new BView(rect, "background", B_FOLLOW_ALL, 0);
626 	background->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
627 	AddChild(background);
628 
629 	// Add the tab view to the background
630 	rect.top += 4;
631 	fTabView = new BTabView(rect, "tab_view");
632 
633 	// Create the controls inside the tabs
634 	rect = fTabView->ContainerView()->Bounds();
635 	_SetupFadeTab(rect);
636 	fModulesView = new ModulesView(rect, B_TRANSLATE("Modules"), fSettings);
637 
638 	fTabView->AddTab(fFadeView);
639 	fTabView->AddTab(fModulesView);
640 	background->AddChild(fTabView);
641 
642 	// Create the password editing window
643 	fPasswordWindow = new PasswordWindow(fSettings);
644 	fPasswordWindow->Run();
645 
646 	SetMinimalSizeLimit(fMinWidth, fMinHeight);
647 	MoveTo(fSettings.WindowFrame().left, fSettings.WindowFrame().top);
648 	ResizeTo(fSettings.WindowFrame().Width(), fSettings.WindowFrame().Height());
649 
650 	fEnableCheckBox->SetValue(
651 		fSettings.TimeFlags() & ENABLE_SAVER ? B_CONTROL_ON : B_CONTROL_OFF);
652 	fRunSlider->SetTime(fSettings.BlankTime());
653 	fTurnOffSlider->SetTime(fSettings.OffTime() + fSettings.BlankTime());
654 	fFadeNow->SetCorner(fSettings.BlankCorner());
655 	fFadeNever->SetCorner(fSettings.NeverBlankCorner());
656 	fPasswordCheckBox->SetValue(fSettings.LockEnable());
657 	fPasswordSlider->SetTime(fSettings.PasswordTime());
658 
659 	fTabView->Select(fSettings.WindowTab());
660 
661 	_UpdateTurnOffScreen();
662 	_UpdateStatus();
663 }
664 
665 
666 ScreenSaverWindow::~ScreenSaverWindow()
667 {
668 	Hide();
669 	_UpdateStatus();
670 
671 	delete fTabView->RemoveTab(1);
672 		// We delete this here in order to make sure the module view saves its
673 		// state while the window is still intact.
674 
675 	fSettings.Save();
676 }
677 
678 
679 //! Create the controls for the fade tab
680 void
681 ScreenSaverWindow::_SetupFadeTab(BRect rect)
682 {
683 	fFadeView = new FadeView(rect, B_TRANSLATE("Fade"), fSettings);
684 
685 	float labelWidth = be_plain_font->StringWidth(B_TRANSLATE("Turn off "
686 		"screen")) + 20.0f;
687 
688 	font_height fontHeight;
689 	be_plain_font->GetHeight(&fontHeight);
690 	float textHeight = ceilf(fontHeight.ascent + fontHeight.descent);
691 
692 	// taken from BRadioButton:
693 	float radioButtonOffset = 2 * floorf(textHeight / 2 - 2)
694 		+ floorf(textHeight / 2);
695 
696 	fEnableCheckBox = new BCheckBox(BRect(0, 0, 1, 1), "EnableCheckBox",
697 		B_TRANSLATE("Enable screen saver"),
698 		new BMessage(kMsgEnableScreenSaverBox));
699 	fEnableCheckBox->ResizeToPreferred();
700 
701 	rect.InsetBy(8, 8);
702 	BBox* box = new BBox(rect, "EnableScreenSaverBox", B_FOLLOW_ALL);
703 	box->SetLabel(fEnableCheckBox);
704 	fFadeView->AddChild(box);
705 
706 	// Run Module
707 	rect.left += radioButtonOffset;
708 	rect.top = fEnableCheckBox->Bounds().bottom + 8.0f;
709 	rect.right = box->Bounds().right - 8;
710 	BStringView* stringView = new BStringView(rect, NULL,
711 		B_TRANSLATE("Run module"));
712 	stringView->ResizeToPreferred();
713 	box->AddChild(stringView);
714 
715 	rect.left += labelWidth;
716 	fRunSlider = new TimeSlider(rect, "RunSlider", kMsgRunSliderChanged,
717 		kMsgRunSliderUpdate);
718 	float width, height;
719 	fRunSlider->GetPreferredSize(&width, &height);
720 	fRunSlider->ResizeTo(fRunSlider->Bounds().Width(), height);
721 	box->AddChild(fRunSlider);
722 
723 	// Turn Off
724 	rect.left = 8;
725 	rect.OffsetBy(0, fRunSlider->Bounds().Height() + 4.0f);
726 	fTurnOffCheckBox = new BCheckBox(rect, "TurnOffScreenCheckBox",
727 		B_TRANSLATE("Turn off screen"), new BMessage(kMsgTurnOffCheckBox));
728 	fTurnOffCheckBox->ResizeToPreferred();
729 	box->AddChild(fTurnOffCheckBox);
730 
731 	rect.left += radioButtonOffset + labelWidth;
732 	fTurnOffSlider = new TimeSlider(rect, "TurnOffSlider",
733 		kMsgTurnOffSliderChanged, kMsgTurnOffSliderUpdate);
734 	fTurnOffSlider->ResizeTo(fTurnOffSlider->Bounds().Width(), height);
735 	box->AddChild(fTurnOffSlider);
736 
737 	// Password
738 	rect.left = 8;
739 	rect.OffsetBy(0, fTurnOffSlider->Bounds().Height() + 4.0f);
740 	fPasswordCheckBox = new BCheckBox(rect, "PasswordCheckbox",
741 		B_TRANSLATE("Password lock"), new BMessage(kMsgPasswordCheckBox));
742 	fPasswordCheckBox->ResizeToPreferred();
743 	box->AddChild(fPasswordCheckBox);
744 
745 	rect.left += radioButtonOffset + labelWidth;
746 	fPasswordSlider = new TimeSlider(rect, "PasswordSlider",
747 		kMsgPasswordSliderChanged, kMsgPasswordSliderUpdate);
748 	fPasswordSlider->ResizeTo(fPasswordSlider->Bounds().Width(), height);
749 	box->AddChild(fPasswordSlider);
750 
751 	rect.OffsetBy(0, fTurnOffSlider->Bounds().Height() + 4.0f);
752 	rect.left = rect.right;
753 	fPasswordButton = new BButton(rect, "PasswordButton",
754 		B_TRANSLATE("Password" B_UTF8_ELLIPSIS),
755 		new BMessage(kMsgChangePassword), B_FOLLOW_TOP | B_FOLLOW_RIGHT);
756 	fPasswordButton->ResizeToPreferred();
757 	fPasswordButton->MoveBy(-fPasswordButton->Bounds().Width(), 0);
758 	box->AddChild(fPasswordButton);
759 
760 	// Bottom
761 
762 	float monitorHeight = 10 + textHeight * 3;
763 	float monitorWidth = monitorHeight * 4 / 3;
764 	rect.left = 15;
765 	rect.top = box->Bounds().Height() - 15 - monitorHeight;
766 	rect.right = rect.left + monitorWidth;
767 	rect.bottom = rect.top + monitorHeight;
768 	box->AddChild(fFadeNow = new ScreenCornerSelector(rect, "FadeNow",
769 		new BMessage(kMsgFadeCornerChanged), B_FOLLOW_LEFT | B_FOLLOW_BOTTOM));
770 
771 	rect.OffsetBy(monitorWidth + 10, 0);
772 	stringView = new BStringView(rect, NULL, B_TRANSLATE("Fade now when"),
773 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
774 	stringView->ResizeToPreferred();
775 	float maxWidth = stringView->Bounds().Width();
776 	box->AddChild(stringView);
777 
778 	rect.OffsetBy(0, stringView->Bounds().Height());
779 	stringView = new BStringView(rect, NULL, B_TRANSLATE("mouse is here"),
780 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
781 	stringView->ResizeToPreferred();
782 	if (maxWidth < stringView->Bounds().Width())
783 		maxWidth = stringView->Bounds().Width();
784 	box->AddChild(stringView);
785 
786 	rect.left += maxWidth + 20;
787 	rect.top = box->Bounds().Height() - 15 - monitorHeight;
788 	rect.right = rect.left + monitorWidth;
789 	rect.bottom = rect.top + monitorHeight;
790 	box->AddChild(fFadeNever = new ScreenCornerSelector(rect, "FadeNever",
791 		new BMessage(kMsgNeverFadeCornerChanged),
792 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM));
793 
794 	rect.OffsetBy(monitorWidth + 10, 0);
795 	stringView = new BStringView(rect, NULL,B_TRANSLATE("Don't fade when"),
796 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
797 	stringView->ResizeToPreferred();
798 	if (maxWidth < stringView->Bounds().Width())
799 		maxWidth = stringView->Bounds().Width();
800 	box->AddChild(stringView);
801 
802 	rect.OffsetBy(0, stringView->Bounds().Height());
803 	stringView = new BStringView(rect, NULL, B_TRANSLATE("mouse is here"),
804 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
805 	stringView->ResizeToPreferred();
806 	if (maxWidth < stringView->Bounds().Width())
807 		maxWidth = stringView->Bounds().Width();
808 	box->AddChild(stringView);
809 
810 	float size = rect.left + maxWidth + 40;
811 	if (fMinWidth < size)
812 		fMinWidth = size;
813 	size = fPasswordButton->Frame().bottom + box->Frame().top
814 		+ monitorHeight + 40 + textHeight * 2;
815 	if (fMinHeight < size)
816 		fMinHeight = size;
817 }
818 
819 
820 void
821 ScreenSaverWindow::_UpdateTurnOffScreen()
822 {
823 	bool enabled = (fSettings.TimeFlags() & ENABLE_DPMS_MASK) != 0;
824 
825 	BScreen screen(this);
826 	uint32 dpmsCapabilities = screen.DPMSCapabilites();
827 
828 	fTurnOffScreenFlags = 0;
829 	if (dpmsCapabilities & B_DPMS_OFF)
830 		fTurnOffScreenFlags |= ENABLE_DPMS_OFF;
831 	if (dpmsCapabilities & B_DPMS_STAND_BY)
832 		fTurnOffScreenFlags |= ENABLE_DPMS_STAND_BY;
833 	if (dpmsCapabilities & B_DPMS_SUSPEND)
834 		fTurnOffScreenFlags |= ENABLE_DPMS_SUSPEND;
835 
836 	fTurnOffCheckBox->SetValue(enabled && fTurnOffScreenFlags != 0
837 		? B_CONTROL_ON : B_CONTROL_OFF);
838 }
839 
840 
841 void
842 ScreenSaverWindow::_UpdateStatus()
843 {
844 	DisableUpdates();
845 
846 	bool enabled = fEnableCheckBox->Value() == B_CONTROL_ON;
847 	fPasswordCheckBox->SetEnabled(enabled);
848 	fTurnOffCheckBox->SetEnabled(enabled && fTurnOffScreenFlags != 0);
849 	fRunSlider->SetEnabled(enabled);
850 	fTurnOffSlider->SetEnabled(enabled && fTurnOffCheckBox->Value());
851 	fPasswordSlider->SetEnabled(enabled && fPasswordCheckBox->Value());
852 	fPasswordButton->SetEnabled(enabled && fPasswordCheckBox->Value());
853 
854 	EnableUpdates();
855 
856 	// Update the saved preferences
857 	fSettings.SetWindowFrame(Frame());
858 	fSettings.SetWindowTab(fTabView->Selection());
859 	fSettings.SetTimeFlags((enabled ? ENABLE_SAVER : 0)
860 		| (fTurnOffCheckBox->Value() ? fTurnOffScreenFlags : 0));
861 	fSettings.SetBlankTime(fRunSlider->Time());
862 	bigtime_t offTime = fTurnOffSlider->Time() - fSettings.BlankTime();
863 	fSettings.SetOffTime(offTime);
864 	fSettings.SetSuspendTime(offTime);
865 	fSettings.SetStandByTime(offTime);
866 	fSettings.SetBlankCorner(fFadeNow->Corner());
867 	fSettings.SetNeverBlankCorner(fFadeNever->Corner());
868 	fSettings.SetLockEnable(fPasswordCheckBox->Value());
869 	fSettings.SetPasswordTime(fPasswordSlider->Time());
870 
871 	// TODO - Tell the password window to update its stuff
872 }
873 
874 
875 void
876 ScreenSaverWindow::SetMinimalSizeLimit(float width, float height)
877 {
878 	if (width < fMinWidth)
879 		width = fMinWidth;
880 	if (height < fMinHeight)
881 		height = fMinHeight;
882 
883 	SetSizeLimits(width, 32767, height, 32767);
884 }
885 
886 
887 void
888 ScreenSaverWindow::MessageReceived(BMessage *msg)
889 {
890 	// "Fade" tab, slider updates
891 
892 	switch (msg->what) {
893 		case kMsgRunSliderChanged:
894 		case kMsgRunSliderUpdate:
895 			if (fRunSlider->Value() > fTurnOffSlider->Value())
896 				fTurnOffSlider->SetValue(fRunSlider->Value());
897 
898 			if (fRunSlider->Value() > fPasswordSlider->Value())
899 				fPasswordSlider->SetValue(fRunSlider->Value());
900 			break;
901 
902 		case kMsgTurnOffSliderChanged:
903 		case kMsgTurnOffSliderUpdate:
904 			if (fRunSlider->Value() > fTurnOffSlider->Value())
905 				fRunSlider->SetValue(fTurnOffSlider->Value());
906 			break;
907 
908 		case kMsgPasswordSliderChanged:
909 		case kMsgPasswordSliderUpdate:
910 			if (fPasswordSlider->Value() < fRunSlider->Value())
911 				fRunSlider->SetValue(fPasswordSlider->Value());
912 			break;
913 	}
914 
915 	switch (msg->what) {
916 		// "Fade" tab
917 
918 		case kMsgTurnOffCheckBox:
919 			fTurnOffSlider->SetEnabled(
920 				fTurnOffCheckBox->Value() == B_CONTROL_ON);
921 			break;
922 
923 		case kMsgRunSliderChanged:
924 		case kMsgTurnOffSliderChanged:
925 		case kMsgPasswordSliderChanged:
926 		case kMsgPasswordCheckBox:
927 		case kMsgEnableScreenSaverBox:
928 		case kMsgFadeCornerChanged:
929 		case kMsgNeverFadeCornerChanged:
930 			_UpdateStatus();
931 			fSettings.Save();
932 			break;
933 
934 		case kMsgChangePassword:
935 			fPasswordWindow->Show();
936 			break;
937 
938 		// "Modules" tab
939 
940 		case kMsgUpdateList:
941 			fModulesView->PopulateScreenSaverList();
942 			break;
943 
944 		default:
945 			BWindow::MessageReceived(msg);
946 			break;
947 	}
948 }
949 
950 
951 void
952 ScreenSaverWindow::ScreenChanged(BRect frame, color_space colorSpace)
953 {
954 	_UpdateTurnOffScreen();
955 }
956 
957 
958 bool
959 ScreenSaverWindow::QuitRequested()
960 {
961 	be_app->PostMessage(B_QUIT_REQUESTED);
962 	return true;
963 }
964 
965