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