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