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