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