xref: /haiku/src/preferences/screensaver/ScreenSaverWindow.cpp (revision 81ec973846ea4816c53ed8901822e43c8b06878d)
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)
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 			BMessage message(kMsgTestSaver);
643 			if (be_roster->Launch(SCREEN_BLANKER_SIG, &message,
644 					&fScreenSaverTestTeam) == B_OK) {
645 				break;
646 			}
647 
648 			// Try really hard to launch it. It's very likely that this fails
649 			// when we run from the CD, and there is only an incomplete mime
650 			// database for example...
651 			BPath path;
652 			if (find_directory(B_SYSTEM_BIN_DIRECTORY, &path) != B_OK
653 				|| path.Append("screen_blanker") != B_OK) {
654 				path.SetTo("/bin/screen_blanker");
655 			}
656 
657 			BEntry entry(path.Path());
658 			entry_ref ref;
659 			if (entry.GetRef(&ref) == B_OK) {
660 				be_roster->Launch(&ref, &message,
661 					&fScreenSaverTestTeam);
662 			}
663 			break;
664 		}
665 
666 		case B_SOME_APP_QUIT:
667 		{
668 			team_id team;
669 			if (message->FindInt32("be:team", &team) == B_OK
670 				&& team == fScreenSaverTestTeam) {
671 				be_roster->StopWatching(this);
672 				_OpenSaver();
673 			}
674 			break;
675 		}
676 
677 		default:
678 			BView::MessageReceived(message);
679 	}
680 }
681 
682 
683 void
684 ModulesView::SaveState()
685 {
686 	BScreenSaver* saver = ScreenSaver();
687 	if (saver == NULL)
688 		return;
689 
690 	BMessage state;
691 	if (saver->SaveState(&state) == B_OK)
692 		fSettings.SetModuleState(fCurrentName.String(), &state);
693 }
694 
695 
696 void
697 ModulesView::EmptyScreenSaverList()
698 {
699 	fScreenSaversListView->DeselectAll();
700 	while (BListItem* item = fScreenSaversListView->RemoveItem((int32)0))
701 		delete item;
702 }
703 
704 
705 void
706 ModulesView::PopulateScreenSaverList()
707 {
708 	// Blackness is a built-in screen saver
709 	ScreenSaverItem* defaultItem
710 		= new ScreenSaverItem(B_TRANSLATE("Blackness"), "");
711 	fScreenSaversListView->AddItem(defaultItem);
712 
713 	// Iterate over add-on directories, and add their files to the list view
714 
715 	directory_which which[] = {
716 		B_USER_NONPACKAGED_ADDONS_DIRECTORY,
717 		B_USER_ADDONS_DIRECTORY,
718 		B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
719 		B_SYSTEM_ADDONS_DIRECTORY,
720 	};
721 	ScreenSaverItem* selectedItem = NULL;
722 
723 	for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
724 		BPath basePath;
725 		if (find_directory(which[i], &basePath) != B_OK)
726 			continue;
727 		else if (basePath.Append("Screen Savers", true) != B_OK)
728 			continue;
729 
730 		BDirectory dir(basePath.Path());
731 		BEntry entry;
732 		while (dir.GetNextEntry(&entry, true) == B_OK) {
733 			char name[B_FILE_NAME_LENGTH];
734 			if (entry.GetName(name) != B_OK)
735 				continue;
736 
737 			BPath path(basePath);
738 			if (path.Append(name) != B_OK)
739 				continue;
740 
741 			ScreenSaverItem* item = new ScreenSaverItem(name, path.Path());
742 			fScreenSaversListView->AddItem(item);
743 
744 			if (selectedItem != NULL)
745 				continue;
746 
747 			if (strcmp(fSettings.ModuleName(), item->Text()) == 0)
748 				selectedItem = item;
749 		}
750 	}
751 
752 	fScreenSaversListView->SortItems(_CompareScreenSaverItems);
753 	if (selectedItem == NULL)
754 		selectedItem = defaultItem;
755 
756 	fScreenSaversListView->Select(fScreenSaversListView->IndexOf(selectedItem));
757 	fScreenSaversListView->ScrollToSelection();
758 }
759 
760 
761 //! Sorting function for ScreenSaverItems
762 int
763 ModulesView::_CompareScreenSaverItems(const void* left, const void* right)
764 {
765 	ScreenSaverItem* leftItem  = *(ScreenSaverItem **)left;
766 	ScreenSaverItem* rightItem = *(ScreenSaverItem **)right;
767 
768 	return strcasecmp(leftItem->Text(), rightItem->Text());
769 }
770 
771 
772 BScreenSaver*
773 ModulesView::ScreenSaver()
774 {
775 	if (fSaverRunner != NULL)
776 		return fSaverRunner->ScreenSaver();
777 
778 	return NULL;
779 }
780 
781 
782 void
783 ModulesView::_CloseSaver()
784 {
785 	// remove old screen saver preview & config
786 
787 	BScreenSaver* saver = ScreenSaver();
788 	BView* view = fPreviewView->RemovePreview();
789 	if (fSettingsView != NULL)
790 		fSettingsBox->RemoveChild(fSettingsView);
791 
792 	if (fSaverRunner != NULL)
793 		fSaverRunner->Quit();
794 
795 	if (saver != NULL)
796 		saver->StopConfig();
797 
798 	delete view;
799 	delete fSettingsView;
800 	delete fSaverRunner;
801 		// the saver runner also unloads the add-on, so it must
802 		// be deleted last
803 
804 	fSettingsView = NULL;
805 	fSaverRunner = NULL;
806 }
807 
808 
809 void
810 ModulesView::_OpenSaver()
811 {
812 	// create new screen saver preview & config
813 
814 	BView* view = fPreviewView->AddPreview();
815 	fCurrentName = fSettings.ModuleName();
816 	fSaverRunner = new ScreenSaverRunner(view->Window(), view, fSettings);
817 
818 #ifdef __HAIKU__
819 	BRect rect = fSettingsBox->InnerFrame().InsetByCopy(4, 4);
820 #else
821 	BRect rect = fSettingsBox->Bounds().InsetByCopy(4, 4);
822 	rect.top += 14;
823 #endif
824 	fSettingsView = new BView(rect, "SettingsView", B_FOLLOW_ALL, B_WILL_DRAW);
825 
826 	fSettingsView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
827 	fSettingsBox->AddChild(fSettingsView);
828 
829 	BScreenSaver* saver = ScreenSaver();
830 	if (saver != NULL && fSettingsView != NULL) {
831 		saver->StartConfig(fSettingsView);
832 		if (saver->StartSaver(view, true) == B_OK) {
833 			fPreviewView->HideNoPreview();
834 			fSaverRunner->Run();
835 		} else
836 			fPreviewView->ShowNoPreview();
837 	} else {
838 		// Failed to load OR this is the "Darkness" screensaver. Show a black
839 		// preview (this is what will happen in both cases when screen_blanker
840 		// runs).
841 		fPreviewView->HideNoPreview();
842 	}
843 
844 	if (fSettingsView->ChildAt(0) == NULL) {
845 		// There are no settings at all, we add the module name here to
846 		// let it look a bit better at least.
847 		BPrivate::BuildScreenSaverDefaultSettingsView(fSettingsView,
848 			fSettings.ModuleName()[0] ? fSettings.ModuleName()
849 				: B_TRANSLATE("Blackness"),
850 				saver != NULL || !fSettings.ModuleName()[0]
851 					? B_TRANSLATE("No options available")
852 					: B_TRANSLATE("Could not load screen saver"));
853 	}
854 }
855 
856 
857 //	#pragma mark - TabView
858 
859 
860 TabView::TabView()
861 	:
862 	BTabView("tab_view", B_WIDTH_FROM_LABEL)
863 {
864 }
865 
866 
867 void
868 TabView::MouseDown(BPoint where)
869 {
870 	BTab* fadeTab = TabAt(0);
871 	BRect fadeTabFrame(TabFrame(0));
872 	BTab* modulesTab = TabAt(1);
873 	BRect modulesTabFrame(TabFrame(1));
874 	ModulesView* modulesView = dynamic_cast<ModulesView*>(modulesTab->View());
875 
876 	if (fadeTab != NULL && Selection() != 0 && fadeTabFrame.Contains(where)
877 		&& modulesView != NULL) {
878 		// clicked on the fade tab
879 		modulesView->SaveState();
880 		modulesView->_CloseSaver();
881 	} else if (modulesTab != NULL && Selection() != 1
882 		&& modulesTabFrame.Contains(where) && modulesView != NULL) {
883 		// clicked on the modules tab
884 		BMessage message(kMsgSaverSelected);
885 		modulesView->MessageReceived(&message);
886 	}
887 
888 	BTabView::MouseDown(where);
889 }
890 
891 
892 //	#pragma mark - ScreenSaverWindow
893 
894 
895 ScreenSaverWindow::ScreenSaverWindow()
896 	:
897 	BWindow(BRect(50, 50, 496, 375),
898 		B_TRANSLATE_SYSTEM_NAME("ScreenSaver"), B_TITLED_WINDOW,
899 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
900 {
901 	fSettings.Load();
902 
903 	fMinWidth = ceilf(std::max(446.0f,
904 		be_control_look->DefaultItemSpacing() * 44.6f));
905 
906 	font_height fontHeight;
907 	be_plain_font->GetHeight(&fontHeight);
908 	float textHeight = ceilf(fontHeight.ascent + fontHeight.descent);
909 
910 	fMinHeight = ceilf(std::max(325.0f, textHeight * 28));
911 
912 	// Create the password editing window
913 	fPasswordWindow = new PasswordWindow(fSettings);
914 	fPasswordWindow->Run();
915 
916 	// Create the tab view
917 	fTabView = new TabView();
918 	fTabView->SetBorder(B_NO_BORDER);
919 
920 	// Create the controls inside the tabs
921 	fFadeView = new FadeView(B_TRANSLATE("General"), fSettings);
922 	fModulesView = new ModulesView(B_TRANSLATE("Screensavers"), fSettings);
923 
924 	fTabView->AddTab(fFadeView);
925 	fTabView->AddTab(fModulesView);
926 
927 	// Create the topmost background view
928 	BView* topView = new BView("topView", B_WILL_DRAW);
929 	topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
930 	topView->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
931 		B_ALIGN_USE_FULL_HEIGHT));
932 	topView->SetExplicitMinSize(BSize(fMinWidth, fMinHeight));
933 	BLayoutBuilder::Group<>(topView, B_VERTICAL)
934 		.SetInsets(0, B_USE_SMALL_SPACING, 0, 0)
935 		.Add(fTabView)
936 		.End();
937 
938 	SetLayout(new BGroupLayout(B_VERTICAL));
939 	GetLayout()->AddView(topView);
940 
941 	fTabView->Select(fSettings.WindowTab());
942 
943 	if (fSettings.WindowFrame().left > 0 && fSettings.WindowFrame().top > 0)
944 		MoveTo(fSettings.WindowFrame().left, fSettings.WindowFrame().top);
945 
946 	if (fSettings.WindowFrame().Width() > 0
947 		&& fSettings.WindowFrame().Height() > 0) {
948 		ResizeTo(fSettings.WindowFrame().Width(),
949 			fSettings.WindowFrame().Height());
950 	}
951 }
952 
953 
954 ScreenSaverWindow::~ScreenSaverWindow()
955 {
956 	Hide();
957 	fFadeView->UpdateStatus();
958 	fSettings.SetWindowTab(fTabView->Selection());
959 
960 	delete fTabView->RemoveTab(1);
961 		// We delete this here in order to make sure the module view saves its
962 		// state while the window is still intact.
963 
964 	fSettings.Save();
965 }
966 
967 
968 void
969 ScreenSaverWindow::MessageReceived(BMessage* message)
970 {
971 	switch (message->what) {
972 		case kMsgChangePassword:
973 			fPasswordWindow->CenterIn(Frame());
974 			fPasswordWindow->Show();
975 			break;
976 
977 		case kMsgUpdateList:
978 			fModulesView->EmptyScreenSaverList();
979 			fModulesView->PopulateScreenSaverList();
980 			break;
981 
982 		default:
983 			BWindow::MessageReceived(message);
984 	}
985 }
986 
987 
988 void
989 ScreenSaverWindow::ScreenChanged(BRect frame, color_space colorSpace)
990 {
991 	fFadeView->UpdateTurnOffScreen();
992 }
993 
994 
995 bool
996 ScreenSaverWindow::QuitRequested()
997 {
998 	be_app->PostMessage(B_QUIT_REQUESTED);
999 	return true;
1000 }
1001