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_TRANSLATION_CONTEXT 49 #define B_TRANSLATION_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 .SetInsets(spacing) 256 .AddGrid(new BGridView(0.0f, spacing)) 257 .Add(fSrcMenuField->CreateLabelLayoutItem(), 0, 0) 258 .Add(fSrcMenuField->CreateMenuBarLayoutItem(), 1, 0) 259 .Add(fDestMenuField->CreateLabelLayoutItem(), 0, 1) 260 .Add(fDestMenuField->CreateMenuBarLayoutItem(), 1, 1) 261 262 .Add(BSpaceLayoutItem::CreateVerticalStrut(5), 0, 2, 2) 263 264 .Add(fPackagesSwitch, 0, 3, 2) 265 .Add(packagesScrollView, 0, 4, 2) 266 .Add(fProgressBar, 0, 5, 2) 267 .Add(fSizeView, 0, 6, 2) 268 .End() 269 270 .AddGroup(B_HORIZONTAL, 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 BAlert* alert = new BAlert("error", errorMessage, B_TRANSLATE("OK")); 350 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 351 alert->Go(); 352 } 353 354 _DisableInterface(false); 355 356 fProgressLayoutItem->SetVisible(false); 357 fPkgSwitchLayoutItem->SetVisible(true); 358 _ShowOptionalPackages(); 359 _UpdateControls(); 360 break; 361 } 362 case START_SCAN: 363 _ScanPartitions(); 364 break; 365 case BEGIN_MESSAGE: 366 switch (fInstallStatus) { 367 case kReadyForInstall: 368 { 369 _SetCopyEngineCancelSemaphore(create_sem(1, 370 "copy engine cancel")); 371 372 BList* list = new BList(); 373 int32 size = 0; 374 fPackagesView->GetPackagesToInstall(list, &size); 375 fWorkerThread->SetLock(fCopyEngineCancelSemaphore); 376 fWorkerThread->SetPackagesList(list); 377 fWorkerThread->SetSpaceRequired(size); 378 fInstallStatus = kInstalling; 379 fWorkerThread->StartInstall(); 380 fBeginButton->SetLabel(B_TRANSLATE("Stop")); 381 _DisableInterface(true); 382 383 fProgressBar->SetTo(0.0, NULL, NULL); 384 385 fPkgSwitchLayoutItem->SetVisible(false); 386 fPackagesLayoutItem->SetVisible(false); 387 fSizeViewLayoutItem->SetVisible(false); 388 fProgressLayoutItem->SetVisible(true); 389 break; 390 } 391 case kInstalling: 392 { 393 _QuitCopyEngine(true); 394 break; 395 } 396 case kFinished: 397 PostMessage(B_QUIT_REQUESTED); 398 break; 399 case kCancelled: 400 break; 401 } 402 break; 403 case SHOW_BOTTOM_MESSAGE: 404 _ShowOptionalPackages(); 405 break; 406 case SOURCE_PARTITION: 407 _PublishPackages(); 408 _UpdateControls(); 409 break; 410 case TARGET_PARTITION: 411 _UpdateControls(); 412 break; 413 case LAUNCH_DRIVE_SETUP: 414 _LaunchDriveSetup(); 415 break; 416 case LAUNCH_BOOTMAN: 417 _LaunchBootManager(); 418 break; 419 case PACKAGE_CHECKBOX: 420 { 421 char buffer[15]; 422 fPackagesView->GetTotalSizeAsString(buffer, sizeof(buffer)); 423 char string[256]; 424 snprintf(string, sizeof(string), 425 B_TRANSLATE("Additional disk space required: %s"), buffer); 426 fSizeView->SetText(string); 427 break; 428 } 429 case ENCOURAGE_DRIVESETUP: 430 { 431 BAlert* alert = new BAlert("use drive setup", B_TRANSLATE("No partitions have " 432 "been found that are suitable for installation. Please set " 433 "up partitions and initialize at least one partition with the " 434 "Be File System."), B_TRANSLATE("OK")); 435 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 436 alert->Go(); 437 break; 438 } 439 case MSG_STATUS_MESSAGE: 440 { 441 // TODO: Was this supposed to prevent status messages still arriving 442 // after the copy engine was shut down? 443 // if (fInstallStatus != kInstalling) 444 // break; 445 float progress; 446 if (msg->FindFloat("progress", &progress) == B_OK) { 447 const char* currentItem; 448 if (msg->FindString("item", ¤tItem) != B_OK) { 449 currentItem = B_TRANSLATE_COMMENT("???", 450 "Unknown currently copied item"); 451 } 452 BString trailingLabel; 453 int32 currentCount; 454 int32 maximumCount; 455 if (msg->FindInt32("current", ¤tCount) == B_OK 456 && msg->FindInt32("maximum", &maximumCount) == B_OK) { 457 char buffer[64]; 458 snprintf(buffer, sizeof(buffer), 459 B_TRANSLATE_COMMENT("%1ld of %2ld", 460 "number of files copied"), 461 currentCount, maximumCount); 462 trailingLabel << buffer; 463 } else { 464 trailingLabel << 465 B_TRANSLATE_COMMENT("?? of ??", "Unknown progress"); 466 } 467 fProgressBar->SetTo(progress, currentItem, 468 trailingLabel.String()); 469 } else { 470 const char *status; 471 if (msg->FindString("status", &status) == B_OK) { 472 fLastStatus = fStatusView->Text(); 473 _SetStatusMessage(status); 474 } else 475 _SetStatusMessage(fLastStatus.String()); 476 } 477 break; 478 } 479 case MSG_INSTALL_FINISHED: 480 { 481 482 _SetCopyEngineCancelSemaphore(-1); 483 484 PartitionMenuItem* dstItem 485 = (PartitionMenuItem*)fDestMenu->FindMarked(); 486 487 char status[1024]; 488 if (be_roster->IsRunning(kDeskbarSignature)) { 489 fBeginButton->SetLabel(B_TRANSLATE("Quit")); 490 snprintf(status, sizeof(status), B_TRANSLATE("Installation " 491 "completed. Boot sector has been written to '%s'. Press " 492 "Quit to leave the Installer or choose a new target " 493 "volume to perform another installation."), 494 dstItem ? dstItem->Name() : B_TRANSLATE_COMMENT("???", 495 "Unknown partition name")); 496 } else { 497 fBeginButton->SetLabel(B_TRANSLATE("Restart")); 498 snprintf(status, sizeof(status), B_TRANSLATE("Installation " 499 "completed. Boot sector has been written to '%s'. Press " 500 "Restart to restart the computer or choose a new target " 501 "volume to perform another installation."), 502 dstItem ? dstItem->Name() : B_TRANSLATE_COMMENT("???", 503 "Unknown partition name")); 504 } 505 506 _SetStatusMessage(status); 507 fInstallStatus = kFinished; 508 _DisableInterface(false); 509 fProgressLayoutItem->SetVisible(false); 510 fPkgSwitchLayoutItem->SetVisible(true); 511 _ShowOptionalPackages(); 512 break; 513 } 514 case B_SOME_APP_LAUNCHED: 515 case B_SOME_APP_QUIT: 516 { 517 const char *signature; 518 if (msg->FindString("be:signature", &signature) != B_OK) 519 break; 520 bool isDriveSetup = !strcasecmp(signature, kDriveSetupSignature); 521 bool isBootManager = !strcasecmp(signature, kBootManagerSignature); 522 if (isDriveSetup || isBootManager) { 523 bool scanPartitions = false; 524 if (isDriveSetup) { 525 bool launched = msg->what == B_SOME_APP_LAUNCHED; 526 // We need to scan partitions if DriveSetup has quit. 527 scanPartitions = fDriveSetupLaunched && !launched; 528 fDriveSetupLaunched = launched; 529 } 530 if (isBootManager) 531 fBootManagerLaunched = msg->what == B_SOME_APP_LAUNCHED; 532 533 fBeginButton->SetEnabled( 534 !fDriveSetupLaunched && !fBootManagerLaunched); 535 _DisableInterface(fDriveSetupLaunched || fBootManagerLaunched); 536 if (fDriveSetupLaunched && fBootManagerLaunched) { 537 _SetStatusMessage(B_TRANSLATE("Running Boot Manager and " 538 "DriveSetup" B_UTF8_ELLIPSIS 539 "\n\nClose both applications to continue with the " 540 "installation.")); 541 } else if (fDriveSetupLaunched) { 542 _SetStatusMessage(B_TRANSLATE("Running DriveSetup" 543 B_UTF8_ELLIPSIS 544 "\n\nClose DriveSetup to continue with the " 545 "installation.")); 546 } else if (fBootManagerLaunched) { 547 _SetStatusMessage(B_TRANSLATE("Running Boot Manager" 548 B_UTF8_ELLIPSIS 549 "\n\nClose Boot Manager to continue with the " 550 "installation.")); 551 } else { 552 // If neither DriveSetup nor Bootman is running, we need 553 // to scan partitions in case DriveSetup has quit, or 554 // we need to update the guidance message. 555 if (scanPartitions) 556 _ScanPartitions(); 557 else 558 _UpdateControls(); 559 } 560 } 561 break; 562 } 563 case MSG_WRITE_BOOT_SECTOR: 564 fWorkerThread->WriteBootSector(fDestMenu); 565 break; 566 567 default: 568 BWindow::MessageReceived(msg); 569 break; 570 } 571 } 572 573 574 bool 575 InstallerWindow::QuitRequested() 576 { 577 if ((Flags() & B_NOT_MINIMIZABLE) != 0) { 578 // This means Deskbar is not running, i.e. Installer is the only 579 // thing on the screen and we will reboot the machine once it quits. 580 581 if (fDriveSetupLaunched && fBootManagerLaunched) { 582 BAlert* alert = new BAlert(B_TRANSLATE("Quit Boot Manager and " 583 "DriveSetup"), B_TRANSLATE("Please close the Boot Manager " 584 "and DriveSetup windows before closing the Installer window."), 585 B_TRANSLATE("OK")); 586 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 587 alert->Go(); 588 return false; 589 } 590 if (fDriveSetupLaunched) { 591 BAlert* alert = new BAlert(B_TRANSLATE("Quit DriveSetup"), 592 B_TRANSLATE("Please close the DriveSetup window before " 593 "closing the Installer window."), B_TRANSLATE("OK")); 594 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 595 alert->Go(); 596 return false; 597 } 598 if (fBootManagerLaunched) { 599 BAlert* alert = new BAlert(B_TRANSLATE("Quit Boot Manager"), 600 B_TRANSLATE("Please close the Boot Manager window before " 601 "closing the Installer window."), B_TRANSLATE("OK")); 602 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 603 alert->Go(); 604 return false; 605 } 606 if (fInstallStatus != kFinished) { 607 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("Installer"), 608 B_TRANSLATE("Are you sure you want to abort the " 609 "installation and restart the system?"), 610 B_TRANSLATE("Cancel"), B_TRANSLATE("Restart system"), NULL, 611 B_WIDTH_AS_USUAL, B_STOP_ALERT); 612 alert->SetShortcut(0, B_ESCAPE); 613 if (alert->Go() == 0) 614 return false; 615 } 616 } else if (fInstallStatus == kInstalling) { 617 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("Installer"), 618 B_TRANSLATE("Are you sure you want to abort the installation?"), 619 B_TRANSLATE("Cancel"), B_TRANSLATE("Abort"), NULL, 620 B_WIDTH_AS_USUAL, B_STOP_ALERT); 621 alert->SetShortcut(0, B_ESCAPE); 622 if (alert->Go() == 0) 623 return false; 624 } 625 626 _QuitCopyEngine(false); 627 fWorkerThread->PostMessage(B_QUIT_REQUESTED); 628 be_app->PostMessage(B_QUIT_REQUESTED); 629 return true; 630 } 631 632 633 // #pragma mark - 634 635 636 void 637 InstallerWindow::_ShowOptionalPackages() 638 { 639 if (fPackagesLayoutItem && fSizeViewLayoutItem) { 640 fPackagesLayoutItem->SetVisible(fPackagesSwitch->Value()); 641 fSizeViewLayoutItem->SetVisible(fPackagesSwitch->Value()); 642 } 643 } 644 645 646 void 647 InstallerWindow::_LaunchDriveSetup() 648 { 649 if (be_roster->Launch(kDriveSetupSignature) != B_OK) { 650 // Try really hard to launch it. It's very likely that this fails, 651 // when we run from the CD and there is only an incomplete mime 652 // database for example... 653 BPath path; 654 if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK 655 || path.Append("DriveSetup") != B_OK) { 656 path.SetTo("/boot/system/apps/DriveSetup"); 657 } 658 BEntry entry(path.Path()); 659 entry_ref ref; 660 if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) { 661 BAlert* alert = new BAlert("error", B_TRANSLATE("DriveSetup, the " 662 "application to configure disk partitions, could not be " 663 "launched."), B_TRANSLATE("OK")); 664 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 665 alert->Go(); 666 } 667 } 668 } 669 670 671 void 672 InstallerWindow::_LaunchBootManager() 673 { 674 // TODO: Currently BootManager always tries to install to the "first" 675 // harddisk. If/when it later supports being installed to a certain 676 // harddisk, we would have to pass it the disk that contains the target 677 // partition here. 678 if (be_roster->Launch(kBootManagerSignature) != B_OK) { 679 // Try really hard to launch it. It's very likely that this fails, 680 // when we run from the CD and there is only an incomplete mime 681 // database for example... 682 BPath path; 683 if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK 684 || path.Append("BootManager") != B_OK) { 685 path.SetTo("/boot/system/apps/BootManager"); 686 } 687 BEntry entry(path.Path()); 688 entry_ref ref; 689 if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) { 690 BAlert* alert = new BAlert("error", B_TRANSLATE("BootManager, the " 691 "application to configure the Haiku boot menu, could not be " 692 "launched."), B_TRANSLATE("OK")); 693 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 694 alert->Go(); 695 } 696 } 697 } 698 699 700 void 701 InstallerWindow::_DisableInterface(bool disable) 702 { 703 fLaunchDriveSetupButton->SetEnabled(!disable); 704 fLaunchBootManagerItem->SetEnabled(!disable); 705 fMakeBootableItem->SetEnabled(!disable); 706 fSrcMenuField->SetEnabled(!disable); 707 fDestMenuField->SetEnabled(!disable); 708 } 709 710 711 void 712 InstallerWindow::_ScanPartitions() 713 { 714 _SetStatusMessage(B_TRANSLATE("Scanning for disks" B_UTF8_ELLIPSIS)); 715 716 BMenuItem *item; 717 while ((item = fSrcMenu->RemoveItem((int32)0))) 718 delete item; 719 while ((item = fDestMenu->RemoveItem((int32)0))) 720 delete item; 721 722 fWorkerThread->ScanDisksPartitions(fSrcMenu, fDestMenu); 723 724 if (fSrcMenu->ItemAt(0) != NULL) 725 _PublishPackages(); 726 727 _UpdateControls(); 728 } 729 730 731 void 732 InstallerWindow::_UpdateControls() 733 { 734 PartitionMenuItem* srcItem = (PartitionMenuItem*)fSrcMenu->FindMarked(); 735 BString label; 736 if (srcItem) { 737 label = srcItem->MenuLabel(); 738 } else { 739 if (fSrcMenu->CountItems() == 0) 740 label = B_TRANSLATE_COMMENT("<none>", "No partition available"); 741 else 742 label = ((PartitionMenuItem*)fSrcMenu->ItemAt(0))->MenuLabel(); 743 } 744 fSrcMenuField->MenuItem()->SetLabel(label.String()); 745 746 // Disable any unsuitable target items, check if at least one partition 747 // is suitable. 748 bool foundOneSuitableTarget = false; 749 for (int32 i = fDestMenu->CountItems() - 1; i >= 0; i--) { 750 PartitionMenuItem* dstItem 751 = (PartitionMenuItem*)fDestMenu->ItemAt(i); 752 if (srcItem != NULL && dstItem->ID() == srcItem->ID()) { 753 // Prevent the user from having picked the same partition as source 754 // and destination. 755 dstItem->SetEnabled(false); 756 dstItem->SetMarked(false); 757 } else 758 dstItem->SetEnabled(dstItem->IsValidTarget()); 759 760 if (dstItem->IsEnabled()) 761 foundOneSuitableTarget = true; 762 } 763 764 PartitionMenuItem* dstItem = (PartitionMenuItem*)fDestMenu->FindMarked(); 765 if (dstItem) { 766 label = dstItem->MenuLabel(); 767 } else { 768 if (fDestMenu->CountItems() == 0) 769 label = B_TRANSLATE_COMMENT("<none>", "No partition available"); 770 else 771 label = B_TRANSLATE("Please choose target"); 772 } 773 fDestMenuField->MenuItem()->SetLabel(label.String()); 774 775 if (srcItem != NULL && dstItem != NULL) { 776 char message[255]; 777 sprintf(message, B_TRANSLATE("Press the Begin button to install from " 778 "'%1s' onto '%2s'."), srcItem->Name(), dstItem->Name()); 779 _SetStatusMessage(message); 780 } else if (srcItem != NULL) { 781 _SetStatusMessage(B_TRANSLATE("Choose the disk you want to install " 782 "onto from the pop-up menu. Then click \"Begin\".")); 783 } else if (dstItem != NULL) { 784 _SetStatusMessage(B_TRANSLATE("Choose the source disk from the " 785 "pop-up menu. Then click \"Begin\".")); 786 } else { 787 _SetStatusMessage(B_TRANSLATE("Choose the source and destination disk " 788 "from the pop-up menus. Then click \"Begin\".")); 789 } 790 791 fInstallStatus = kReadyForInstall; 792 fBeginButton->SetLabel(B_TRANSLATE("Begin")); 793 fBeginButton->SetEnabled(srcItem && dstItem); 794 795 // adjust "Write Boot Sector" and "Set up boot menu" buttons 796 if (dstItem != NULL) { 797 char buffer[256]; 798 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Write boot sector to '%s'"), 799 dstItem->Name()); 800 label = buffer; 801 } else 802 label = B_TRANSLATE("Write boot sector"); 803 fMakeBootableItem->SetEnabled(dstItem != NULL); 804 fMakeBootableItem->SetLabel(label.String()); 805 // TODO: Once bootman support writing to specific disks, enable this, since 806 // we would pass it the disk which contains the target partition. 807 // fLaunchBootManagerItem->SetEnabled(dstItem != NULL); 808 809 if (!fEncouragedToSetupPartitions && !foundOneSuitableTarget) { 810 // Focus the users attention on the DriveSetup button 811 fEncouragedToSetupPartitions = true; 812 PostMessage(ENCOURAGE_DRIVESETUP); 813 } 814 } 815 816 817 void 818 InstallerWindow::_PublishPackages() 819 { 820 fPackagesView->Clean(); 821 PartitionMenuItem *item = (PartitionMenuItem *)fSrcMenu->FindMarked(); 822 if (item == NULL) 823 return; 824 825 BPath directory; 826 BDiskDeviceRoster roster; 827 BDiskDevice device; 828 BPartition *partition; 829 if (roster.GetPartitionWithID(item->ID(), &device, &partition) == B_OK) { 830 if (partition->GetMountPoint(&directory) != B_OK) 831 return; 832 } else if (roster.GetDeviceWithID(item->ID(), &device) == B_OK) { 833 if (device.GetMountPoint(&directory) != B_OK) 834 return; 835 } else 836 return; // shouldn't happen 837 838 directory.Append(PACKAGES_DIRECTORY); 839 BDirectory dir(directory.Path()); 840 if (dir.InitCheck() != B_OK) 841 return; 842 843 BEntry packageEntry; 844 BList packages; 845 while (dir.GetNextEntry(&packageEntry) == B_OK) { 846 Package* package = Package::PackageFromEntry(packageEntry); 847 if (package != NULL) 848 packages.AddItem(package); 849 } 850 packages.SortItems(_ComparePackages); 851 852 fPackagesView->AddPackages(packages, new BMessage(PACKAGE_CHECKBOX)); 853 PostMessage(PACKAGE_CHECKBOX); 854 } 855 856 857 void 858 InstallerWindow::_SetStatusMessage(const char *text) 859 { 860 fStatusView->SetText(text); 861 862 // Make the status view taller if needed 863 BSize size = fStatusView->ExplicitMinSize(); 864 float heightNeeded = fStatusView->TextHeight(0, fStatusView->CountLines()) + 15.0; 865 if (heightNeeded > size.height) 866 fStatusView->SetExplicitMinSize(BSize(size.width, heightNeeded)); 867 } 868 869 870 void 871 InstallerWindow::_SetCopyEngineCancelSemaphore(sem_id id, bool alreadyLocked) 872 { 873 if (fCopyEngineCancelSemaphore >= 0) { 874 if (!alreadyLocked) 875 acquire_sem(fCopyEngineCancelSemaphore); 876 delete_sem(fCopyEngineCancelSemaphore); 877 } 878 fCopyEngineCancelSemaphore = id; 879 } 880 881 882 void 883 InstallerWindow::_QuitCopyEngine(bool askUser) 884 { 885 if (fCopyEngineCancelSemaphore < 0) 886 return; 887 888 // First of all block the copy engine, so that it doesn't continue 889 // while the alert is showing, which would be irritating. 890 acquire_sem(fCopyEngineCancelSemaphore); 891 892 bool quit = true; 893 if (askUser) { 894 BAlert* alert = new BAlert("cancel", 895 B_TRANSLATE("Are you sure you want to to stop the installation?"), 896 B_TRANSLATE_COMMENT("Continue", "In alert after pressing Stop"), 897 B_TRANSLATE_COMMENT("Stop", "In alert after pressing Stop"), 0, 898 B_WIDTH_AS_USUAL, B_STOP_ALERT); 899 alert->SetShortcut(1, B_ESCAPE); 900 quit = alert->Go(); 901 } 902 903 if (quit) { 904 // Make it quit by having it's lock fail... 905 _SetCopyEngineCancelSemaphore(-1, true); 906 } else 907 release_sem(fCopyEngineCancelSemaphore); 908 } 909 910 911 // #pragma mark - 912 913 914 int 915 InstallerWindow::_ComparePackages(const void* firstArg, const void* secondArg) 916 { 917 const Group* group1 = *static_cast<const Group* const *>(firstArg); 918 const Group* group2 = *static_cast<const Group* const *>(secondArg); 919 const Package* package1 = dynamic_cast<const Package*>(group1); 920 const Package* package2 = dynamic_cast<const Package*>(group2); 921 int sameGroup = strcmp(group1->GroupName(), group2->GroupName()); 922 if (sameGroup != 0) 923 return sameGroup; 924 if (package2 == NULL) 925 return -1; 926 if (package1 == NULL) 927 return 1; 928 return strcmp(package1->Name(), package2->Name()); 929 } 930 931 932