diff --git a/src/apps/haikudepot/HaikuDepotConstants.h b/src/apps/haikudepot/HaikuDepotConstants.h index 9671451c7e..97014a1d20 100644 --- a/src/apps/haikudepot/HaikuDepotConstants.h +++ b/src/apps/haikudepot/HaikuDepotConstants.h @@ -27,6 +27,9 @@ enum { #define RATING_MIN 0.0f +#define RGB_COLOR_WHITE (rgb_color) { 255, 255, 255, 255 } + + #define HD_ERROR_BASE (B_ERRORS_END + 1) #define HD_NETWORK_INACCESSIBLE (HD_ERROR_BASE + 1) #define HD_CLIENT_TOO_OLD (HD_ERROR_BASE + 2) diff --git a/src/apps/haikudepot/Jamfile b/src/apps/haikudepot/Jamfile index dbc2ef984f..f6912f45a9 100644 --- a/src/apps/haikudepot/Jamfile +++ b/src/apps/haikudepot/Jamfile @@ -143,6 +143,8 @@ local applicationSources = ScrollableGroupView.cpp SharedBitmap.cpp UserLoginWindow.cpp + UserUsageConditions.cpp + UserUsageConditionsWindow.cpp WorkStatusView.cpp # network + server / local processes diff --git a/src/apps/haikudepot/model/LocalIconStore.h b/src/apps/haikudepot/model/LocalIconStore.h index 5745d21137..008b1fcf95 100644 --- a/src/apps/haikudepot/model/LocalIconStore.h +++ b/src/apps/haikudepot/model/LocalIconStore.h @@ -2,8 +2,6 @@ * Copyright 2017, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ - - #ifndef LOCAL_ICON_STORE_H #define LOCAL_ICON_STORE_H diff --git a/src/apps/haikudepot/model/UserUsageConditions.cpp b/src/apps/haikudepot/model/UserUsageConditions.cpp new file mode 100644 index 0000000000..24fc6fdf54 --- /dev/null +++ b/src/apps/haikudepot/model/UserUsageConditions.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2019, Andrew Lindesay . + * + * All rights reserved. Distributed under the terms of the MIT License. + */ +#include "UserUsageConditions.h" + + +// These are keys that are used to store this object's data into a BMessage +// instance. + +#define KEY_COPY_MARKDOWN "copyMarkdown" +#define KEY_CODE "code" +#define KEY_MINIMUM_AGE "minimumAge" + + +UserUsageConditions::UserUsageConditions(BMessage* from) + : + fCode(""), + fCopyMarkdown(""), + fMinimumAge(0) +{ + int16 minimumAge; + + if (from->FindInt16(KEY_MINIMUM_AGE, &minimumAge) != B_OK) + printf("expected key [%s] in the message data\n", KEY_MINIMUM_AGE); + fMinimumAge = (uint8) minimumAge; + + if (from->FindString(KEY_CODE, &fCode) != B_OK) + printf("expected key [%s] in the message data\n", KEY_CODE); + + if (from->FindString(KEY_COPY_MARKDOWN, &fCopyMarkdown) != B_OK) + printf("expected key [%s] in the message data\n", KEY_COPY_MARKDOWN); +} + + +UserUsageConditions::UserUsageConditions() + : + fCode(""), + fCopyMarkdown(""), + fMinimumAge(0) +{ +} + + +UserUsageConditions::~UserUsageConditions() +{ +} + + +const BString& +UserUsageConditions::Code() const +{ + return fCode; +} + + +const uint8 +UserUsageConditions::MinimumAge() const +{ + return fMinimumAge; +} + + +const BString& +UserUsageConditions::CopyMarkdown() const +{ + return fCopyMarkdown; +} + + +void +UserUsageConditions::SetCode(const BString& code) +{ + fCode = code; +} + + +void +UserUsageConditions::SetMinimumAge(uint8 age) +{ + fMinimumAge = age; +} + + +void +UserUsageConditions::SetCopyMarkdown(const BString& copyMarkdown) +{ + fCopyMarkdown = copyMarkdown; +} + + +status_t +UserUsageConditions::Archive(BMessage* into, bool deep) const +{ + into->AddInt16(KEY_MINIMUM_AGE, (int16) fMinimumAge); + into->AddString(KEY_CODE, fCode); + into->AddString(KEY_COPY_MARKDOWN, fCopyMarkdown); + return B_OK; +} \ No newline at end of file diff --git a/src/apps/haikudepot/model/UserUsageConditions.h b/src/apps/haikudepot/model/UserUsageConditions.h new file mode 100644 index 0000000000..5dffca35e4 --- /dev/null +++ b/src/apps/haikudepot/model/UserUsageConditions.h @@ -0,0 +1,37 @@ +/* + * Copyright 2019, Andrew Lindesay . + * + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef USER_USAGE_CONDITIONS_H +#define USER_USAGE_CONDITIONS_H + +#include + +#include +#include + + +class UserUsageConditions : public BArchivable { +public: + UserUsageConditions(BMessage* from); + UserUsageConditions(); + virtual ~UserUsageConditions(); + + const BString& Code() const; + const uint8 MinimumAge() const; + const BString& CopyMarkdown() const; + + void SetCode(const BString& code); + void SetMinimumAge(uint8 age); + void SetCopyMarkdown(const BString& copyMarkdown); + + status_t Archive(BMessage* into, bool deep = true) const; +private: + BString fCode; + BString fCopyMarkdown; + uint8 fMinimumAge; +}; + + +#endif // USER_USAGE_CONDITIONS_H diff --git a/src/apps/haikudepot/server/WebAppInterface.cpp b/src/apps/haikudepot/server/WebAppInterface.cpp index 30231264a3..3928b50d45 100644 --- a/src/apps/haikudepot/server/WebAppInterface.cpp +++ b/src/apps/haikudepot/server/WebAppInterface.cpp @@ -411,6 +411,102 @@ WebAppInterface::RetrieveUserRating(const BString& packageName, } +/*! \brief Returns data relating to the user usage conditions + + \param code defines the version of the data to return or if empty then the + latest is returned. + + This method will go to the server and get details relating to the user usage + conditions. It does this in two API calls; first gets the details (the + minimum age) and in the second call, the text of the conditions is returned. +*/ + +status_t +WebAppInterface::RetrieveUserUsageConditions(const BString& code, + UserUsageConditions& conditions) +{ + BMessage responseEnvelopeMessage; + status_t result = _RetrieveUserUsageConditionsMeta(code, + responseEnvelopeMessage); + + if (result != B_OK) + return result; + + BMessage resultMessage; + if (responseEnvelopeMessage.FindMessage("result", &resultMessage) != B_OK) { + fprintf(stderr, "bad response envelope missing 'result' entry\n"); + return B_BAD_DATA; + } + + BString metaDataCode; + double metaDataMinimumAge; + BString copyMarkdown; + + if ( (resultMessage.FindString("code", &metaDataCode) != B_OK) + || (resultMessage.FindDouble( + "minimumAge", &metaDataMinimumAge) != B_OK) ) { + printf("unexpected response from server with missing user usage " + "conditions data\n"); + return B_BAD_DATA; + } + + BMallocIO* copyMarkdownData = new BMallocIO(); + result = _RetrieveUserUsageConditionsCopy(metaDataCode, copyMarkdownData); + + if (result != B_OK) + return result; + + conditions.SetCode(metaDataCode); + conditions.SetMinimumAge(metaDataMinimumAge); + conditions.SetCopyMarkdown( + BString(static_cast(copyMarkdownData->Buffer()), + copyMarkdownData->BufferLength())); + + return B_OK; +} + + +status_t +WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code, + BMessage& message) +{ + BMallocIO* requestEnvelopeData = new BMallocIO(); + BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); + + requestEnvelopeWriter.WriteObjectStart(); + _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, + "getUserUsageConditions"); + requestEnvelopeWriter.WriteObjectName("params"); + requestEnvelopeWriter.WriteArrayStart(); + + requestEnvelopeWriter.WriteObjectStart(); + + if (!code.IsEmpty()) { + requestEnvelopeWriter.WriteObjectName("code"); + requestEnvelopeWriter.WriteString(code.String()); + } + + requestEnvelopeWriter.WriteObjectEnd(); + requestEnvelopeWriter.WriteArrayEnd(); + requestEnvelopeWriter.WriteObjectEnd(); + + // now fetch this information into an object. + + return _SendJsonRequest("user", requestEnvelopeData, + _LengthAndSeekToZero(requestEnvelopeData), 0, message); +} + + +status_t +WebAppInterface::_RetrieveUserUsageConditionsCopy(const BString& code, + BDataIO* stream) +{ + return _SendRawGetRequest( + BString("/__user/usageconditions/") << code << "/document.md", + stream); +} + + status_t WebAppInterface::CreateUserRating(const BString& packageName, const BPackageVersion& version, @@ -555,36 +651,9 @@ status_t WebAppInterface::RetrieveScreenshot(const BString& code, int32 width, int32 height, BDataIO* stream) { - BUrl url = ServerSettings::CreateFullUrl( + return _SendRawGetRequest( BString("/__pkgscreenshot/") << code << ".png" << "?tw=" - << width << "&th=" << height); - - bool isSecure = url.Protocol() == "https"; - - ProtocolListener listener(Logger::IsTraceEnabled()); - listener.SetDownloadIO(stream); - - BHttpHeaders headers; - ServerSettings::AugmentHeaders(headers); - - BHttpRequest request(url, isSecure, "HTTP", &listener); - request.SetMethod(B_HTTP_GET); - request.SetHeaders(headers); - - thread_id thread = request.Run(); - wait_for_thread(thread, NULL); - - const BHttpResult& result = dynamic_cast( - request.Result()); - - int32 statusCode = result.StatusCode(); - - if (statusCode == 200) - return B_OK; - - fprintf(stderr, "failed to get screenshot from '%s': %" B_PRIi32 "\n", - url.UrlString().String(), statusCode); - return B_ERROR; + << width << "&th=" << height, stream); } @@ -830,6 +899,40 @@ WebAppInterface::_SendJsonRequest(const char* domain, const BString& jsonString, } +status_t +WebAppInterface::_SendRawGetRequest(const BString urlPathComponents, + BDataIO* stream) +{ + BUrl url = ServerSettings::CreateFullUrl(urlPathComponents); + bool isSecure = url.Protocol() == "https"; + + ProtocolListener listener(Logger::IsTraceEnabled()); + listener.SetDownloadIO(stream); + + BHttpHeaders headers; + ServerSettings::AugmentHeaders(headers); + + BHttpRequest request(url, isSecure, "HTTP", &listener); + request.SetMethod(B_HTTP_GET); + request.SetHeaders(headers); + + thread_id thread = request.Run(); + wait_for_thread(thread, NULL); + + const BHttpResult& result = dynamic_cast( + request.Result()); + + int32 statusCode = result.StatusCode(); + + if (statusCode == 200) + return B_OK; + + fprintf(stderr, "failed to get data from '%s': %" B_PRIi32 "\n", + url.UrlString().String(), statusCode); + return B_ERROR; +} + + void WebAppInterface::_LogPayload(BPositionIO* requestData, size_t size) { diff --git a/src/apps/haikudepot/server/WebAppInterface.h b/src/apps/haikudepot/server/WebAppInterface.h index 4b99fa231f..4eb8ac29d4 100644 --- a/src/apps/haikudepot/server/WebAppInterface.h +++ b/src/apps/haikudepot/server/WebAppInterface.h @@ -13,6 +13,7 @@ #include #include "List.h" +#include "UserUsageConditions.h" class BDataIO; @@ -88,6 +89,10 @@ public: int rating, bool active, BMessage& message); + status_t RetrieveUserUsageConditions( + const BString& code, + UserUsageConditions& conditions); + status_t RetrieveScreenshot( const BString& code, int32 width, int32 height, @@ -110,6 +115,13 @@ public: static int32 ErrorCodeFromResponse(BMessage& response); private: + status_t _SendRawGetRequest( + const BString urlPathComponents, + BDataIO* stream); + status_t _RetrieveUserUsageConditionsMeta( + const BString& code, BMessage& message); + status_t _RetrieveUserUsageConditionsCopy( + const BString& code, BDataIO* stream); void _WriteStandardJsonRpcEnvelopeValues( BJsonWriter& writer, const char* methodName); diff --git a/src/apps/haikudepot/ui/MainWindow.cpp b/src/apps/haikudepot/ui/MainWindow.cpp index e0a32ec567..270623045f 100644 --- a/src/apps/haikudepot/ui/MainWindow.cpp +++ b/src/apps/haikudepot/ui/MainWindow.cpp @@ -48,6 +48,7 @@ #include "support.h" #include "ScreenshotWindow.h" #include "UserLoginWindow.h" +#include "UserUsageConditionsWindow.h" #include "WorkStatusView.h" @@ -56,23 +57,24 @@ enum { - MSG_BULK_LOAD_DONE = 'mmwd', - MSG_REFRESH_REPOS = 'mrrp', - MSG_MANAGE_REPOS = 'mmrp', - MSG_SOFTWARE_UPDATER = 'mswu', - MSG_LOG_IN = 'lgin', - MSG_LOG_OUT = 'lgot', - MSG_AUTHORIZATION_CHANGED = 'athc', - MSG_CATEGORIES_LIST_CHANGED = 'clic', - MSG_PACKAGE_CHANGED = 'pchd', - MSG_WORK_STATUS_CHANGE = 'wsch', - MSG_WORK_STATUS_CLEAR = 'wscl', + MSG_BULK_LOAD_DONE = 'mmwd', + MSG_REFRESH_REPOS = 'mrrp', + MSG_MANAGE_REPOS = 'mmrp', + MSG_SOFTWARE_UPDATER = 'mswu', + MSG_LOG_IN = 'lgin', + MSG_LOG_OUT = 'lgot', + MSG_AUTHORIZATION_CHANGED = 'athc', + MSG_CATEGORIES_LIST_CHANGED = 'clic', + MSG_PACKAGE_CHANGED = 'pchd', + MSG_WORK_STATUS_CHANGE = 'wsch', + MSG_WORK_STATUS_CLEAR = 'wscl', + MSG_VIEW_LATEST_USER_USAGE_CONDITIONS = 'vluc', - MSG_SHOW_FEATURED_PACKAGES = 'sofp', - MSG_SHOW_AVAILABLE_PACKAGES = 'savl', - MSG_SHOW_INSTALLED_PACKAGES = 'sins', - MSG_SHOW_SOURCE_PACKAGES = 'ssrc', - MSG_SHOW_DEVELOP_PACKAGES = 'sdvl' + MSG_SHOW_FEATURED_PACKAGES = 'sofp', + MSG_SHOW_AVAILABLE_PACKAGES = 'savl', + MSG_SHOW_INSTALLED_PACKAGES = 'sins', + MSG_SHOW_SOURCE_PACKAGES = 'ssrc', + MSG_SHOW_DEVELOP_PACKAGES = 'sdvl' }; @@ -334,6 +336,10 @@ MainWindow::MessageReceived(BMessage* message) fModel.SetUsername(""); break; + case MSG_VIEW_LATEST_USER_USAGE_CONDITIONS: + _ViewLatestUserUsageConditions(); + break; + case MSG_AUTHORIZATION_CHANGED: _UpdateAuthorization(); break; @@ -734,6 +740,11 @@ MainWindow::_BuildUserMenu(BMenuBar* menuBar) new BMessage(MSG_LOG_OUT)); fUserMenu->AddItem(fLogOutItem); + fLogOutItem = new BMenuItem(B_TRANSLATE("View latest user usage conditions" + B_UTF8_ELLIPSIS), + new BMessage(MSG_VIEW_LATEST_USER_USAGE_CONDITIONS)); + fUserMenu->AddItem(fLogOutItem); + menuBar->AddItem(fUserMenu); } @@ -1332,3 +1343,12 @@ MainWindow::_ShowScreenshot() fScreenshotWindow->Unlock(); } + + +void +MainWindow::_ViewLatestUserUsageConditions() +{ + UserUsageConditionsWindow* window = new UserUsageConditionsWindow( + this, BRect(0, 0, 500, 400), fModel, LATEST); + window->Show(); +} \ No newline at end of file diff --git a/src/apps/haikudepot/ui/MainWindow.h b/src/apps/haikudepot/ui/MainWindow.h index 9210824bcf..841b2800ff 100644 --- a/src/apps/haikudepot/ui/MainWindow.h +++ b/src/apps/haikudepot/ui/MainWindow.h @@ -101,6 +101,8 @@ private: void _RatePackage(); void _ShowScreenshot(); + void _ViewLatestUserUsageConditions(); + private: FilterView* fFilterView; TabView* fListTabs; diff --git a/src/apps/haikudepot/ui/RatePackageWindow.cpp b/src/apps/haikudepot/ui/RatePackageWindow.cpp index e4a7c4aba4..1743043d29 100644 --- a/src/apps/haikudepot/ui/RatePackageWindow.cpp +++ b/src/apps/haikudepot/ui/RatePackageWindow.cpp @@ -465,7 +465,7 @@ RatePackageWindow::_SetWorkerThread(thread_id thread) } -int32 +/*static*/ int32 RatePackageWindow::_QueryRatingThreadEntry(void* data) { RatePackageWindow* window = reinterpret_cast(data); diff --git a/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp b/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp new file mode 100644 index 0000000000..829e08d2a2 --- /dev/null +++ b/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp @@ -0,0 +1,314 @@ +/* + * Copyright 2019, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "UserUsageConditionsWindow.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "AppUtils.h" +#include "BarberPole.h" +#include "HaikuDepotConstants.h" +#include "Logger.h" +#include "MarkupTextView.h" +#include "Model.h" +#include "UserUsageConditions.h" +#include "WebAppInterface.h" + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "UserUsageConditions" + +#define PLACEHOLDER_TEXT "..." + +#define INTRODUCTION_TEXT_LATEST "HaikuDepot communicates with a " \ + "sever component called HaikuDepotServer. These are the latest user " \ + "usage conditions for use of the HaikuDepotServer service." + +#define INTRODUCTION_TEXT_USER "HaikuDepot communicates with a " \ + "sever component called HaikuDepotServer. These are the user usage " \ + "conditions that the user has agreed to in relation to the use of the " \ + "HaikuDepotServer service." + +/*! This is the anticipated number of lines of test that appear in the + introduction. +*/ + +#define LINES_INTRODUCTION_TEXT 2 + + +enum { + MSG_USER_USAGE_CONDITIONS_DATA = 'uucd', + MSG_USER_USAGE_CONDITIONS_ERROR = 'uuce' +}; + + +UserUsageConditionsWindow::UserUsageConditionsWindow(BWindow* parent, + BRect frame, Model& model, UserUsageConditionsSelectionMode mode) + : + BWindow(frame, B_TRANSLATE("User Usage Conditions"), + B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL, + B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS + | B_NOT_RESIZABLE | B_NOT_ZOOMABLE), + fMode(mode), + fModel(model), + fWorkerThread(-1) +{ + AddToSubset(parent); + + if (mode != LATEST) + debugger("only the LATEST user usage conditions are handled for now"); + + fWorkerIndicator = new BarberPole("fetch data worker indicator"); + BSize workerIndicatorSize; + workerIndicatorSize.SetHeight(20); + fWorkerIndicator->SetExplicitMinSize(workerIndicatorSize); + + fCopyView = new MarkupTextView("copy view"); + fCopyView->SetViewUIColor(B_NO_COLOR); + fCopyView->SetLowColor(RGB_COLOR_WHITE); + fCopyView->SetInsets(8.0f); + + BScrollView* scrollView = new BScrollView("copy scroll view", fCopyView, + 0, false, true, B_PLAIN_BORDER); + + BTextView* introductionTextView = new BTextView("introduction text view"); + introductionTextView->AdoptSystemColors(); + introductionTextView->MakeEditable(false); + introductionTextView->MakeSelectable(false); + introductionTextView->SetText(B_TRANSLATE(_IntroductionTextForMode(mode))); + + fAgeNoteStringView = new BStringView("age note string view", + PLACEHOLDER_TEXT); + fAgeNoteStringView->AdoptSystemColors(); + fAgeNoteStringView->SetExplicitMaxSize( + BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + + BFont versionFont(be_plain_font); + versionFont.SetSize(9.0); + + fVersionStringView = new BStringView("version string view", + PLACEHOLDER_TEXT); + fVersionStringView->AdoptSystemColors(); + fVersionStringView->SetFont(&versionFont); + fVersionStringView->SetAlignment(B_ALIGN_RIGHT); + fVersionStringView->SetHighUIColor(B_PANEL_TEXT_COLOR, B_DARKEN_3_TINT); + fVersionStringView->SetExplicitMaxSize( + BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + + BSize introductionSize; + introductionSize.SetHeight( + _ExpectedIntroductionTextHeight(introductionTextView)); + introductionTextView->SetExplicitPreferredSize(introductionSize); + + BButton* okButton = new BButton("ok", B_TRANSLATE("OK"), + new BMessage(B_QUIT_REQUESTED)); + + BLayoutBuilder::Group<>(this, B_VERTICAL) + .SetInsets(B_USE_WINDOW_INSETS) + .Add(introductionTextView, 1) + .AddGlue() + .Add(fVersionStringView, 1) + .Add(scrollView, 96) + .Add(fAgeNoteStringView, 1) + .AddGroup(B_HORIZONTAL, 1) + .AddGlue() + .Add(okButton) + .End() + .Add(fWorkerIndicator, 1) + .End(); + + CenterIn(parent->Frame()); + + _FetchData(); + // start a new thread to pull down the user usage conditions data. +} + + +UserUsageConditionsWindow::~UserUsageConditionsWindow() +{ +} + + +void +UserUsageConditionsWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_USER_USAGE_CONDITIONS_DATA: + { + UserUsageConditions data(message); + _DisplayData(data); + fWorkerIndicator->Stop(); + break; + } + default: + BWindow::MessageReceived(message); + break; + } +} + + +bool +UserUsageConditionsWindow::QuitRequested() +{ + // for now we just don't allow the quit when the background thread + // is processing. In the future it would be good if the HTTP + // requests were re-organized such that cancellations were easier to + // implement. + + if (fWorkerThread == -1) + return true; + if (Logger::IsInfoEnabled()) { + fprintf(stderr, "unable to quit when the user usage " + "conditions window is still fetching data\n"); + } + return false; +} + + +/*! This method is called on the main thread in order to initiate the background + processing to obtain the user usage conditions data. It will take + responsibility for coordinating the creation of the thread and starting the + thread etc... +*/ + +void +UserUsageConditionsWindow::_FetchData() +{ + if (-1 != fWorkerThread) + debugger("illegal state - attempt to fetch, but fetch in progress"); + thread_id thread = spawn_thread(&_FetchDataThreadEntry, + "Fetch user usage conditions data", B_NORMAL_PRIORITY, this); + if (thread >= 0) { + fWorkerIndicator->Start(); + _SetWorkerThread(thread); + resume_thread(fWorkerThread); + } else { + debugger("unable to start a thread to fetch the user usage " + "conditions."); + } +} + + +/*! This method is called from the thread in order to start the thread; it is + the entry-point for the background processing to obtain the user usage + conditions. +*/ + +/*static*/ int32 +UserUsageConditionsWindow::_FetchDataThreadEntry(void* data) +{ + UserUsageConditionsWindow* win + = reinterpret_cast(data); + win->_FetchDataPerform(); + return 0; +} + + +/*! This method will perform the task of obtaining data about the user usage + conditions. +*/ + +void +UserUsageConditionsWindow::_FetchDataPerform() +{ + UserUsageConditions conditions; + WebAppInterface interface = fModel.GetWebAppInterface(); + + if (interface.RetrieveUserUsageConditions(NULL, conditions) == B_OK) { + BMessage dataMessage(MSG_USER_USAGE_CONDITIONS_DATA); + conditions.Archive(&dataMessage, true); + BMessenger(this).SendMessage(&dataMessage); + } else { + AppUtils::NotifySimpleError( + B_TRANSLATE("User Usage Conditions Download Problem"), + B_TRANSLATE("An error has arisen downloading the user usage " + "conditions. Check the log for details and try again.")); + BMessenger(this).SendMessage(B_QUIT_REQUESTED); + } + + _SetWorkerThread(-1); +} + + +void +UserUsageConditionsWindow::_SetWorkerThread(thread_id thread) +{ + if (!Lock()) { + if (Logger::IsInfoEnabled()) + fprintf(stderr, "failed to lock window\n"); + } else { + fWorkerThread = thread; + Unlock(); + } +} + + +void +UserUsageConditionsWindow::_DisplayData(const UserUsageConditions& data) +{ + fCopyView->SetText(data.CopyMarkdown()); + fAgeNoteStringView->SetText(_MinimumAgeText(data.MinimumAge())); + fVersionStringView->SetText(_VersionText(data.Code())); +} + + +/*static*/ const BString +UserUsageConditionsWindow::_VersionText(const BString& code) +{ + BString versionText( + B_TRANSLATE("Version %Code%")); + versionText.ReplaceAll("%Code%", code); + return versionText; +} + + +/*static*/ const BString +UserUsageConditionsWindow::_MinimumAgeText(uint8 minimumAge) +{ + BString minimumAgeString; + minimumAgeString.SetToFormat("%" B_PRId8, minimumAge); + BString ageNoteText( + B_TRANSLATE("Users are required to be %AgeYears% years of age or " + "older.")); + ageNoteText.ReplaceAll("%AgeYears%", minimumAgeString); + return ageNoteText; +} + + +/*static*/ const BString +UserUsageConditionsWindow::_IntroductionTextForMode( + UserUsageConditionsSelectionMode mode) +{ + switch (mode) { + case LATEST: + return INTRODUCTION_TEXT_LATEST; + case USER: + return INTRODUCTION_TEXT_USER; + default: + return "???"; + } +} + + +/*static*/ float +UserUsageConditionsWindow::_ExpectedIntroductionTextHeight( + BTextView* introductionTextView) +{ + float insetTop; + float insetBottom; + introductionTextView->GetInsets(NULL, &insetTop, NULL, &insetBottom); + + BSize introductionSize; + font_height fh; + be_plain_font->GetHeight(&fh); + return ((fh.ascent + fh.descent + fh.leading) * LINES_INTRODUCTION_TEXT) + + insetTop + insetBottom; +} diff --git a/src/apps/haikudepot/ui/UserUsageConditionsWindow.h b/src/apps/haikudepot/ui/UserUsageConditionsWindow.h new file mode 100644 index 0000000000..07c4fdcaff --- /dev/null +++ b/src/apps/haikudepot/ui/UserUsageConditionsWindow.h @@ -0,0 +1,66 @@ +/* + * Copyright 2019, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef USER_USAGE_CONDITIONS_WINDOW_H +#define USER_USAGE_CONDITIONS_WINDOW_H + +#include +#include +#include + +#include "PackageInfo.h" +#include "UserUsageConditions.h" + + +class BarberPole; +class BTextView; +class BStringView; +class MarkupTextView; +class Model; + + +enum UserUsageConditionsSelectionMode { + LATEST = 1, + USER = 2 +}; + + +class UserUsageConditionsWindow : public BWindow { +public: + UserUsageConditionsWindow(BWindow* parent, + BRect frame, Model& model, + UserUsageConditionsSelectionMode mode); + virtual ~UserUsageConditionsWindow(); + + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + +private: + static const BString _VersionText(const BString& code); + static const BString _MinimumAgeText(uint8 minimumAge); + static const BString _IntroductionTextForMode( + UserUsageConditionsSelectionMode mode); + static float _ExpectedIntroductionTextHeight( + BTextView* introductionTextView); + + void _DisplayData(const UserUsageConditions& data); + + void _FetchData(); + void _SetWorkerThread(thread_id thread); + static int32 _FetchDataThreadEntry(void* data); + void _FetchDataPerform(); + +private: + UserUsageConditionsSelectionMode + fMode; + MarkupTextView* fCopyView; + Model& fModel; + BStringView* fAgeNoteStringView; + BStringView* fVersionStringView; + BarberPole* fWorkerIndicator; + thread_id fWorkerThread; +}; + + +#endif // USER_USAGE_CONDITIONS_WINDOW_H