xref: /haiku/src/preferences/screensaver/ScreenSaverWindow.cpp (revision 29f8805f6c70f1c819eb58ac2220647d8e40d6e7)
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_TRANSLATION_CONTEXT
44 #define B_TRANSLATION_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_NONPACKAGED_ADDONS_DIRECTORY,
446 		B_USER_ADDONS_DIRECTORY,
447 		B_COMMON_NONPACKAGED_ADDONS_DIRECTORY,
448 		B_COMMON_ADDONS_DIRECTORY,
449 		B_SYSTEM_ADDONS_DIRECTORY,
450 	};
451 	ScreenSaverItem* selectItem = NULL;
452 
453 	for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
454 		BPath basePath;
455 		find_directory(which[i], &basePath);
456 		basePath.Append("Screen Savers", true);
457 
458 		BDirectory dir(basePath.Path());
459 		BEntry entry;
460 		while (dir.GetNextEntry(&entry, true) == B_OK) {
461 			char name[B_FILE_NAME_LENGTH];
462 			if (entry.GetName(name) != B_OK)
463 				continue;
464 
465 			BPath path = basePath;
466 			path.Append(name);
467 
468 			ScreenSaverItem* item = new ScreenSaverItem(name, path.Path());
469 			fListView->AddItem(item);
470 
471 			if (!strcmp(fSettings.ModuleName(), item->Text())
472 				|| (!strcmp(fSettings.ModuleName(), "")
473 					&& !strcmp(item->Text(), B_TRANSLATE("Blackness"))))
474 				selectItem = item;
475 		}
476 	}
477 
478 	fListView->SortItems(_CompareScreenSaverItems);
479 
480 	fListView->Select(fListView->IndexOf(selectItem));
481 	fListView->ScrollToSelection();
482 }
483 
484 
485 //! Sorting function for ScreenSaverItems
486 int
487 ModulesView::_CompareScreenSaverItems(const void* left, const void* right)
488 {
489 	ScreenSaverItem* leftItem  = *(ScreenSaverItem **)left;
490 	ScreenSaverItem* rightItem = *(ScreenSaverItem **)right;
491 
492 	return strcasecmp(leftItem->Text(), rightItem->Text());
493 }
494 
495 
496 BScreenSaver*
497 ModulesView::_ScreenSaver()
498 {
499 	if (fSaverRunner != NULL)
500 		return fSaverRunner->ScreenSaver();
501 
502 	return NULL;
503 }
504 
505 
506 void
507 ModulesView::_CloseSaver()
508 {
509 	// remove old screen saver preview & config
510 
511 	BScreenSaver* saver = _ScreenSaver();
512 	BView* view = fPreviewView->RemovePreview();
513 	if (fSettingsView != NULL)
514 		fSettingsBox->RemoveChild(fSettingsView);
515 
516 	if (fSaverRunner != NULL)
517 		fSaverRunner->Quit();
518 	if (saver != NULL)
519 		saver->StopConfig();
520 
521 	delete view;
522 	delete fSettingsView;
523 	delete fSaverRunner;
524 		// the saver runner also unloads the add-on, so it must
525 		// be deleted last
526 
527 	fSettingsView = NULL;
528 	fSaverRunner = NULL;
529 }
530 
531 
532 void
533 ModulesView::_OpenSaver()
534 {
535 	// create new screen saver preview & config
536 
537 	BView* view = fPreviewView->AddPreview();
538 	fCurrentName = fSettings.ModuleName();
539 	fSaverRunner = new ScreenSaverRunner(Window(), view, true, fSettings);
540 	BScreenSaver* saver = _ScreenSaver();
541 
542 #ifdef __HAIKU__
543 	BRect rect = fSettingsBox->InnerFrame().InsetByCopy(4, 4);
544 #else
545 	BRect rect = fSettingsBox->Bounds().InsetByCopy(4, 4);
546 	rect.top += 14;
547 #endif
548 	fSettingsView = new BView(rect, "SettingsView", B_FOLLOW_ALL, B_WILL_DRAW);
549 	fSettingsView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
550 	fSettingsBox->AddChild(fSettingsView);
551 
552 	if (saver != NULL) {
553 		fSaverRunner->Run();
554 		saver->StartConfig(fSettingsView);
555 	}
556 
557 	if (fSettingsView->ChildAt(0) == NULL) {
558 		// There are no settings at all, we add the module name here to
559 		// let it look a bit better at least.
560 		BPrivate::BuildScreenSaverDefaultSettingsView(fSettingsView,
561 			fSettings.ModuleName()[0] ? fSettings.ModuleName() :
562 			B_TRANSLATE("Blackness"), saver || !fSettings.ModuleName()[0]
563 				? B_TRANSLATE("No options available") :
564 				B_TRANSLATE("Could not load screen saver"));
565 	}
566 
567 	ScreenSaverWindow* window = dynamic_cast<ScreenSaverWindow*>(Window());
568 	if (window == NULL)
569 		return;
570 
571 	// find the minimal size of the settings view
572 
573 	float right = 0, bottom = 0;
574 	int32 i = 0;
575 	while ((view = fSettingsView->ChildAt(i++)) != NULL) {
576 		// very simple heuristic...
577 		float viewRight = view->Frame().right;
578 		if ((view->ResizingMode() & _rule_(0, 0xf, 0, 0xf))
579 				== B_FOLLOW_LEFT_RIGHT) {
580 			float width, height;
581 			view->GetPreferredSize(&width, &height);
582 			viewRight = view->Frame().left + width / 2;
583 		} else if ((view->ResizingMode() & _rule_(0, 0xf, 0, 0xf))
584 				== B_FOLLOW_RIGHT)
585 			viewRight = 8 + view->Frame().Width();
586 
587 		float viewBottom = view->Frame().bottom;
588 		if ((view->ResizingMode() & _rule_(0xf, 0, 0xf, 0))
589 				== B_FOLLOW_TOP_BOTTOM) {
590 			float width, height;
591 			view->GetPreferredSize(&width, &height);
592 			viewBottom = view->Frame().top + height;
593 		} else if ((view->ResizingMode() & _rule_(0xf, 0, 0xf, 0))
594 				== B_FOLLOW_BOTTOM)
595 			viewBottom = 8 + view->Frame().Height();
596 
597 		if (viewRight > right)
598 			right = viewRight;
599 		if (viewBottom > bottom)
600 			bottom = viewBottom;
601 	}
602 
603 	if (right < kMinSettingsWidth)
604 		right = kMinSettingsWidth;
605 	if (bottom < kMinSettingsHeight)
606 		bottom = kMinSettingsHeight;
607 
608 	BPoint leftTop = fSettingsView->LeftTop();
609 	fSettingsView->ConvertToScreen(&leftTop);
610 	window->ConvertFromScreen(&leftTop);
611 	window->SetMinimalSizeLimit(leftTop.x + right + 16,
612 		leftTop.y + bottom + 16);
613 }
614 
615 
616 //	#pragma mark -
617 
618 
619 ScreenSaverWindow::ScreenSaverWindow()
620 	:
621 	BWindow(BRect(50, 50, 496, 375), B_TRANSLATE_SYSTEM_NAME("ScreenSaver"),
622 		B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS)
623 {
624 	fSettings.Load();
625 
626 	BRect rect = Bounds();
627 	fMinWidth = 430;
628 	fMinHeight = 325;
629 
630 	// Create a background view
631 	BView *background = new BView(rect, "background", B_FOLLOW_ALL, 0);
632 	background->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
633 	AddChild(background);
634 
635 	// Add the tab view to the background
636 	rect.top += 4;
637 	fTabView = new BTabView(rect, "tab_view");
638 
639 	// Create the controls inside the tabs
640 	rect = fTabView->ContainerView()->Bounds();
641 	_SetupFadeTab(rect);
642 	fModulesView = new ModulesView(rect, B_TRANSLATE("Screensavers"), fSettings);
643 
644 	fTabView->AddTab(fFadeView);
645 	fTabView->AddTab(fModulesView);
646 	background->AddChild(fTabView);
647 
648 	// Create the password editing window
649 	fPasswordWindow = new PasswordWindow(fSettings);
650 	fPasswordWindow->Run();
651 
652 	SetMinimalSizeLimit(fMinWidth, fMinHeight);
653 	MoveTo(fSettings.WindowFrame().left, fSettings.WindowFrame().top);
654 	ResizeTo(fSettings.WindowFrame().Width(), fSettings.WindowFrame().Height());
655 
656 	fEnableCheckBox->SetValue(
657 		fSettings.TimeFlags() & ENABLE_SAVER ? B_CONTROL_ON : B_CONTROL_OFF);
658 	fRunSlider->SetTime(fSettings.BlankTime());
659 	fTurnOffSlider->SetTime(fSettings.OffTime() + fSettings.BlankTime());
660 	fFadeNow->SetCorner(fSettings.BlankCorner());
661 	fFadeNever->SetCorner(fSettings.NeverBlankCorner());
662 	fPasswordCheckBox->SetValue(fSettings.LockEnable());
663 	fPasswordSlider->SetTime(fSettings.PasswordTime());
664 
665 	fTabView->Select(fSettings.WindowTab());
666 
667 	_UpdateTurnOffScreen();
668 	_UpdateStatus();
669 }
670 
671 
672 ScreenSaverWindow::~ScreenSaverWindow()
673 {
674 	Hide();
675 	_UpdateStatus();
676 
677 	delete fTabView->RemoveTab(1);
678 		// We delete this here in order to make sure the module view saves its
679 		// state while the window is still intact.
680 
681 	fSettings.Save();
682 }
683 
684 
685 //! Create the controls for the "General" tab
686 void
687 ScreenSaverWindow::_SetupFadeTab(BRect rect)
688 {
689 	fFadeView = new FadeView(rect, B_TRANSLATE("General"), fSettings);
690 
691 	float StringWidth1 = be_plain_font->StringWidth(B_TRANSLATE
692 		("Start screensaver"));
693 	float StringWidth2 = be_plain_font->StringWidth(B_TRANSLATE
694 		("Turn off screen"));
695 	float StringWidth3 = be_plain_font->StringWidth(B_TRANSLATE
696 		("Password lock"));
697 
698 	float labelWidth = StringWidth1;
699 	if (labelWidth < StringWidth2)
700 		labelWidth = StringWidth2;
701 	if (labelWidth < StringWidth3)
702 		labelWidth = StringWidth3;
703 
704 	labelWidth += 20.0f;
705 
706 	font_height fontHeight;
707 	be_plain_font->GetHeight(&fontHeight);
708 	float textHeight = ceilf(fontHeight.ascent + fontHeight.descent);
709 
710 	// taken from BRadioButton:
711 	float radioButtonOffset = 2 * floorf(textHeight / 2 - 2)
712 		+ floorf(textHeight / 2);
713 
714 	fEnableCheckBox = new BCheckBox(BRect(0, 0, 1, 1), "EnableCheckBox",
715 		B_TRANSLATE("Enable screensaver"),
716 		new BMessage(kMsgEnableScreenSaverBox));
717 	fEnableCheckBox->ResizeToPreferred();
718 
719 	rect.InsetBy(8, 8);
720 	BBox* box = new BBox(rect, "EnableScreenSaverBox", B_FOLLOW_ALL);
721 	box->SetLabel(fEnableCheckBox);
722 	fFadeView->AddChild(box);
723 
724 	// Start Screensaver
725 	rect.left += radioButtonOffset + 6;
726 	rect.top = fEnableCheckBox->Bounds().bottom + 8.0f;
727 	rect.right = box->Bounds().right - 8;
728 	BStringView* stringView = new BStringView(rect, NULL,
729 		B_TRANSLATE("Start screensaver"));
730 	stringView->ResizeToPreferred();
731 	box->AddChild(stringView);
732 
733 	rect.left += labelWidth - 4;
734 	fRunSlider = new TimeSlider(rect, "RunSlider", kMsgRunSliderChanged,
735 		kMsgRunSliderUpdate);
736 	float width, height;
737 	fRunSlider->GetPreferredSize(&width, &height);
738 	fRunSlider->ResizeTo(fRunSlider->Bounds().Width(), height);
739 	box->AddChild(fRunSlider);
740 
741 	// Turn Off
742 	rect.left = 10;
743 	rect.OffsetBy(0, fRunSlider->Bounds().Height() + 4.0f);
744 	fTurnOffCheckBox = new BCheckBox(rect, "TurnOffScreenCheckBox",
745 		B_TRANSLATE("Turn off screen"), new BMessage(kMsgTurnOffCheckBox));
746 	fTurnOffCheckBox->ResizeToPreferred();
747 	box->AddChild(fTurnOffCheckBox);
748 
749 	rect.top += 3;
750 	rect.left += radioButtonOffset + labelWidth;
751 	fTurnOffSlider = new TimeSlider(rect, "TurnOffSlider",
752 		kMsgTurnOffSliderChanged, kMsgTurnOffSliderUpdate);
753 	fTurnOffSlider->ResizeTo(fTurnOffSlider->Bounds().Width(), height);
754 	box->AddChild(fTurnOffSlider);
755 
756 	rgb_color textColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
757 		B_DISABLED_LABEL_TINT);
758 	BRect textRect(0, 0, fTurnOffSlider->Bounds().Width(), height);
759 	textRect.InsetBy(0, 3);
760 	fTurnOffNotSupported = new BTextView(rect, "not_supported",
761 		textRect, be_plain_font, &textColor, B_FOLLOW_ALL, B_WILL_DRAW);
762 	fTurnOffNotSupported->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
763 	fTurnOffNotSupported->MakeEditable(false);
764 	fTurnOffNotSupported->MakeSelectable(false);
765 	fTurnOffNotSupported->SetText(
766 		B_TRANSLATE("Display Power Management Signaling not available"));
767 
768 	fTurnOffNotSupported->ResizeTo(fTurnOffSlider->Bounds().Width(), height);
769 	box->AddChild(fTurnOffNotSupported);
770 
771 	// Password
772 	rect.left = 10;
773 	rect.OffsetBy(0, fTurnOffSlider->Bounds().Height() + 4.0f);
774 	fPasswordCheckBox = new BCheckBox(rect, "PasswordCheckbox",
775 		B_TRANSLATE("Password lock"), new BMessage(kMsgPasswordCheckBox));
776 	fPasswordCheckBox->ResizeToPreferred();
777 	box->AddChild(fPasswordCheckBox);
778 
779 	rect.top += 3;
780 	rect.left += radioButtonOffset + labelWidth;
781 	fPasswordSlider = new TimeSlider(rect, "PasswordSlider",
782 		kMsgPasswordSliderChanged, kMsgPasswordSliderUpdate);
783 	fPasswordSlider->ResizeTo(fPasswordSlider->Bounds().Width(), height);
784 	box->AddChild(fPasswordSlider);
785 
786 	rect.OffsetBy(0, fTurnOffSlider->Bounds().Height() + 4.0f);
787 	rect.left = rect.right;
788 	fPasswordButton = new BButton(rect, "PasswordButton",
789 		B_TRANSLATE("Password" B_UTF8_ELLIPSIS),
790 		new BMessage(kMsgChangePassword), B_FOLLOW_TOP | B_FOLLOW_RIGHT);
791 	fPasswordButton->ResizeToPreferred();
792 	fPasswordButton->MoveBy(-fPasswordButton->Bounds().Width(), 0);
793 	box->AddChild(fPasswordButton);
794 
795 	// Bottom
796 
797 	float monitorHeight = 10 + textHeight * 3;
798 	float monitorWidth = monitorHeight * 4 / 3;
799 	rect.left = 11;
800 	rect.top = box->Bounds().Height() - 15 - monitorHeight;
801 	rect.right = rect.left + monitorWidth;
802 	rect.bottom = rect.top + monitorHeight;
803 	box->AddChild(fFadeNow = new ScreenCornerSelector(rect, "FadeNow",
804 		new BMessage(kMsgFadeCornerChanged), B_FOLLOW_LEFT | B_FOLLOW_BOTTOM));
805 
806 	rect.OffsetBy(monitorWidth + 10, 0);
807 	stringView = new BStringView(rect, NULL, B_TRANSLATE("Fade now when"),
808 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
809 	stringView->ResizeToPreferred();
810 	float maxWidth = stringView->Bounds().Width();
811 	box->AddChild(stringView);
812 
813 	rect.OffsetBy(0, stringView->Bounds().Height());
814 	stringView = new BStringView(rect, NULL, B_TRANSLATE("mouse is here"),
815 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
816 	stringView->ResizeToPreferred();
817 	if (maxWidth < stringView->Bounds().Width())
818 		maxWidth = stringView->Bounds().Width();
819 	box->AddChild(stringView);
820 
821 	rect.left += maxWidth + 20;
822 	rect.top = box->Bounds().Height() - 15 - monitorHeight;
823 	rect.right = rect.left + monitorWidth;
824 	rect.bottom = rect.top + monitorHeight;
825 	box->AddChild(fFadeNever = new ScreenCornerSelector(rect, "FadeNever",
826 		new BMessage(kMsgNeverFadeCornerChanged),
827 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM));
828 
829 	rect.OffsetBy(monitorWidth + 10, 0);
830 	stringView = new BStringView(rect, NULL,B_TRANSLATE("Don't fade when"),
831 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
832 	stringView->ResizeToPreferred();
833 	if (maxWidth < stringView->Bounds().Width())
834 		maxWidth = stringView->Bounds().Width();
835 	box->AddChild(stringView);
836 
837 	rect.OffsetBy(0, stringView->Bounds().Height());
838 	stringView = new BStringView(rect, NULL, B_TRANSLATE("mouse is here"),
839 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
840 	stringView->ResizeToPreferred();
841 	if (maxWidth < stringView->Bounds().Width())
842 		maxWidth = stringView->Bounds().Width();
843 	box->AddChild(stringView);
844 
845 	float size = rect.left + maxWidth + 40;
846 	if (fMinWidth < size)
847 		fMinWidth = size;
848 	size = fPasswordButton->Frame().bottom + box->Frame().top
849 		+ monitorHeight + 40 + textHeight * 2;
850 	if (fMinHeight < size)
851 		fMinHeight = size;
852 }
853 
854 
855 void
856 ScreenSaverWindow::_UpdateTurnOffScreen()
857 {
858 	bool enabled = (fSettings.TimeFlags() & ENABLE_DPMS_MASK) != 0;
859 
860 	BScreen screen(this);
861 	uint32 dpmsCapabilities = screen.DPMSCapabilites();
862 
863 	fTurnOffScreenFlags = 0;
864 	if (dpmsCapabilities & B_DPMS_OFF)
865 		fTurnOffScreenFlags |= ENABLE_DPMS_OFF;
866 	if (dpmsCapabilities & B_DPMS_STAND_BY)
867 		fTurnOffScreenFlags |= ENABLE_DPMS_STAND_BY;
868 	if (dpmsCapabilities & B_DPMS_SUSPEND)
869 		fTurnOffScreenFlags |= ENABLE_DPMS_SUSPEND;
870 
871 	fTurnOffCheckBox->SetValue(enabled && fTurnOffScreenFlags != 0
872 		? B_CONTROL_ON : B_CONTROL_OFF);
873 
874 	enabled = fEnableCheckBox->Value() == B_CONTROL_ON;
875 	fTurnOffCheckBox->SetEnabled(enabled && fTurnOffScreenFlags != 0);
876 	if (fTurnOffScreenFlags != 0) {
877 		fTurnOffNotSupported->Hide();
878 		fTurnOffSlider->Show();
879 	} else {
880 		fTurnOffSlider->Hide();
881 		fTurnOffNotSupported->Show();
882 	}
883 }
884 
885 
886 void
887 ScreenSaverWindow::_UpdateStatus()
888 {
889 	DisableUpdates();
890 
891 	bool enabled = fEnableCheckBox->Value() == B_CONTROL_ON;
892 	fPasswordCheckBox->SetEnabled(enabled);
893 	fTurnOffCheckBox->SetEnabled(enabled && fTurnOffScreenFlags != 0);
894 	fRunSlider->SetEnabled(enabled);
895 	fTurnOffSlider->SetEnabled(enabled && fTurnOffCheckBox->Value());
896 	fPasswordSlider->SetEnabled(enabled && fPasswordCheckBox->Value());
897 	fPasswordButton->SetEnabled(enabled && fPasswordCheckBox->Value());
898 
899 	EnableUpdates();
900 
901 	// Update the saved preferences
902 	fSettings.SetWindowFrame(Frame());
903 	fSettings.SetWindowTab(fTabView->Selection());
904 	fSettings.SetTimeFlags((enabled ? ENABLE_SAVER : 0)
905 		| (fTurnOffCheckBox->Value() ? fTurnOffScreenFlags : 0));
906 	fSettings.SetBlankTime(fRunSlider->Time());
907 	bigtime_t offTime = fTurnOffSlider->Time() - fSettings.BlankTime();
908 	fSettings.SetOffTime(offTime);
909 	fSettings.SetSuspendTime(offTime);
910 	fSettings.SetStandByTime(offTime);
911 	fSettings.SetBlankCorner(fFadeNow->Corner());
912 	fSettings.SetNeverBlankCorner(fFadeNever->Corner());
913 	fSettings.SetLockEnable(fPasswordCheckBox->Value());
914 	fSettings.SetPasswordTime(fPasswordSlider->Time());
915 
916 	// TODO - Tell the password window to update its stuff
917 }
918 
919 
920 void
921 ScreenSaverWindow::SetMinimalSizeLimit(float width, float height)
922 {
923 	if (width < fMinWidth)
924 		width = fMinWidth;
925 	if (height < fMinHeight)
926 		height = fMinHeight;
927 
928 	SetSizeLimits(width, 32767, height, 32767);
929 }
930 
931 
932 void
933 ScreenSaverWindow::MessageReceived(BMessage *msg)
934 {
935 	// "Settings" tab, slider updates
936 
937 	switch (msg->what) {
938 		case kMsgRunSliderChanged:
939 		case kMsgRunSliderUpdate:
940 			if (fRunSlider->Value() > fTurnOffSlider->Value())
941 				fTurnOffSlider->SetValue(fRunSlider->Value());
942 
943 			if (fRunSlider->Value() > fPasswordSlider->Value())
944 				fPasswordSlider->SetValue(fRunSlider->Value());
945 			break;
946 
947 		case kMsgTurnOffSliderChanged:
948 		case kMsgTurnOffSliderUpdate:
949 			if (fRunSlider->Value() > fTurnOffSlider->Value())
950 				fRunSlider->SetValue(fTurnOffSlider->Value());
951 			break;
952 
953 		case kMsgPasswordSliderChanged:
954 		case kMsgPasswordSliderUpdate:
955 			if (fPasswordSlider->Value() < fRunSlider->Value())
956 				fRunSlider->SetValue(fPasswordSlider->Value());
957 			break;
958 	}
959 
960 	switch (msg->what) {
961 		// "General" tab
962 
963 		case kMsgTurnOffCheckBox:
964 			fTurnOffSlider->SetEnabled(
965 				fTurnOffCheckBox->Value() == B_CONTROL_ON);
966 			break;
967 
968 		case kMsgRunSliderChanged:
969 		case kMsgTurnOffSliderChanged:
970 		case kMsgPasswordSliderChanged:
971 		case kMsgPasswordCheckBox:
972 		case kMsgEnableScreenSaverBox:
973 		case kMsgFadeCornerChanged:
974 		case kMsgNeverFadeCornerChanged:
975 			_UpdateStatus();
976 			fSettings.Save();
977 			break;
978 
979 		case kMsgChangePassword:
980 			fPasswordWindow->Show();
981 			break;
982 
983 		// "Screensavers" tab
984 
985 		case kMsgUpdateList:
986 			fModulesView->PopulateScreenSaverList();
987 			break;
988 
989 		default:
990 			BWindow::MessageReceived(msg);
991 			break;
992 	}
993 }
994 
995 
996 void
997 ScreenSaverWindow::ScreenChanged(BRect frame, color_space colorSpace)
998 {
999 	_UpdateTurnOffScreen();
1000 }
1001 
1002 
1003 bool
1004 ScreenSaverWindow::QuitRequested()
1005 {
1006 	be_app->PostMessage(B_QUIT_REQUESTED);
1007 	return true;
1008 }
1009 
1010