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