xref: /haiku/src/preferences/screensaver/ScreenSaverWindow.cpp (revision 9bac05ae6cce56d06edcdc28036bf104af7ca2a3)
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  */
11 
12 
13 #include "ScreenSaverWindow.h"
14 
15 #include <stdio.h>
16 #include <strings.h>
17 
18 #include <Alignment.h>
19 #include <Application.h>
20 #include <Box.h>
21 #include <Button.h>
22 #include <Catalog.h>
23 #include <CheckBox.h>
24 #include <ControlLook.h>
25 #include <Directory.h>
26 #include <DurationFormat.h>
27 #include <Entry.h>
28 #include <File.h>
29 #include <FindDirectory.h>
30 #include <Font.h>
31 #include <Layout.h>
32 #include <LayoutBuilder.h>
33 #include <ListItem.h>
34 #include <ListView.h>
35 #include <Path.h>
36 #include <Rect.h>
37 #include <Roster.h>
38 #include <Screen.h>
39 #include <ScreenSaver.h>
40 #include <ScreenSaverRunner.h>
41 #include <ScrollView.h>
42 #include <Size.h>
43 #include <Slider.h>
44 #include <StringView.h>
45 #include <TabView.h>
46 #include <TextView.h>
47 
48 #include <algorithm>
49 	// for std::max and std::min
50 
51 #include <BuildScreenSaverDefaultSettingsView.h>
52 
53 #include "PreviewView.h"
54 #include "ScreenCornerSelector.h"
55 #include "ScreenSaverItem.h"
56 
57 
58 #undef B_TRANSLATION_CONTEXT
59 #define B_TRANSLATION_CONTEXT "ScreenSaver"
60 
61 
62 const uint32 kPreviewMonitorGap = 16;
63 const uint32 kMinSettingsWidth = 230;
64 const uint32 kMinSettingsHeight = 120;
65 
66 const int32 kMsgSaverSelected = 'SSEL';
67 const int32 kMsgTestSaver = 'TEST';
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(0, useconds, &string);
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)
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_SMALL_SPACING)
388 		.Add(box)
389 		.End();
390 }
391 
392 
393 void
394 FadeView::AttachedToWindow()
395 {
396 	fEnableCheckBox->SetTarget(this);
397 	fRunSlider->SetTarget(this);
398 	fTurnOffCheckBox->SetTarget(this);
399 	fTurnOffSlider->SetTarget(this);
400 	fFadeNow->SetTarget(this);
401 	fFadeNever->SetTarget(this);
402 	fPasswordCheckBox->SetTarget(this);
403 	fPasswordSlider->SetTarget(this);
404 
405 	fEnableCheckBox->SetValue(
406 		fSettings.TimeFlags() & ENABLE_SAVER ? B_CONTROL_ON : B_CONTROL_OFF);
407 	fRunSlider->SetTime(fSettings.BlankTime());
408 	fTurnOffSlider->SetTime(fSettings.OffTime() + fSettings.BlankTime());
409 	fFadeNow->SetCorner(fSettings.BlankCorner());
410 	fFadeNever->SetCorner(fSettings.NeverBlankCorner());
411 	fPasswordCheckBox->SetValue(fSettings.LockEnable());
412 	fPasswordSlider->SetTime(fSettings.PasswordTime());
413 
414 	UpdateTurnOffScreen();
415 	UpdateStatus();
416 }
417 
418 
419 void
420 FadeView::MessageReceived(BMessage *message)
421 {
422 	switch (message->what) {
423 		case kMsgRunSliderChanged:
424 		case kMsgRunSliderUpdate:
425 			if (fRunSlider->Value() > fTurnOffSlider->Value())
426 				fTurnOffSlider->SetValue(fRunSlider->Value());
427 
428 			if (fRunSlider->Value() > fPasswordSlider->Value())
429 				fPasswordSlider->SetValue(fRunSlider->Value());
430 			break;
431 
432 		case kMsgTurnOffSliderChanged:
433 		case kMsgTurnOffSliderUpdate:
434 			if (fRunSlider->Value() > fTurnOffSlider->Value())
435 				fRunSlider->SetValue(fTurnOffSlider->Value());
436 			break;
437 
438 		case kMsgPasswordSliderChanged:
439 		case kMsgPasswordSliderUpdate:
440 			if (fPasswordSlider->Value() < fRunSlider->Value())
441 				fRunSlider->SetValue(fPasswordSlider->Value());
442 			break;
443 
444 		case kMsgTurnOffCheckBox:
445 			fTurnOffSlider->SetEnabled(
446 				fTurnOffCheckBox->Value() == B_CONTROL_ON);
447 			break;
448 	}
449 
450 	switch (message->what) {
451 		case kMsgRunSliderChanged:
452 		case kMsgTurnOffSliderChanged:
453 		case kMsgPasswordSliderChanged:
454 		case kMsgPasswordCheckBox:
455 		case kMsgEnableScreenSaverBox:
456 		case kMsgFadeCornerChanged:
457 		case kMsgNeverFadeCornerChanged:
458 			UpdateStatus();
459 			fSettings.Save();
460 			break;
461 
462 		default:
463 			BView::MessageReceived(message);
464 	}
465 }
466 
467 
468 void
469 FadeView::UpdateTurnOffScreen()
470 {
471 	bool enabled = (fSettings.TimeFlags() & ENABLE_DPMS_MASK) != 0;
472 
473 	BScreen screen(Window());
474 	uint32 dpmsCapabilities = screen.DPMSCapabilites();
475 
476 	fTurnOffScreenFlags = 0;
477 	if (dpmsCapabilities & B_DPMS_OFF)
478 		fTurnOffScreenFlags |= ENABLE_DPMS_OFF;
479 	if (dpmsCapabilities & B_DPMS_STAND_BY)
480 		fTurnOffScreenFlags |= ENABLE_DPMS_STAND_BY;
481 	if (dpmsCapabilities & B_DPMS_SUSPEND)
482 		fTurnOffScreenFlags |= ENABLE_DPMS_SUSPEND;
483 
484 	fTurnOffCheckBox->SetValue(enabled && fTurnOffScreenFlags != 0
485 		? B_CONTROL_ON : B_CONTROL_OFF);
486 
487 	enabled = fEnableCheckBox->Value() == B_CONTROL_ON;
488 	fTurnOffCheckBox->SetEnabled(enabled && fTurnOffScreenFlags != 0);
489 	if (fTurnOffScreenFlags != 0) {
490 		fTurnOffNotSupported->Hide();
491 		fTurnOffSlider->Show();
492 	} else {
493 		fTurnOffSlider->Hide();
494 		fTurnOffNotSupported->Show();
495 	}
496 }
497 
498 
499 void
500 FadeView::UpdateStatus()
501 {
502 	Window()->DisableUpdates();
503 
504 	bool enabled = fEnableCheckBox->Value() == B_CONTROL_ON;
505 	fPasswordCheckBox->SetEnabled(enabled);
506 	fTurnOffCheckBox->SetEnabled(enabled && fTurnOffScreenFlags != 0);
507 	fRunSlider->SetEnabled(enabled);
508 	fTurnOffSlider->SetEnabled(enabled && fTurnOffCheckBox->Value());
509 	fPasswordSlider->SetEnabled(enabled && fPasswordCheckBox->Value());
510 	fPasswordButton->SetEnabled(enabled && fPasswordCheckBox->Value());
511 
512 	Window()->EnableUpdates();
513 
514 	// Update the saved preferences
515 	fSettings.SetWindowFrame(Frame());
516 	fSettings.SetTimeFlags((enabled ? ENABLE_SAVER : 0)
517 		| (fTurnOffCheckBox->Value() ? fTurnOffScreenFlags : 0));
518 	fSettings.SetBlankTime(fRunSlider->Time());
519 	bigtime_t offTime = fTurnOffSlider->Time() - fSettings.BlankTime();
520 	fSettings.SetOffTime(offTime);
521 	fSettings.SetSuspendTime(offTime);
522 	fSettings.SetStandByTime(offTime);
523 	fSettings.SetBlankCorner(fFadeNow->Corner());
524 	fSettings.SetNeverBlankCorner(fFadeNever->Corner());
525 	fSettings.SetLockEnable(fPasswordCheckBox->Value());
526 	fSettings.SetPasswordTime(fPasswordSlider->Time());
527 
528 	// TODO - Tell the password window to update its stuff
529 }
530 
531 
532 //	#pragma mark - ModulesView
533 
534 
535 ModulesView::ModulesView(const char* name, ScreenSaverSettings& settings)
536 	:
537 	BView(name, B_WILL_DRAW),
538 	fSettings(settings),
539 	fScreenSaversListView(new BListView("SaversListView")),
540 	fTestButton(new BButton("TestButton", B_TRANSLATE("Test"),
541 		new BMessage(kMsgTestSaver))),
542 	fSaverRunner(NULL),
543 	fSettingsBox(new BBox("SettingsBox")),
544 	fSettingsView(NULL),
545 	fPreviewView(new PreviewView("preview")),
546 	fScreenSaverTestTeam(-1)
547 {
548 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
549 
550 	fScreenSaversListView->SetSelectionMessage(
551 		new BMessage(kMsgSaverSelected));
552 	BScrollView* saversListScrollView = new BScrollView("scroll_list",
553 		fScreenSaversListView, 0, false, true);
554 
555 	fSettingsBox->SetLabel(B_TRANSLATE("Screensaver settings"));
556 
557 	BLayoutBuilder::Group<>(this, B_HORIZONTAL)
558 		.SetInsets(B_USE_DEFAULT_SPACING)
559 		.AddGroup(B_VERTICAL)
560 			.Add(fPreviewView)
561 			.Add(saversListScrollView)
562 			.AddGroup(B_HORIZONTAL)
563 				.Add(fTestButton)
564 				.AddGlue()
565 				.End()
566 			.End()
567 		.Add(fSettingsBox)
568 		.End();
569 }
570 
571 
572 ModulesView::~ModulesView()
573 {
574 	delete fTestButton;
575 	delete fSettingsBox;
576 	delete fPreviewView;
577 }
578 
579 
580 void
581 ModulesView::DetachedFromWindow()
582 {
583 	SaveState();
584 	EmptyScreenSaverList();
585 
586 	_CloseSaver();
587 }
588 
589 
590 void
591 ModulesView::AttachedToWindow()
592 {
593 	fScreenSaversListView->SetTarget(this);
594 	fTestButton->SetTarget(this);
595 }
596 
597 
598 void
599 ModulesView::AllAttached()
600 {
601 	PopulateScreenSaverList();
602 	fScreenSaversListView->Invoke();
603 }
604 
605 
606 void
607 ModulesView::MessageReceived(BMessage* message)
608 {
609 	switch (message->what) {
610 		case kMsgSaverSelected:
611 		{
612 			int32 selection = fScreenSaversListView->CurrentSelection();
613 			if (selection < 0)
614 				break;
615 
616 			ScreenSaverItem* item
617 				= (ScreenSaverItem*)fScreenSaversListView->ItemAt(selection);
618 			if (item == NULL)
619 				break;
620 
621 			if (strcmp(item->Text(), B_TRANSLATE("Blackness")) == 0)
622 				fSettings.SetModuleName("");
623 			else
624 				fSettings.SetModuleName(item->Text());
625 
626 			SaveState();
627 			_CloseSaver();
628 			_OpenSaver();
629 			fSettings.Save();
630 			break;
631 		}
632 
633 		case kMsgTestSaver:
634 		{
635 			SaveState();
636 			fSettings.Save();
637 
638 			_CloseSaver();
639 
640 			be_roster->StartWatching(BMessenger(this, Looper()),
641 				B_REQUEST_QUIT);
642 			if (be_roster->Launch(SCREEN_BLANKER_SIG, &fSettings.Message(),
643 					&fScreenSaverTestTeam) == B_OK) {
644 				break;
645 			}
646 
647 			// Try really hard to launch it. It's very likely that this fails
648 			// when we run from the CD, and there is only an incomplete mime
649 			// database for example...
650 			BPath path;
651 			if (find_directory(B_SYSTEM_BIN_DIRECTORY, &path) != B_OK
652 				|| path.Append("screen_blanker") != B_OK) {
653 				path.SetTo("/bin/screen_blanker");
654 			}
655 
656 			BEntry entry(path.Path());
657 			entry_ref ref;
658 			if (entry.GetRef(&ref) == B_OK) {
659 				be_roster->Launch(&ref, &fSettings.Message(),
660 					&fScreenSaverTestTeam);
661 			}
662 			break;
663 		}
664 
665 		case B_SOME_APP_QUIT:
666 		{
667 			team_id team;
668 			if (message->FindInt32("be:team", &team) == B_OK
669 				&& team == fScreenSaverTestTeam) {
670 				be_roster->StopWatching(this);
671 				_OpenSaver();
672 			}
673 			break;
674 		}
675 
676 		default:
677 			BView::MessageReceived(message);
678 	}
679 }
680 
681 
682 void
683 ModulesView::SaveState()
684 {
685 	BScreenSaver* saver = ScreenSaver();
686 	if (saver == NULL)
687 		return;
688 
689 	BMessage state;
690 	if (saver->SaveState(&state) == B_OK)
691 		fSettings.SetModuleState(fCurrentName.String(), &state);
692 }
693 
694 
695 void
696 ModulesView::EmptyScreenSaverList()
697 {
698 	fScreenSaversListView->DeselectAll();
699 	while (BListItem* item = fScreenSaversListView->RemoveItem((int32)0))
700 		delete item;
701 }
702 
703 
704 void
705 ModulesView::PopulateScreenSaverList()
706 {
707 	// Blackness is a built-in screen saver
708 	ScreenSaverItem* defaultItem
709 		= new ScreenSaverItem(B_TRANSLATE("Blackness"), "");
710 	fScreenSaversListView->AddItem(defaultItem);
711 
712 	// Iterate over add-on directories, and add their files to the list view
713 
714 	directory_which which[] = {
715 		B_USER_NONPACKAGED_ADDONS_DIRECTORY,
716 		B_USER_ADDONS_DIRECTORY,
717 		B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
718 		B_SYSTEM_ADDONS_DIRECTORY,
719 	};
720 	ScreenSaverItem* selectedItem = NULL;
721 
722 	for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
723 		BPath basePath;
724 		if (find_directory(which[i], &basePath) != B_OK)
725 			continue;
726 		else if (basePath.Append("Screen Savers", true) != B_OK)
727 			continue;
728 
729 		BDirectory dir(basePath.Path());
730 		BEntry entry;
731 		while (dir.GetNextEntry(&entry, true) == B_OK) {
732 			char name[B_FILE_NAME_LENGTH];
733 			if (entry.GetName(name) != B_OK)
734 				continue;
735 
736 			BPath path(basePath);
737 			if (path.Append(name) != B_OK)
738 				continue;
739 
740 			ScreenSaverItem* item = new ScreenSaverItem(name, path.Path());
741 			fScreenSaversListView->AddItem(item);
742 
743 			if (selectedItem != NULL)
744 				continue;
745 
746 			if (strcmp(fSettings.ModuleName(), item->Text()) == 0)
747 				selectedItem = item;
748 		}
749 	}
750 
751 	fScreenSaversListView->SortItems(_CompareScreenSaverItems);
752 	if (selectedItem == NULL)
753 		selectedItem = defaultItem;
754 
755 	fScreenSaversListView->Select(fScreenSaversListView->IndexOf(selectedItem));
756 	fScreenSaversListView->ScrollToSelection();
757 }
758 
759 
760 //! Sorting function for ScreenSaverItems
761 int
762 ModulesView::_CompareScreenSaverItems(const void* left, const void* right)
763 {
764 	ScreenSaverItem* leftItem  = *(ScreenSaverItem **)left;
765 	ScreenSaverItem* rightItem = *(ScreenSaverItem **)right;
766 
767 	return strcasecmp(leftItem->Text(), rightItem->Text());
768 }
769 
770 
771 BScreenSaver*
772 ModulesView::ScreenSaver()
773 {
774 	if (fSaverRunner != NULL)
775 		return fSaverRunner->ScreenSaver();
776 
777 	return NULL;
778 }
779 
780 
781 void
782 ModulesView::_CloseSaver()
783 {
784 	// remove old screen saver preview & config
785 
786 	BScreenSaver* saver = ScreenSaver();
787 	BView* view = fPreviewView->RemovePreview();
788 	if (fSettingsView != NULL)
789 		fSettingsBox->RemoveChild(fSettingsView);
790 
791 	if (fSaverRunner != NULL)
792 		fSaverRunner->Quit();
793 
794 	if (saver != NULL)
795 		saver->StopConfig();
796 
797 	delete view;
798 	delete fSettingsView;
799 	delete fSaverRunner;
800 		// the saver runner also unloads the add-on, so it must
801 		// be deleted last
802 
803 	fSettingsView = NULL;
804 	fSaverRunner = NULL;
805 }
806 
807 
808 void
809 ModulesView::_OpenSaver()
810 {
811 	// create new screen saver preview & config
812 
813 	BView* view = fPreviewView->AddPreview();
814 	fCurrentName = fSettings.ModuleName();
815 	fSaverRunner = new ScreenSaverRunner(view->Window(), view, fSettings);
816 
817 #ifdef __HAIKU__
818 	BRect rect = fSettingsBox->InnerFrame().InsetByCopy(4, 4);
819 #else
820 	BRect rect = fSettingsBox->Bounds().InsetByCopy(4, 4);
821 	rect.top += 14;
822 #endif
823 	fSettingsView = new BView(rect, "SettingsView", B_FOLLOW_ALL, B_WILL_DRAW);
824 
825 	fSettingsView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
826 	fSettingsBox->AddChild(fSettingsView);
827 
828 	BScreenSaver* saver = ScreenSaver();
829 	if (saver != NULL && fSettingsView != NULL) {
830 		saver->StartConfig(fSettingsView);
831 		if (saver->StartSaver(view, true) == B_OK) {
832 			fPreviewView->HideNoPreview();
833 			fSaverRunner->Run();
834 		} else
835 			fPreviewView->ShowNoPreview();
836 	} else
837 		fPreviewView->ShowNoPreview();
838 
839 	if (fSettingsView->ChildAt(0) == NULL) {
840 		// There are no settings at all, we add the module name here to
841 		// let it look a bit better at least.
842 		BPrivate::BuildScreenSaverDefaultSettingsView(fSettingsView,
843 			fSettings.ModuleName()[0] ? fSettings.ModuleName()
844 				: B_TRANSLATE("Blackness"),
845 				saver != NULL || !fSettings.ModuleName()[0]
846 					? B_TRANSLATE("No options available")
847 					: B_TRANSLATE("Could not load screen saver"));
848 	}
849 }
850 
851 
852 //	#pragma mark - TabView
853 
854 
855 TabView::TabView()
856 	:
857 	BTabView("tab_view", B_WIDTH_FROM_LABEL)
858 {
859 }
860 
861 
862 void
863 TabView::MouseDown(BPoint where)
864 {
865 	BTab* fadeTab = TabAt(0);
866 	BRect fadeTabFrame(TabFrame(0));
867 	BTab* modulesTab = TabAt(1);
868 	BRect modulesTabFrame(TabFrame(1));
869 	ModulesView* modulesView = dynamic_cast<ModulesView*>(modulesTab->View());
870 
871 	if (fadeTab != NULL && Selection() != 0 && fadeTabFrame.Contains(where)
872 		&& modulesView != NULL) {
873 		// clicked on the fade tab
874 		modulesView->SaveState();
875 		modulesView->_CloseSaver();
876 	} else if (modulesTab != NULL && Selection() != 1
877 		&& modulesTabFrame.Contains(where) && modulesView != NULL) {
878 		// clicked on the modules tab
879 		BMessage message(kMsgSaverSelected);
880 		modulesView->MessageReceived(&message);
881 	}
882 
883 	BTabView::MouseDown(where);
884 }
885 
886 
887 //	#pragma mark - ScreenSaverWindow
888 
889 
890 ScreenSaverWindow::ScreenSaverWindow()
891 	:
892 	BWindow(BRect(50, 50, 496, 375),
893 		B_TRANSLATE_SYSTEM_NAME("ScreenSaver"), B_TITLED_WINDOW,
894 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
895 {
896 	fSettings.Load();
897 
898 	fMinWidth = ceilf(std::max(446.0f,
899 		be_control_look->DefaultItemSpacing() * 44.6f));
900 
901 	font_height fontHeight;
902 	be_plain_font->GetHeight(&fontHeight);
903 	float textHeight = ceilf(fontHeight.ascent + fontHeight.descent);
904 
905 	fMinHeight = ceilf(std::max(325.0f, textHeight * 28));
906 
907 	// Create the password editing window
908 	fPasswordWindow = new PasswordWindow(fSettings);
909 	fPasswordWindow->Run();
910 
911 	// Create the tab view
912 	fTabView = new TabView();
913 	fTabView->SetBorder(B_NO_BORDER);
914 
915 	// Create the controls inside the tabs
916 	fFadeView = new FadeView(B_TRANSLATE("General"), fSettings);
917 	fModulesView = new ModulesView(B_TRANSLATE("Screensavers"), fSettings);
918 
919 	fTabView->AddTab(fFadeView);
920 	fTabView->AddTab(fModulesView);
921 
922 	// Create the topmost background view
923 	BView* topView = new BView("topView", B_WILL_DRAW);
924 	topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
925 	topView->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
926 		B_ALIGN_USE_FULL_HEIGHT));
927 	topView->SetExplicitMinSize(BSize(fMinWidth, fMinHeight));
928 	BLayoutBuilder::Group<>(topView, B_VERTICAL)
929 		.SetInsets(0, B_USE_SMALL_SPACING, 0, 0)
930 		.Add(fTabView)
931 		.End();
932 
933 	SetLayout(new BGroupLayout(B_VERTICAL));
934 	GetLayout()->AddView(topView);
935 
936 	fTabView->Select(fSettings.WindowTab());
937 
938 	if (fSettings.WindowFrame().left > 0 && fSettings.WindowFrame().top > 0)
939 		MoveTo(fSettings.WindowFrame().left, fSettings.WindowFrame().top);
940 
941 	if (fSettings.WindowFrame().Width() > 0
942 		&& fSettings.WindowFrame().Height() > 0) {
943 		ResizeTo(fSettings.WindowFrame().Width(),
944 			fSettings.WindowFrame().Height());
945 	}
946 }
947 
948 
949 ScreenSaverWindow::~ScreenSaverWindow()
950 {
951 	Hide();
952 	fFadeView->UpdateStatus();
953 	fSettings.SetWindowTab(fTabView->Selection());
954 
955 	delete fTabView->RemoveTab(1);
956 		// We delete this here in order to make sure the module view saves its
957 		// state while the window is still intact.
958 
959 	fSettings.Save();
960 }
961 
962 
963 void
964 ScreenSaverWindow::MessageReceived(BMessage* message)
965 {
966 	switch (message->what) {
967 		case kMsgChangePassword:
968 			fPasswordWindow->CenterIn(Frame());
969 			fPasswordWindow->Show();
970 			break;
971 
972 		case kMsgUpdateList:
973 			fModulesView->EmptyScreenSaverList();
974 			fModulesView->PopulateScreenSaverList();
975 			break;
976 
977 		default:
978 			BWindow::MessageReceived(message);
979 	}
980 }
981 
982 
983 void
984 ScreenSaverWindow::ScreenChanged(BRect frame, color_space colorSpace)
985 {
986 	fFadeView->UpdateTurnOffScreen();
987 }
988 
989 
990 bool
991 ScreenSaverWindow::QuitRequested()
992 {
993 	be_app->PostMessage(B_QUIT_REQUESTED);
994 	return true;
995 }
996