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