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