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