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