xref: /haiku/src/preferences/screensaver/ScreenSaverWindow.cpp (revision 2659cdba83dc62cbff17e5dc4f9691ccff8df8da)
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 
TimeSlider(const char * name,uint32 changedMessage,uint32 updateMessage)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 
~TimeSlider()229 TimeSlider::~TimeSlider()
230 {
231 }
232 
233 
234 void
SetValue(int32 value)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
SetTime(bigtime_t useconds)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
Time() const261 TimeSlider::Time() const
262 {
263 	return 1000000LL * kTimeInUnits[Value()];
264 }
265 
266 
267 void
_TimeToString(bigtime_t useconds,BString & string)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 
FadeView(const char * name,ScreenSaverSettings & settings)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
AttachedToWindow()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
MessageReceived(BMessage * message)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
UpdateTurnOffScreen()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
UpdateStatus()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
_UpdateColors()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 
ModulesView(const char * name,ScreenSaverSettings & settings)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 	fScreenSaversListView->SetInvocationMessage(
581 		new BMessage(kMsgTestSaver));
582 	BScrollView* saversListScrollView = new BScrollView("scroll_list",
583 		fScreenSaversListView, 0, false, true);
584 
585 	fSettingsBox->SetLabel(B_TRANSLATE("Screensaver settings"));
586 	fSettingsBox->SetExplicitMinSize(BSize(
587 		floorf(be_control_look->DefaultItemSpacing()
588 			* ((kWindowWidth - 157.0f) / kDefaultItemSpacingAt12pt)),
589 		B_SIZE_UNSET));
590 
591 	BLayoutBuilder::Group<>(this, B_HORIZONTAL)
592 		.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
593 			B_USE_WINDOW_SPACING, 0)
594 		.AddGroup(B_VERTICAL)
595 			.Add(fPreviewView)
596 			.Add(saversListScrollView)
597 			.Add(fTestButton)
598 			.End()
599 		.Add(fSettingsBox)
600 		.End();
601 }
602 
603 
~ModulesView()604 ModulesView::~ModulesView()
605 {
606 	stop_watching(this);
607 
608 	delete fTestButton;
609 	delete fSettingsBox;
610 	delete fPreviewView;
611 }
612 
613 
614 void
DetachedFromWindow()615 ModulesView::DetachedFromWindow()
616 {
617 	SaveState();
618 	EmptyScreenSaverList();
619 
620 	_CloseSaver();
621 }
622 
623 
624 void
AttachedToWindow()625 ModulesView::AttachedToWindow()
626 {
627 	fScreenSaversListView->SetTarget(this);
628 	fTestButton->SetTarget(this);
629 }
630 
631 
632 void
AllAttached()633 ModulesView::AllAttached()
634 {
635 	PopulateScreenSaverList();
636 }
637 
638 
639 void
MessageReceived(BMessage * message)640 ModulesView::MessageReceived(BMessage* message)
641 {
642 	switch (message->what) {
643 		case kMsgSaverSelected:
644 		{
645 			int32 selection = fScreenSaversListView->CurrentSelection();
646 			if (selection < 0)
647 				break;
648 
649 			ScreenSaverItem* item
650 				= (ScreenSaverItem*)fScreenSaversListView->ItemAt(selection);
651 			if (item == NULL)
652 				break;
653 
654 			if (strcmp(item->Text(), B_TRANSLATE("Blackness")) == 0)
655 				fSettings.SetModuleName("");
656 			else
657 				fSettings.SetModuleName(item->Text());
658 
659 			SaveState();
660 			_CloseSaver();
661 			_OpenSaver();
662 			fSettings.Save();
663 			break;
664 		}
665 
666 		case kMsgTestSaver:
667 		{
668 			SaveState();
669 			fSettings.Save();
670 
671 			_CloseSaver();
672 
673 			be_roster->StartWatching(BMessenger(this, Looper()),
674 				B_REQUEST_QUIT);
675 			BMessage message(kMsgTestSaver);
676 			if (be_roster->Launch(SCREEN_BLANKER_SIG, &message,
677 					&fScreenSaverTestTeam) == B_OK) {
678 				break;
679 			}
680 
681 			// Try really hard to launch it. It's very likely that this fails
682 			// when we run from the CD, and there is only an incomplete mime
683 			// database for example...
684 			BPath path;
685 			if (find_directory(B_SYSTEM_BIN_DIRECTORY, &path) != B_OK
686 				|| path.Append("screen_blanker") != B_OK) {
687 				path.SetTo("/bin/screen_blanker");
688 			}
689 
690 			BEntry entry(path.Path());
691 			entry_ref ref;
692 			if (entry.GetRef(&ref) == B_OK) {
693 				be_roster->Launch(&ref, &message,
694 					&fScreenSaverTestTeam);
695 			}
696 			break;
697 		}
698 
699 		case B_NODE_MONITOR:
700 		{
701 			switch (message->GetInt32("opcode", 0)) {
702 				case B_ENTRY_CREATED:
703 				{
704 					const char* name;
705 					node_ref nodeRef;
706 
707 					message->FindString("name", &name);
708 					message->FindInt32("device", &nodeRef.device);
709 					message->FindInt64("directory", &nodeRef.node);
710 
711 					BDirectory dir(&nodeRef);
712 
713 					if (dir.InitCheck() == B_OK) {
714 						BPath path(&dir);
715 						_AddNewScreenSaverToList(name, &path);
716 					}
717 					break;
718 				}
719 
720 
721 				case B_ENTRY_MOVED:
722 				case B_ENTRY_REMOVED:
723 				{
724 					const char* name;
725 
726 					message->FindString("name", &name);
727 					_RemoveScreenSaverFromList(name);
728 
729 					break;
730 				}
731 
732 				default:
733 					// ignore any other operations
734 					break;
735 			}
736 			break;
737 		}
738 
739 		case B_SOME_APP_QUIT:
740 		{
741 			team_id team;
742 			if (message->FindInt32("be:team", &team) == B_OK
743 				&& team == fScreenSaverTestTeam) {
744 				be_roster->StopWatching(this);
745 				_OpenSaver();
746 			}
747 			break;
748 		}
749 
750 		default:
751 			BView::MessageReceived(message);
752 	}
753 }
754 
755 
756 void
SaveState()757 ModulesView::SaveState()
758 {
759 	BScreenSaver* saver = ScreenSaver();
760 	if (saver == NULL)
761 		return;
762 
763 	BMessage state;
764 	if (saver->SaveState(&state) == B_OK)
765 		fSettings.SetModuleState(fCurrentName.String(), &state);
766 }
767 
768 
769 void
EmptyScreenSaverList()770 ModulesView::EmptyScreenSaverList()
771 {
772 	fScreenSaversListView->DeselectAll();
773 	while (BListItem* item = fScreenSaversListView->RemoveItem((int32)0))
774 		delete item;
775 }
776 
777 
778 void
PopulateScreenSaverList()779 ModulesView::PopulateScreenSaverList()
780 {
781 	// Blackness is a built-in screen saver
782 	ScreenSaverItem* defaultItem
783 		= new ScreenSaverItem(B_TRANSLATE("Blackness"), "");
784 	fScreenSaversListView->AddItem(defaultItem);
785 
786 	// Iterate over add-on directories, and add their files to the list view
787 
788 	directory_which which[] = {
789 		B_USER_NONPACKAGED_ADDONS_DIRECTORY,
790 		B_USER_ADDONS_DIRECTORY,
791 		B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
792 		B_SYSTEM_ADDONS_DIRECTORY,
793 	};
794 	ScreenSaverItem* selectedItem = NULL;
795 
796 	for (uint32 i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
797 		BPath basePath;
798 		if (find_directory(which[i], &basePath) != B_OK)
799 			continue;
800 		else if (basePath.Append("Screen Savers", true) != B_OK)
801 			continue;
802 
803 		BDirectory dir(basePath.Path());
804 		BEntry entry;
805 		node_ref nodeRef;
806 
807 		dir.GetNodeRef(&nodeRef);
808 		watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
809 
810 		while (dir.GetNextEntry(&entry, true) == B_OK) {
811 			char name[B_FILE_NAME_LENGTH];
812 			if (entry.GetName(name) != B_OK)
813 				continue;
814 
815 			BPath path(basePath);
816 			if (path.Append(name) != B_OK)
817 				continue;
818 
819 			ScreenSaverItem* item = new ScreenSaverItem(name, path.Path());
820 			fScreenSaversListView->AddItem(item);
821 
822 			if (selectedItem != NULL)
823 				continue;
824 
825 			if (strcmp(fSettings.ModuleName(), item->Text()) == 0)
826 				selectedItem = item;
827 		}
828 	}
829 
830 	fScreenSaversListView->SortItems(_CompareScreenSaverItems);
831 	if (selectedItem == NULL)
832 		selectedItem = defaultItem;
833 
834 	fScreenSaversListView->Select(fScreenSaversListView->IndexOf(selectedItem));
835 	fScreenSaversListView->ScrollToSelection();
836 }
837 
838 
839 //! Sorting function for ScreenSaverItems
840 int
_CompareScreenSaverItems(const void * left,const void * right)841 ModulesView::_CompareScreenSaverItems(const void* left, const void* right)
842 {
843 	ScreenSaverItem* leftItem  = *(ScreenSaverItem **)left;
844 	ScreenSaverItem* rightItem = *(ScreenSaverItem **)right;
845 
846 	return strcasecmp(leftItem->Text(), rightItem->Text());
847 }
848 
849 
850 BScreenSaver*
ScreenSaver()851 ModulesView::ScreenSaver()
852 {
853 	if (fSaverRunner != NULL)
854 		return fSaverRunner->ScreenSaver();
855 
856 	return NULL;
857 }
858 
859 
860 void
_CloseSaver()861 ModulesView::_CloseSaver()
862 {
863 	// remove old screen saver preview & config
864 
865 	BScreenSaver* saver = ScreenSaver();
866 	BView* view = fPreviewView->RemovePreview();
867 	if (fSettingsView != NULL)
868 		fSettingsBox->RemoveChild(fSettingsView);
869 
870 	if (fSaverRunner != NULL)
871 		fSaverRunner->Quit();
872 
873 	if (saver != NULL)
874 		saver->StopConfig();
875 
876 	delete view;
877 	delete fSettingsView;
878 	delete fSaverRunner;
879 		// the saver runner also unloads the add-on, so it must
880 		// be deleted last
881 
882 	fSettingsView = NULL;
883 	fSaverRunner = NULL;
884 }
885 
886 
887 void
_OpenSaver()888 ModulesView::_OpenSaver()
889 {
890 	// create new screen saver preview & config
891 
892 	BView* view = fPreviewView->AddPreview();
893 	fCurrentName = fSettings.ModuleName();
894 	fSaverRunner = new ScreenSaverRunner(view->Window(), view, fSettings);
895 
896 #ifdef __HAIKU__
897 	BRect rect = fSettingsBox->InnerFrame().InsetByCopy(4, 4);
898 #else
899 	BRect rect = fSettingsBox->Bounds().InsetByCopy(4, 4);
900 	rect.top += 14;
901 #endif
902 	fSettingsView = new BView(rect, "SettingsView", B_FOLLOW_ALL, B_WILL_DRAW);
903 
904 	fSettingsView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
905 	fSettingsBox->AddChild(fSettingsView);
906 
907 	BScreenSaver* saver = ScreenSaver();
908 	if (saver != NULL && fSettingsView != NULL) {
909 		saver->StartConfig(fSettingsView);
910 		if (saver->StartSaver(view, true) == B_OK) {
911 			fPreviewView->HideNoPreview();
912 			fSaverRunner->Run();
913 		} else
914 			fPreviewView->ShowNoPreview();
915 	} else {
916 		// Failed to load OR this is the "Blackness" screensaver. Show a black
917 		// preview (this is what will happen in both cases when screen_blanker
918 		// runs).
919 		fPreviewView->HideNoPreview();
920 	}
921 
922 	if (fSettingsView->ChildAt(0) == NULL) {
923 		// There are no settings at all, we add the module name here to
924 		// let it look a bit better at least.
925 		BPrivate::BuildDefaultSettingsView(fSettingsView,
926 			fSettings.ModuleName()[0] ? fSettings.ModuleName()
927 				: B_TRANSLATE("Blackness"),
928 				saver != NULL || !fSettings.ModuleName()[0]
929 					? B_TRANSLATE("No options available")
930 					: B_TRANSLATE("Could not load screen saver"));
931 	}
932 }
933 
934 
935 void
_AddNewScreenSaverToList(const char * name,BPath * path)936 ModulesView::_AddNewScreenSaverToList(const char* name, BPath* path)
937 {
938 	int32 oldSelected = fScreenSaversListView->CurrentSelection();
939 	ScreenSaverItem* selectedItem = (ScreenSaverItem*)fScreenSaversListView->ItemAt(
940 		oldSelected);
941 
942 	path->Append(name);
943 	fScreenSaversListView->AddItem(new ScreenSaverItem(name, path->Path()));
944 	fScreenSaversListView->SortItems(_CompareScreenSaverItems);
945 
946 	if (selectedItem != NULL) {
947 		fScreenSaversListView->Select(fScreenSaversListView->IndexOf(
948 			selectedItem));
949 		fScreenSaversListView->ScrollToSelection();
950 	}
951 }
952 
953 
954 void
_RemoveScreenSaverFromList(const char * name)955 ModulesView::_RemoveScreenSaverFromList(const char* name)
956 {
957 	int32 oldSelected = fScreenSaversListView->CurrentSelection();
958 	ScreenSaverItem* selectedItem = (ScreenSaverItem*)fScreenSaversListView->ItemAt(
959 		oldSelected);
960 
961 	if (strcasecmp(selectedItem->Text(), name) == 0) {
962 		fScreenSaversListView->RemoveItem(selectedItem);
963 		fScreenSaversListView->SortItems(_CompareScreenSaverItems);
964 		fScreenSaversListView->Select(0);
965 		fScreenSaversListView->ScrollToSelection();
966 		return;
967 	}
968 
969 	for (int i = 0, max = fScreenSaversListView->CountItems(); i < max; i++) {
970 		ScreenSaverItem* item = (ScreenSaverItem*)fScreenSaversListView->ItemAt(
971 			i);
972 
973 		if (strcasecmp(item->Text(), name) == 0) {
974 			fScreenSaversListView->RemoveItem(item);
975 			delete item;
976 			break;
977 		}
978 	}
979 
980 	fScreenSaversListView->SortItems(_CompareScreenSaverItems);
981 
982 	oldSelected = fScreenSaversListView->IndexOf(selectedItem);
983 	fScreenSaversListView->Select(oldSelected);
984 	fScreenSaversListView->ScrollToSelection();
985 }
986 
987 
988 //	#pragma mark - TabView
989 
990 
TabView()991 TabView::TabView()
992 	:
993 	BTabView("tab_view", B_WIDTH_FROM_LABEL)
994 {
995 }
996 
997 
998 void
MouseDown(BPoint where)999 TabView::MouseDown(BPoint where)
1000 {
1001 	BTab* fadeTab = TabAt(0);
1002 	BRect fadeTabFrame(TabFrame(0));
1003 	BTab* modulesTab = TabAt(1);
1004 	BRect modulesTabFrame(TabFrame(1));
1005 	ModulesView* modulesView = NULL;
1006 
1007 	if (modulesTab != NULL)
1008 		modulesView = dynamic_cast<ModulesView*>(modulesTab->View());
1009 
1010 	if (fadeTab != NULL && Selection() != 0 && fadeTabFrame.Contains(where)
1011 		&& modulesView != NULL) {
1012 		// clicked on the fade tab
1013 		modulesView->SaveState();
1014 		modulesView->_CloseSaver();
1015 	} else if (modulesTab != NULL && Selection() != 1
1016 		&& modulesTabFrame.Contains(where) && modulesView != NULL) {
1017 		// clicked on the modules tab
1018 		BMessage message(kMsgSaverSelected);
1019 		modulesView->MessageReceived(&message);
1020 	}
1021 
1022 	BTabView::MouseDown(where);
1023 }
1024 
1025 
1026 //	#pragma mark - ScreenSaverWindow
1027 
1028 
ScreenSaverWindow()1029 ScreenSaverWindow::ScreenSaverWindow()
1030 	:
1031 	BWindow(BRect(50.0f, 50.0f, 50.0f + kWindowWidth, 50.0f + kWindowHeight),
1032 		B_TRANSLATE_SYSTEM_NAME("ScreenSaver"), B_TITLED_WINDOW,
1033 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
1034 {
1035 	fSettings.Load();
1036 
1037 	fMinWidth = floorf(be_control_look->DefaultItemSpacing()
1038 		* (kWindowWidth / kDefaultItemSpacingAt12pt));
1039 
1040 	font_height fontHeight;
1041 	be_plain_font->GetHeight(&fontHeight);
1042 	float textHeight = ceilf(fontHeight.ascent + fontHeight.descent);
1043 
1044 	fMinHeight = ceilf(std::max(kWindowHeight, textHeight * 28));
1045 
1046 	// Create the password editing window
1047 	fPasswordWindow = new PasswordWindow(fSettings);
1048 	fPasswordWindow->Run();
1049 
1050 	// Create the tab view
1051 	fTabView = new TabView();
1052 	fTabView->SetBorder(B_NO_BORDER);
1053 
1054 	// Create the controls inside the tabs
1055 	fFadeView = new FadeView(B_TRANSLATE("General"), fSettings);
1056 	fModulesView = new ModulesView(B_TRANSLATE("Screensavers"), fSettings);
1057 
1058 	fTabView->AddTab(fFadeView);
1059 	fTabView->AddTab(fModulesView);
1060 
1061 	// Create the topmost background view
1062 	BView* topView = new BView("topView", B_WILL_DRAW);
1063 	topView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1064 	topView->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
1065 		B_ALIGN_USE_FULL_HEIGHT));
1066 	topView->SetExplicitMinSize(BSize(fMinWidth, fMinHeight));
1067 	BLayoutBuilder::Group<>(topView, B_VERTICAL)
1068 		.SetInsets(0, B_USE_DEFAULT_SPACING, 0, B_USE_WINDOW_SPACING)
1069 		.Add(fTabView)
1070 		.End();
1071 
1072 	SetLayout(new BGroupLayout(B_VERTICAL));
1073 	GetLayout()->AddView(topView);
1074 
1075 	fTabView->Select(fSettings.WindowTab());
1076 
1077 	if (fSettings.WindowFrame().left > 0 && fSettings.WindowFrame().top > 0)
1078 		MoveTo(fSettings.WindowFrame().left, fSettings.WindowFrame().top);
1079 
1080 	if (fSettings.WindowFrame().Width() > 0
1081 		&& fSettings.WindowFrame().Height() > 0) {
1082 		ResizeTo(fSettings.WindowFrame().Width(),
1083 			fSettings.WindowFrame().Height());
1084 	}
1085 
1086 	CenterOnScreen();
1087 }
1088 
1089 
~ScreenSaverWindow()1090 ScreenSaverWindow::~ScreenSaverWindow()
1091 {
1092 	Hide();
1093 	fFadeView->UpdateStatus();
1094 	fSettings.SetWindowTab(fTabView->Selection());
1095 
1096 	delete fTabView->RemoveTab(1);
1097 		// We delete this here in order to make sure the module view saves its
1098 		// state while the window is still intact.
1099 
1100 	fSettings.Save();
1101 }
1102 
1103 
1104 void
MessageReceived(BMessage * message)1105 ScreenSaverWindow::MessageReceived(BMessage* message)
1106 {
1107 	switch (message->what) {
1108 		case kMsgChangePassword:
1109 			fPasswordWindow->CenterIn(Frame());
1110 			fPasswordWindow->Show();
1111 			break;
1112 
1113 		case kMsgUpdateList:
1114 			fModulesView->EmptyScreenSaverList();
1115 			fModulesView->PopulateScreenSaverList();
1116 			break;
1117 
1118 		default:
1119 			BWindow::MessageReceived(message);
1120 	}
1121 }
1122 
1123 
1124 void
ScreenChanged(BRect frame,color_space colorSpace)1125 ScreenSaverWindow::ScreenChanged(BRect frame, color_space colorSpace)
1126 {
1127 	fFadeView->UpdateTurnOffScreen();
1128 }
1129 
1130 
1131 bool
QuitRequested()1132 ScreenSaverWindow::QuitRequested()
1133 {
1134 	be_app->PostMessage(B_QUIT_REQUESTED);
1135 	return true;
1136 }
1137