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