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