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