HaikuDepot: Display User Usage Conditions

This change will allow the user to view the user
usage conditions from the HDS system in a
HaikuDepot window.  The display of the text is not
currently well formatted in that the Markdown is
not yet properly parsed, but the display of the
data is working.

Relates to 15209

Change-Id: Ia6ad4ef995f5fe3c29c40221964e44d4554a033d
Reviewed-on: https://review.haiku-os.org/c/haiku/+/1750
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
Andrew Lindesay 2019-07-09 12:23:14 +01:00 committed by waddlesplash
parent 75e5739772
commit 01339a54db
12 changed files with 705 additions and 48 deletions

View File

@ -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)

View File

@ -143,6 +143,8 @@ local applicationSources =
ScrollableGroupView.cpp
SharedBitmap.cpp
UserLoginWindow.cpp
UserUsageConditions.cpp
UserUsageConditionsWindow.cpp
WorkStatusView.cpp
# network + server / local processes

View File

@ -2,8 +2,6 @@
* Copyright 2017, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LOCAL_ICON_STORE_H
#define LOCAL_ICON_STORE_H

View File

@ -0,0 +1,100 @@
/*
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
*
* 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;
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
*
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef USER_USAGE_CONDITIONS_H
#define USER_USAGE_CONDITIONS_H
#include <stdio.h>
#include <Archivable.h>
#include <String.h>
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

View File

@ -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<const char*>(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<const BHttpResult&>(
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<const BHttpResult&>(
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)
{

View File

@ -13,6 +13,7 @@
#include <package/PackageVersion.h>
#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);

View File

@ -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();
}

View File

@ -101,6 +101,8 @@ private:
void _RatePackage();
void _ShowScreenshot();
void _ViewLatestUserUsageConditions();
private:
FilterView* fFilterView;
TabView* fListTabs;

View File

@ -465,7 +465,7 @@ RatePackageWindow::_SetWorkerThread(thread_id thread)
}
int32
/*static*/ int32
RatePackageWindow::_QueryRatingThreadEntry(void* data)
{
RatePackageWindow* window = reinterpret_cast<RatePackageWindow*>(data);

View File

@ -0,0 +1,314 @@
/*
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "UserUsageConditionsWindow.h"
#include <Button.h>
#include <Catalog.h>
#include <Font.h>
#include <LayoutBuilder.h>
#include <ScrollView.h>
#include <StringView.h>
#include <TextView.h>
#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<UserUsageConditionsWindow*>(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;
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2019, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef USER_USAGE_CONDITIONS_WINDOW_H
#define USER_USAGE_CONDITIONS_WINDOW_H
#include <Locker.h>
#include <Messenger.h>
#include <Window.h>
#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