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