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