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