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