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