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