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