1 /* 2 * Copyright 2005-2008, Jérôme DUVAL. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "InstallerWindow.h" 7 8 #include <stdio.h> 9 #include <string.h> 10 11 #include <Alert.h> 12 #include <Application.h> 13 #include <Autolock.h> 14 #include <Box.h> 15 #include <ClassInfo.h> 16 #include <Directory.h> 17 #include <Path.h> 18 #include <PopUpMenu.h> 19 #include <Roster.h> 20 #include <String.h> 21 #include <TranslationUtils.h> 22 #include <TranslatorFormats.h> 23 24 #include "DialogPane.h" 25 #include "PartitionMenuItem.h" 26 27 28 #define DRIVESETUP_SIG "application/x-vnd.Haiku-DriveSetup" 29 30 const uint32 BEGIN_MESSAGE = 'iBGN'; 31 const uint32 SHOW_BOTTOM_MESSAGE = 'iSBT'; 32 const uint32 SETUP_MESSAGE = 'iSEP'; 33 const uint32 START_SCAN = 'iSSC'; 34 const uint32 PACKAGE_CHECKBOX = 'iPCB'; 35 36 class LogoView : public BBox { 37 public: 38 LogoView(const BRect &r); 39 ~LogoView(void); 40 virtual void Draw(BRect update); 41 private: 42 BBitmap *fLogo; 43 BPoint fDrawPoint; 44 }; 45 46 47 LogoView::LogoView(const BRect &r) 48 : BBox(r, "logoview", B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW, 49 B_NO_BORDER) 50 { 51 fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "haikulogo.png"); 52 if (fLogo) { 53 fDrawPoint.x = (r.Width() - fLogo->Bounds().Width()) / 2; 54 fDrawPoint.y = 0; 55 } 56 } 57 58 59 LogoView::~LogoView(void) 60 { 61 delete fLogo; 62 } 63 64 65 void 66 LogoView::Draw(BRect update) 67 { 68 if (fLogo) 69 DrawBitmap(fLogo, fDrawPoint); 70 } 71 72 73 // #pragma mark - 74 75 76 InstallerWindow::InstallerWindow(BRect frame_rect) 77 : BWindow(frame_rect, "Installer", B_TITLED_WINDOW, 78 B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_NOT_RESIZABLE), 79 fDriveSetupLaunched(false), 80 fInstallStatus(kReadyForInstall), 81 fLastSrcItem(NULL), 82 fLastTargetItem(NULL) 83 { 84 fCopyEngine = new CopyEngine(this); 85 86 BRect bounds = Bounds(); 87 bounds.bottom += 1; 88 bounds.right += 1; 89 fBackBox = new BBox(bounds, NULL, B_FOLLOW_ALL, 90 B_WILL_DRAW | B_FRAME_EVENTS, B_NO_BORDER); 91 AddChild(fBackBox); 92 93 BRect logoRect = fBackBox->Bounds(); 94 logoRect.left += 1; 95 logoRect.top = 12; 96 logoRect.right -= 226; 97 logoRect.bottom = logoRect.top + 46 + B_H_SCROLL_BAR_HEIGHT; 98 LogoView *logoView = new LogoView(logoRect); 99 fBackBox->AddChild(logoView); 100 101 BRect statusRect(bounds.right - 222, logoRect.top + 2, bounds.right - 14, 102 logoRect.bottom - B_H_SCROLL_BAR_HEIGHT + 4); 103 BRect textRect(statusRect); 104 textRect.OffsetTo(B_ORIGIN); 105 textRect.InsetBy(2, 2); 106 fStatusView = new BTextView(statusRect, "statusView", textRect, 107 be_plain_font, NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW); 108 fStatusView->MakeEditable(false); 109 fStatusView->MakeSelectable(false); 110 111 BScrollView *scroll = new BScrollView("statusScroll", fStatusView, 112 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS); 113 fBackBox->AddChild(scroll); 114 115 fBeginButton = new BButton(BRect(bounds.right - 90, bounds.bottom - 35, 116 bounds.right - 11, bounds.bottom - 11), 117 "begin_button", "Begin", new BMessage(BEGIN_MESSAGE), 118 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 119 fBeginButton->MakeDefault(true); 120 fBeginButton->SetEnabled(false); 121 fBackBox->AddChild(fBeginButton); 122 123 fSetupButton = new BButton(BRect(bounds.left + 11, bounds.bottom - 35, 124 bounds.left + be_plain_font->StringWidth("Setup partitions") + 36, 125 bounds.bottom - 22), "setup_button", "Setup partitions" B_UTF8_ELLIPSIS, 126 new BMessage(SETUP_MESSAGE), B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); 127 fBackBox->AddChild(fSetupButton); 128 fSetupButton->Hide(); 129 130 fPackagesView = new PackagesView(BRect(bounds.left + 12, bounds.top + 4, 131 bounds.right - 15 - B_V_SCROLL_BAR_WIDTH, bounds.bottom - 61), 132 "packages_view"); 133 fPackagesScrollView = new BScrollView("packagesScroll", fPackagesView, 134 B_FOLLOW_LEFT | B_FOLLOW_BOTTOM, B_WILL_DRAW, false, true); 135 fBackBox->AddChild(fPackagesScrollView); 136 fPackagesScrollView->Hide(); 137 138 fDrawButton = new PaneSwitch(BRect(bounds.left + 12, bounds.bottom - 33, 139 bounds.left + 120, bounds.bottom - 20), "options_button"); 140 141 fDrawButton->SetLabels("Fewer options", "More options"); 142 fDrawButton->SetMessage(new BMessage(SHOW_BOTTOM_MESSAGE)); 143 144 fBackBox->AddChild(fDrawButton); 145 146 fDestMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false); 147 fSrcMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false); 148 149 BRect fieldRect(bounds.left + 13, bounds.top + 70, bounds.right - 13, 150 bounds.top + 90); 151 fSrcMenuField = new BMenuField(fieldRect, "srcMenuField", 152 "Install from: ", fSrcMenu); 153 fSrcMenuField->SetDivider(bounds.right - 300); 154 fSrcMenuField->SetAlignment(B_ALIGN_RIGHT); 155 fBackBox->AddChild(fSrcMenuField); 156 157 fieldRect.OffsetBy(0, 23); 158 fDestMenuField = new BMenuField(fieldRect, "destMenuField", 159 "Onto: ", fDestMenu); 160 fDestMenuField->SetDivider(bounds.right - 300); 161 fDestMenuField->SetAlignment(B_ALIGN_RIGHT); 162 fBackBox->AddChild(fDestMenuField); 163 164 BRect sizeRect = fBackBox->Bounds(); 165 sizeRect.top = 105; 166 sizeRect.bottom = sizeRect.top + 15; 167 sizeRect.right -= 12; 168 const char* requiredDiskSpaceString = "Disk space required: 0.0 KB"; 169 sizeRect.left = sizeRect.right - be_plain_font->StringWidth( 170 requiredDiskSpaceString) - 40; 171 fSizeView = new BStringView(sizeRect, "size_view", 172 requiredDiskSpaceString, B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 173 fSizeView->SetAlignment(B_ALIGN_RIGHT); 174 fBackBox->AddChild(fSizeView); 175 fSizeView->Hide(); 176 177 // finish creating window 178 Show(); 179 180 fDriveSetupLaunched = be_roster->IsRunning(DRIVESETUP_SIG); 181 be_roster->StartWatching(this); 182 183 PostMessage(START_SCAN); 184 } 185 186 InstallerWindow::~InstallerWindow() 187 { 188 be_roster->StopWatching(this); 189 } 190 191 192 void 193 InstallerWindow::MessageReceived(BMessage *msg) 194 { 195 switch (msg->what) { 196 case RESET_INSTALL: 197 fInstallStatus = kReadyForInstall; 198 fBeginButton->SetEnabled(true); 199 DisableInterface(false); 200 fBeginButton->SetLabel("Begin"); 201 break; 202 case START_SCAN: 203 StartScan(); 204 break; 205 case BEGIN_MESSAGE: 206 switch (fInstallStatus) { 207 case kReadyForInstall: 208 { 209 BList *list = new BList(); 210 int32 size = 0; 211 fPackagesView->GetPackagesToInstall(list, &size); 212 fCopyEngine->SetPackagesList(list); 213 fCopyEngine->SetSpaceRequired(size); 214 fInstallStatus = kInstalling; 215 BMessenger(fCopyEngine).SendMessage(ENGINE_START); 216 fBeginButton->SetLabel("Stop"); 217 DisableInterface(true); 218 break; 219 } 220 case kInstalling: 221 if (fCopyEngine->Cancel()) { 222 fInstallStatus = kCancelled; 223 SetStatusMessage("Installation cancelled."); 224 } 225 break; 226 case kFinished: 227 PostMessage(B_QUIT_REQUESTED); 228 break; 229 case kCancelled: 230 break; 231 } 232 break; 233 case SHOW_BOTTOM_MESSAGE: 234 ShowBottom(); 235 break; 236 case SRC_PARTITION: 237 if (fLastSrcItem == fSrcMenu->FindMarked()) 238 break; 239 fLastSrcItem = fSrcMenu->FindMarked(); 240 PublishPackages(); 241 AdjustMenus(); 242 break; 243 case TARGET_PARTITION: 244 if (fLastTargetItem == fDestMenu->FindMarked()) 245 break; 246 fLastTargetItem = fDestMenu->FindMarked(); 247 AdjustMenus(); 248 break; 249 case SETUP_MESSAGE: 250 LaunchDriveSetup(); 251 break; 252 case PACKAGE_CHECKBOX: { 253 char buffer[15]; 254 fPackagesView->GetTotalSizeAsString(buffer); 255 char string[255]; 256 sprintf(string, "Disk space required: %s", buffer); 257 fSizeView->SetText(string); 258 break; 259 } 260 case STATUS_MESSAGE: { 261 if (fInstallStatus != kInstalling) 262 break; 263 const char *status; 264 if (msg->FindString("status", &status) == B_OK) { 265 fLastStatus = fStatusView->Text(); 266 SetStatusMessage(status); 267 } else 268 SetStatusMessage(fLastStatus.String()); 269 break; 270 } 271 case INSTALL_FINISHED: 272 fBeginButton->SetLabel("Quit"); 273 SetStatusMessage("Installation completed."); 274 fInstallStatus = kFinished; 275 DisableInterface(false); 276 break; 277 case B_SOME_APP_LAUNCHED: 278 case B_SOME_APP_QUIT: 279 { 280 const char *signature; 281 if (msg->FindString("be:signature", &signature) == B_OK 282 && strcasecmp(signature, DRIVESETUP_SIG) == 0) { 283 fDriveSetupLaunched = msg->what == B_SOME_APP_LAUNCHED; 284 fBeginButton->SetEnabled(!fDriveSetupLaunched); 285 DisableInterface(fDriveSetupLaunched); 286 if (fDriveSetupLaunched) 287 SetStatusMessage("Running DriveSetup" B_UTF8_ELLIPSIS 288 "\nClose DriveSetup to continue with the\n" 289 "installation."); 290 else 291 StartScan(); 292 } 293 break; 294 } 295 default: 296 BWindow::MessageReceived(msg); 297 break; 298 } 299 } 300 301 bool 302 InstallerWindow::QuitRequested() 303 { 304 if (fDriveSetupLaunched) { 305 (new BAlert("driveSetup", 306 "Please close the DriveSetup window before closing the\n" 307 "Installer window.", "OK"))->Go(); 308 return false; 309 } 310 be_app->PostMessage(B_QUIT_REQUESTED); 311 fCopyEngine->PostMessage(B_QUIT_REQUESTED); 312 return true; 313 } 314 315 316 void 317 InstallerWindow::ShowBottom() 318 { 319 if (fDrawButton->Value()) { 320 ResizeTo(INSTALLER_RIGHT, 306); 321 if (fSetupButton->IsHidden()) 322 fSetupButton->Show(); 323 if (fPackagesScrollView->IsHidden()) 324 fPackagesScrollView->Show(); 325 if (fSizeView->IsHidden()) 326 fSizeView->Show(); 327 } else { 328 if (!fSetupButton->IsHidden()) 329 fSetupButton->Hide(); 330 if (!fPackagesScrollView->IsHidden()) 331 fPackagesScrollView->Hide(); 332 if (!fSizeView->IsHidden()) 333 fSizeView->Hide(); 334 ResizeTo(INSTALLER_RIGHT, 160); 335 } 336 } 337 338 339 void 340 InstallerWindow::LaunchDriveSetup() 341 { 342 if (be_roster->Launch(DRIVESETUP_SIG) != B_OK) { 343 // Try really hard to launch it. It's very likely that this fails, 344 // when we run from the CD and there is only an incomplete mime 345 // database for example... 346 BPath path; 347 if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK 348 || path.Append("DriveSetup") != B_OK) { 349 path.SetTo("/boot/system/apps/DriveSetup"); 350 } 351 BEntry entry(path.Path()); 352 entry_ref ref; 353 if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) { 354 BAlert* alert = new BAlert("error", "DriveSetup, the application " 355 "to configure disk partitions, could not be launched.", 356 "Ok"); 357 alert->Go(); 358 } 359 } 360 } 361 362 363 void 364 InstallerWindow::DisableInterface(bool disable) 365 { 366 fSetupButton->SetEnabled(!disable); 367 fSrcMenuField->SetEnabled(!disable); 368 fDestMenuField->SetEnabled(!disable); 369 } 370 371 372 void 373 InstallerWindow::StartScan() 374 { 375 SetStatusMessage("Scanning for disks" B_UTF8_ELLIPSIS); 376 377 BMenuItem *item; 378 while ((item = fSrcMenu->RemoveItem((int32)0))) 379 delete item; 380 while ((item = fDestMenu->RemoveItem((int32)0))) 381 delete item; 382 383 fCopyEngine->ScanDisksPartitions(fSrcMenu, fDestMenu); 384 385 if (fSrcMenu->ItemAt(0)) { 386 PublishPackages(); 387 } 388 AdjustMenus(); 389 SetStatusMessage("Choose the disk you want to install onto from the " 390 "pop-up menu. Then click \"Begin\"."); 391 } 392 393 394 void 395 InstallerWindow::AdjustMenus() 396 { 397 PartitionMenuItem *item1 = (PartitionMenuItem *)fSrcMenu->FindMarked(); 398 BString label; 399 if (item1) { 400 label = item1->MenuLabel(); 401 } else { 402 if (fSrcMenu->CountItems() == 0) 403 label = "<none>"; 404 else { 405 label = ((PartitionMenuItem *)fSrcMenu->ItemAt(0))->MenuLabel(); 406 } 407 } 408 fSrcMenuField->TruncateString(&label, B_TRUNCATE_END, 260); 409 fSrcMenuField->MenuItem()->SetLabel(label.String()); 410 411 PartitionMenuItem *item2 = (PartitionMenuItem *)fDestMenu->FindMarked(); 412 if (item2) { 413 label = item2->MenuLabel(); 414 } else { 415 if (fDestMenu->CountItems() == 0) 416 label = "<none>"; 417 else { 418 label = ((PartitionMenuItem *)fDestMenu->ItemAt(0))->MenuLabel(); 419 } 420 } 421 fDestMenuField->TruncateString(&label, B_TRUNCATE_END, 260); 422 fDestMenuField->MenuItem()->SetLabel(label.String()); 423 char message[255]; 424 sprintf(message, "Press the Begin button to install from '%s' onto '%s'", 425 item1 ? item1->Name() : "null", item2 ? item2->Name() : "null"); 426 SetStatusMessage(message); 427 if (item1 && item2) 428 fBeginButton->SetEnabled(true); 429 } 430 431 432 void 433 InstallerWindow::PublishPackages() 434 { 435 fPackagesView->Clean(); 436 PartitionMenuItem *item = (PartitionMenuItem *)fSrcMenu->FindMarked(); 437 if (!item) 438 return; 439 440 #ifdef __HAIKU__ 441 BPath directory; 442 BDiskDeviceRoster roster; 443 BDiskDevice device; 444 BPartition *partition; 445 if (roster.GetPartitionWithID(item->ID(), &device, &partition) == B_OK) { 446 if (partition->GetMountPoint(&directory) != B_OK) 447 return; 448 } else if (roster.GetDeviceWithID(item->ID(), &device) == B_OK) { 449 if (device.GetMountPoint(&directory) != B_OK) 450 return; 451 } else 452 return; // shouldn't happen 453 #else 454 BPath directory = "/BeOS 5 PE Max Edition V3.1 beta"; 455 #endif 456 457 directory.Append(PACKAGES_DIRECTORY); 458 BDirectory dir(directory.Path()); 459 if (dir.InitCheck() != B_OK) 460 return; 461 462 BEntry packageEntry; 463 BList packages; 464 while (dir.GetNextEntry(&packageEntry) == B_OK) { 465 Package *package = Package::PackageFromEntry(packageEntry); 466 if (package) { 467 packages.AddItem(package); 468 } 469 } 470 packages.SortItems(ComparePackages); 471 472 fPackagesView->AddPackages(packages, new BMessage(PACKAGE_CHECKBOX)); 473 PostMessage(PACKAGE_CHECKBOX); 474 } 475 476 477 int 478 InstallerWindow::ComparePackages(const void *firstArg, const void *secondArg) 479 { 480 const Group *group1 = *static_cast<const Group * const *>(firstArg); 481 const Group *group2 = *static_cast<const Group * const *>(secondArg); 482 const Package *package1 = dynamic_cast<const Package *>(group1); 483 const Package *package2 = dynamic_cast<const Package *>(group2); 484 int sameGroup = strcmp(group1->GroupName(), group2->GroupName()); 485 if (sameGroup != 0) 486 return sameGroup; 487 if (!package2) 488 return -1; 489 if (!package1) 490 return 1; 491 return strcmp(package1->Name(), package2->Name()); 492 } 493 494 495 void 496 InstallerWindow::SetStatusMessage(const char *text) 497 { 498 fStatusView->SetText(text); 499 } 500 501