1 /* 2 * Copyright 2020-2023, Panagiotis "Ivory" Vasilopoulos <git@n0toose.net> 3 * Copyright 2009-2010, Stephan Aßmus <superstippi@gmx.de> 4 * Copyright 2005-2008, Jérôme DUVAL 5 * All rights reserved. Distributed under the terms of the MIT License. 6 */ 7 8 9 #include "InstallerWindow.h" 10 11 #include <stdio.h> 12 #include <strings.h> 13 14 #include <Alert.h> 15 #include <Application.h> 16 #include <Autolock.h> 17 #include <Box.h> 18 #include <Button.h> 19 #include <Catalog.h> 20 #include <ColorConversion.h> 21 #include <ControlLook.h> 22 #include <Directory.h> 23 #include <FindDirectory.h> 24 #include <LayoutBuilder.h> 25 #include <LayoutUtils.h> 26 #include <Locale.h> 27 #include <MenuBar.h> 28 #include <MenuField.h> 29 #include <Path.h> 30 #include <PopUpMenu.h> 31 #include <Roster.h> 32 #include <Screen.h> 33 #include <ScrollView.h> 34 #include <SeparatorView.h> 35 #include <SpaceLayoutItem.h> 36 #include <StatusBar.h> 37 #include <String.h> 38 #include <TextView.h> 39 #include <TranslationUtils.h> 40 #include <TranslatorFormats.h> 41 42 #include "tracker_private.h" 43 44 #include "DialogPane.h" 45 #include "InstallerDefs.h" 46 #include "PackageViews.h" 47 #include "PartitionMenuItem.h" 48 #include "WorkerThread.h" 49 50 51 #undef B_TRANSLATION_CONTEXT 52 #define B_TRANSLATION_CONTEXT "InstallerWindow" 53 54 55 static const char* kDriveSetupSignature = "application/x-vnd.Haiku-DriveSetup"; 56 static const char* kBootManagerSignature = "application/x-vnd.Haiku-BootManager"; 57 58 const uint32 BEGIN_MESSAGE = 'iBGN'; 59 const uint32 SHOW_BOTTOM_MESSAGE = 'iSBT'; 60 const uint32 LAUNCH_DRIVE_SETUP = 'iSEP'; 61 const uint32 LAUNCH_BOOTMAN = 'iWBM'; 62 const uint32 START_SCAN = 'iSSC'; 63 const uint32 PACKAGE_CHECKBOX = 'iPCB'; 64 const uint32 ENCOURAGE_DRIVESETUP = 'iENC'; 65 66 67 class LogoView : public BView { 68 public: 69 LogoView(const BRect& frame); 70 LogoView(); 71 virtual ~LogoView(); 72 73 virtual void Draw(BRect update); 74 75 virtual void GetPreferredSize(float* _width, 76 float* _height); 77 78 private: 79 void _Init(); 80 81 BBitmap* fLogo; 82 }; 83 84 85 LogoView::LogoView(const BRect& frame) 86 : 87 BView(frame, "logoview", B_FOLLOW_LEFT | B_FOLLOW_TOP, 88 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 89 { 90 _Init(); 91 } 92 93 94 LogoView::LogoView() 95 : 96 BView("logoview", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 97 { 98 _Init(); 99 } 100 101 102 LogoView::~LogoView(void) 103 { 104 delete fLogo; 105 } 106 107 108 void 109 LogoView::Draw(BRect update) 110 { 111 BRect bounds(Bounds()); 112 SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR)); 113 FillRect(bounds, B_SOLID_LOW); 114 115 if (fLogo == NULL) 116 return; 117 118 BPoint placement; 119 placement.x = (bounds.left + bounds.right - fLogo->Bounds().Width()) / 2; 120 placement.y = (bounds.top + bounds.bottom - fLogo->Bounds().Height()) / 2; 121 122 DrawBitmap(fLogo, placement); 123 } 124 125 126 void 127 LogoView::GetPreferredSize(float* _width, float* _height) 128 { 129 float width = 0.0; 130 float height = 0.0; 131 if (fLogo) { 132 width = fLogo->Bounds().Width(); 133 height = fLogo->Bounds().Height(); 134 } 135 if (_width) 136 *_width = width; 137 if (_height) 138 *_height = height; 139 } 140 141 142 void 143 LogoView::_Init() 144 { 145 SetDrawingMode(B_OP_OVER); 146 147 #ifdef HAIKU_DISTRO_COMPATIBILITY_OFFICIAL 148 rgb_color bgColor = ui_color(B_DOCUMENT_BACKGROUND_COLOR); 149 150 if (bgColor.IsLight()) 151 fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "logo.png"); 152 else 153 fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "logo_dark.png"); 154 #else 155 fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "walter_logo.png"); 156 #endif 157 } 158 159 160 // #pragma mark - 161 162 163 static BLayoutItem* 164 layout_item_for(BView* view) 165 { 166 BLayout* layout = view->Parent()->GetLayout(); 167 int32 index = layout->IndexOfView(view); 168 return layout->ItemAt(index); 169 } 170 171 172 InstallerWindow::InstallerWindow() 173 : 174 BWindow(BRect(-2400, -2000, -1800, -1800), 175 B_TRANSLATE_SYSTEM_NAME("Installer"), B_TITLED_WINDOW, 176 B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS), 177 fEncouragedToSetupPartitions(false), 178 fDriveSetupLaunched(false), 179 fBootManagerLaunched(false), 180 fInstallStatus(kReadyForInstall), 181 fWorkerThread(new WorkerThread(this)), 182 fCopyEngineCancelSemaphore(-1) 183 { 184 if (!be_roster->IsRunning(kTrackerSignature)) 185 SetWorkspaces(B_ALL_WORKSPACES); 186 187 LogoView* logoView = new LogoView(); 188 189 rgb_color baseColor = ui_color(B_DOCUMENT_TEXT_COLOR); 190 fStatusView = new BTextView("statusView", be_plain_font, &baseColor, 191 B_WILL_DRAW); 192 fStatusView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR); 193 fStatusView->MakeEditable(false); 194 fStatusView->MakeSelectable(false); 195 196 BSize logoSize = logoView->MinSize(); 197 logoView->SetExplicitMaxSize(logoSize); 198 199 // In the status view, make sure that we can display 5 lines of text of ~28 characters each 200 font_height height; 201 fStatusView->GetFontHeight(&height); 202 float fontHeight = height.ascent + height.descent + height.leading; 203 fStatusView->SetExplicitMinSize(BSize(fStatusView->StringWidth("W") * 28, 204 fontHeight * 5 + 8)); 205 206 // Create a group view with a white background since the logo and status text won't have the 207 // same height, this background will show in the remaining space 208 fLogoGroup = new BGroupView(B_HORIZONTAL, 10); 209 fLogoGroup->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR); 210 fLogoGroup->GroupLayout()->SetInsets(0, 0, 10, 0); 211 fLogoGroup->AddChild(logoView); 212 fLogoGroup->AddChild(fStatusView); 213 214 fDestMenu = new BPopUpMenu(B_TRANSLATE("scanning" B_UTF8_ELLIPSIS), 215 true, false); 216 fSrcMenu = new BPopUpMenu(B_TRANSLATE("scanning" B_UTF8_ELLIPSIS), 217 true, false); 218 219 fSrcMenuField = new BMenuField("srcMenuField", 220 B_TRANSLATE("Install from:"), fSrcMenu); 221 fSrcMenuField->SetAlignment(B_ALIGN_RIGHT); 222 223 fDestMenuField = new BMenuField("destMenuField", B_TRANSLATE("Onto:"), 224 fDestMenu); 225 fDestMenuField->SetAlignment(B_ALIGN_RIGHT); 226 227 fPackagesSwitch = new PaneSwitch("options_button"); 228 fPackagesSwitch->SetLabels(B_TRANSLATE("Hide optional packages"), 229 B_TRANSLATE("Show optional packages")); 230 fPackagesSwitch->SetMessage(new BMessage(SHOW_BOTTOM_MESSAGE)); 231 fPackagesSwitch->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 232 B_SIZE_UNSET)); 233 fPackagesSwitch->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 234 B_ALIGN_TOP)); 235 236 fPackagesView = new PackagesView("packages_view"); 237 BScrollView* packagesScrollView = new BScrollView("packagesScroll", 238 fPackagesView, B_WILL_DRAW, false, true); 239 240 const char* requiredDiskSpaceString 241 = B_TRANSLATE("Additional disk space required: 0.0 KiB"); 242 fSizeView = new BStringView("size_view", requiredDiskSpaceString); 243 fSizeView->SetAlignment(B_ALIGN_RIGHT); 244 fSizeView->SetExplicitAlignment( 245 BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP)); 246 247 fProgressBar = new BStatusBar("progress", 248 B_TRANSLATE("Install progress: ")); 249 fProgressBar->SetMaxValue(100.0); 250 251 fBeginButton = new BButton("begin_button", B_TRANSLATE("Begin"), 252 new BMessage(BEGIN_MESSAGE)); 253 fBeginButton->MakeDefault(true); 254 fBeginButton->SetEnabled(false); 255 256 fLaunchDriveSetupButton = new BButton("setup_button", 257 B_TRANSLATE("Set up partitions" B_UTF8_ELLIPSIS), 258 new BMessage(LAUNCH_DRIVE_SETUP)); 259 260 fLaunchBootManagerItem = new BMenuItem(B_TRANSLATE("Set up boot menu" B_UTF8_ELLIPSIS), 261 new BMessage(LAUNCH_BOOTMAN)); 262 fLaunchBootManagerItem->SetEnabled(false); 263 264 fMakeBootableItem = new BMenuItem(B_TRANSLATE("Write boot sector"), 265 new BMessage(MSG_WRITE_BOOT_SECTOR)); 266 fMakeBootableItem->SetEnabled(false); 267 BMenuBar* mainMenu = new BMenuBar("main menu"); 268 BMenu* toolsMenu = new BMenu(B_TRANSLATE("Tools")); 269 toolsMenu->AddItem(fLaunchBootManagerItem); 270 toolsMenu->AddItem(fMakeBootableItem); 271 mainMenu->AddItem(toolsMenu); 272 273 BGroupView* packagesGroup = new BGroupView(B_VERTICAL, B_USE_ITEM_SPACING); 274 packagesGroup->AddChild(fPackagesSwitch); 275 packagesGroup->AddChild(packagesScrollView); 276 packagesGroup->AddChild(fProgressBar); 277 packagesGroup->AddChild(fSizeView); 278 279 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 280 .Add(mainMenu) 281 .Add(fLogoGroup) 282 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 283 .AddGroup(B_VERTICAL, B_USE_ITEM_SPACING) 284 .SetInsets(B_USE_WINDOW_SPACING) 285 .AddGrid(new BGridView(B_USE_ITEM_SPACING, B_USE_ITEM_SPACING)) 286 .AddMenuField(fSrcMenuField, 0, 0) 287 .AddMenuField(fDestMenuField, 0, 1) 288 .AddGlue(2, 0, 1, 2) 289 .Add(BSpaceLayoutItem::CreateVerticalStrut(5), 0, 2, 3) 290 .End() 291 .Add(packagesGroup) 292 .AddGroup(B_HORIZONTAL, B_USE_WINDOW_SPACING) 293 .Add(fLaunchDriveSetupButton) 294 .AddGlue() 295 .Add(fBeginButton) 296 .End() 297 .End() 298 .End(); 299 300 // Make the optional packages and progress bar invisible on start 301 fPackagesLayoutItem = layout_item_for(packagesScrollView); 302 fPkgSwitchLayoutItem = layout_item_for(fPackagesSwitch); 303 fSizeViewLayoutItem = layout_item_for(fSizeView); 304 fProgressLayoutItem = layout_item_for(fProgressBar); 305 306 fPackagesLayoutItem->SetVisible(false); 307 fSizeViewLayoutItem->SetVisible(false); 308 fProgressLayoutItem->SetVisible(false); 309 310 // finish creating window 311 if (!be_roster->IsRunning(kDeskbarSignature)) 312 SetFlags(Flags() | B_NOT_MINIMIZABLE); 313 314 CenterOnScreen(); 315 Show(); 316 317 // Register to receive notifications when apps launch or quit... 318 be_roster->StartWatching(this); 319 // ... and check the two we are interested in. 320 fDriveSetupLaunched = be_roster->IsRunning(kDriveSetupSignature); 321 fBootManagerLaunched = be_roster->IsRunning(kBootManagerSignature); 322 323 if (Lock()) { 324 fLaunchDriveSetupButton->SetEnabled(!fDriveSetupLaunched); 325 fLaunchBootManagerItem->SetEnabled(!fBootManagerLaunched); 326 Unlock(); 327 } 328 329 PostMessage(START_SCAN); 330 } 331 332 333 InstallerWindow::~InstallerWindow() 334 { 335 _SetCopyEngineCancelSemaphore(-1); 336 be_roster->StopWatching(this); 337 } 338 339 340 void 341 InstallerWindow::MessageReceived(BMessage *msg) 342 { 343 switch (msg->what) { 344 case MSG_RESET: 345 { 346 _SetCopyEngineCancelSemaphore(-1); 347 348 status_t error; 349 if (msg->FindInt32("error", &error) == B_OK) { 350 char errorMessage[2048]; 351 snprintf(errorMessage, sizeof(errorMessage), 352 B_TRANSLATE("An error was encountered and the " 353 "installation was not completed:\n\n" 354 "Error: %s"), strerror(error)); 355 BAlert* alert = new BAlert("error", errorMessage, B_TRANSLATE("OK")); 356 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 357 alert->Go(); 358 } 359 360 _DisableInterface(false); 361 362 fProgressLayoutItem->SetVisible(false); 363 fPkgSwitchLayoutItem->SetVisible(true); 364 _ShowOptionalPackages(); 365 _UpdateControls(); 366 break; 367 } 368 case START_SCAN: 369 _ScanPartitions(); 370 break; 371 case BEGIN_MESSAGE: 372 switch (fInstallStatus) { 373 case kReadyForInstall: 374 { 375 // get source and target 376 PartitionMenuItem* targetItem 377 = (PartitionMenuItem*)fDestMenu->FindMarked(); 378 PartitionMenuItem* srcItem 379 = (PartitionMenuItem*)fSrcMenu->FindMarked(); 380 if (srcItem == NULL || targetItem == NULL) 381 break; 382 383 _SetCopyEngineCancelSemaphore(create_sem(1, 384 "copy engine cancel")); 385 386 BList* list = new BList(); 387 int32 size = 0; 388 fPackagesView->GetPackagesToInstall(list, &size); 389 fWorkerThread->SetLock(fCopyEngineCancelSemaphore); 390 fWorkerThread->SetPackagesList(list); 391 fWorkerThread->SetSpaceRequired(size); 392 fInstallStatus = kInstalling; 393 fWorkerThread->StartInstall(srcItem->ID(), 394 targetItem->ID()); 395 fBeginButton->SetLabel(B_TRANSLATE("Stop")); 396 _DisableInterface(true); 397 398 fProgressBar->SetTo(0.0, NULL, NULL); 399 400 fPkgSwitchLayoutItem->SetVisible(false); 401 fPackagesLayoutItem->SetVisible(false); 402 fSizeViewLayoutItem->SetVisible(false); 403 fProgressLayoutItem->SetVisible(true); 404 break; 405 } 406 case kInstalling: 407 { 408 _QuitCopyEngine(true); 409 break; 410 } 411 case kFinished: 412 PostMessage(B_QUIT_REQUESTED); 413 break; 414 case kCancelled: 415 break; 416 } 417 break; 418 case SHOW_BOTTOM_MESSAGE: 419 _ShowOptionalPackages(); 420 break; 421 case SOURCE_PARTITION: 422 _PublishPackages(); 423 _UpdateControls(); 424 break; 425 case TARGET_PARTITION: 426 _UpdateControls(); 427 break; 428 case LAUNCH_DRIVE_SETUP: 429 _LaunchDriveSetup(); 430 break; 431 case LAUNCH_BOOTMAN: 432 _LaunchBootManager(); 433 break; 434 case PACKAGE_CHECKBOX: 435 { 436 char buffer[15]; 437 fPackagesView->GetTotalSizeAsString(buffer, sizeof(buffer)); 438 char string[256]; 439 snprintf(string, sizeof(string), 440 B_TRANSLATE("Additional disk space required: %s"), buffer); 441 fSizeView->SetText(string); 442 fSizeView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 443 break; 444 } 445 case ENCOURAGE_DRIVESETUP: 446 { 447 BAlert* alert = new BAlert("use drive setup", B_TRANSLATE("No partitions have " 448 "been found that are suitable for installation. Please set " 449 "up partitions and format at least one partition with the " 450 "Be File System."), B_TRANSLATE("OK")); 451 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 452 alert->Go(); 453 break; 454 } 455 case MSG_STATUS_MESSAGE: 456 { 457 float progress; 458 if (msg->FindFloat("progress", &progress) == B_OK) { 459 const char* currentItem; 460 if (msg->FindString("item", ¤tItem) != B_OK) { 461 currentItem = B_TRANSLATE_COMMENT("???", 462 "Unknown currently copied item"); 463 } 464 BString trailingLabel; 465 int32 currentCount; 466 int32 maximumCount; 467 if (msg->FindInt32("current", ¤tCount) == B_OK 468 && msg->FindInt32("maximum", &maximumCount) == B_OK) { 469 char buffer[64]; 470 snprintf(buffer, sizeof(buffer), 471 B_TRANSLATE_COMMENT("%1ld of %2ld", 472 "number of files copied"), 473 currentCount, maximumCount); 474 trailingLabel << buffer; 475 } else { 476 trailingLabel << 477 B_TRANSLATE_COMMENT("?? of ??", "Unknown progress"); 478 } 479 fProgressBar->SetTo(progress, currentItem, 480 trailingLabel.String()); 481 } else { 482 const char *status; 483 if (msg->FindString("status", &status) == B_OK) { 484 fLastStatus = fStatusView->Text(); 485 _SetStatusMessage(status); 486 } else 487 _SetStatusMessage(fLastStatus.String()); 488 } 489 break; 490 } 491 case MSG_INSTALL_FINISHED: 492 { 493 494 _SetCopyEngineCancelSemaphore(-1); 495 496 PartitionMenuItem* dstItem 497 = (PartitionMenuItem*)fDestMenu->FindMarked(); 498 499 BString status; 500 if (be_roster->IsRunning(kDeskbarSignature)) { 501 fBeginButton->SetLabel(B_TRANSLATE("Quit")); 502 status.SetToFormat(B_TRANSLATE("Installation " 503 "completed. Boot sector has been written to '%s'. Press " 504 "'Quit' to leave the Installer or choose a new target " 505 "volume to perform another installation."), 506 dstItem ? dstItem->Name() : B_TRANSLATE_COMMENT("???", 507 "Unknown partition name")); 508 } else { 509 fBeginButton->SetLabel(B_TRANSLATE("Restart")); 510 status.SetToFormat(B_TRANSLATE("Installation " 511 "completed. Boot sector has been written to '%s'. Press " 512 "'Restart' to restart the computer or choose a new target " 513 "volume to perform another installation."), 514 dstItem ? dstItem->Name() : B_TRANSLATE_COMMENT("???", 515 "Unknown partition name")); 516 } 517 518 _SetStatusMessage(status.String()); 519 fInstallStatus = kFinished; 520 521 _DisableInterface(false); 522 fProgressLayoutItem->SetVisible(false); 523 fPkgSwitchLayoutItem->SetVisible(true); 524 _ShowOptionalPackages(); 525 break; 526 } 527 case B_SOME_APP_LAUNCHED: 528 case B_SOME_APP_QUIT: 529 { 530 const char *signature; 531 if (msg->FindString("be:signature", &signature) != B_OK) 532 break; 533 bool isDriveSetup = !strcasecmp(signature, kDriveSetupSignature); 534 bool isBootManager = !strcasecmp(signature, kBootManagerSignature); 535 if (isDriveSetup || isBootManager) { 536 bool scanPartitions = false; 537 if (isDriveSetup) { 538 bool launched = msg->what == B_SOME_APP_LAUNCHED; 539 // We need to scan partitions if DriveSetup has quit. 540 scanPartitions = fDriveSetupLaunched && !launched; 541 fDriveSetupLaunched = launched; 542 } 543 if (isBootManager) 544 fBootManagerLaunched = msg->what == B_SOME_APP_LAUNCHED; 545 546 fBeginButton->SetEnabled( 547 !fDriveSetupLaunched && !fBootManagerLaunched); 548 _DisableInterface(fDriveSetupLaunched || fBootManagerLaunched); 549 if (fDriveSetupLaunched && fBootManagerLaunched) { 550 _SetStatusMessage(B_TRANSLATE("Running BootManager and " 551 "DriveSetup" B_UTF8_ELLIPSIS 552 "\n\nClose both applications to continue with the " 553 "installation.")); 554 } else if (fDriveSetupLaunched) { 555 _SetStatusMessage(B_TRANSLATE("Running DriveSetup" 556 B_UTF8_ELLIPSIS 557 "\n\nClose DriveSetup to continue with the " 558 "installation.")); 559 } else if (fBootManagerLaunched) { 560 _SetStatusMessage(B_TRANSLATE("Running BootManager" 561 B_UTF8_ELLIPSIS 562 "\n\nClose BootManager to continue with the " 563 "installation.")); 564 } else { 565 // If neither DriveSetup nor Bootman is running, we need 566 // to scan partitions in case DriveSetup has quit, or 567 // we need to update the guidance message, unless install 568 // was already finished. 569 if (scanPartitions) 570 _ScanPartitions(); 571 else if (fInstallStatus != kFinished) 572 _UpdateControls(); 573 else 574 PostMessage(MSG_INSTALL_FINISHED); 575 } 576 } 577 break; 578 } 579 case MSG_WRITE_BOOT_SECTOR: 580 fWorkerThread->WriteBootSector(fDestMenu); 581 break; 582 583 default: 584 BWindow::MessageReceived(msg); 585 break; 586 } 587 } 588 589 590 bool 591 InstallerWindow::QuitRequested() 592 { 593 if ((Flags() & B_NOT_MINIMIZABLE) != 0) { 594 // This means Deskbar is not running, i.e. Installer is the only 595 // thing on the screen and we will reboot the machine once it quits. 596 597 if (fDriveSetupLaunched && fBootManagerLaunched) { 598 BAlert* alert = new BAlert(B_TRANSLATE("Quit BootManager and " 599 "DriveSetup"), B_TRANSLATE("Please close the BootManager " 600 "and DriveSetup windows before closing the Installer window."), 601 B_TRANSLATE("OK")); 602 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 603 alert->Go(); 604 return false; 605 } 606 if (fDriveSetupLaunched) { 607 BAlert* alert = new BAlert(B_TRANSLATE("Quit DriveSetup"), 608 B_TRANSLATE("Please close the DriveSetup window before " 609 "closing the Installer window."), B_TRANSLATE("OK")); 610 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 611 alert->Go(); 612 return false; 613 } 614 if (fBootManagerLaunched) { 615 BAlert* alert = new BAlert(B_TRANSLATE("Quit BootManager"), 616 B_TRANSLATE("Please close the BootManager window before " 617 "closing the Installer window."), B_TRANSLATE("OK")); 618 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 619 alert->Go(); 620 return false; 621 } 622 if (fInstallStatus != kFinished) { 623 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("Installer"), 624 B_TRANSLATE("Are you sure you want to stop the installation?"), 625 B_TRANSLATE("Cancel"), B_TRANSLATE("Stop"), NULL, 626 B_WIDTH_AS_USUAL, B_STOP_ALERT); 627 alert->SetShortcut(0, B_ESCAPE); 628 if (alert->Go() == 0) 629 return false; 630 } 631 } else if (fInstallStatus == kInstalling) { 632 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("Installer"), 633 B_TRANSLATE("The installation is not complete yet!\n" 634 "Are you sure you want to stop it?"), 635 B_TRANSLATE("Cancel"), B_TRANSLATE("Stop"), NULL, 636 B_WIDTH_AS_USUAL, B_STOP_ALERT); 637 alert->SetShortcut(0, B_ESCAPE); 638 if (alert->Go() == 0) 639 return false; 640 } 641 642 _QuitCopyEngine(false); 643 644 BMessage quitWithInstallStatus(B_QUIT_REQUESTED); 645 quitWithInstallStatus.AddBool("install_complete", 646 fInstallStatus == kFinished); 647 648 fWorkerThread->PostMessage(&quitWithInstallStatus); 649 be_app->PostMessage(&quitWithInstallStatus); 650 return true; 651 } 652 653 654 // #pragma mark - 655 656 657 void 658 InstallerWindow::_ShowOptionalPackages() 659 { 660 if (fPackagesLayoutItem && fSizeViewLayoutItem) { 661 fPackagesLayoutItem->SetVisible(fPackagesSwitch->Value()); 662 fSizeViewLayoutItem->SetVisible(fPackagesSwitch->Value()); 663 } 664 } 665 666 667 void 668 InstallerWindow::_LaunchDriveSetup() 669 { 670 if (be_roster->Launch(kDriveSetupSignature) != B_OK) { 671 // Try really hard to launch it. It's very likely that this fails, 672 // when we run from the CD and there is only an incomplete mime 673 // database for example... 674 BPath path; 675 if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK 676 || path.Append("DriveSetup") != B_OK) { 677 path.SetTo("/boot/system/apps/DriveSetup"); 678 } 679 BEntry entry(path.Path()); 680 entry_ref ref; 681 if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) { 682 BAlert* alert = new BAlert("error", B_TRANSLATE("DriveSetup, the " 683 "application to configure disk partitions, could not be " 684 "launched."), B_TRANSLATE("OK")); 685 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 686 alert->Go(); 687 } 688 } 689 } 690 691 692 void 693 InstallerWindow::_LaunchBootManager() 694 { 695 // TODO: Currently BootManager always tries to install to the "first" 696 // harddisk. If/when it later supports being installed to a certain 697 // harddisk, we would have to pass it the disk that contains the target 698 // partition here. 699 if (be_roster->Launch(kBootManagerSignature) != B_OK) { 700 // Try really hard to launch it. It's very likely that this fails, 701 // when we run from the CD and there is only an incomplete mime 702 // database for example... 703 BPath path; 704 if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK 705 || path.Append("BootManager") != B_OK) { 706 path.SetTo("/boot/system/apps/BootManager"); 707 } 708 BEntry entry(path.Path()); 709 entry_ref ref; 710 if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) { 711 BAlert* alert = new BAlert( 712 B_TRANSLATE("Failed to launch BootManager"), 713 B_TRANSLATE("BootManager, the application to configure the " 714 "Haiku boot menu, could not be launched."), 715 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 716 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 717 alert->Go(); 718 } 719 } 720 } 721 722 723 void 724 InstallerWindow::_DisableInterface(bool disable) 725 { 726 fLaunchDriveSetupButton->SetEnabled(!disable); 727 fLaunchBootManagerItem->SetEnabled(!disable); 728 fMakeBootableItem->SetEnabled(!disable); 729 fSrcMenuField->SetEnabled(!disable); 730 fDestMenuField->SetEnabled(!disable); 731 } 732 733 734 void 735 InstallerWindow::_ScanPartitions() 736 { 737 _SetStatusMessage(B_TRANSLATE("Scanning for disks" B_UTF8_ELLIPSIS)); 738 739 BMenuItem *item; 740 while ((item = fSrcMenu->RemoveItem((int32)0))) 741 delete item; 742 while ((item = fDestMenu->RemoveItem((int32)0))) 743 delete item; 744 745 fWorkerThread->ScanDisksPartitions(fSrcMenu, fDestMenu); 746 747 if (fSrcMenu->ItemAt(0) != NULL) 748 _PublishPackages(); 749 750 _UpdateControls(); 751 } 752 753 754 void 755 InstallerWindow::_UpdateControls() 756 { 757 PartitionMenuItem* srcItem = (PartitionMenuItem*)fSrcMenu->FindMarked(); 758 BString label; 759 if (srcItem) { 760 label = srcItem->MenuLabel(); 761 } else { 762 if (fSrcMenu->CountItems() == 0) 763 label = B_TRANSLATE_COMMENT("<none>", "No partition available"); 764 else 765 label = ((PartitionMenuItem*)fSrcMenu->ItemAt(0))->MenuLabel(); 766 } 767 fSrcMenuField->MenuItem()->SetLabel(label.String()); 768 769 // Disable any unsuitable target items, check if at least one partition 770 // is suitable. 771 bool foundOneSuitableTarget = false; 772 for (int32 i = fDestMenu->CountItems() - 1; i >= 0; i--) { 773 PartitionMenuItem* dstItem 774 = (PartitionMenuItem*)fDestMenu->ItemAt(i); 775 if (srcItem != NULL && dstItem->ID() == srcItem->ID()) { 776 // Prevent the user from having picked the same partition as source 777 // and destination. 778 dstItem->SetEnabled(false); 779 dstItem->SetMarked(false); 780 } else 781 dstItem->SetEnabled(dstItem->IsValidTarget()); 782 783 if (dstItem->IsEnabled()) 784 foundOneSuitableTarget = true; 785 } 786 787 PartitionMenuItem* dstItem = (PartitionMenuItem*)fDestMenu->FindMarked(); 788 if (dstItem) { 789 label = dstItem->MenuLabel(); 790 } else { 791 if (fDestMenu->CountItems() == 0) 792 label = B_TRANSLATE_COMMENT("<none>", "No partition available"); 793 else 794 label = B_TRANSLATE("Please choose target"); 795 } 796 fDestMenuField->MenuItem()->SetLabel(label.String()); 797 798 BString statusText; 799 if (srcItem != NULL && dstItem != NULL) { 800 statusText.SetToFormat(B_TRANSLATE("Press the 'Begin' button to install " 801 "from '%1s' onto '%2s'."), srcItem->Name(), dstItem->Name()); 802 } else if (srcItem != NULL) { 803 BString partitionRequiredHaiku = B_TRANSLATE( 804 "Haiku has to be installed on a partition that uses " 805 "the Be File System, but there are currently no such " 806 "partitions available on your system."); 807 808 BString partitionRequiredDebranded = B_TRANSLATE( 809 "This operating system has to be installed on a partition " 810 "that uses the Be File System, but there are currently " 811 "no such partitions available on your system."); 812 813 if (!foundOneSuitableTarget) { 814 #ifdef HAIKU_DISTRO_COMPATIBILITY_OFFICIAL 815 statusText.Append(partitionRequiredHaiku); 816 #else 817 statusText.Append(partitionRequiredDebranded); 818 #endif 819 statusText.Append(" "); 820 statusText.Append(B_TRANSLATE( 821 "Click on 'Set up partitions" B_UTF8_ELLIPSIS 822 "' to create one.")); 823 } else { 824 statusText = B_TRANSLATE( 825 "Choose the disk you want to install " 826 "onto from the pop-up menu. Then click 'Begin'."); 827 } 828 } else if (dstItem != NULL) { 829 statusText = B_TRANSLATE("Choose the source disk from the " 830 "pop-up menu. Then click 'Begin'."); 831 } else { 832 statusText = B_TRANSLATE("Choose the source and destination disk " 833 "from the pop-up menus. Then click 'Begin'."); 834 } 835 836 _SetStatusMessage(statusText.String()); 837 838 fInstallStatus = kReadyForInstall; 839 fBeginButton->SetLabel(B_TRANSLATE("Begin")); 840 fBeginButton->SetEnabled(srcItem && dstItem); 841 842 // adjust "Write Boot Sector" and "Set up boot menu" buttons 843 if (dstItem != NULL) { 844 char buffer[256]; 845 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Write boot sector to '%s'"), 846 dstItem->Name()); 847 label = buffer; 848 } else 849 label = B_TRANSLATE("Write boot sector"); 850 fMakeBootableItem->SetEnabled(dstItem != NULL); 851 fMakeBootableItem->SetLabel(label.String()); 852 // TODO: Once bootman support writing to specific disks, enable this, since 853 // we would pass it the disk which contains the target partition. 854 // fLaunchBootManagerItem->SetEnabled(dstItem != NULL); 855 856 if (!fEncouragedToSetupPartitions && !foundOneSuitableTarget) { 857 // Focus the users attention on the DriveSetup button 858 fEncouragedToSetupPartitions = true; 859 PostMessage(ENCOURAGE_DRIVESETUP); 860 } 861 } 862 863 864 void 865 InstallerWindow::_PublishPackages() 866 { 867 fPackagesView->Clean(); 868 PartitionMenuItem *item = (PartitionMenuItem *)fSrcMenu->FindMarked(); 869 if (item == NULL) 870 return; 871 872 BPath directory; 873 BDiskDeviceRoster roster; 874 BDiskDevice device; 875 BPartition *partition; 876 if (roster.GetPartitionWithID(item->ID(), &device, &partition) == B_OK) { 877 if (partition->GetMountPoint(&directory) != B_OK) 878 return; 879 } else if (roster.GetDeviceWithID(item->ID(), &device) == B_OK) { 880 if (device.GetMountPoint(&directory) != B_OK) 881 return; 882 } else 883 return; // shouldn't happen 884 885 directory.Append(kPackagesDirectoryPath); 886 BDirectory dir(directory.Path()); 887 if (dir.InitCheck() != B_OK) 888 return; 889 890 BEntry packageEntry; 891 BList packages; 892 while (dir.GetNextEntry(&packageEntry) == B_OK) { 893 Package* package = Package::PackageFromEntry(packageEntry); 894 if (package != NULL) 895 packages.AddItem(package); 896 } 897 packages.SortItems(_ComparePackages); 898 899 fPackagesView->AddPackages(packages, new BMessage(PACKAGE_CHECKBOX)); 900 PostMessage(PACKAGE_CHECKBOX); 901 } 902 903 904 void 905 InstallerWindow::_SetStatusMessage(const char *text) 906 { 907 fStatusView->SetText(text); 908 fStatusView->InvalidateLayout(); 909 // In case the status message makes the text view higher than the 910 // logo, then we need to resize te whole window to fit it. 911 } 912 913 914 void 915 InstallerWindow::_SetCopyEngineCancelSemaphore(sem_id id, bool alreadyLocked) 916 { 917 if (fCopyEngineCancelSemaphore >= 0) { 918 if (!alreadyLocked) 919 acquire_sem(fCopyEngineCancelSemaphore); 920 delete_sem(fCopyEngineCancelSemaphore); 921 } 922 fCopyEngineCancelSemaphore = id; 923 } 924 925 926 void 927 InstallerWindow::_QuitCopyEngine(bool askUser) 928 { 929 if (fCopyEngineCancelSemaphore < 0) 930 return; 931 932 // First of all block the copy engine, so that it doesn't continue 933 // while the alert is showing, which would be irritating. 934 acquire_sem(fCopyEngineCancelSemaphore); 935 936 bool quit = true; 937 if (askUser) { 938 BAlert* alert = new BAlert("cancel", 939 B_TRANSLATE("Are you sure you want to to stop the installation?"), 940 B_TRANSLATE_COMMENT("Continue", "In alert after pressing Stop"), 941 B_TRANSLATE_COMMENT("Stop", "In alert after pressing Stop"), 0, 942 B_WIDTH_AS_USUAL, B_STOP_ALERT); 943 alert->SetShortcut(1, B_ESCAPE); 944 quit = alert->Go() != 0; 945 } 946 947 if (quit) { 948 // Make it quit by having it's lock fail... 949 _SetCopyEngineCancelSemaphore(-1, true); 950 } else 951 release_sem(fCopyEngineCancelSemaphore); 952 } 953 954 955 // #pragma mark - 956 957 958 int 959 InstallerWindow::_ComparePackages(const void* firstArg, const void* secondArg) 960 { 961 const Group* group1 = *static_cast<const Group* const *>(firstArg); 962 const Group* group2 = *static_cast<const Group* const *>(secondArg); 963 const Package* package1 = dynamic_cast<const Package*>(group1); 964 const Package* package2 = dynamic_cast<const Package*>(group2); 965 int sameGroup = strcmp(group1->GroupName(), group2->GroupName()); 966 if (sameGroup != 0) 967 return sameGroup; 968 if (package2 == NULL) 969 return -1; 970 if (package1 == NULL) 971 return 1; 972 return strcmp(package1->Name(), package2->Name()); 973 } 974 975 976