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