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