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