From ad7783e44df140933511f09e6750f2de4b3ba608 Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Sat, 11 Mar 2017 15:16:09 -0500 Subject: [PATCH] Updates to SoftwareUpdater Committing work done to make SoftwareUpdater functional. The application will find available updates, confirm with the user to apply updates, download packages and apply them. There are a few bugs and features remaining to be fixed and completed: *Bug with window resizing after packages start downloading- list view does not resize down *Tooltips to be implemented (showing repository and other details) *Proper handling of no network or failed repository connections *Testing of the problem solver *Mode to only check for availability of updates and post notification with results --- src/apps/softwareupdater/Jamfile | 10 + src/apps/softwareupdater/SoftwareUpdater.rdef | 2 +- .../softwareupdater/SoftwareUpdaterApp.cpp | 60 +- src/apps/softwareupdater/SoftwareUpdaterApp.h | 18 +- .../softwareupdater/SoftwareUpdaterWindow.cpp | 787 +++++++++++++++--- .../softwareupdater/SoftwareUpdaterWindow.h | 159 +++- src/apps/softwareupdater/StripeView.cpp | 4 +- src/apps/softwareupdater/UpdateAction.cpp | 96 +++ src/apps/softwareupdater/UpdateAction.h | 27 + src/apps/softwareupdater/UpdateManager.cpp | 252 +++++- src/apps/softwareupdater/UpdateManager.h | 33 +- src/apps/softwareupdater/WorkingLooper.cpp | 35 + src/apps/softwareupdater/WorkingLooper.h | 31 + src/apps/softwareupdater/constants.h | 51 ++ 14 files changed, 1412 insertions(+), 153 deletions(-) create mode 100644 src/apps/softwareupdater/UpdateAction.cpp create mode 100644 src/apps/softwareupdater/UpdateAction.h create mode 100644 src/apps/softwareupdater/WorkingLooper.cpp create mode 100644 src/apps/softwareupdater/WorkingLooper.h create mode 100644 src/apps/softwareupdater/constants.h diff --git a/src/apps/softwareupdater/Jamfile b/src/apps/softwareupdater/Jamfile index 4fd6d404da..c042b8c2c6 100644 --- a/src/apps/softwareupdater/Jamfile +++ b/src/apps/softwareupdater/Jamfile @@ -1,10 +1,20 @@ SubDir HAIKU_TOP src apps softwareupdater ; +UsePrivateHeaders interface shared package ; + +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src servers package ] ; + Application SoftwareUpdater : SoftwareUpdaterApp.cpp SoftwareUpdaterWindow.cpp StripeView.cpp + UpdateAction.cpp UpdateManager.cpp + WorkingLooper.cpp + + # package_daemon + ProblemWindow.cpp + : be localestub package translation [ TargetLibsupc++ ] : SoftwareUpdater.rdef ; diff --git a/src/apps/softwareupdater/SoftwareUpdater.rdef b/src/apps/softwareupdater/SoftwareUpdater.rdef index d970cd9fb9..30fe4fd3be 100644 --- a/src/apps/softwareupdater/SoftwareUpdater.rdef +++ b/src/apps/softwareupdater/SoftwareUpdater.rdef @@ -9,7 +9,7 @@ resource app_version { variety = B_APPV_BETA, internal = 0, short_info = "SoftwareUpdater", - long_info = "Software Updater ©2008-2016 Haiku" + long_info = "Software Updater ©2008-2017 Haiku" }; resource vector_icon { diff --git a/src/apps/softwareupdater/SoftwareUpdaterApp.cpp b/src/apps/softwareupdater/SoftwareUpdaterApp.cpp index c21eb5d9e7..988f2d4029 100644 --- a/src/apps/softwareupdater/SoftwareUpdaterApp.cpp +++ b/src/apps/softwareupdater/SoftwareUpdaterApp.cpp @@ -1,41 +1,75 @@ /* - * Copyright 2016 Haiku, Inc. All rights reserved. + * Copyright 2016-2017 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT license * * Authors: * Alexander von Gluck IV + * Brian Hill */ #include "SoftwareUpdaterApp.h" -#include -#include - -#include "SoftwareUpdaterWindow.h" - - -#undef B_TRANSLATION_CONTEXT -#define B_TRANSLATION_CONTEXT "SoftwareUpdater" +#include "constants.h" SoftwareUpdaterApp::SoftwareUpdaterApp() : - BApplication("application/x-vnd.haiku-softwareupdater") + BApplication("application/x-vnd.haiku-softwareupdater"), + fWorker(NULL), + fFinalQuitFlag(false) { } SoftwareUpdaterApp::~SoftwareUpdaterApp() { + if (fWorker) { + fWorker->Lock(); + fWorker->Quit(); + } +} + + +bool +SoftwareUpdaterApp::QuitRequested() +{ + if (fFinalQuitFlag) + return true; + + // Simulate a cancel request from window- this gives the updater a chance + // to quit cleanly + if (fWindowMessenger.IsValid()) { + fWindowMessenger.SendMessage(kMsgCancel); + return false; + } + + return true; } void SoftwareUpdaterApp::ReadyToRun() { - SoftwareUpdaterWindow* window = new SoftwareUpdaterWindow(); - if (window) - window->Show(); + fWorker = new WorkingLooper(); +} + + +void +SoftwareUpdaterApp::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgRegister: + message->FindMessenger(kKeyMessenger, &fWindowMessenger); + break; + + case kMsgFinalQuit: + fFinalQuitFlag = true; + PostMessage(B_QUIT_REQUESTED); + break; + + default: + BApplication::MessageReceived(message); + } } diff --git a/src/apps/softwareupdater/SoftwareUpdaterApp.h b/src/apps/softwareupdater/SoftwareUpdaterApp.h index 6231e45df5..dd233a0aec 100644 --- a/src/apps/softwareupdater/SoftwareUpdaterApp.h +++ b/src/apps/softwareupdater/SoftwareUpdaterApp.h @@ -1,22 +1,32 @@ /* - * Copyright 2016 Haiku, Inc. All rights reserved. + * Copyright 2016-2017 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT license * * Authors: * Alexander von Gluck IV + * Brian Hill */ #ifndef _SOFTWARE_UPDATER_APP_H #define _SOFTWARE_UPDATER_APP_H + #include +#include "WorkingLooper.h" + class SoftwareUpdaterApp : public BApplication { public: - SoftwareUpdaterApp(); - ~SoftwareUpdaterApp(); + SoftwareUpdaterApp(); + ~SoftwareUpdaterApp(); + virtual bool QuitRequested(); + void ReadyToRun(); + void MessageReceived(BMessage* message); - void ReadyToRun(); +private: + WorkingLooper* fWorker; + BMessenger fWindowMessenger; + bool fFinalQuitFlag; }; diff --git a/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp b/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp index a1856cdccf..701ece7e0a 100644 --- a/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp +++ b/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp @@ -1,121 +1,138 @@ /* - * Copyright 2016 Haiku, Inc. All rights reserved. + * Copyright 2016-2017 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT license * * Authors: * Alexander von Gluck IV + * Brian Hill */ #include "SoftwareUpdaterWindow.h" -#include +#include #include #include -#include -#include -#include +#include +#include #include +#include #include -#include -#include -#include -#include -#include +#include +#include #include +#include "constants.h" #undef B_TRANSLATION_CONTEXT -#define B_TRANSLATION_CONTEXT "SoftwareUpdater" - - -const uint32 kMsgUpdate = 'iUPD'; -const uint32 kMsgExit = 'iEXT'; - - -using namespace BPackageKit; +#define B_TRANSLATION_CONTEXT "SoftwareUpdaterWindow" SoftwareUpdaterWindow::SoftwareUpdaterWindow() : - BWindow(BRect(0, 0, 0, 300), "Software Update", - B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE), -// fUpdateManager(NULL), + BWindow(BRect(0, 0, 300, 100), + B_TRANSLATE_SYSTEM_NAME("SoftwareUpdater"), B_TITLED_WINDOW, + B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE | B_NOT_CLOSABLE), fStripeView(NULL), fHeaderView(NULL), fDetailView(NULL), fUpdateButton(NULL), - fCancelButton(NULL) + fCancelButton(NULL), + fStatusBar(NULL), + fIcon(NULL), + fCurrentState(STATE_HEAD), + fWaitingSem(-1), + fWaitingForButton(false), + fUserCancelRequested(false) { - fUpdateManager = new UpdateManager( - BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_HOME); - fUpdateManager->SetDebugLevel(10); - fUpdateManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES - | BPackageManager::B_ADD_REMOTE_REPOSITORIES); - - BBitmap* icon = new BBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32); + fIcon = new BBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32); team_info teamInfo; get_team_info(B_CURRENT_TEAM, &teamInfo); app_info appInfo; be_roster->GetRunningAppInfo(teamInfo.team, &appInfo); - BNodeInfo::GetTrackerIcon(&appInfo.ref, icon, B_LARGE_ICON); + BNodeInfo::GetTrackerIcon(&appInfo.ref, fIcon, B_LARGE_ICON); - fStripeView = new StripeView(icon); + fStripeView = new StripeView(fIcon); - BButton* fUpdateButton = new BButton("Update now", - new BMessage(kMsgUpdate)); - fUpdateButton->Hide(); - - BButton* fCancelButton = new BButton("Cancel", - new BMessage(kMsgExit)); + fUpdateButton = new BButton(B_TRANSLATE("Update now"), + new BMessage(kMsgUpdateConfirmed)); + fUpdateButton->MakeDefault(true); +// fUpdateButton->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_UNSET, +// B_ALIGN_BOTTOM)); + fCancelButton = new BButton(B_TRANSLATE("Cancel"), + new BMessage(kMsgCancel)); +// fCancelButton->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_UNSET, +// B_ALIGN_BOTTOM)); fHeaderView = new BStringView("header", - "Checking for updates...", B_WILL_DRAW); - fDetailView = new BStringView("detail", "Contacting software repositories" - " to check for package updates.", B_WILL_DRAW); + B_TRANSLATE("Checking for updates"), B_WILL_DRAW); + fHeaderView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + fHeaderView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); + fDetailView = new BStringView("detail", B_TRANSLATE("Contacting software " + "repositories to check for package updates."), B_WILL_DRAW); + fDetailView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + fDetailView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); + fStatusBar = new BStatusBar("progress"); + fStatusBar->SetMaxValue(1.0); + + fListView = new PackageListView(); + fScrollView = new BScrollView("scrollview", fListView, B_WILL_DRAW, + false, true); BFont font; fHeaderView->GetFont(&font); font.SetFace(B_BOLD_FACE); font.SetSize(font.Size() * 1.5); - fHeaderView->SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE - | B_FONT_FLAGS); - + fHeaderView->SetFont(&font, + B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE | B_FONT_FLAGS); + BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0) .Add(fStripeView) - .AddGroup(B_VERTICAL, B_USE_SMALL_SPACING) - .SetInsets(0, B_USE_DEFAULT_SPACING, - B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING) - .Add(fHeaderView) - .Add(fDetailView) - .AddStrut(B_USE_DEFAULT_SPACING) - .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) + .AddGroup(B_VERTICAL, 0) + .SetInsets(0, B_USE_WINDOW_SPACING, + B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING) + .AddGroup(new BGroupView(B_VERTICAL, B_USE_ITEM_SPACING)) + .Add(fHeaderView) + .Add(fDetailView) + .Add(fStatusBar) + .Add(fScrollView) + .End() + .AddStrut(B_USE_SMALL_SPACING) + .AddGroup(new BGroupView(B_HORIZONTAL)) .AddGlue() .Add(fCancelButton) .Add(fUpdateButton) .End() .End() - .AddGlue() - //.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER)) - ; + .End(); + + fDetailsLayoutItem = layout_item_for(fDetailView); + fProgressLayoutItem = layout_item_for(fStatusBar); + fPackagesLayoutItem = layout_item_for(fScrollView); + fUpdateButtonLayoutItem = layout_item_for(fUpdateButton); + + _SetState(STATE_DISPLAY_STATUS); CenterOnScreen(); Show(); - - // Refresh our repos and update UI - _Refresh(); + SetFlags(Flags() ^ B_AUTO_UPDATE_SIZE_LIMITS); + + // Prevent resizing for now + fDefaultRect = Bounds(); + SetSizeLimits(fDefaultRect.Width(), fDefaultRect.Width(), + fDefaultRect.Height(), fDefaultRect.Height()); + + BMessage registerMessage(kMsgRegister); + registerMessage.AddMessenger(kKeyMessenger, BMessenger(this)); + be_app->PostMessage(®isterMessage); + + fCancelAlertResponse.SetMessage(new BMessage(kMsgCancelResponse)); + fCancelAlertResponse.SetTarget(this); } SoftwareUpdaterWindow::~SoftwareUpdaterWindow() { -} - - -bool -SoftwareUpdaterWindow::QuitRequested() -{ - be_app->PostMessage(B_QUIT_REQUESTED); - return true; + delete fIcon; } @@ -123,54 +140,628 @@ void SoftwareUpdaterWindow::MessageReceived(BMessage* message) { switch (message->what) { - case kMsgExit: - QuitRequested(); + + case kMsgTextUpdate: + { + if (fCurrentState == STATE_DISPLAY_PROGRESS) + _SetState(STATE_DISPLAY_STATUS); + else if (fCurrentState != STATE_DISPLAY_STATUS) + break; + + BString header; + BString detail; + Lock(); + status_t result = message->FindString(kKeyHeader, &header); + if (result == B_OK && header != fHeaderView->Text()) + fHeaderView->SetText(header.String()); + result = message->FindString(kKeyDetail, &detail); + if (result == B_OK) + fDetailView->SetText(detail.String()); + Unlock(); break; - case kMsgUpdate: + } + + case kMsgProgressUpdate: + { + if (fCurrentState == STATE_DISPLAY_STATUS) + _SetState(STATE_DISPLAY_PROGRESS); + else if (fCurrentState != STATE_DISPLAY_PROGRESS) + break; + + BString packageName; + status_t result = message->FindString(kKeyPackageName, &packageName); + if (result != B_OK) + break; + BString packageCount; + result = message->FindString(kKeyPackageCount, &packageCount); + if (result != B_OK) + break; + float percent; + result = message->FindFloat(kKeyPercentage, &percent); + if (result != B_OK) + break; + + BString header; + Lock(); + result = message->FindString(kKeyHeader, &header); + if (result == B_OK && header != fHeaderView->Text()) + fHeaderView->SetText(header.String()); + fStatusBar->SetTo(percent, packageName.String(), + packageCount.String()); + Unlock(); break; + } + + case kMsgCancel: + { + BAlert* alert = new BAlert("cancel request", B_TRANSLATE("Updates" + " have not been completed, are you sure you want to quit?"), + B_TRANSLATE("Quit"), B_TRANSLATE("Don't quit"), NULL, + B_WIDTH_AS_USUAL, B_WARNING_ALERT); + alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); + alert->Go(&fCancelAlertResponse); + break; + } + + case kMsgCancelResponse: + { + // Verify whether the cancel request was confirmed + int32 selection = -1; + message->FindInt32("which", &selection); + if (selection != 0) + break; + + Lock(); + fHeaderView->SetText(B_TRANSLATE("Cancelling updates")); + fDetailView->SetText( + B_TRANSLATE("Attempting to cancel the updates...")); + Unlock(); + fUserCancelRequested = true; + + if (fWaitingForButton) { + fButtonResult = message->what; + delete_sem(fWaitingSem); + fWaitingSem = -1; + } + break; + } + + case kMsgUpdateConfirmed: + { + if (fWaitingForButton) { + fButtonResult = message->what; + delete_sem(fWaitingSem); + fWaitingSem = -1; + } + break; + } + default: BWindow::MessageReceived(message); } } -void -SoftwareUpdaterWindow::_Error(const char* error) +bool +SoftwareUpdaterWindow::ConfirmUpdates(const char* text) { - fHeaderView->SetText("Error encountered!"); - fDetailView->SetText(error); - fUpdateButton->Hide(); - fCancelButton->Show(); + Lock(); + fHeaderView->SetText(B_TRANSLATE("Updates found")); + fDetailView->SetText(text); + fListView->SortItems(); + Unlock(); + + uint32 priorState = _GetState(); + _SetState(STATE_GET_CONFIRMATION); + + _WaitForButtonClick(); + _SetState(priorState); + return fButtonResult == kMsgUpdateConfirmed; } -void -SoftwareUpdaterWindow::_Refresh() -{ - BPackageRoster roster; - BStringList repoNames(20); - status_t result = roster.GetRepositoryNames(repoNames); - if (result != B_OK) { - _Error("Unable to obtain repository names."); +void +SoftwareUpdaterWindow::UpdatesApplying(const char* header, const char* detail) +{ + Lock(); + fHeaderView->SetText(header); + fDetailView->SetText(detail); + Unlock(); + _SetState(STATE_APPLY_UPDATES); +} + + +bool +SoftwareUpdaterWindow::UserCancelRequested() +{ + if (_GetState() > STATE_GET_CONFIRMATION) + return false; + + return fUserCancelRequested; +} + + +void +SoftwareUpdaterWindow::AddPackageInfo(uint32 install_type, + const char* package_name, const char* cur_ver, const char* new_ver, + const char* summary, const char* repository) +{ + Lock(); + fListView->AddPackage(install_type, package_name, cur_ver, new_ver, + summary, repository); + Unlock(); +} + + +BLayoutItem* +SoftwareUpdaterWindow::layout_item_for(BView* view) +{ + BLayout* layout = view->Parent()->GetLayout(); + int32 index = layout->IndexOfView(view); + return layout->ItemAt(index); +} + + +uint32 +SoftwareUpdaterWindow::_WaitForButtonClick() +{ + fButtonResult = 0; + fWaitingForButton = true; + fWaitingSem = create_sem(0, "WaitingSem"); + while (acquire_sem(fWaitingSem) == B_INTERRUPTED) { + } + fWaitingForButton = false; + return fButtonResult; +} + + +void +SoftwareUpdaterWindow::_SetState(uint32 state) +{ + if (state <= STATE_HEAD || state >= STATE_MAX) return; + + Lock(); + + // Initial settings + if (fCurrentState == STATE_HEAD) { + fProgressLayoutItem->SetVisible(false); + fPackagesLayoutItem->SetVisible(false); } + fCurrentState = state; + + // Update confirmation button + // Show only when asking for confirmation to update + if (fCurrentState == STATE_GET_CONFIRMATION) + fUpdateButtonLayoutItem->SetVisible(true); + else + fUpdateButtonLayoutItem->SetVisible(false); + + // View package info view + // Show at confirmation prompt, hide at final update + if (fCurrentState == STATE_GET_CONFIRMATION) { + fPackagesLayoutItem->SetVisible(true); + // Re-enable resizing + SetSizeLimits(fDefaultRect.Width(), 9999, + fDefaultRect.Height() + fListView->MinSize().Height() + 30, 9999); + ResizeTo(Bounds().Width(), 400); + } + + // Progress bar and string view + // Hide detail text while showing status bar + if (fCurrentState == STATE_DISPLAY_PROGRESS) { + fDetailsLayoutItem->SetVisible(false); + fProgressLayoutItem->SetVisible(true); + } + else { + fProgressLayoutItem->SetVisible(false); + fDetailsLayoutItem->SetVisible(true); + } + + // Cancel button + fCancelButton->SetEnabled(fCurrentState != STATE_APPLY_UPDATES); + + Unlock(); +} - for (int i = 0; i < repoNames.CountStrings(); i++) { - const BString& repoName = repoNames.StringAt(i); - BRepositoryConfig repoConfig; - result = roster.GetRepositoryConfig(repoName, &repoConfig); - if (result != B_OK) { - printf("Skipping '%s' repo due to unknown config\n", - repoName.String()); - continue; - } - /* - BRefreshRepositoryRequest request(context, repoConfig); - result = request.Process(); - if (result != B_OK) { - printf("Skipping %s repo due to unreachable repo"); - continue; - } - */ + +uint32 +SoftwareUpdaterWindow::_GetState() +{ + return fCurrentState; +} + + +SuperItem::SuperItem(const char* label) + : + BListItem(), + fLabel(label), + fPackageIcon(NULL) +{ +} + + +SuperItem::~SuperItem() +{ + delete fPackageIcon; +} + + +void +SuperItem::DrawItem(BView* owner, BRect item_rect, bool complete) +{ + float width; + owner->GetPreferredSize(&width, NULL); + BString label(fLabel); + owner->TruncateString(&label, B_TRUNCATE_END, width); + owner->SetFont(&fBoldFont); + owner->DrawString(label.String(), BPoint(item_rect.left, + item_rect.bottom - fFontHeight.descent - 1)); + owner->SetFont(&fRegularFont); +} + + +void +SuperItem::Update(BView *owner, const BFont *font) +{ + fRegularFont = *font; + fBoldFont = *font; + fBoldFont.SetFace(B_BOLD_FACE); + BListItem::Update(owner, &fBoldFont); + + // Calculate height for PackageItem + fRegularFont.GetHeight(&fFontHeight); + fPackageItemHeight = 2 * (fFontHeight.ascent + fFontHeight.descent + + fFontHeight.leading); + + // Calculate height for this item + fBoldFont.GetHeight(&fFontHeight); + SetHeight(fFontHeight.ascent + fFontHeight.descent + + fFontHeight.leading + 4); + + _GetPackageIcon(); +} + + +void +SuperItem::_GetPackageIcon() +{ + delete fPackageIcon; + fIconSize = int(fPackageItemHeight * .7); + + status_t result = B_ERROR; + BRect iconRect(0, 0, fIconSize - 1, fIconSize - 1); + fPackageIcon = new BBitmap(iconRect, 0, B_RGBA32); + BMimeType nodeType; + nodeType.SetTo("application/x-vnd.haiku-package"); + result = nodeType.GetIcon(fPackageIcon, icon_size(fIconSize)); + // Get super type icon + if (result != B_OK) { + BMimeType superType; + if (nodeType.GetSupertype(&superType) == B_OK) + result = superType.GetIcon(fPackageIcon, icon_size(fIconSize)); + } + if (result != B_OK) { + delete fPackageIcon; + fPackageIcon = NULL; + } +} + + +PackageItem::PackageItem(const char* name, const char* version, + const char* summary, const char* tooltip, SuperItem* super) + : + BListItem(), + fName(name), + fVersion(version), + fSummary(summary), + fTooltip(tooltip), + fSuperItem(super) +{ + fLabelOffset = be_control_look->DefaultLabelSpacing(); +// SetToolTip(fTooltip); +} + + +void +PackageItem::DrawItem(BView* owner, BRect item_rect, bool complete) +{ + float width; + owner->GetPreferredSize(&width, NULL); + float nameWidth = width / 2.0; + float offset_width = 0; + + BBitmap* icon = fSuperItem->GetIcon(); + if (icon != NULL && icon->IsValid()) { + int16 iconSize = fSuperItem->GetIconSize(); + float offsetMarginHeight = floor((Height() - iconSize) / 2); + + //owner->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); + owner->SetDrawingMode(B_OP_ALPHA); + owner->DrawBitmap(icon, BPoint(item_rect.left, + item_rect.top + offsetMarginHeight)); + owner->SetDrawingMode(B_OP_COPY); + offset_width += iconSize + fLabelOffset; + } + + // Package name + font_height fontHeight = fSuperItem->GetFontHeight(); + BString name(fName); + owner->TruncateString(&name, B_TRUNCATE_END, nameWidth); + BPoint cursor(item_rect.left + offset_width, + item_rect.bottom - fSmallTotalHeight - fontHeight.descent - 1); + owner->DrawString(name.String(), cursor); + cursor.x += owner->StringWidth(name.String()) + fLabelOffset; + + // Change font and color + owner->SetFont(&fSmallFont); + owner->SetHighColor(tint_color(ui_color(B_LIST_ITEM_TEXT_COLOR), 0.7)); + + // Version + BString version(fVersion); + owner->TruncateString(&version, B_TRUNCATE_END, width - cursor.x); + owner->DrawString(version.String(), cursor); + + // Summary + BString summary(fSummary); + cursor.x = item_rect.left + offset_width; + cursor.y = item_rect.bottom - fontHeight.descent; + owner->TruncateString(&summary, B_TRUNCATE_END, width - cursor.x); + owner->DrawString(summary.String(), cursor); + + owner->SetFont(&fRegularFont); +} + + +void +PackageItem::Update(BView *owner, const BFont *font) +{ + BListItem::Update(owner, font); + SetItemHeight(font); +} + + +void +PackageItem::SetItemHeight(const BFont* font) +{ + SetHeight(fSuperItem->GetPackageItemHeight()); + + fRegularFont = *font; + fSmallFont = *font; + fSmallFont.SetSize(font->Size() - 2); + fSmallFont.GetHeight(&fSmallFontHeight); + fSmallTotalHeight = fSmallFontHeight.ascent + fSmallFontHeight.descent + + fSmallFontHeight.leading; +} + + +int +PackageItem::ICompare(PackageItem* item) +{ + // sort by package name + return fName.ICompare(item->fName); +} + + +int +SortPackageItems(const BListItem* item1, const BListItem* item2) +{ + PackageItem* first = (PackageItem*)item1; + PackageItem* second = (PackageItem*)item2; + return first->ICompare(second); +} + + +PackageListView::PackageListView() + : + BOutlineListView("Package list"), + fSuperUpdateItem(NULL), + fSuperInstallItem(NULL), + fSuperUninstallItem(NULL) +{ + SetExplicitMinSize(BSize(B_SIZE_UNSET, 40)); + SetExplicitPreferredSize(BSize(B_SIZE_UNSET, 400)); +} + + +void +PackageListView::FrameResized(float newWidth, float newHeight) +{ + BListView::FrameResized(newWidth, newHeight); + + float count = CountItems(); + for (int32 i = 0; i < count; i++) { + BListItem *item = ItemAt(i); + item->Update(this, be_plain_font); + } + Invalidate(); +} + + +void +PackageListView::AddPackage(uint32 install_type, const char* name, + const char* cur_ver, const char* new_ver, const char* summary, + const char* repository) +{ + SuperItem* super; + BString version; + BString tooltip; + switch (install_type) { + case PACKAGE_UPDATE: + { + if (fSuperUpdateItem == NULL) { + fSuperUpdateItem = new SuperItem( + B_TRANSLATE("Packages to be updated")); + AddItem(fSuperUpdateItem); + } + super = fSuperUpdateItem; + + version.SetTo(new_ver); + tooltip.SetTo(B_TRANSLATE("Repository:")); + tooltip.Append(" ").Append(repository) + .Append("\n").Append(B_TRANSLATE("Update version")) + .Append(" ").Append(cur_ver) + .Append(" ").Append(B_TRANSLATE("to")) + .Append(" ").Append(new_ver); + break; + } + + case PACKAGE_INSTALL: + { + if (fSuperInstallItem == NULL) { + fSuperInstallItem = new SuperItem( + B_TRANSLATE("New packages to be installed")); + AddItem(fSuperInstallItem); + } + super = fSuperInstallItem; + + version.SetTo(new_ver); + tooltip.SetTo(B_TRANSLATE("Repository:")); + tooltip.Append(" ").Append(repository) + .Append("\n").Append(B_TRANSLATE("Install version")) + .Append(" ").Append(new_ver); + break; + } + + case PACKAGE_UNINSTALL: + { + if (fSuperUninstallItem == NULL) { + fSuperUninstallItem = new SuperItem( + B_TRANSLATE("Packages to be uninstalled")); + AddItem(fSuperUninstallItem); + } + super = fSuperUninstallItem; + + version.SetTo(""); + tooltip.SetTo(B_TRANSLATE("Repository:")); + tooltip.Append(" ").Append(repository) + .Append("\n").Append(B_TRANSLATE("Uninstall version")) + .Append(" ").Append(new_ver); + break; + } + + default: + return; + + } + PackageItem* item = new PackageItem(name, version.String(), summary, + tooltip.String(), super); + AddUnder(item, super); +} + + +void +PackageListView::SortItems() +{ + if (fSuperUpdateItem != NULL) + SortItemsUnder(fSuperUpdateItem, true, SortPackageItems); +} + +/* +BSize +PackageListView::PreferredSize() +{ + return BSize(B_SIZE_UNSET, 200); +}*/ + +/* +void +PackageListView::GetPreferredSize(float* _width, float* _height) +{ + // TODO: Something more nice as default? I need to see how this looks + // when there are actually any packages... + if (_width != NULL) + *_width = 400.0; + + if (_height != NULL) + *_height = 200.0; +}*/ + +/* +BSize +PackageListView::MaxSize() +{ + return BLayoutUtils::ComposeSize(ExplicitMaxSize(), + BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); +} +*/ + + +FinalWindow::FinalWindow(BRect rect, BPoint location, const char* header, + const char* detail) + : + BWindow(rect, + B_TRANSLATE_SYSTEM_NAME("SoftwareUpdater"), B_TITLED_WINDOW, + B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE + | B_NOT_CLOSABLE | B_NOT_RESIZABLE), + fHeaderView(NULL), + fDetailView(NULL), + fCancelButton(NULL) +{ + fIcon = new BBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32); + team_info teamInfo; + get_team_info(B_CURRENT_TEAM, &teamInfo); + app_info appInfo; + be_roster->GetRunningAppInfo(teamInfo.team, &appInfo); + BNodeInfo::GetTrackerIcon(&appInfo.ref, fIcon, B_LARGE_ICON); + + SetSizeLimits(rect.Width(), B_SIZE_UNLIMITED, 0, B_SIZE_UNLIMITED); + fStripeView = new StripeView(fIcon); + fHeaderView = new BStringView("header", header, B_WILL_DRAW); + fHeaderView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + fHeaderView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); + fDetailView = new BStringView("detail", detail, B_WILL_DRAW); + fDetailView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + fDetailView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); + fCancelButton = new BButton(B_TRANSLATE("Quit"), + new BMessage(kMsgCancel)); + fCancelButton->MakeDefault(true); + + BFont font; + fHeaderView->GetFont(&font); + font.SetFace(B_BOLD_FACE); + font.SetSize(font.Size() * 1.5); + fHeaderView->SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE + | B_FONT_FLAGS); + + BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0) + .Add(fStripeView) + .AddGroup(B_VERTICAL, 0) + .SetInsets(0, B_USE_WINDOW_SPACING, + B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING) + .AddGroup(B_VERTICAL, B_USE_ITEM_SPACING) + .Add(fHeaderView) + .Add(fDetailView) + .AddGroup(B_HORIZONTAL) + .AddGlue() + .Add(fCancelButton) + .End() + .End() + .End() + .End(); + + MoveTo(location); + Show(); +} + + +FinalWindow::~FinalWindow() +{ + delete fIcon; +} + + +void +FinalWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + + case kMsgCancel: + PostMessage(B_QUIT_REQUESTED); + be_app->PostMessage(kMsgFinalQuit); + break; + + default: + BWindow::MessageReceived(message); } } diff --git a/src/apps/softwareupdater/SoftwareUpdaterWindow.h b/src/apps/softwareupdater/SoftwareUpdaterWindow.h index a0776b1de2..4c43f240ed 100644 --- a/src/apps/softwareupdater/SoftwareUpdaterWindow.h +++ b/src/apps/softwareupdater/SoftwareUpdaterWindow.h @@ -1,44 +1,181 @@ /* - * Copyright 2016 Haiku, Inc. All rights reserved. + * Copyright 2016-2017 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT license * * Authors: * Alexander von Gluck IV + * Brian Hill */ #ifndef _SOFTWARE_UPDATER_WINDOW_H #define _SOFTWARE_UPDATER_WINDOW_H #include -#include +#include +#include +#include +#include +#include #include #include #include "StripeView.h" -#include "UpdateManager.h" + +using namespace BPrivate; + +enum { + PACKAGE_UPDATE, + PACKAGE_INSTALL, + PACKAGE_UNINSTALL +}; + + +class SuperItem : public BListItem { +public: + SuperItem(const char* label); + ~SuperItem(); + virtual void DrawItem(BView*, BRect, bool); + virtual void Update(BView *owner, const BFont *font); + font_height GetFontHeight() { return fFontHeight; }; + float GetPackageItemHeight() + { return fPackageItemHeight; }; + BBitmap* GetIcon() { return fPackageIcon; }; + int16 GetIconSize() { return fIconSize; }; + +private: + void _GetPackageIcon(); + + BString fLabel; + BFont fRegularFont; + BFont fBoldFont; + font_height fFontHeight; + float fPackageItemHeight; + BBitmap* fPackageIcon; + int16 fIconSize; +}; + + +class PackageItem : public BListItem { +public: + PackageItem(const char* name, + const char* version, + const char* summary, + const char* tooltip, + SuperItem* super); + virtual void DrawItem(BView*, BRect, bool); + virtual void Update(BView *owner, const BFont *font); + void SetItemHeight(const BFont* font); + int ICompare(PackageItem* item); + +private: + BString fName; + BString fVersion; + BString fSummary; + BString fTooltip; + BFont fRegularFont; + BFont fSmallFont; + font_height fSmallFontHeight; + float fSmallTotalHeight; + float fLabelOffset; + SuperItem* fSuperItem; +}; + + +class PackageListView : public BOutlineListView { +public: + PackageListView(); + virtual void FrameResized(float newWidth, float newHeight); +// virtual BSize PreferredSize(); +// virtual void GetPreferredSize(float* _width, float* _height); +// virtual BSize MaxSize(); + void AddPackage(uint32 install_type, + const char* name, + const char* cur_ver, + const char* new_ver, + const char* summary, + const char* repository); + void SortItems(); + +private: + SuperItem* fSuperUpdateItem; + SuperItem* fSuperInstallItem; + SuperItem* fSuperUninstallItem; +}; class SoftwareUpdaterWindow : public BWindow { public: SoftwareUpdaterWindow(); ~SoftwareUpdaterWindow(); - bool QuitRequested(); void MessageReceived(BMessage* message); + bool ConfirmUpdates(const char* text); + void UpdatesApplying(const char* header, + const char* detail); + bool UserCancelRequested(); + void AddPackageInfo(uint32 install_type, + const char* package_name, + const char* cur_ver, + const char* new_ver, + const char* summary, + const char* repository); + const BBitmap* GetIcon() { return fIcon; }; + BRect GetDefaultRect() { return fDefaultRect; }; + BPoint GetLocation() { return Frame().LeftTop(); }; + BLayoutItem* layout_item_for(BView* view); private: - void _Error(const char* error); - void _Refresh(); - - UpdateManager* fUpdateManager; + uint32 _WaitForButtonClick(); + void _SetState(uint32 state); + uint32 _GetState(); + + BRect fDefaultRect; StripeView* fStripeView; - app_info* fAppInfo; - BStringView* fHeaderView; BStringView* fDetailView; BButton* fUpdateButton; BButton* fCancelButton; + BStatusBar* fStatusBar; +#if USE_PANE_SWITCH + PaneSwitch* fPackagesSwitch; + BLayoutItem* fPkgSwitchLayoutItem; +#endif + PackageListView* fListView; + BScrollView* fScrollView; + BLayoutItem* fDetailsLayoutItem; + BLayoutItem* fPackagesLayoutItem; + BLayoutItem* fProgressLayoutItem; + BLayoutItem* fUpdateButtonLayoutItem; + BBitmap* fIcon; + + uint32 fCurrentState; + sem_id fWaitingSem; + bool fWaitingForButton; + uint32 fButtonResult; + bool fUserCancelRequested; + BInvoker fCancelAlertResponse; + }; -#endif +int SortPackageItems(const BListItem* item1, const BListItem* item2); + + +class FinalWindow : public BWindow { +public: + FinalWindow(BRect rect, BPoint location, + const char* header, const char* detail); + ~FinalWindow(); + + void MessageReceived(BMessage* message); +private: + + StripeView* fStripeView; + BStringView* fHeaderView; + BStringView* fDetailView; + BButton* fCancelButton; + BBitmap* fIcon; +}; + + +#endif // _SOFTWARE_UPDATER_WINDOW_H diff --git a/src/apps/softwareupdater/StripeView.cpp b/src/apps/softwareupdater/StripeView.cpp index de716283ff..c28a4e82c2 100644 --- a/src/apps/softwareupdater/StripeView.cpp +++ b/src/apps/softwareupdater/StripeView.cpp @@ -26,7 +26,7 @@ StripeView::StripeView(BBitmap* icon) if (icon != NULL) width += icon->Bounds().Width() + 32.0f; - SetExplicitMinSize(BSize(width, B_SIZE_UNSET)); + SetExplicitSize(BSize(width, B_SIZE_UNSET)); SetExplicitPreferredSize(BSize(width, B_SIZE_UNLIMITED)); } @@ -68,6 +68,6 @@ StripeView::SetIcon(BBitmap* icon) if (icon != NULL) width += icon->Bounds().Width() + 32.0f; - SetExplicitMinSize(BSize(width, B_SIZE_UNSET)); + SetExplicitSize(BSize(width, B_SIZE_UNSET)); SetExplicitPreferredSize(BSize(width, B_SIZE_UNLIMITED)); } diff --git a/src/apps/softwareupdater/UpdateAction.cpp b/src/apps/softwareupdater/UpdateAction.cpp new file mode 100644 index 0000000000..54f7165a4b --- /dev/null +++ b/src/apps/softwareupdater/UpdateAction.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2017, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Brian Hill + */ + + +#include "UpdateAction.h" + +#include +#include +#include +#include + +#include "constants.h" + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "UpdateAction" + + +using namespace BPackageKit; +//using namespace BPackageKit::BPrivate; +using namespace BPackageKit::BManager::BPrivate; + + +UpdateAction::UpdateAction() +{ + fUpdateManager = new(std::nothrow) + UpdateManager(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM); +} + + +UpdateAction::~UpdateAction() +{ + delete fUpdateManager; +} + + +status_t +UpdateAction::Perform() +{ + fUpdateManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES + | BPackageManager::B_ADD_REMOTE_REPOSITORIES + | BPackageManager::B_REFRESH_REPOSITORIES); + + try { + // These values indicate that all updates should be installed + int packageCount = 0; + const char* const packages = ""; + + // perform the update +// fUpdateManager->SetDebugLevel(1); + fUpdateManager->Update(&packages, packageCount); + } catch (BFatalErrorException ex) { + BString errorString; + errorString.SetToFormat( + "Fatal error occurred while updating packages: " + "%s (%s)\n", ex.Message().String(), + ex.Details().String()); + BAlert* alert(new(std::nothrow) BAlert(B_TRANSLATE("Fatal error"), + errorString, B_TRANSLATE("Close"), NULL, NULL, + B_WIDTH_AS_USUAL, B_STOP_ALERT)); + if (alert != NULL) + alert->Go(); + fUpdateManager->FinalUpdate(B_TRANSLATE("Updates did not complete"), + ex.Message()); + return ex.Error(); + } catch (BAbortedByUserException ex) { + fprintf(stderr, "Updates aborted by user: %s\n", + ex.Message().String()); + // No need for a final message since user initiated cancel request + // Note: activate FinalUpdate() call for testing final message window + //fUpdateManager->FinalUpdate(B_TRANSLATE("Updates cancelled"), + // B_TRANSLATE("No packages have been updated.")); + // Note: comment out when testing final message window + be_app->PostMessage(kMsgFinalQuit); + return B_OK; + } catch (BNothingToDoException ex) { + fprintf(stderr, "Nothing to do while updating packages : %s\n", + ex.Message().String()); + fUpdateManager->FinalUpdate(B_TRANSLATE("No updates available"), + B_TRANSLATE("There were no updates found.")); + return B_OK; + } catch (BException ex) { + fprintf(stderr, "Exception occurred while updating packages : %s\n", + ex.Message().String()); + fUpdateManager->FinalUpdate(B_TRANSLATE("Updates did not complete"), + ex.Message()); + return B_ERROR; + } + + return B_OK; +} diff --git a/src/apps/softwareupdater/UpdateAction.h b/src/apps/softwareupdater/UpdateAction.h new file mode 100644 index 0000000000..97fce70afc --- /dev/null +++ b/src/apps/softwareupdater/UpdateAction.h @@ -0,0 +1,27 @@ +/* + * Copyright 2017, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Brian Hill + */ +#ifndef UPDATE_ACTION_H +#define UPDATE_ACTION_H + + +#include "UpdateManager.h" + + +class UpdateAction { +public: + UpdateAction(); + ~UpdateAction(); + status_t Perform(); + +private: + + UpdateManager* fUpdateManager; +}; + + +#endif // UPDATE_ACTION_H diff --git a/src/apps/softwareupdater/UpdateManager.cpp b/src/apps/softwareupdater/UpdateManager.cpp index 8242a9d2e3..afbb65aadf 100644 --- a/src/apps/softwareupdater/UpdateManager.cpp +++ b/src/apps/softwareupdater/UpdateManager.cpp @@ -1,11 +1,12 @@ /* - * Copyright 2013-2015, Haiku, Inc. All Rights Reserved. + * Copyright 2013-2017, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler * Rene Gollent * Ingo Weinhold + * Brian Hill */ @@ -14,28 +15,53 @@ #include #include +#include +#include +#include + #include #include #include +#include #include #include #include +#include "constants.h" +#include "AutoDeleter.h" +#include "ProblemWindow.h" +#include "ResultWindow.h" using namespace BPackageKit; +using namespace BPackageKit::BManager::BPrivate; + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "UpdateManager" UpdateManager::UpdateManager(BPackageInstallationLocation location) : BPackageManager(location, &fClientInstallationInterface, this), BPackageManager::UserInteractionHandler(), - fClientInstallationInterface() + fClientInstallationInterface(), + fStatusWindow(NULL), + fFinalWindow(NULL), + fCurrentStep(ACTION_STEP_INIT), + fChangesConfirmed(false) { + fStatusWindow = new SoftwareUpdaterWindow(); + _SetCurrentStep(ACTION_STEP_START); } UpdateManager::~UpdateManager() { + if (fStatusWindow != NULL) + fStatusWindow->PostMessage(B_QUIT_REQUESTED); + if (fFinalWindow != NULL) + fFinalWindow->PostMessage(B_QUIT_REQUESTED); + if (fProblemWindow != NULL) + fProblemWindow->PostMessage(B_QUIT_REQUESTED); } @@ -54,13 +80,29 @@ void UpdateManager::JobAborted(BSupportKit::BJob* job) { //DIE(job->Result(), "aborted"); + printf("Job aborted\n"); +} + + +void +UpdateManager::FinalUpdate(const char* header, const char* text) +{ + _FinalUpdate(header, text); } void UpdateManager::HandleProblems() { - int32 problemCount = fSolver->CountProblems(); + if (fProblemWindow == NULL) + fProblemWindow = new ProblemWindow; + + ProblemWindow::SolverPackageSet installPackages; + ProblemWindow::SolverPackageSet uninstallPackages; + if (!fProblemWindow->Go(fSolver,installPackages, uninstallPackages)) + throw BAbortedByUserException(); + +/* int32 problemCount = fSolver->CountProblems(); for (int32 i = 0; i < problemCount; i++) { // print problem and possible solutions BSolverProblem* problem = fSolver->ProblemAt(i); @@ -96,7 +138,7 @@ UpdateManager::HandleProblems() if (strcmp(buffer, "s\n") == 0) break; - /* + char* end; long selected = strtol(buffer, &end, 0); if (end == buffer || *end != '\n' || selected < 1 @@ -109,10 +151,10 @@ UpdateManager::HandleProblems() problem->SolutionAt(selected - 1)); if (error != B_OK) DIE(error, "failed to set solution"); - */ + break; } - } + }*/ } @@ -122,20 +164,55 @@ UpdateManager::ConfirmChanges(bool fromMostSpecific) printf("The following changes will be made:\n"); int32 count = fInstalledRepositories.CountItems(); + int32 upgradeCount = 0; + int32 installCount = 0; + int32 uninstallCount = 0; + if (fromMostSpecific) { for (int32 i = count - 1; i >= 0; i--) - _PrintResult(*fInstalledRepositories.ItemAt(i)); + _PrintResult(*fInstalledRepositories.ItemAt(i), upgradeCount, + installCount, uninstallCount); } else { for (int32 i = 0; i < count; i++) - _PrintResult(*fInstalledRepositories.ItemAt(i)); + _PrintResult(*fInstalledRepositories.ItemAt(i), upgradeCount, + installCount, uninstallCount); } - - /* - if (!fDecisionProvider.YesNoDecisionNeeded(BString(), "Continue?", "yes", - "no", "yes")) { - //exit(1); + + printf("Upgrade count=%" B_PRId32 ", Install count=%" B_PRId32 + ", Uninstall count=%" B_PRId32 "\n", + upgradeCount, installCount, uninstallCount); + BString text; + if (upgradeCount == 1) + text.SetTo(B_TRANSLATE_COMMENT("There is 1 update " + "%dependancies%available.", + "Do not translate %dependancies%")); + else + text.SetTo(B_TRANSLATE_COMMENT("There are %count% updates " + "%dependancies%available.", + "Do not translate %count% or %dependancies%")); + BString countString; + countString << upgradeCount; + text.ReplaceFirst("%count%", countString); + BString dependancies(""); + if (installCount) { + dependancies.SetTo("("); + dependancies.Append(B_TRANSLATE("with")).Append(" "); + if (installCount == 1) + dependancies.Append(B_TRANSLATE("1 new dependancy")); + else { + dependancies << installCount; + dependancies.Append(" ").Append(B_TRANSLATE("new dependancies")); + } + dependancies.Append(") "); } - */ + text.ReplaceFirst("%dependancies%", dependancies); + fChangesConfirmed = fStatusWindow->ConfirmUpdates(text.String()); + if (!fChangesConfirmed) + throw BAbortedByUserException(); + + _SetCurrentStep(ACTION_STEP_DOWNLOAD); + fPackageDownloadsTotal = upgradeCount + installCount; + fPackageDownloadsCount = 1; } @@ -157,6 +234,18 @@ UpdateManager::Warn(status_t error, const char* format, ...) void UpdateManager::ProgressPackageDownloadStarted(const char* packageName) { + if (fCurrentStep == ACTION_STEP_DOWNLOAD) { + BString header(B_TRANSLATE("Downloading packages")); + BString packageCount; + packageCount.SetToFormat( + B_TRANSLATE_COMMENT("%i of %i", "Do not translate %i"), + fPackageDownloadsCount, + fPackageDownloadsTotal); + _UpdateDownloadProgress(header.String(), packageName, packageCount, + 0.0); + fNewDownloadStarted = false; + } + printf("Downloading %s...\n", packageName); } @@ -165,10 +254,23 @@ void UpdateManager::ProgressPackageDownloadActive(const char* packageName, float completionPercentage, off_t bytes, off_t totalBytes) { - return; - /* - if (!fInteractive) - return; + if (fCurrentStep == ACTION_STEP_DOWNLOAD) { + // Fix a bug where a 100% completion percentage gets sent at the start + // of a package download + if (!fNewDownloadStarted) { + if (completionPercentage > 0 && completionPercentage < 1) + fNewDownloadStarted = true; + else + completionPercentage = 0.0; + } + BString packageCount; + packageCount.SetToFormat( + B_TRANSLATE_COMMENT("%i of %i", "Do not translate %i"), + fPackageDownloadsCount, + fPackageDownloadsTotal); + _UpdateDownloadProgress(NULL, packageName, packageCount, + completionPercentage); + } static const char* progressChars[] = { "\xE2\x96\x8F", @@ -213,13 +315,16 @@ UpdateManager::ProgressPackageDownloadActive(const char* packageName, printf(" %3d%%", (int)(completionPercentage * 100)); fflush(stdout); - */ + } void UpdateManager::ProgressPackageDownloadComplete(const char* packageName) { + if (fCurrentStep == ACTION_STEP_DOWNLOAD) + fPackageDownloadsCount++; + // Overwrite the progress bar with whitespace printf("\r"); struct winsize w; @@ -235,6 +340,10 @@ UpdateManager::ProgressPackageDownloadComplete(const char* packageName) void UpdateManager::ProgressPackageChecksumStarted(const char* title) { + // Repository checksums + if (fCurrentStep == ACTION_STEP_START) + _UpdateStatusWindow(NULL, title); + printf("%s...", title); } @@ -249,6 +358,11 @@ UpdateManager::ProgressPackageChecksumComplete(const char* title) void UpdateManager::ProgressStartApplyingChanges(InstalledRepository& repository) { + _SetCurrentStep(ACTION_STEP_APPLY); + BString header(B_TRANSLATE("Applying changes")); + BString detail(B_TRANSLATE("Packages are being updated")); + fStatusWindow->UpdatesApplying(header.String(), detail.String()); + printf("[%s] Applying changes ...\n", repository.Name().String()); } @@ -257,6 +371,12 @@ void UpdateManager::ProgressTransactionCommitted(InstalledRepository& repository, const BCommitTransactionResult& result) { + _SetCurrentStep(ACTION_STEP_COMPLETE); + BString header(B_TRANSLATE("Updates completed")); + BString detail(B_TRANSLATE("A reboot may be necessary to complete some " + "updates.")); + _FinalUpdate(header.String(), detail.String()); + const char* repositoryName = repository.Name().String(); int32 issueCount = result.CountIssues(); @@ -285,7 +405,8 @@ UpdateManager::ProgressApplyingChangesDone(InstalledRepository& repository) void -UpdateManager::_PrintResult(InstalledRepository& installationRepository) +UpdateManager::_PrintResult(InstalledRepository& installationRepository, + int32& upgradeCount, int32& installCount, int32& uninstallCount) { if (!installationRepository.HasChanges()) return; @@ -319,7 +440,8 @@ UpdateManager::_PrintResult(InstalledRepository& installationRepository) if (dynamic_cast(package->Repository()) != NULL) repository = "local file"; else - repository.SetToFormat("repository %s", package->Repository()->Name().String()); + repository.SetToFormat("repository %s", + package->Repository()->Name().String()); int position = upgradedPackages.IndexOf(package->Info().Name()); if (position >= 0) { @@ -328,18 +450,106 @@ UpdateManager::_PrintResult(InstalledRepository& installationRepository) upgradedPackageVersions.StringAt(position).String(), package->Info().Version().ToString().String(), repository.String()); + fStatusWindow->AddPackageInfo(PACKAGE_UPDATE, + package->Info().Name().String(), + upgradedPackageVersions.StringAt(position).String(), + package->Info().Version().ToString().String(), + package->Info().Summary().String(), + package->Repository()->Name().String()); + upgradeCount++; } else { printf(" install package %s-%s from %s\n", package->Info().Name().String(), package->Info().Version().ToString().String(), repository.String()); + fStatusWindow->AddPackageInfo(PACKAGE_INSTALL, + package->Info().Name().String(), + NULL, + package->Info().Version().ToString().String(), + package->Info().Summary().String(), + package->Repository()->Name().String()); + installCount++; } } + BStringList uninstallList; for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); i++) { if (upgradedPackages.HasString(package->Info().Name())) continue; printf(" uninstall package %s\n", package->VersionedName().String()); + fStatusWindow->AddPackageInfo(PACKAGE_UNINSTALL, + package->Info().Name().String(), + package->Info().Version().ToString(), + NULL, + package->Info().Summary().String(), + package->Repository()->Name().String()); + uninstallCount++; } } + + +void +UpdateManager::_UpdateStatusWindow(const char* header, const char* detail) +{ + if (header == NULL && detail == NULL) + return; + + if (fStatusWindow->UserCancelRequested()) + throw BAbortedByUserException(); + + BMessage message(kMsgTextUpdate); + if (header != NULL) + message.AddString(kKeyHeader, header); + if (detail != NULL) + message.AddString(kKeyDetail, detail); + fStatusWindow->PostMessage(&message); +} + + +void +UpdateManager::_UpdateDownloadProgress(const char* header, + const char* packageName, const char* packageCount, + float completionPercentage) +{ + if (packageName == NULL || packageCount == NULL) + return; + + if (fStatusWindow->UserCancelRequested()) + throw BAbortedByUserException(); + + BMessage message(kMsgProgressUpdate); + if (header != NULL) + message.AddString(kKeyHeader, header); + message.AddString(kKeyPackageName, packageName); + message.AddString(kKeyPackageCount, packageCount); + message.AddFloat(kKeyPercentage, completionPercentage); + fStatusWindow->PostMessage(&message); +} + + +void +UpdateManager::_FinalUpdate(const char* header, const char* text) +{ + BNotification notification(B_INFORMATION_NOTIFICATION); + notification.SetGroup("SoftwareUpdater"); + notification.SetTitle(header); + notification.SetContent(text); + const BBitmap* icon = fStatusWindow->GetIcon(); + if (icon != NULL) + notification.SetIcon(icon); + notification.Send(); + + BPoint location(fStatusWindow->GetLocation()); + BRect rect(fStatusWindow->GetDefaultRect()); + fStatusWindow->PostMessage(B_QUIT_REQUESTED); + fStatusWindow = NULL; + + fFinalWindow = new FinalWindow(rect, location, header, text); +} + +void +UpdateManager::_SetCurrentStep(int32 step) +{ + fCurrentStep = step; +} diff --git a/src/apps/softwareupdater/UpdateManager.h b/src/apps/softwareupdater/UpdateManager.h index efef0bb48c..cf7e93a6a2 100644 --- a/src/apps/softwareupdater/UpdateManager.h +++ b/src/apps/softwareupdater/UpdateManager.h @@ -1,10 +1,11 @@ /* - * Copyright 2013-2015, Haiku, Inc. All Rights Reserved. + * Copyright 2013-2017, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Ingo Weinhold * Rene Gollent + * Brian Hill */ #ifndef UPDATE_MANAGER_H #define UPDATE_MANAGER_H @@ -13,6 +14,9 @@ #include #include +#include "SoftwareUpdaterWindow.h" + +class ProblemWindow; //using namespace BPackageKit; using BPackageKit::BPrivate::BDaemonClient; @@ -28,7 +32,9 @@ public: virtual void JobFailed(BSupportKit::BJob* job); virtual void JobAborted(BSupportKit::BJob* job); - + + void FinalUpdate(const char* header, + const char* text); private: // UserInteractionHandler virtual void HandleProblems(); @@ -60,11 +66,32 @@ private: private: void _PrintResult(InstalledRepository& - installationRepository); + installationRepository, + int32& upgradeCount, + int32& installCount, + int32& uninstallCount); + void _UpdateStatusWindow(const char* header, + const char* detail); + void _UpdateDownloadProgress(const char* header, + const char* packageName, + const char* packageCount, + float completionPercentage); + void _FinalUpdate(const char* header, + const char* text); + void _SetCurrentStep(int32 step); private: BPackageManager::ClientInstallationInterface fClientInstallationInterface; + + SoftwareUpdaterWindow* fStatusWindow; + FinalWindow* fFinalWindow; + ProblemWindow* fProblemWindow; + uint32 fCurrentStep; + bool fChangesConfirmed; + bool fNewDownloadStarted; + int32 fPackageDownloadsTotal; + int32 fPackageDownloadsCount; }; diff --git a/src/apps/softwareupdater/WorkingLooper.cpp b/src/apps/softwareupdater/WorkingLooper.cpp new file mode 100644 index 0000000000..0bb38ade20 --- /dev/null +++ b/src/apps/softwareupdater/WorkingLooper.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2017, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Brian Hill + */ + + +#include "WorkingLooper.h" + + +WorkingLooper::WorkingLooper() + : + BLooper("WorkingLooper") +{ + Run(); + PostMessage(kMsgStart); +} + + +void +WorkingLooper::MessageReceived(BMessage* message) +{ + switch (message->what) { + case kMsgStart: + { + fAction.Perform(); + break; + } + + default: + BLooper::MessageReceived(message); + } +} diff --git a/src/apps/softwareupdater/WorkingLooper.h b/src/apps/softwareupdater/WorkingLooper.h new file mode 100644 index 0000000000..61c366ca37 --- /dev/null +++ b/src/apps/softwareupdater/WorkingLooper.h @@ -0,0 +1,31 @@ +/* + * Copyright 2017, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Brian Hill + */ +#ifndef _WORKING_LOOPER_H +#define _WORKING_LOOPER_H + + +#include +#include + +#include "UpdateAction.h" + + +const uint32 kMsgStart = 'iSTA'; + + +class WorkingLooper : public BLooper { +public: + WorkingLooper(); + void MessageReceived(BMessage* message); + +private: + UpdateAction fAction; +}; + + +#endif // _WORKING_LOOPER_H diff --git a/src/apps/softwareupdater/constants.h b/src/apps/softwareupdater/constants.h new file mode 100644 index 0000000000..648b5c494a --- /dev/null +++ b/src/apps/softwareupdater/constants.h @@ -0,0 +1,51 @@ +/* + * Copyright 2017, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Brian Hill + */ +#ifndef CONSTANTS_H +#define CONSTANTS_H + +enum { + ACTION_STEP_INIT = 0, + ACTION_STEP_START, + ACTION_STEP_DOWNLOAD, + ACTION_STEP_APPLY, + ACTION_STEP_COMPLETE, + ACTION_STEP_MAX +}; + +enum { + STATE_HEAD = 0, + STATE_DISPLAY_STATUS, + STATE_DISPLAY_PROGRESS, + STATE_GET_CONFIRMATION, + STATE_APPLY_UPDATES, + STATE_MAX +}; + +// Message what values +static const uint32 kMsgTextUpdate = 'iUPD'; +static const uint32 kMsgProgressUpdate = 'iPRO'; +static const uint32 kMsgCancel = 'iCAN'; +static const uint32 kMsgCancelResponse = 'iCRE'; +static const uint32 kMsgUpdateConfirmed = 'iCON'; +static const uint32 kMsgClose = 'iCLO'; +static const uint32 kMsgShow = 'iSHO'; +static const uint32 kMsgShowInfo = 'iSHI'; +static const uint32 kMsgRegister = 'iREG'; +static const uint32 kMsgFinalQuit = 'iFIN'; + +// Message data keys +#define kKeyHeader "key_header" +#define kKeyDetail "key_detail" +#define kKeyPackageName "key_packagename" +#define kKeyPackageCount "key_packagecount" +#define kKeyPercentage "key_percentage" +#define kKeyFrame "key_frame" +#define kKeyMessenger "key_messenger" + + +#endif // CONSTANTS_H