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