1 /* 2 * Copyright 2009, 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 <ClassInfo.h> 18 #include <Directory.h> 19 #include <FindDirectory.h> 20 #include <GridLayoutBuilder.h> 21 #include <GroupLayoutBuilder.h> 22 #include <LayoutUtils.h> 23 #include <MenuBar.h> 24 #include <MenuField.h> 25 #include <Path.h> 26 #include <PopUpMenu.h> 27 #include <Roster.h> 28 #include <Screen.h> 29 #include <ScrollView.h> 30 #include <SeparatorView.h> 31 #include <SpaceLayoutItem.h> 32 #include <StatusBar.h> 33 #include <String.h> 34 #include <TextView.h> 35 #include <TranslationUtils.h> 36 #include <TranslatorFormats.h> 37 38 #include "tracker_private.h" 39 40 #include "DialogPane.h" 41 #include "PackageViews.h" 42 #include "PartitionMenuItem.h" 43 #include "WorkerThread.h" 44 45 46 #define DRIVESETUP_SIG "application/x-vnd.Haiku-DriveSetup" 47 48 const uint32 BEGIN_MESSAGE = 'iBGN'; 49 const uint32 SHOW_BOTTOM_MESSAGE = 'iSBT'; 50 const uint32 SETUP_MESSAGE = 'iSEP'; 51 const uint32 START_SCAN = 'iSSC'; 52 const uint32 PACKAGE_CHECKBOX = 'iPCB'; 53 54 class LogoView : public BView { 55 public: 56 LogoView(const BRect& frame); 57 LogoView(); 58 virtual ~LogoView(); 59 60 virtual void Draw(BRect update); 61 62 virtual void GetPreferredSize(float* _width, float* _height); 63 64 private: 65 void _Init(); 66 67 BBitmap* fLogo; 68 }; 69 70 71 LogoView::LogoView(const BRect& frame) 72 : 73 BView(frame, "logoview", B_FOLLOW_LEFT | B_FOLLOW_TOP, 74 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 75 { 76 _Init(); 77 } 78 79 80 LogoView::LogoView() 81 : 82 BView("logoview", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 83 { 84 _Init(); 85 } 86 87 88 LogoView::~LogoView(void) 89 { 90 delete fLogo; 91 } 92 93 94 void 95 LogoView::Draw(BRect update) 96 { 97 if (fLogo == NULL) 98 return; 99 100 BRect bounds(Bounds()); 101 BPoint placement; 102 placement.x = (bounds.left + bounds.right - fLogo->Bounds().Width()) / 2; 103 placement.y = (bounds.top + bounds.bottom - fLogo->Bounds().Height()) / 2; 104 105 DrawBitmap(fLogo, placement); 106 } 107 108 109 void 110 LogoView::GetPreferredSize(float* _width, float* _height) 111 { 112 float width = 0.0; 113 float height = 0.0; 114 if (fLogo) { 115 width = fLogo->Bounds().Width(); 116 height = fLogo->Bounds().Height(); 117 } 118 if (_width) 119 *_width = width; 120 if (_height) 121 *_height = height; 122 } 123 124 125 void 126 LogoView::_Init() 127 { 128 fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "haikulogo.png"); 129 } 130 131 132 // #pragma mark - 133 134 135 static BLayoutItem* 136 layout_item_for(BView* view) 137 { 138 BLayout* layout = view->Parent()->GetLayout(); 139 int32 index = layout->IndexOfView(view); 140 return layout->ItemAt(index); 141 } 142 143 144 InstallerWindow::InstallerWindow() 145 : BWindow(BRect(-2000, -2000, -1800, -1800), "Installer", B_TITLED_WINDOW, 146 B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS), 147 fNeedsToCenterOnScreen(true), 148 fEncouragedToSetupPartitions(false), 149 fDriveSetupLaunched(false), 150 fInstallStatus(kReadyForInstall), 151 fWorkerThread(new WorkerThread(this)), 152 fCopyEngineCancelSemaphore(-1) 153 { 154 LogoView* logoView = new LogoView(); 155 156 fStatusView = new BTextView("statusView", be_plain_font, NULL, B_WILL_DRAW); 157 fStatusView->SetInsets(10, 10, 10, 10); 158 fStatusView->MakeEditable(false); 159 fStatusView->MakeSelectable(false); 160 161 BSize logoSize = logoView->MinSize(); 162 logoView->SetExplicitMaxSize(logoSize); 163 fStatusView->SetExplicitMinSize(BSize(logoSize.width * 0.66, B_SIZE_UNSET)); 164 165 fDestMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false); 166 fSrcMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false); 167 168 fSrcMenuField = new BMenuField("srcMenuField", "Install from: ", fSrcMenu, 169 NULL); 170 fSrcMenuField->SetAlignment(B_ALIGN_RIGHT); 171 172 fDestMenuField = new BMenuField("destMenuField", "Onto: ", fDestMenu, NULL); 173 fDestMenuField->SetAlignment(B_ALIGN_RIGHT); 174 175 fPackagesSwitch = new PaneSwitch("options_button"); 176 fPackagesSwitch->SetLabels("Hide Optional Packages", 177 "Show Optional Packages"); 178 fPackagesSwitch->SetMessage(new BMessage(SHOW_BOTTOM_MESSAGE)); 179 fPackagesSwitch->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 180 B_SIZE_UNSET)); 181 fPackagesSwitch->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 182 B_ALIGN_TOP)); 183 184 fPackagesView = new PackagesView("packages_view"); 185 BScrollView* packagesScrollView = new BScrollView("packagesScroll", 186 fPackagesView, B_WILL_DRAW, false, true); 187 188 const char* requiredDiskSpaceString 189 = "Additional disk space required: 0.0 KB"; 190 fSizeView = new BStringView("size_view", requiredDiskSpaceString); 191 fSizeView->SetAlignment(B_ALIGN_RIGHT); 192 fSizeView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); 193 fSizeView->SetExplicitAlignment( 194 BAlignment(B_ALIGN_RIGHT, B_ALIGN_MIDDLE)); 195 196 fProgressBar = new BStatusBar("progress", "Install Progress: "); 197 fProgressBar->SetMaxValue(100.0); 198 199 fBeginButton = new BButton("begin_button", "Begin", 200 new BMessage(BEGIN_MESSAGE)); 201 fBeginButton->MakeDefault(true); 202 fBeginButton->SetEnabled(false); 203 204 fSetupButton = new BButton("setup_button", 205 "Setup partitions" B_UTF8_ELLIPSIS, new BMessage(SETUP_MESSAGE)); 206 207 fMakeBootableButton = new BButton("makebootable_button", 208 "Write Boot Sector", new BMessage(MSG_WRITE_BOOT_SECTOR)); 209 fMakeBootableButton->SetEnabled(false); 210 211 SetLayout(new BGroupLayout(B_HORIZONTAL)); 212 AddChild(BGroupLayoutBuilder(B_VERTICAL) 213 .Add(BGroupLayoutBuilder(B_HORIZONTAL) 214 .Add(logoView) 215 .Add(fStatusView) 216 ) 217 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) 218 .Add(BGroupLayoutBuilder(B_VERTICAL, 10) 219 .Add(BGridLayoutBuilder(0, 10) 220 .Add(fSrcMenuField->CreateLabelLayoutItem(), 0, 0) 221 .Add(fSrcMenuField->CreateMenuBarLayoutItem(), 1, 0) 222 .Add(fDestMenuField->CreateLabelLayoutItem(), 0, 1) 223 .Add(fDestMenuField->CreateMenuBarLayoutItem(), 1, 1) 224 225 .Add(BSpaceLayoutItem::CreateVerticalStrut(5), 0, 2, 2) 226 227 .Add(fPackagesSwitch, 0, 3, 2) 228 .Add(packagesScrollView, 0, 4, 2) 229 .Add(fProgressBar, 0, 5, 2) 230 .Add(fSizeView, 0, 6, 2) 231 ) 232 233 .Add(BGroupLayoutBuilder(B_HORIZONTAL, 10) 234 .Add(fSetupButton) 235 .Add(fMakeBootableButton) 236 .AddGlue() 237 .Add(fBeginButton) 238 ) 239 .SetInsets(10, 10, 10, 10) 240 ) 241 ); 242 243 // Make the optional packages and progress bar invisible on start 244 fPackagesLayoutItem = layout_item_for(packagesScrollView); 245 fPkgSwitchLayoutItem = layout_item_for(fPackagesSwitch); 246 fSizeViewLayoutItem = layout_item_for(fSizeView); 247 fProgressLayoutItem = layout_item_for(fProgressBar); 248 249 fPackagesLayoutItem->SetVisible(false); 250 fSizeViewLayoutItem->SetVisible(false); 251 fProgressLayoutItem->SetVisible(false); 252 253 // finish creating window 254 if (!be_roster->IsRunning(kDeskbarSignature)) 255 SetFlags(Flags() | B_NOT_MINIMIZABLE); 256 257 Show(); 258 259 fDriveSetupLaunched = be_roster->IsRunning(DRIVESETUP_SIG); 260 261 if (Lock()) { 262 fSetupButton->SetEnabled(!fDriveSetupLaunched); 263 Unlock(); 264 } 265 266 be_roster->StartWatching(this); 267 268 PostMessage(START_SCAN); 269 } 270 271 272 InstallerWindow::~InstallerWindow() 273 { 274 _SetCopyEngineCancelSemaphore(-1); 275 be_roster->StopWatching(this); 276 } 277 278 279 void 280 InstallerWindow::FrameResized(float width, float height) 281 { 282 BWindow::FrameResized(width, height); 283 284 if (fNeedsToCenterOnScreen) { 285 // We have created ourselves off-screen, since the size adoption 286 // because of the layout management may happen after Show(). We 287 // assume that the first frame event is because of this adoption and 288 // move ourselves to the screen center... 289 fNeedsToCenterOnScreen = false; 290 BRect frame = BScreen(this).Frame(); 291 MoveTo(frame.left + (frame.Width() - Frame().Width()) / 2, 292 frame.top + (frame.Height() - Frame().Height()) / 2); 293 } 294 } 295 296 297 void 298 InstallerWindow::MessageReceived(BMessage *msg) 299 { 300 switch (msg->what) { 301 case MSG_RESET: 302 { 303 _SetCopyEngineCancelSemaphore(-1); 304 305 status_t error; 306 if (msg->FindInt32("error", &error) == B_OK) { 307 char errorMessage[2048]; 308 snprintf(errorMessage, sizeof(errorMessage), "An error was " 309 "encountered and the installation was not completed:\n\n" 310 "Error: %s", strerror(error)); 311 (new BAlert("error", errorMessage, "Ok"))->Go(); 312 } 313 314 _DisableInterface(false); 315 316 fProgressLayoutItem->SetVisible(false); 317 fPkgSwitchLayoutItem->SetVisible(true); 318 _ShowOptionalPackages(); 319 _UpdateControls(); 320 break; 321 } 322 case START_SCAN: 323 _ScanPartitions(); 324 break; 325 case BEGIN_MESSAGE: 326 switch (fInstallStatus) { 327 case kReadyForInstall: 328 { 329 _SetCopyEngineCancelSemaphore(create_sem(1, 330 "copy engine cancel")); 331 332 BList* list = new BList(); 333 int32 size = 0; 334 fPackagesView->GetPackagesToInstall(list, &size); 335 fWorkerThread->SetLock(fCopyEngineCancelSemaphore); 336 fWorkerThread->SetPackagesList(list); 337 fWorkerThread->SetSpaceRequired(size); 338 fInstallStatus = kInstalling; 339 fWorkerThread->StartInstall(); 340 fBeginButton->SetLabel("Stop"); 341 _DisableInterface(true); 342 343 fProgressBar->SetTo(0.0, NULL, NULL); 344 345 fPkgSwitchLayoutItem->SetVisible(false); 346 fPackagesLayoutItem->SetVisible(false); 347 fSizeViewLayoutItem->SetVisible(false); 348 fProgressLayoutItem->SetVisible(true); 349 break; 350 } 351 case kInstalling: 352 { 353 _QuitCopyEngine(true); 354 break; 355 } 356 case kFinished: 357 PostMessage(B_QUIT_REQUESTED); 358 break; 359 case kCancelled: 360 break; 361 } 362 break; 363 case SHOW_BOTTOM_MESSAGE: 364 _ShowOptionalPackages(); 365 break; 366 case SOURCE_PARTITION: 367 _PublishPackages(); 368 _UpdateControls(); 369 break; 370 case TARGET_PARTITION: 371 _UpdateControls(); 372 break; 373 case SETUP_MESSAGE: 374 _LaunchDriveSetup(); 375 break; 376 case PACKAGE_CHECKBOX: 377 { 378 char buffer[15]; 379 fPackagesView->GetTotalSizeAsString(buffer); 380 char string[255]; 381 sprintf(string, "Additional disk space required: %s", buffer); 382 fSizeView->SetText(string); 383 break; 384 } 385 case MSG_STATUS_MESSAGE: 386 { 387 // TODO: Was this supposed to prevent status messages still arriving 388 // after the copy engine was shut down? 389 // if (fInstallStatus != kInstalling) 390 // break; 391 float progress; 392 if (msg->FindFloat("progress", &progress) == B_OK) { 393 const char* currentItem; 394 if (msg->FindString("item", ¤tItem) != B_OK) 395 currentItem = "???"; 396 BString trailingLabel; 397 int32 currentCount; 398 int32 maximumCount; 399 if (msg->FindInt32("current", ¤tCount) == B_OK 400 && msg->FindInt32("maximum", &maximumCount) == B_OK) { 401 trailingLabel << currentCount << " of " << maximumCount; 402 } else { 403 trailingLabel << "?? of ??"; 404 } 405 fProgressBar->SetTo(progress, currentItem, 406 trailingLabel.String()); 407 } else { 408 const char *status; 409 if (msg->FindString("status", &status) == B_OK) { 410 fLastStatus = fStatusView->Text(); 411 _SetStatusMessage(status); 412 } else 413 _SetStatusMessage(fLastStatus.String()); 414 } 415 break; 416 } 417 case MSG_INSTALL_FINISHED: 418 { 419 420 _SetCopyEngineCancelSemaphore(-1); 421 422 fBeginButton->SetLabel("Quit"); 423 424 PartitionMenuItem* dstItem 425 = (PartitionMenuItem*)fDestMenu->FindMarked(); 426 427 const char* quitString; 428 if (be_roster->IsRunning(kDeskbarSignature)) 429 quitString = "leave the Installer"; 430 else 431 quitString = "restart the computer"; 432 433 char status[1024]; 434 snprintf(status, sizeof(status), "Installation completed. " 435 "Boot sector has been written to '%s'. Press Quit to %s " 436 "or chose a new target volume to perform another " 437 "installation.", dstItem ? dstItem->Name() : "???", quitString); 438 _SetStatusMessage(status); 439 fInstallStatus = kFinished; 440 _DisableInterface(false); 441 fProgressLayoutItem->SetVisible(false); 442 fPkgSwitchLayoutItem->SetVisible(true); 443 _ShowOptionalPackages(); 444 break; 445 } 446 case B_SOME_APP_LAUNCHED: 447 case B_SOME_APP_QUIT: 448 { 449 const char *signature; 450 if (msg->FindString("be:signature", &signature) == B_OK 451 && strcasecmp(signature, DRIVESETUP_SIG) == 0) { 452 fDriveSetupLaunched = msg->what == B_SOME_APP_LAUNCHED; 453 fBeginButton->SetEnabled(!fDriveSetupLaunched); 454 _DisableInterface(fDriveSetupLaunched); 455 if (fDriveSetupLaunched) 456 _SetStatusMessage("Running DriveSetup" B_UTF8_ELLIPSIS 457 "\n\nClose DriveSetup to continue with the " 458 "installation."); 459 else 460 _ScanPartitions(); 461 } 462 break; 463 } 464 case MSG_WRITE_BOOT_SECTOR: 465 fWorkerThread->WriteBootSector(fDestMenu); 466 break; 467 468 default: 469 BWindow::MessageReceived(msg); 470 break; 471 } 472 } 473 474 475 bool 476 InstallerWindow::QuitRequested() 477 { 478 if (fDriveSetupLaunched) { 479 (new BAlert("driveSetup", 480 "Please close the DriveSetup window before closing the " 481 "Installer window.", "Ok"))->Go(); 482 return false; 483 } 484 _QuitCopyEngine(false); 485 fWorkerThread->PostMessage(B_QUIT_REQUESTED); 486 be_app->PostMessage(B_QUIT_REQUESTED); 487 return true; 488 } 489 490 491 // #pragma mark - 492 493 494 void 495 InstallerWindow::_ShowOptionalPackages() 496 { 497 if (fPackagesLayoutItem && fSizeViewLayoutItem) { 498 fPackagesLayoutItem->SetVisible(fPackagesSwitch->Value()); 499 fSizeViewLayoutItem->SetVisible(fPackagesSwitch->Value()); 500 } 501 } 502 503 504 void 505 InstallerWindow::_LaunchDriveSetup() 506 { 507 if (be_roster->Launch(DRIVESETUP_SIG) != B_OK) { 508 // Try really hard to launch it. It's very likely that this fails, 509 // when we run from the CD and there is only an incomplete mime 510 // database for example... 511 BPath path; 512 if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK 513 || path.Append("DriveSetup") != B_OK) { 514 path.SetTo("/boot/system/apps/DriveSetup"); 515 } 516 BEntry entry(path.Path()); 517 entry_ref ref; 518 if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) { 519 BAlert* alert = new BAlert("error", "DriveSetup, the application " 520 "to configure disk partitions, could not be launched.", 521 "Ok"); 522 alert->Go(); 523 } 524 } 525 } 526 527 528 void 529 InstallerWindow::_DisableInterface(bool disable) 530 { 531 fSetupButton->SetEnabled(!disable); 532 fMakeBootableButton->SetEnabled(!disable); 533 fSrcMenuField->SetEnabled(!disable); 534 fDestMenuField->SetEnabled(!disable); 535 } 536 537 538 void 539 InstallerWindow::_ScanPartitions() 540 { 541 _SetStatusMessage("Scanning for disks" B_UTF8_ELLIPSIS); 542 543 BMenuItem *item; 544 while ((item = fSrcMenu->RemoveItem((int32)0))) 545 delete item; 546 while ((item = fDestMenu->RemoveItem((int32)0))) 547 delete item; 548 549 fWorkerThread->ScanDisksPartitions(fSrcMenu, fDestMenu); 550 551 if (fSrcMenu->ItemAt(0)) { 552 _PublishPackages(); 553 } 554 _UpdateControls(); 555 } 556 557 558 void 559 InstallerWindow::_UpdateControls() 560 { 561 PartitionMenuItem* srcItem = (PartitionMenuItem*)fSrcMenu->FindMarked(); 562 BString label; 563 if (srcItem) { 564 label = srcItem->MenuLabel(); 565 } else { 566 if (fSrcMenu->CountItems() == 0) 567 label = "<none>"; 568 else 569 label = ((PartitionMenuItem*)fSrcMenu->ItemAt(0))->MenuLabel(); 570 } 571 fSrcMenuField->MenuItem()->SetLabel(label.String()); 572 573 // Disable any unsuitable target items, check if at least one partition 574 // is suitable. 575 bool foundOneSuitableTarget = false; 576 for (int32 i = fDestMenu->CountItems() - 1; i >= 0; i--) { 577 PartitionMenuItem* dstItem 578 = (PartitionMenuItem*)fDestMenu->ItemAt(i); 579 if (srcItem != NULL && dstItem->ID() == srcItem->ID()) { 580 // Prevent the user from having picked the same partition as source 581 // and destination. 582 dstItem->SetEnabled(false); 583 dstItem->SetMarked(false); 584 } else 585 dstItem->SetEnabled(dstItem->IsValidTarget()); 586 587 if (dstItem->IsEnabled()) 588 foundOneSuitableTarget = true; 589 } 590 591 PartitionMenuItem* dstItem = (PartitionMenuItem*)fDestMenu->FindMarked(); 592 if (dstItem) { 593 label = dstItem->MenuLabel(); 594 } else { 595 if (fDestMenu->CountItems() == 0) 596 label = "<none>"; 597 else 598 label = "Please Choose Target"; 599 } 600 fDestMenuField->MenuItem()->SetLabel(label.String()); 601 602 if (srcItem && dstItem) { 603 char message[255]; 604 sprintf(message, "Press the Begin button to install from '%s' onto " 605 "'%s'.", srcItem->Name(), dstItem->Name()); 606 _SetStatusMessage(message); 607 } else if (srcItem) { 608 _SetStatusMessage("Choose the disk you want to install onto from the " 609 "pop-up menu. Then click \"Begin\"."); 610 } else if (dstItem) { 611 _SetStatusMessage("Choose the source disk from the " 612 "pop-up menu. Then click \"Begin\"."); 613 } else { 614 _SetStatusMessage("Choose the source and destination disk from the " 615 "pop-up menus. Then click \"Begin\"."); 616 } 617 618 fInstallStatus = kReadyForInstall; 619 fBeginButton->SetLabel("Begin"); 620 fBeginButton->SetEnabled(srcItem && dstItem); 621 622 // adjust "Write Boot Sector" button 623 label = "Write Boot Sector"; 624 if (dstItem) 625 label << " to \'" <<dstItem->Name() << '\''; 626 fMakeBootableButton->SetEnabled(dstItem); 627 fMakeBootableButton->SetLabel(label.String()); 628 629 if (!fEncouragedToSetupPartitions && !foundOneSuitableTarget) { 630 // Focus the users attention on the DriveSetup button 631 fEncouragedToSetupPartitions = true; 632 (new BAlert("use drive setup", "No partitions have been found that " 633 "are suitable for installation. Please setup partitions and " 634 "initialize at least one partition with the Be File System." , 635 "Ok"))->Go(); 636 } 637 } 638 639 640 void 641 InstallerWindow::_PublishPackages() 642 { 643 fPackagesView->Clean(); 644 PartitionMenuItem *item = (PartitionMenuItem *)fSrcMenu->FindMarked(); 645 if (!item) 646 return; 647 648 #ifdef __HAIKU__ 649 BPath directory; 650 BDiskDeviceRoster roster; 651 BDiskDevice device; 652 BPartition *partition; 653 if (roster.GetPartitionWithID(item->ID(), &device, &partition) == B_OK) { 654 if (partition->GetMountPoint(&directory) != B_OK) 655 return; 656 } else if (roster.GetDeviceWithID(item->ID(), &device) == B_OK) { 657 if (device.GetMountPoint(&directory) != B_OK) 658 return; 659 } else 660 return; // shouldn't happen 661 #else 662 BPath directory = "/BeOS 5 PE Max Edition V3.1 beta"; 663 #endif 664 665 directory.Append(PACKAGES_DIRECTORY); 666 BDirectory dir(directory.Path()); 667 if (dir.InitCheck() != B_OK) 668 return; 669 670 BEntry packageEntry; 671 BList packages; 672 while (dir.GetNextEntry(&packageEntry) == B_OK) { 673 Package *package = Package::PackageFromEntry(packageEntry); 674 if (package) { 675 packages.AddItem(package); 676 } 677 } 678 packages.SortItems(_ComparePackages); 679 680 fPackagesView->AddPackages(packages, new BMessage(PACKAGE_CHECKBOX)); 681 PostMessage(PACKAGE_CHECKBOX); 682 } 683 684 685 void 686 InstallerWindow::_SetStatusMessage(const char *text) 687 { 688 fStatusView->SetText(text); 689 } 690 691 692 void 693 InstallerWindow::_SetCopyEngineCancelSemaphore(sem_id id, bool alreadyLocked) 694 { 695 if (fCopyEngineCancelSemaphore >= 0) { 696 if (!alreadyLocked) 697 acquire_sem(fCopyEngineCancelSemaphore); 698 delete_sem(fCopyEngineCancelSemaphore); 699 } 700 fCopyEngineCancelSemaphore = id; 701 } 702 703 704 void 705 InstallerWindow::_QuitCopyEngine(bool askUser) 706 { 707 if (fCopyEngineCancelSemaphore < 0) 708 return; 709 710 // First of all block the copy engine, so that it doesn't continue 711 // while the alert is showing, which would be irritating. 712 acquire_sem(fCopyEngineCancelSemaphore); 713 714 bool quit = true; 715 if (askUser) { 716 quit = (new BAlert("cancel", 717 "Are you sure you want to to stop the installation?", 718 "Continue", "Stop", 0, 719 B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0; 720 } 721 722 if (quit) { 723 // Make it quit by having it's lock fail... 724 _SetCopyEngineCancelSemaphore(-1, true); 725 } else 726 release_sem(fCopyEngineCancelSemaphore); 727 } 728 729 730 // #pragma mark - 731 732 733 int 734 InstallerWindow::_ComparePackages(const void *firstArg, const void *secondArg) 735 { 736 const Group *group1 = *static_cast<const Group * const *>(firstArg); 737 const Group *group2 = *static_cast<const Group * const *>(secondArg); 738 const Package *package1 = dynamic_cast<const Package *>(group1); 739 const Package *package2 = dynamic_cast<const Package *>(group2); 740 int sameGroup = strcmp(group1->GroupName(), group2->GroupName()); 741 if (sameGroup != 0) 742 return sameGroup; 743 if (!package2) 744 return -1; 745 if (!package1) 746 return 1; 747 return strcmp(package1->Name(), package2->Name()); 748 } 749 750 751