1 /* 2 * Copyright (c) 2007, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Author: 6 * Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org> 7 */ 8 9 10 #include "InstalledPackageInfo.h" 11 #include "PackageImageViewer.h" 12 #include "PackageTextViewer.h" 13 #include "PackageView.h" 14 15 #include <Alert.h> 16 #include <Button.h> 17 #include <Directory.h> 18 #include <FindDirectory.h> 19 #include <MenuItem.h> 20 #include <Path.h> 21 #include <PopUpMenu.h> 22 #include <ScrollView.h> 23 #include <TextView.h> 24 #include <Volume.h> 25 #include <VolumeRoster.h> 26 #include <Window.h> 27 28 #include <GroupLayout.h> 29 #include <GroupLayoutBuilder.h> 30 #include <GroupView.h> 31 32 #include <fs_info.h> 33 #include <stdio.h> // For debugging 34 35 // Macro reserved for later localization 36 #define T(x) x 37 38 const float kMaxDescHeight = 125.0f; 39 const uint32 kSeparatorIndex = 3; 40 41 42 43 static void 44 convert_size(uint64 size, char *buffer, uint32 n) 45 { 46 if (size < 1024) 47 snprintf(buffer, n, "%llu bytes", size); 48 else if (size < 1024 * 1024) 49 snprintf(buffer, n, "%.1f KiB", size / 1024.0f); 50 else if (size < 1024 * 1024 * 1024) 51 snprintf(buffer, n, "%.1f MiB", size / (1024.0f*1024.0f)); 52 else 53 snprintf(buffer, n, "%.1f GiB", size / (1024.0f*1024.0f*1024.0f)); 54 } 55 56 57 58 // #pragma mark - 59 60 61 PackageView::PackageView(BRect frame, const entry_ref *ref) 62 : BView(frame, "package_view", B_FOLLOW_NONE, 0), 63 //BView("package_view", B_WILL_DRAW, new BGroupLayout(B_HORIZONTAL)), 64 fOpenPanel(new BFilePanel(B_OPEN_PANEL, NULL, NULL, 65 B_DIRECTORY_NODE, false)), 66 fInfo(ref) 67 { 68 _InitView(); 69 70 // Check whether the package has been successfuly parsed 71 status_t ret = fInfo.InitCheck(); 72 if (ret == B_OK) 73 _InitProfiles(); 74 else if (ret != B_NO_INIT) { 75 BAlert *warning = new BAlert(T("parsing_failed"), 76 T("I was unable to read the given package file.\nOne of the possible " 77 "reasons for this might be that the requested file is not a valid " 78 "BeOS .pkg package."), T("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 79 B_WARNING_ALERT); 80 warning->Go(); 81 82 BWindow *parent = Window(); 83 if (parent && parent->Lock()) 84 parent->Quit(); 85 } 86 87 ResizeTo(Bounds().Width(), fInstall->Frame().bottom + 4); 88 } 89 90 91 PackageView::~PackageView() 92 { 93 delete fOpenPanel; 94 } 95 96 97 void 98 PackageView::AttachedToWindow() 99 { 100 // Set the window title 101 BWindow *parent = Window(); 102 BString title; 103 BString name = fInfo.GetName(); 104 if (name.CountChars() == 0) { 105 title = T("Package installer"); 106 } 107 else { 108 title = T("Install "); 109 title += name; 110 } 111 parent->SetTitle(title.String()); 112 fInstall->SetTarget(this); 113 114 fOpenPanel->SetTarget(BMessenger(this)); 115 fInstallTypes->SetTargetForItems(this); 116 117 if (fInfo.InitCheck() == B_OK) { 118 // If the package is valid, we can set up the default group and all 119 // other things. If not, then the application will close just after 120 // attaching the view to the window 121 _GroupChanged(0); 122 123 fStatusWindow = new PackageStatus(T("Installation progress")); 124 125 // Show the splash screen, if present 126 BMallocIO *image = fInfo.GetSplashScreen(); 127 if (image) { 128 PackageImageViewer *imageViewer = new PackageImageViewer(image); 129 imageViewer->Go(); 130 } 131 132 // Show the disclaimer/info text popup, if present 133 BString disclaimer = fInfo.GetDisclaimer(); 134 if (disclaimer.Length() != 0) { 135 PackageTextViewer *text = new PackageTextViewer(disclaimer.String()); 136 int32 selection = text->Go(); 137 // The user didn't accept our disclaimer, this means we cannot continue. 138 if (selection == 0) { 139 BWindow *parent = Window(); 140 if (parent && parent->Lock()) 141 parent->Quit(); 142 } 143 } 144 } 145 } 146 147 148 void 149 PackageView::MessageReceived(BMessage *msg) 150 { 151 switch (msg->what) { 152 case P_MSG_INSTALL: 153 { 154 fInstall->SetEnabled(false); 155 fStatusWindow->Show(); 156 BAlert *notify; 157 status_t ret = Install(); 158 if (ret == B_OK) { 159 notify = new BAlert("installation_success", 160 T("The package you requested has been successfully installed " 161 "on your system."), T("OK")); 162 163 notify->Go(); 164 fStatusWindow->Hide(); 165 166 BWindow *parent = Window(); 167 if (parent && parent->Lock()) 168 parent->Quit(); 169 } 170 else if (ret == B_FILE_EXISTS) 171 notify = new BAlert("installation_aborted", 172 T("The installation of the package has been aborted."), T("OK")); 173 else 174 notify = new BAlert("installation_failed", // TODO: Review this 175 T("The requested package failed to install on your system. This " 176 "might be a problem with the target package file. Please consult " 177 "this issue with the package distributor."), T("OK"), NULL, 178 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 179 notify->Go(); 180 fStatusWindow->Hide(); 181 fInstall->SetEnabled(true); 182 183 break; 184 } 185 case P_MSG_PATH_CHANGED: 186 { 187 BString path; 188 if (msg->FindString("path", &path) == B_OK) { 189 fCurrentPath.SetTo(path.String()); 190 } 191 break; 192 } 193 case P_MSG_OPEN_PANEL: 194 fOpenPanel->Show(); 195 break; 196 case P_MSG_GROUP_CHANGED: 197 { 198 int32 index; 199 if (msg->FindInt32("index", &index) == B_OK) { 200 _GroupChanged(index); 201 } 202 break; 203 } 204 case B_REFS_RECEIVED: 205 { 206 entry_ref ref; 207 if (msg->FindRef("refs", &ref) == B_OK) { 208 BPath path(&ref); 209 210 BMenuItem * item = fDestField->MenuItem(); 211 dev_t device = dev_for_path(path.Path()); 212 BVolume volume(device); 213 if (volume.InitCheck() != B_OK) 214 break; 215 216 BString name = path.Path(); 217 char sizeString[32]; 218 219 convert_size(volume.FreeBytes(), sizeString, 32); 220 name << " (" << sizeString << " free)"; 221 222 item->SetLabel(name.String()); 223 fCurrentPath.SetTo(path.Path()); 224 } 225 break; 226 } 227 case B_SIMPLE_DATA: 228 if (msg->WasDropped()) { 229 uint32 type; 230 int32 count; 231 status_t ret = msg->GetInfo("refs", &type, &count); 232 // Check whether the message means someone dropped a file 233 // to our view 234 if (ret == B_OK && type == B_REF_TYPE) { 235 // If it is, send it along with the refs to the application 236 msg->what = B_REFS_RECEIVED; 237 be_app->PostMessage(msg); 238 } 239 } 240 default: 241 BView::MessageReceived(msg); 242 break; 243 } 244 } 245 246 247 status_t 248 PackageView::Install() 249 { 250 pkg_profile *type = static_cast<pkg_profile *>(fInfo.GetProfile(fCurrentType)); 251 uint32 n = type->items.CountItems(); 252 253 fStatusWindow->Reset(n + 4); 254 255 fStatusWindow->StageStep(1, "Preparing package"); 256 257 InstalledPackageInfo packageInfo(fInfo.GetName(), fInfo.GetVersion()); 258 259 status_t err = packageInfo.InitCheck(); 260 if (err == B_OK) { 261 // The package is already installed, inform the user 262 BAlert *reinstall = new BAlert("reinstall", 263 T("The given package seems to be already installed on your system. " 264 "Would you like to uninstall the existing one and continue the " 265 "installation?"), T("Yes"), T("No")); 266 267 if (reinstall->Go() == 0) { 268 // Uninstall the package 269 err = packageInfo.Uninstall(); 270 if (err != B_OK) 271 return err; 272 273 err = packageInfo.SetTo(fInfo.GetName(), fInfo.GetVersion(), true); 274 if (err != B_OK) 275 return err; 276 } 277 else { 278 // Abort the installation 279 return B_FILE_EXISTS; 280 } 281 } 282 else if (err == B_ENTRY_NOT_FOUND) { 283 err = packageInfo.SetTo(fInfo.GetName(), fInfo.GetVersion(), true); 284 if (err != B_OK) 285 return err; 286 } 287 else if (fStatusWindow->Stopped()) 288 return B_FILE_EXISTS; 289 else 290 return err; 291 292 fStatusWindow->StageStep(1, "Installing files and directories"); 293 294 // Install files and directories 295 PkgItem *iter; 296 BPath installedTo; 297 uint32 i; 298 BString label; 299 300 packageInfo.SetName(fInfo.GetName()); 301 // TODO: Here's a small problem, since right now it's not quite sure 302 // which description is really used as such. The one displayed on 303 // the installer is mostly package installation description, but 304 // most people use it for describing the application in more detail 305 // then in the short description. 306 // For now, we'll use the short description if possible. 307 BString description = fInfo.GetShortDescription(); 308 if (description.Length() <= 0) 309 description = fInfo.GetDescription(); 310 packageInfo.SetDescription(description.String()); 311 packageInfo.SetSpaceNeeded(type->space_needed); 312 313 for (i = 0;i < n;i++) { 314 iter = static_cast<PkgItem *>(type->items.ItemAt(i)); 315 err = iter->WriteToPath(fCurrentPath.Path(), &installedTo); 316 if (err != B_OK) 317 return err; 318 if (fStatusWindow->Stopped()) 319 return B_FILE_EXISTS; 320 label = ""; 321 label << (uint32)(i + 1) << " of " << (uint32)n; 322 fStatusWindow->StageStep(1, NULL, label.String()); 323 324 packageInfo.AddItem(installedTo.Path()); 325 } 326 327 fStatusWindow->StageStep(1, "Finishing installation", ""); 328 329 err = packageInfo.Save(); 330 if (err != B_OK) 331 return err; 332 333 fStatusWindow->StageStep(1, "Done"); 334 335 return B_OK; 336 } 337 338 339 /* 340 void 341 PackageView::_InitView() 342 { 343 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 344 345 BTextView *description = new BTextView(BRect(0, 0, 20, 20), "description", 346 BRect(4, 4, 16, 16), B_FOLLOW_NONE, B_WILL_DRAW); 347 description->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 348 description->SetText(fInfo.GetDescription()); 349 description->MakeEditable(false); 350 description->MakeSelectable(false); 351 352 fInstallTypes = new BPopUpMenu("none"); 353 354 BMenuField *installType = new BMenuField("install_type", 355 T("Installation type:"), fInstallTypes, 0); 356 installType->SetAlignment(B_ALIGN_RIGHT); 357 installType->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_MIDDLE)); 358 359 fInstallDesc = new BTextView(BRect(0, 0, 10, 10), "install_desc", 360 BRect(2, 2, 8, 8), B_FOLLOW_NONE, B_WILL_DRAW); 361 fInstallDesc->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 362 fInstallDesc->MakeEditable(false); 363 fInstallDesc->MakeSelectable(false); 364 fInstallDesc->SetText(T("No installation type selected")); 365 fInstallDesc->TextHeight(0, fInstallDesc->TextLength()); 366 367 fInstall = new BButton("install_button", T("Install"), 368 new BMessage(P_MSG_INSTALL)); 369 370 BView *installField = BGroupLayoutBuilder(B_VERTICAL, 5.0f) 371 .AddGroup(B_HORIZONTAL) 372 .Add(installType) 373 .AddGlue() 374 .End() 375 .Add(fInstallDesc); 376 377 BBox *installBox = new BBox("install_box"); 378 installBox->AddChild(installField); 379 380 BView *root = BGroupLayoutBuilder(B_VERTICAL, 3.0f) 381 .Add(description) 382 .Add(installBox) 383 .AddGroup(B_HORIZONTAL) 384 .AddGlue() 385 .Add(fInstall) 386 .End(); 387 388 AddChild(root); 389 390 fInstall->MakeDefault(true); 391 }*/ 392 393 394 void 395 PackageView::_InitView() 396 { 397 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 398 399 BRect rect = Bounds(); 400 BTextView *description = new BTextView(rect, "description", 401 rect.InsetByCopy(5, 5), B_FOLLOW_NONE, B_WILL_DRAW); 402 description->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 403 description->SetText(fInfo.GetDescription()); 404 description->MakeEditable(false); 405 description->MakeSelectable(false); 406 407 float length = description->TextHeight(0, description->TextLength()) + 5; 408 if (length > kMaxDescHeight) { 409 // Set a scroller for the description. 410 description->ResizeTo(rect.Width() - B_V_SCROLL_BAR_WIDTH, kMaxDescHeight); 411 BScrollView *scroller = new BScrollView("desciption_view", description, 412 B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS, false, true, B_NO_BORDER); 413 414 AddChild(scroller); 415 rect = scroller->Frame(); 416 } 417 else { 418 description->ResizeTo(rect.Width(), length); 419 AddChild(description); 420 rect = description->Frame(); 421 } 422 423 rect.top = rect.bottom + 2; 424 rect.bottom += 100; 425 BBox *installBox = new BBox(rect.InsetByCopy(2, 2), "install_box"); 426 427 fInstallTypes = new BPopUpMenu("none"); 428 429 BMenuField *installType = new BMenuField(BRect(2, 2, 100, 50), "install_type", 430 T("Installation type:"), fInstallTypes, false); 431 installType->SetDivider(installType->StringWidth(installType->Label()) + 8); 432 installType->SetAlignment(B_ALIGN_RIGHT); 433 installType->ResizeToPreferred(); 434 435 installBox->AddChild(installType); 436 437 rect = installBox->Bounds().InsetBySelf(4, 4); 438 rect.top = installType->Frame().bottom; 439 fInstallDesc = new BTextView(rect, "install_desc", 440 BRect(2, 2, rect.Width() - 2, rect.Height() - 2), B_FOLLOW_NONE, 441 B_WILL_DRAW); 442 fInstallDesc->MakeEditable(false); 443 fInstallDesc->MakeSelectable(false); 444 fInstallDesc->SetText(T("No installation type selected")); 445 fInstallDesc->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 446 447 fInstallDesc->ResizeTo(rect.Width() - B_V_SCROLL_BAR_WIDTH, 60); 448 BScrollView *scroller = new BScrollView("desciption_view", fInstallDesc, 449 B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS, false, true, B_NO_BORDER); 450 451 installBox->ResizeTo(installBox->Bounds().Width(), 452 scroller->Frame().bottom + 10); 453 454 installBox->AddChild(scroller); 455 456 AddChild(installBox); 457 458 fDestination = new BPopUpMenu("none"); 459 460 rect = installBox->Frame(); 461 rect.top = rect.bottom + 5; 462 rect.bottom += 35; 463 fDestField = new BMenuField(rect, "install_to", T("Install to:"), 464 fDestination, false); 465 fDestField->SetDivider(fDestField->StringWidth(fDestField->Label()) + 8); 466 fDestField->SetAlignment(B_ALIGN_RIGHT); 467 fDestField->ResizeToPreferred(); 468 469 AddChild(fDestField); 470 471 fInstall = new BButton(rect, "install_button", T("Install"), 472 new BMessage(P_MSG_INSTALL)); 473 fInstall->ResizeToPreferred(); 474 AddChild(fInstall); 475 fInstall->MoveTo(Bounds().Width() - fInstall->Bounds().Width() - 10, rect.top + 2); 476 fInstall->MakeDefault(true); 477 } 478 479 480 void 481 PackageView::_InitProfiles() 482 { 483 // Set all profiles 484 int i = 0, num = fInfo.GetProfileCount(); 485 pkg_profile *prof; 486 BMenuItem *item = 0; 487 char sizeString[32]; 488 BString name = ""; 489 BMessage *message; 490 491 if (num > 0) { // Add the default item 492 prof = fInfo.GetProfile(0); 493 convert_size(prof->space_needed, sizeString, 32); 494 name << prof->name << " (" << sizeString << ")"; 495 496 message = new BMessage(P_MSG_GROUP_CHANGED); 497 message->AddInt32("index", 0); 498 item = new BMenuItem(name.String(), message); 499 fInstallTypes->AddItem(item); 500 item->SetMarked(true); 501 fCurrentType = 0; 502 } 503 504 for (i = 1; i < num; i++) { 505 prof = fInfo.GetProfile(i); 506 507 if (prof) { 508 convert_size(prof->space_needed, sizeString, 32); 509 name = prof->name; 510 name << " (" << sizeString << ")"; 511 512 message = new BMessage(P_MSG_GROUP_CHANGED); 513 message->AddInt32("index", i); 514 item = new BMenuItem(name.String(), message); 515 fInstallTypes->AddItem(item); 516 } 517 else 518 fInstallTypes->AddSeparatorItem(); 519 } 520 } 521 522 523 status_t 524 PackageView::_GroupChanged(int32 index) 525 { 526 if (index < 0) 527 return B_ERROR; 528 529 BMenuItem *iter; 530 int32 i, num = fDestination->CountItems(); 531 532 // Clear the choice list 533 for (i = 0;i < num;i++) { 534 iter = fDestination->RemoveItem((int32)0); 535 delete iter; 536 } 537 538 fCurrentType = index; 539 pkg_profile *prof = fInfo.GetProfile(index); 540 BString test; 541 fInstallDesc->SetText(prof->description.String()); 542 543 if (prof) { 544 BMenuItem *item = 0; 545 BPath path; 546 BMessage *temp; 547 BVolume volume; 548 549 if (prof->path_type == P_INSTALL_PATH) { 550 dev_t device; 551 BString name; 552 char sizeString[32]; 553 554 if (find_directory(B_BEOS_APPS_DIRECTORY, &path) == B_OK) { 555 device = dev_for_path(path.Path()); 556 if (volume.SetTo(device) == B_OK && !volume.IsReadOnly()) { 557 temp = new BMessage(P_MSG_PATH_CHANGED); 558 temp->AddString("path", BString(path.Path())); 559 560 convert_size(volume.FreeBytes(), sizeString, 32); 561 name = path.Path(); 562 name << " (" << sizeString << " free)"; 563 item = new BMenuItem(name.String(), temp); 564 item->SetTarget(this); 565 fDestination->AddItem(item); 566 } 567 } 568 if (find_directory(B_APPS_DIRECTORY, &path) == B_OK) { 569 device = dev_for_path(path.Path()); 570 if (volume.SetTo(device) == B_OK && !volume.IsReadOnly()) { 571 temp = new BMessage(P_MSG_PATH_CHANGED); 572 temp->AddString("path", BString(path.Path())); 573 574 convert_size(volume.FreeBytes(), sizeString, 32); 575 name = path.Path(); 576 name << " (" << sizeString << " free)"; 577 item = new BMenuItem(name.String(), temp); 578 item->SetTarget(this); 579 fDestination->AddItem(item); 580 } 581 } 582 583 if (item) { 584 item->SetMarked(true); 585 fCurrentPath.SetTo(path.Path()); 586 } 587 fDestination->AddSeparatorItem(); 588 589 item = new BMenuItem("Other...", new BMessage(P_MSG_OPEN_PANEL)); 590 item->SetTarget(this); 591 fDestination->AddItem(item); 592 593 fDestField->SetEnabled(true); 594 } 595 else if (prof->path_type == P_USER_PATH) { 596 BString name; 597 char sizeString[32], volumeName[B_FILE_NAME_LENGTH]; 598 BVolumeRoster roster; 599 BDirectory mountPoint; 600 601 while (roster.GetNextVolume(&volume) != B_BAD_VALUE) { 602 if (volume.IsReadOnly() || 603 volume.GetRootDirectory(&mountPoint) != B_OK) 604 continue; 605 606 if (path.SetTo(&mountPoint, NULL) != B_OK) 607 continue; 608 609 temp = new BMessage(P_MSG_PATH_CHANGED); 610 temp->AddString("path", BString(path.Path())); 611 612 convert_size(volume.FreeBytes(), sizeString, 32); 613 volume.GetName(volumeName); 614 name = volumeName; 615 name << " (" << sizeString << " free)"; 616 item = new BMenuItem(name.String(), temp); 617 item->SetTarget(this); 618 fDestination->AddItem(item); 619 } 620 621 fDestField->SetEnabled(true); 622 } 623 else 624 fDestField->SetEnabled(false); 625 } 626 627 return B_OK; 628 } 629 630