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