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