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