1 /* 2 * Copyright 2016-2017 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT license 4 * 5 * Authors: 6 * Alexander von Gluck IV <kallisti5@unixzen.com> 7 * Brian Hill <supernova@warpmail.net> 8 */ 9 10 11 #include "SoftwareUpdaterWindow.h" 12 13 #include <Alert.h> 14 #include <AppDefs.h> 15 #include <Application.h> 16 #include <Catalog.h> 17 #include <ControlLook.h> 18 #include <LayoutBuilder.h> 19 #include <LayoutUtils.h> 20 #include <Message.h> 21 #include <Roster.h> 22 #include <String.h> 23 24 #include "constants.h" 25 26 #undef B_TRANSLATION_CONTEXT 27 #define B_TRANSLATION_CONTEXT "SoftwareUpdaterWindow" 28 29 30 SoftwareUpdaterWindow::SoftwareUpdaterWindow() 31 : 32 BWindow(BRect(0, 0, 300, 100), 33 B_TRANSLATE_SYSTEM_NAME("SoftwareUpdater"), B_TITLED_WINDOW, 34 B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE | B_NOT_CLOSABLE), 35 fStripeView(NULL), 36 fHeaderView(NULL), 37 fDetailView(NULL), 38 fUpdateButton(NULL), 39 fCancelButton(NULL), 40 fStatusBar(NULL), 41 fCurrentState(STATE_HEAD), 42 fWaitingSem(-1), 43 fWaitingForButton(false), 44 fUpdateConfirmed(false), 45 fUserCancelRequested(false), 46 fWarningAlertCount(0) 47 { 48 BBitmap icon = GetIcon(32 * icon_layout_scale()); 49 fStripeView = new StripeView(icon); 50 51 fUpdateButton = new BButton(B_TRANSLATE("Update now"), 52 new BMessage(kMsgUpdateConfirmed)); 53 fUpdateButton->MakeDefault(true); 54 fCancelButton = new BButton(B_TRANSLATE("Cancel"), 55 new BMessage(kMsgCancel)); 56 57 fHeaderView = new BStringView("header", 58 B_TRANSLATE("Checking for updates"), B_WILL_DRAW); 59 fHeaderView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 60 fHeaderView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); 61 fDetailView = new BStringView("detail", B_TRANSLATE("Contacting software " 62 "repositories to check for package updates."), B_WILL_DRAW); 63 fDetailView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 64 fDetailView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); 65 fStatusBar = new BStatusBar("progress"); 66 fStatusBar->SetMaxValue(100); 67 68 fListView = new PackageListView(); 69 fScrollView = new BScrollView("scrollview", fListView, B_WILL_DRAW, 70 false, true); 71 72 fDetailsCheckbox = new BCheckBox("detailscheckbox", 73 B_TRANSLATE("Show more details"), 74 new BMessage(kMsgMoreDetailsToggle)); 75 76 BFont font; 77 fHeaderView->GetFont(&font); 78 font.SetFace(B_BOLD_FACE); 79 font.SetSize(font.Size() * 1.5); 80 fHeaderView->SetFont(&font, 81 B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE | B_FONT_FLAGS); 82 83 BLayoutBuilder::Group<>(this, B_HORIZONTAL, B_USE_ITEM_SPACING) 84 .Add(fStripeView) 85 .AddGroup(B_VERTICAL, 0) 86 .SetInsets(0, B_USE_WINDOW_SPACING, 87 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING) 88 .AddGroup(new BGroupView(B_VERTICAL, B_USE_ITEM_SPACING)) 89 .Add(fHeaderView) 90 .Add(fDetailView) 91 .Add(fStatusBar) 92 .Add(fScrollView) 93 .End() 94 .AddStrut(B_USE_SMALL_SPACING) 95 .AddGroup(new BGroupView(B_HORIZONTAL)) 96 .Add(fDetailsCheckbox) 97 .AddGlue() 98 .End() 99 .AddGroup(new BGroupView(B_HORIZONTAL)) 100 .AddGlue() 101 .Add(fCancelButton) 102 .Add(fUpdateButton) 103 .End() 104 .End() 105 .End(); 106 107 fDetailsLayoutItem = layout_item_for(fDetailView); 108 fProgressLayoutItem = layout_item_for(fStatusBar); 109 fPackagesLayoutItem = layout_item_for(fScrollView); 110 fUpdateButtonLayoutItem = layout_item_for(fUpdateButton); 111 fDetailsCheckboxLayoutItem = layout_item_for(fDetailsCheckbox); 112 113 _SetState(STATE_DISPLAY_STATUS); 114 CenterOnScreen(); 115 Show(); 116 SetFlags(Flags() ^ B_AUTO_UPDATE_SIZE_LIMITS); 117 118 // Prevent resizing for now 119 fDefaultRect = Bounds(); 120 SetSizeLimits(fDefaultRect.Width(), fDefaultRect.Width(), 121 fDefaultRect.Height(), fDefaultRect.Height()); 122 123 BMessage registerMessage(kMsgRegister); 124 registerMessage.AddMessenger(kKeyMessenger, BMessenger(this)); 125 be_app->PostMessage(®isterMessage); 126 127 fCancelAlertResponse.SetMessage(new BMessage(kMsgCancelResponse)); 128 fCancelAlertResponse.SetTarget(this); 129 fWarningAlertDismissed.SetMessage(new BMessage(kMsgWarningDismissed)); 130 fWarningAlertDismissed.SetTarget(this); 131 } 132 133 134 void 135 SoftwareUpdaterWindow::MessageReceived(BMessage* message) 136 { 137 switch (message->what) { 138 139 case kMsgTextUpdate: 140 { 141 if (fCurrentState == STATE_DISPLAY_PROGRESS) 142 _SetState(STATE_DISPLAY_STATUS); 143 else if (fCurrentState != STATE_DISPLAY_STATUS) 144 break; 145 146 BString header; 147 BString detail; 148 Lock(); 149 status_t result = message->FindString(kKeyHeader, &header); 150 if (result == B_OK && header != fHeaderView->Text()) 151 fHeaderView->SetText(header.String()); 152 result = message->FindString(kKeyDetail, &detail); 153 if (result == B_OK) 154 fDetailView->SetText(detail.String()); 155 Unlock(); 156 break; 157 } 158 159 case kMsgProgressUpdate: 160 { 161 if (fCurrentState == STATE_DISPLAY_STATUS) 162 _SetState(STATE_DISPLAY_PROGRESS); 163 else if (fCurrentState != STATE_DISPLAY_PROGRESS) 164 break; 165 166 BString packageName; 167 status_t result = message->FindString(kKeyPackageName, &packageName); 168 if (result != B_OK) 169 break; 170 BString packageCount; 171 result = message->FindString(kKeyPackageCount, &packageCount); 172 if (result != B_OK) 173 break; 174 float percent; 175 result = message->FindFloat(kKeyPercentage, &percent); 176 if (result != B_OK) 177 break; 178 179 BString header; 180 Lock(); 181 result = message->FindString(kKeyHeader, &header); 182 if (result == B_OK && header != fHeaderView->Text()) 183 fHeaderView->SetText(header.String()); 184 fStatusBar->SetTo(percent, packageName.String(), 185 packageCount.String()); 186 Unlock(); 187 188 fListView->UpdatePackageProgress(packageName.String(), percent); 189 break; 190 } 191 192 case kMsgCancel: 193 { 194 if (_GetState() == STATE_FINAL_MESSAGE) { 195 PostMessage(B_QUIT_REQUESTED); 196 be_app->PostMessage(kMsgFinalQuit); 197 break; 198 } 199 if (!fUpdateConfirmed) { 200 // Downloads have not started yet, we will request to cancel 201 // without confirming 202 Lock(); 203 fHeaderView->SetText(B_TRANSLATE("Cancelling updates")); 204 fDetailView->SetText( 205 B_TRANSLATE("Attempting to cancel the updates...")); 206 fCancelButton->SetEnabled(false); 207 Unlock(); 208 fUserCancelRequested = true; 209 210 if (fWaitingForButton) { 211 fButtonResult = message->what; 212 delete_sem(fWaitingSem); 213 fWaitingSem = -1; 214 } 215 break; 216 } 217 218 // Confirm with the user to cancel 219 BAlert* alert = new BAlert("cancel request", B_TRANSLATE("Updates" 220 " have not been completed, are you sure you want to quit?"), 221 B_TRANSLATE("Quit"), B_TRANSLATE("Don't quit"), NULL, 222 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 223 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 224 alert->Go(&fCancelAlertResponse); 225 break; 226 } 227 228 case kMsgCancelResponse: 229 { 230 // Verify whether the cancel alert was confirmed 231 int32 selection = -1; 232 message->FindInt32("which", &selection); 233 if (selection != 0) 234 break; 235 236 Lock(); 237 fHeaderView->SetText(B_TRANSLATE("Cancelling updates")); 238 fDetailView->SetText( 239 B_TRANSLATE("Attempting to cancel the updates...")); 240 fCancelButton->SetEnabled(false); 241 Unlock(); 242 fUserCancelRequested = true; 243 244 if (fWaitingForButton) { 245 fButtonResult = message->what; 246 delete_sem(fWaitingSem); 247 fWaitingSem = -1; 248 } 249 break; 250 } 251 252 case kMsgUpdateConfirmed: 253 { 254 if (fWaitingForButton) { 255 fButtonResult = message->what; 256 delete_sem(fWaitingSem); 257 fWaitingSem = -1; 258 fUpdateConfirmed = true; 259 } 260 break; 261 } 262 263 case kMsgMoreDetailsToggle: 264 fListView->SetMoreDetails(fDetailsCheckbox->Value() != 0); 265 break; 266 267 case kMsgWarningDismissed: 268 fWarningAlertCount--; 269 break; 270 271 case kMsgGetUpdateType: 272 { 273 BString text( 274 B_TRANSLATE("Please choose from these update options:\n\n" 275 "Update:\n" 276 " Updates all installed packages.\n" 277 "Full sync:\n" 278 " Synchronizes the installed packages with the repositories." 279 )); 280 BAlert* alert = new BAlert("update_type", 281 text, 282 B_TRANSLATE_COMMENT("Cancel", "Alert button label"), 283 B_TRANSLATE_COMMENT("Full sync","Alert button label"), 284 B_TRANSLATE_COMMENT("Update","Alert button label"), 285 B_WIDTH_AS_USUAL, B_INFO_ALERT); 286 int32 result = alert->Go(); 287 int32 action = INVALID_SELECTION; 288 switch(result) { 289 case 0: 290 action = CANCEL_UPDATE; 291 break; 292 293 case 1: 294 action = FULLSYNC; 295 break; 296 297 case 2: 298 action = UPDATE; 299 break; 300 } 301 BMessage reply; 302 reply.AddInt32(kKeyAlertResult, action); 303 message->SendReply(&reply); 304 break; 305 } 306 307 case kMsgNoRepositories: 308 { 309 BString text( 310 B_TRANSLATE_COMMENT( 311 "No remote repositories are available. Please verify that some" 312 " repositories are enabled using the Repositories preflet or" 313 " the \'pkgman\' command.", "Error message")); 314 BAlert* alert = new BAlert("repositories", text, 315 B_TRANSLATE_COMMENT("Quit", "Alert button label"), 316 B_TRANSLATE_COMMENT("Open Repositories","Alert button label"), 317 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 318 int32 result = alert->Go(); 319 BMessage reply; 320 reply.AddInt32(kKeyAlertResult, result); 321 message->SendReply(&reply); 322 break; 323 } 324 325 default: 326 BWindow::MessageReceived(message); 327 } 328 } 329 330 331 bool 332 SoftwareUpdaterWindow::ConfirmUpdates() 333 { 334 Lock(); 335 fHeaderView->SetText(B_TRANSLATE("Updates found")); 336 fDetailView->SetText(B_TRANSLATE("The following changes will be made:")); 337 fListView->SortItems(); 338 Unlock(); 339 340 uint32 priorState = _GetState(); 341 _SetState(STATE_GET_CONFIRMATION); 342 343 _WaitForButtonClick(); 344 _SetState(priorState); 345 return fButtonResult == kMsgUpdateConfirmed; 346 } 347 348 349 void 350 SoftwareUpdaterWindow::UpdatesApplying(const char* header, const char* detail) 351 { 352 Lock(); 353 fHeaderView->SetText(header); 354 fDetailView->SetText(detail); 355 Unlock(); 356 _SetState(STATE_APPLY_UPDATES); 357 } 358 359 360 bool 361 SoftwareUpdaterWindow::UserCancelRequested() 362 { 363 if (_GetState() > STATE_GET_CONFIRMATION) 364 return false; 365 366 return fUserCancelRequested; 367 } 368 369 370 void 371 SoftwareUpdaterWindow::AddPackageInfo(uint32 install_type, 372 const char* package_name, const char* cur_ver, const char* new_ver, 373 const char* summary, const char* repository, const char* file_name) 374 { 375 Lock(); 376 fListView->AddPackage(install_type, package_name, cur_ver, new_ver, 377 summary, repository, file_name); 378 Unlock(); 379 } 380 381 382 void 383 SoftwareUpdaterWindow::ShowWarningAlert(const char* text) 384 { 385 BAlert* alert = new BAlert("warning", text, B_TRANSLATE("OK"), NULL, NULL, 386 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 387 alert->Go(&fWarningAlertDismissed); 388 alert->CenterIn(Frame()); 389 // Offset multiple alerts 390 alert->MoveBy(fWarningAlertCount * 15, fWarningAlertCount * 15); 391 fWarningAlertCount++; 392 } 393 394 395 BBitmap 396 SoftwareUpdaterWindow::GetIcon(int32 iconSize) 397 { 398 BBitmap icon(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32); 399 team_info teamInfo; 400 get_team_info(B_CURRENT_TEAM, &teamInfo); 401 app_info appInfo; 402 be_roster->GetRunningAppInfo(teamInfo.team, &appInfo); 403 BNodeInfo::GetTrackerIcon(&appInfo.ref, &icon, icon_size(iconSize)); 404 return icon; 405 } 406 407 408 BBitmap 409 SoftwareUpdaterWindow::GetNotificationIcon() 410 { 411 return GetIcon(B_LARGE_ICON); 412 } 413 414 415 void 416 SoftwareUpdaterWindow::FinalUpdate(const char* header, const char* detail) 417 { 418 if (_GetState() == STATE_FINAL_MESSAGE) 419 return; 420 421 _SetState(STATE_FINAL_MESSAGE); 422 Lock(); 423 fHeaderView->SetText(header); 424 fDetailView->SetText(detail); 425 Unlock(); 426 } 427 428 429 BLayoutItem* 430 SoftwareUpdaterWindow::layout_item_for(BView* view) 431 { 432 BLayout* layout = view->Parent()->GetLayout(); 433 int32 index = layout->IndexOfView(view); 434 return layout->ItemAt(index); 435 } 436 437 438 uint32 439 SoftwareUpdaterWindow::_WaitForButtonClick() 440 { 441 fButtonResult = 0; 442 fWaitingForButton = true; 443 fWaitingSem = create_sem(0, "WaitingSem"); 444 while (acquire_sem(fWaitingSem) == B_INTERRUPTED) { 445 } 446 fWaitingForButton = false; 447 return fButtonResult; 448 } 449 450 451 void 452 SoftwareUpdaterWindow::_SetState(uint32 state) 453 { 454 if (state <= STATE_HEAD || state >= STATE_MAX) 455 return; 456 457 Lock(); 458 459 // Initial settings 460 if (fCurrentState == STATE_HEAD) { 461 fProgressLayoutItem->SetVisible(false); 462 fPackagesLayoutItem->SetVisible(false); 463 fDetailsCheckboxLayoutItem->SetVisible(false); 464 } 465 fCurrentState = state; 466 467 // Update confirmation button 468 // Show only when asking for confirmation to update 469 if (fCurrentState == STATE_GET_CONFIRMATION) 470 fUpdateButtonLayoutItem->SetVisible(true); 471 else 472 fUpdateButtonLayoutItem->SetVisible(false); 473 474 // View package info view 475 // Show at confirmation prompt, hide at final update 476 if (fCurrentState == STATE_GET_CONFIRMATION) { 477 fPackagesLayoutItem->SetVisible(true); 478 fDetailsCheckboxLayoutItem->SetVisible(true); 479 // Re-enable resizing 480 float defaultWidth = fDefaultRect.Width(); 481 SetSizeLimits(defaultWidth, 9999, 482 fDefaultRect.Height() + 4 * fListView->ItemHeight(), 9999); 483 ResizeTo(defaultWidth, .75 * defaultWidth); 484 } else if (fCurrentState == STATE_FINAL_MESSAGE) { 485 fPackagesLayoutItem->SetVisible(false); 486 fDetailsCheckboxLayoutItem->SetVisible(false); 487 float defaultWidth = fDefaultRect.Width(); 488 float defaultHeight = fDefaultRect.Height(); 489 SetSizeLimits(defaultWidth, defaultWidth, defaultHeight, 490 defaultHeight); 491 ResizeTo(defaultWidth, defaultHeight); 492 } 493 494 // Progress bar and string view 495 // Hide detail text while showing status bar 496 if (fCurrentState == STATE_DISPLAY_PROGRESS) { 497 fDetailsLayoutItem->SetVisible(false); 498 fProgressLayoutItem->SetVisible(true); 499 } else { 500 fProgressLayoutItem->SetVisible(false); 501 fDetailsLayoutItem->SetVisible(true); 502 } 503 504 // Cancel button 505 if (fCurrentState == STATE_FINAL_MESSAGE) 506 fCancelButton->SetLabel(B_TRANSLATE("Quit")); 507 fCancelButton->SetEnabled(fCurrentState != STATE_APPLY_UPDATES); 508 509 Unlock(); 510 } 511 512 513 uint32 514 SoftwareUpdaterWindow::_GetState() 515 { 516 return fCurrentState; 517 } 518 519 520 SuperItem::SuperItem(const char* label) 521 : 522 BListItem(), 523 fLabel(label), 524 fShowMoreDetails(false), 525 fPackageIcon(NULL), 526 fItemCount(0) 527 { 528 } 529 530 531 SuperItem::~SuperItem() 532 { 533 delete fPackageIcon; 534 } 535 536 537 void 538 SuperItem::DrawItem(BView* owner, BRect item_rect, bool complete) 539 { 540 owner->PushState(); 541 542 float width; 543 owner->GetPreferredSize(&width, NULL); 544 BString label(fLabel); 545 label.Append(" ("); 546 label << fItemCount; 547 label.Append(")"); 548 owner->TruncateString(&label, B_TRUNCATE_END, width); 549 owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR)); 550 owner->SetFont(&fBoldFont); 551 owner->DrawString(label.String(), BPoint(item_rect.left, 552 item_rect.bottom - fFontHeight.descent - 1)); 553 554 owner->PopState(); 555 } 556 557 558 void 559 SuperItem::Update(BView *owner, const BFont *font) 560 { 561 fRegularFont = *font; 562 fBoldFont = *font; 563 fBoldFont.SetFace(B_BOLD_FACE); 564 BListItem::Update(owner, &fBoldFont); 565 _SetHeights(); 566 } 567 568 569 void 570 SuperItem::SetDetailLevel(bool showMoreDetails) 571 { 572 fShowMoreDetails = showMoreDetails; 573 _SetHeights(); 574 } 575 576 577 void 578 SuperItem::_SetHeights() 579 { 580 // Calculate height for PackageItem 581 fRegularFont.GetHeight(&fFontHeight); 582 int lineCount = fShowMoreDetails ? 3 : 2; 583 fPackageItemHeight = lineCount * (fFontHeight.ascent + fFontHeight.descent 584 + fFontHeight.leading); 585 586 // Calculate height for this item 587 fBoldFont.GetHeight(&fFontHeight); 588 SetHeight(fFontHeight.ascent + fFontHeight.descent 589 + fFontHeight.leading + 4); 590 591 _GetPackageIcon(); 592 } 593 594 595 void 596 SuperItem::_GetPackageIcon() 597 { 598 delete fPackageIcon; 599 fIconSize = int(fPackageItemHeight * .8); 600 601 status_t result = B_ERROR; 602 BRect iconRect(0, 0, fIconSize - 1, fIconSize - 1); 603 fPackageIcon = new BBitmap(iconRect, 0, B_RGBA32); 604 BMimeType nodeType; 605 nodeType.SetTo("application/x-vnd.haiku-package"); 606 result = nodeType.GetIcon(fPackageIcon, icon_size(fIconSize)); 607 // Get super type icon 608 if (result != B_OK) { 609 BMimeType superType; 610 if (nodeType.GetSupertype(&superType) == B_OK) 611 result = superType.GetIcon(fPackageIcon, icon_size(fIconSize)); 612 } 613 if (result != B_OK) { 614 delete fPackageIcon; 615 fPackageIcon = NULL; 616 } 617 } 618 619 620 PackageItem::PackageItem(const char* name, const char* simple_version, 621 const char* detailed_version, const char* repository, const char* summary, 622 const char* file_name, SuperItem* super) 623 : 624 BListItem(), 625 fName(name), 626 fSimpleVersion(simple_version), 627 fDetailedVersion(detailed_version), 628 fRepository(repository), 629 fSummary(summary), 630 fSuperItem(super), 631 fFileName(file_name), 632 fDownloadProgress(0), 633 fDrawBarFlag(false) 634 { 635 fLabelOffset = be_control_look->DefaultLabelSpacing(); 636 } 637 638 639 void 640 PackageItem::DrawItem(BView* owner, BRect item_rect, bool complete) 641 { 642 owner->PushState(); 643 644 float width; 645 owner->GetPreferredSize(&width, NULL); 646 float nameWidth = width / 2.0; 647 float offset_width = 0; 648 bool showMoreDetails = fSuperItem->GetDetailLevel(); 649 650 BBitmap* icon = fSuperItem->GetIcon(); 651 if (icon != NULL && icon->IsValid()) { 652 int16 iconSize = fSuperItem->GetIconSize(); 653 float offsetMarginHeight = floor((Height() - iconSize) / 2); 654 655 //owner->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 656 owner->SetDrawingMode(B_OP_ALPHA); 657 BPoint location = BPoint(item_rect.left, 658 item_rect.top + offsetMarginHeight); 659 owner->DrawBitmap(icon, location); 660 owner->SetDrawingMode(B_OP_COPY); 661 662 if (fDrawBarFlag) 663 _DrawBar(location, owner, icon_size(iconSize)); 664 665 offset_width += iconSize + fLabelOffset; 666 } 667 668 owner->SetFont(&fRegularFont); 669 owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR)); 670 671 // Package name 672 font_height fontHeight = fSuperItem->GetFontHeight(); 673 BString name(fName); 674 owner->TruncateString(&name, B_TRUNCATE_END, nameWidth); 675 BPoint cursor(item_rect.left + offset_width, 676 item_rect.bottom - fSmallTotalHeight - fontHeight.descent - 1); 677 if (showMoreDetails) 678 cursor.y -= fSmallTotalHeight + 1; 679 owner->DrawString(name.String(), cursor); 680 cursor.x += owner->StringWidth(name.String()) + fLabelOffset; 681 682 // Change font and color 683 owner->SetFont(&fSmallFont); 684 owner->SetHighColor(tint_color(ui_color(B_LIST_ITEM_TEXT_COLOR), 0.7)); 685 686 // Simple version or repository 687 BString versionOrRepo; 688 if (showMoreDetails) 689 versionOrRepo.SetTo(fRepository); 690 else 691 versionOrRepo.SetTo(fSimpleVersion); 692 owner->TruncateString(&versionOrRepo, B_TRUNCATE_END, width - cursor.x); 693 owner->DrawString(versionOrRepo.String(), cursor); 694 695 // Summary 696 BString summary(fSummary); 697 cursor.x = item_rect.left + offset_width; 698 cursor.y += fSmallTotalHeight; 699 owner->TruncateString(&summary, B_TRUNCATE_END, width - cursor.x); 700 owner->DrawString(summary.String(), cursor); 701 702 // Detailed version 703 if (showMoreDetails) { 704 BString version(fDetailedVersion); 705 cursor.y += fSmallTotalHeight; 706 owner->TruncateString(&version, B_TRUNCATE_END, width - cursor.x); 707 owner->DrawString(version.String(), cursor); 708 } 709 710 owner->PopState(); 711 } 712 713 714 // Modified slightly from Tracker's BPose::DrawBar 715 void 716 PackageItem::_DrawBar(BPoint where, BView* view, icon_size which) 717 { 718 int32 yOffset; 719 int32 size = which - 1; 720 int32 barWidth = (int32)(7.0f / 32.0f * (float)which); 721 if (barWidth < 4) { 722 barWidth = 4; 723 yOffset = 0; 724 } else 725 yOffset = 2; 726 int32 barHeight = size - 3 - 2 * yOffset; 727 728 729 // the black shadowed line 730 view->SetHighColor(32, 32, 32, 92); 731 view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset)); 732 view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset)); 733 view->StrokeLine(BPoint(where.x + size - barWidth + 1, 734 where.y + size - yOffset)); 735 736 view->SetDrawingMode(B_OP_ALPHA); 737 738 // the gray frame 739 view->SetHighColor(76, 76, 76, 192); 740 BRect rect(where.x + size - barWidth,where.y + yOffset, 741 where.x + size - 1,where.y + size - 1 - yOffset); 742 view->StrokeRect(rect); 743 744 // calculate bar height 745 int32 barPos = barHeight - int32(barHeight * fDownloadProgress / 100.0); 746 if (barPos < 0) 747 barPos = 0; 748 else if (barPos > barHeight) 749 barPos = barHeight; 750 751 // the free space bar 752 view->SetHighColor(255, 255, 255, 192); 753 754 rect.InsetBy(1,1); 755 BRect bar(rect); 756 bar.bottom = bar.top + barPos - 1; 757 if (barPos > 0) 758 view->FillRect(bar); 759 760 // the used space bar 761 bar.top = bar.bottom + 1; 762 bar.bottom = rect.bottom; 763 view->SetHighColor(0, 203, 0, 192); 764 view->FillRect(bar); 765 } 766 767 768 void 769 PackageItem::Update(BView *owner, const BFont *font) 770 { 771 BListItem::Update(owner, font); 772 SetItemHeight(font); 773 } 774 775 776 void 777 PackageItem::SetItemHeight(const BFont* font) 778 { 779 SetHeight(fSuperItem->GetPackageItemHeight()); 780 781 fRegularFont = *font; 782 fSmallFont = *font; 783 fSmallFont.SetSize(font->Size() - 2); 784 fSmallFont.GetHeight(&fSmallFontHeight); 785 fSmallTotalHeight = fSmallFontHeight.ascent + fSmallFontHeight.descent 786 + fSmallFontHeight.leading; 787 } 788 789 790 int 791 PackageItem::NameCompare(PackageItem* item) 792 { 793 // sort by package name 794 return fName.ICompare(item->fName); 795 } 796 797 798 void 799 PackageItem::SetDownloadProgress(float percent) 800 { 801 fDownloadProgress = percent; 802 } 803 804 805 int 806 SortPackageItems(const BListItem* item1, const BListItem* item2) 807 { 808 PackageItem* first = (PackageItem*)item1; 809 PackageItem* second = (PackageItem*)item2; 810 return first->NameCompare(second); 811 } 812 813 814 PackageListView::PackageListView() 815 : 816 BOutlineListView("Package list"), 817 fSuperUpdateItem(NULL), 818 fSuperInstallItem(NULL), 819 fSuperUninstallItem(NULL), 820 fShowMoreDetails(false), 821 fLastProgressItem(NULL), 822 fLastProgressValue(-1) 823 { 824 SetExplicitMinSize(BSize(B_SIZE_UNSET, 40)); 825 SetExplicitPreferredSize(BSize(B_SIZE_UNSET, 400)); 826 } 827 828 829 void 830 PackageListView::FrameResized(float newWidth, float newHeight) 831 { 832 BOutlineListView::FrameResized(newWidth, newHeight); 833 834 float count = CountItems(); 835 for (int32 i = 0; i < count; i++) { 836 BListItem *item = ItemAt(i); 837 item->Update(this, be_plain_font); 838 } 839 Invalidate(); 840 } 841 842 843 void 844 PackageListView::AddPackage(uint32 install_type, const char* name, 845 const char* cur_ver, const char* new_ver, const char* summary, 846 const char* repository, const char* file_name) 847 { 848 SuperItem* super; 849 BString simpleVersion; 850 BString detailedVersion(""); 851 BString repositoryText(B_TRANSLATE_COMMENT("from repository", 852 "List item text")); 853 repositoryText.Append(" ").Append(repository); 854 855 switch (install_type) { 856 case PACKAGE_UPDATE: 857 { 858 if (fSuperUpdateItem == NULL) { 859 fSuperUpdateItem = new SuperItem(B_TRANSLATE_COMMENT( 860 "Packages to be updated", "List super item label")); 861 AddItem(fSuperUpdateItem); 862 } 863 super = fSuperUpdateItem; 864 865 simpleVersion.SetTo(new_ver); 866 detailedVersion.Append(B_TRANSLATE_COMMENT("Updating version", 867 "List item text")) 868 .Append(" ").Append(cur_ver) 869 .Append(" ").Append(B_TRANSLATE_COMMENT("to", 870 "List item text")) 871 .Append(" ").Append(new_ver); 872 break; 873 } 874 875 case PACKAGE_INSTALL: 876 { 877 if (fSuperInstallItem == NULL) { 878 fSuperInstallItem = new SuperItem(B_TRANSLATE_COMMENT( 879 "New packages to be installed", "List super item label")); 880 AddItem(fSuperInstallItem); 881 } 882 super = fSuperInstallItem; 883 884 simpleVersion.SetTo(new_ver); 885 detailedVersion.Append(B_TRANSLATE_COMMENT("Installing version", 886 "List item text")) 887 .Append(" ").Append(new_ver); 888 break; 889 } 890 891 case PACKAGE_UNINSTALL: 892 { 893 if (fSuperUninstallItem == NULL) { 894 fSuperUninstallItem = new SuperItem(B_TRANSLATE_COMMENT( 895 "Packages to be uninstalled", "List super item label")); 896 AddItem(fSuperUninstallItem); 897 } 898 super = fSuperUninstallItem; 899 900 simpleVersion.SetTo(""); 901 detailedVersion.Append(B_TRANSLATE_COMMENT("Uninstalling version", 902 "List item text")) 903 .Append(" ").Append(cur_ver); 904 break; 905 } 906 907 default: 908 return; 909 910 } 911 PackageItem* item = new PackageItem(name, simpleVersion.String(), 912 detailedVersion.String(), repositoryText.String(), summary, file_name, 913 super); 914 AddUnder(item, super); 915 super->SetItemCount(CountItemsUnder(super, true)); 916 } 917 918 919 void 920 PackageListView::UpdatePackageProgress(const char* packageName, float percent) 921 { 922 // Update only every 1 percent change 923 int16 wholePercent = int16(percent); 924 if (wholePercent == fLastProgressValue) 925 return; 926 fLastProgressValue = wholePercent; 927 928 // A new package started downloading, find the PackageItem by name 929 if (percent == 0) { 930 fLastProgressItem = NULL; 931 int32 count = FullListCountItems(); 932 for (int32 i = 0; i < count; i++) { 933 PackageItem* item = dynamic_cast<PackageItem*>(FullListItemAt(i)); 934 if (item != NULL && strcmp(item->FileName(), packageName) == 0) { 935 fLastProgressItem = item; 936 fLastProgressItem->ShowProgressBar(); 937 break; 938 } 939 } 940 } 941 942 if (fLastProgressItem != NULL) { 943 fLastProgressItem->SetDownloadProgress(percent); 944 Invalidate(); 945 } 946 } 947 948 949 void 950 PackageListView::SortItems() 951 { 952 if (fSuperUpdateItem != NULL) 953 SortItemsUnder(fSuperUpdateItem, true, SortPackageItems); 954 if (fSuperInstallItem != NULL) 955 SortItemsUnder(fSuperInstallItem, true, SortPackageItems); 956 if (fSuperUninstallItem != NULL) 957 SortItemsUnder(fSuperUninstallItem, true, SortPackageItems); 958 } 959 960 961 float 962 PackageListView::ItemHeight() 963 { 964 if (fSuperUpdateItem != NULL) 965 return fSuperUpdateItem->GetPackageItemHeight(); 966 if (fSuperInstallItem != NULL) 967 return fSuperInstallItem->GetPackageItemHeight(); 968 if (fSuperUninstallItem != NULL) 969 return fSuperUninstallItem->GetPackageItemHeight(); 970 return 0; 971 } 972 973 974 void 975 PackageListView::SetMoreDetails(bool showMore) 976 { 977 fShowMoreDetails = showMore; 978 _SetItemHeights(); 979 InvalidateLayout(); 980 ResizeToPreferred(); 981 } 982 983 984 void 985 PackageListView::_SetItemHeights() 986 { 987 int32 itemCount = 0; 988 float itemHeight = 0; 989 BListItem* item = NULL; 990 if (fSuperUpdateItem != NULL) { 991 fSuperUpdateItem->SetDetailLevel(fShowMoreDetails); 992 itemHeight = fSuperUpdateItem->GetPackageItemHeight(); 993 itemCount = CountItemsUnder(fSuperUpdateItem, true); 994 for (int32 i = 0; i < itemCount; i++) { 995 item = ItemUnderAt(fSuperUpdateItem, true, i); 996 item->SetHeight(itemHeight); 997 } 998 } 999 if (fSuperInstallItem != NULL) { 1000 fSuperInstallItem->SetDetailLevel(fShowMoreDetails); 1001 itemHeight = fSuperInstallItem->GetPackageItemHeight(); 1002 itemCount = CountItemsUnder(fSuperInstallItem, true); 1003 for (int32 i = 0; i < itemCount; i++) { 1004 item = ItemUnderAt(fSuperInstallItem, true, i); 1005 item->SetHeight(itemHeight); 1006 } 1007 1008 } 1009 if (fSuperUninstallItem != NULL) { 1010 fSuperUninstallItem->SetDetailLevel(fShowMoreDetails); 1011 itemHeight = fSuperUninstallItem->GetPackageItemHeight(); 1012 itemCount = CountItemsUnder(fSuperUninstallItem, true); 1013 for (int32 i = 0; i < itemCount; i++) { 1014 item = ItemUnderAt(fSuperUninstallItem, true, i); 1015 item->SetHeight(itemHeight); 1016 } 1017 1018 } 1019 } 1020