* The package info area toggles a package's rating to a "Rate package..." button when the mouse hovers it. * Clicking that button opens a window where one can enter a rating. * Totally not working yet, but I want this in VCS.
1645 lines
37 KiB
C++
1645 lines
37 KiB
C++
/*
|
|
* Copyright 2013, Stephan Aßmus <[email protected]>.
|
|
* All rights reserved. Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
#include "PackageInfoView.h"
|
|
|
|
#include <algorithm>
|
|
#include <stdio.h>
|
|
|
|
#include <Autolock.h>
|
|
#include <Bitmap.h>
|
|
#include <Button.h>
|
|
#include <CardLayout.h>
|
|
#include <Catalog.h>
|
|
#include <Cursor.h>
|
|
#include <Font.h>
|
|
#include <GridView.h>
|
|
#include <LayoutBuilder.h>
|
|
#include <LayoutUtils.h>
|
|
#include <Message.h>
|
|
#include <TabView.h>
|
|
#include <ScrollView.h>
|
|
#include <SpaceLayoutItem.h>
|
|
#include <StatusBar.h>
|
|
#include <StringView.h>
|
|
|
|
#include <support/Url.h>
|
|
|
|
#include "BitmapButton.h"
|
|
#include "BitmapView.h"
|
|
#include "MarkupParser.h"
|
|
#include "PackageActionHandler.h"
|
|
#include "PackageManager.h"
|
|
#include "TextDocumentView.h"
|
|
#include "TextView.h"
|
|
|
|
|
|
#undef B_TRANSLATION_CONTEXT
|
|
#define B_TRANSLATION_CONTEXT "PackageInfoView"
|
|
|
|
|
|
static const rgb_color kLightBlack = (rgb_color){ 60, 60, 60, 255 };
|
|
static const float kContentTint = (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0f;
|
|
|
|
|
|
//! Layouts the scrollbar so it looks nice with no border and the document
|
|
// window look.
|
|
class CustomScrollView : public BScrollView {
|
|
public:
|
|
CustomScrollView(const char* name, BView* target)
|
|
:
|
|
BScrollView(name, target, 0, false, true, B_NO_BORDER)
|
|
{
|
|
}
|
|
|
|
virtual void DoLayout()
|
|
{
|
|
BRect innerFrame = Bounds();
|
|
innerFrame.right -= B_V_SCROLL_BAR_WIDTH + 1;
|
|
|
|
BView* target = Target();
|
|
if (target != NULL) {
|
|
Target()->MoveTo(innerFrame.left, innerFrame.top);
|
|
Target()->ResizeTo(innerFrame.Width(), innerFrame.Height());
|
|
}
|
|
|
|
BScrollBar* scrollBar = ScrollBar(B_VERTICAL);
|
|
if (scrollBar != NULL) {
|
|
BRect rect = innerFrame;
|
|
rect.left = rect.right + 1;
|
|
rect.right = rect.left + B_V_SCROLL_BAR_WIDTH;
|
|
rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
|
|
|
|
scrollBar->MoveTo(rect.left, rect.top);
|
|
scrollBar->ResizeTo(rect.Width(), rect.Height());
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class RatingsScrollView : public CustomScrollView {
|
|
public:
|
|
RatingsScrollView(const char* name, BView* target)
|
|
:
|
|
CustomScrollView(name, target)
|
|
{
|
|
}
|
|
|
|
virtual void DoLayout()
|
|
{
|
|
CustomScrollView::DoLayout();
|
|
|
|
BScrollBar* scrollBar = ScrollBar(B_VERTICAL);
|
|
BView* target = Target();
|
|
if (target != NULL && scrollBar != NULL) {
|
|
// Set the scroll steps
|
|
BView* item = target->ChildAt(0);
|
|
if (item != NULL) {
|
|
scrollBar->SetSteps(item->MinSize().height + 1,
|
|
item->MinSize().height + 1);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class MarkupTextView : public TextDocumentView {
|
|
public:
|
|
MarkupTextView(const char* name)
|
|
:
|
|
TextDocumentView(name)
|
|
{
|
|
CharacterStyle regularStyle;
|
|
|
|
float fontSize = regularStyle.Font().Size();
|
|
|
|
ParagraphStyle paragraphStyle;
|
|
paragraphStyle.SetJustify(true);
|
|
paragraphStyle.SetSpacingTop(ceilf(fontSize * 0.3f));
|
|
paragraphStyle.SetLineSpacing(ceilf(fontSize * 0.2f));
|
|
|
|
fMarkupParser.SetStyles(regularStyle, paragraphStyle);
|
|
}
|
|
|
|
void SetText(const BString& markupText)
|
|
{
|
|
SetTextDocument(fMarkupParser.CreateDocumentFromMarkup(markupText));
|
|
}
|
|
|
|
void SetText(const BString heading, const BString& markupText)
|
|
{
|
|
TextDocumentRef document(new(std::nothrow) TextDocument(), true);
|
|
|
|
Paragraph paragraph(fMarkupParser.HeadingParagraphStyle());
|
|
paragraph.Append(TextSpan(heading,
|
|
fMarkupParser.HeadingCharacterStyle()));
|
|
document->Append(paragraph);
|
|
|
|
fMarkupParser.AppendMarkup(document, markupText);
|
|
|
|
SetTextDocument(document);
|
|
}
|
|
|
|
void SetDisabledText(const BString& text)
|
|
{
|
|
TextDocumentRef document(new(std::nothrow) TextDocument(), true);
|
|
|
|
ParagraphStyle paragraphStyle;
|
|
paragraphStyle.SetAlignment(ALIGN_CENTER);
|
|
|
|
CharacterStyle disabledStyle(fMarkupParser.NormalCharacterStyle());
|
|
disabledStyle.SetForegroundColor(kLightBlack);
|
|
|
|
Paragraph paragraph(paragraphStyle);
|
|
paragraph.Append(TextSpan(text, disabledStyle));
|
|
document->Append(paragraph);
|
|
|
|
SetTextDocument(document);
|
|
}
|
|
|
|
private:
|
|
MarkupParser fMarkupParser;
|
|
};
|
|
|
|
|
|
class LinkView : public BStringView, public BInvoker {
|
|
public:
|
|
LinkView(const char* name, const char* string, BMessage* message,
|
|
rgb_color color)
|
|
:
|
|
BStringView(name, string),
|
|
BInvoker(message, NULL),
|
|
fNormalColor(color),
|
|
fHoverColor(make_color(1, 141, 211)),
|
|
fEnabled(true),
|
|
fMouseInside(false)
|
|
{
|
|
_UpdateLinkColor();
|
|
}
|
|
|
|
virtual void MouseMoved(BPoint where, uint32 transit,
|
|
const BMessage* dragMessage)
|
|
{
|
|
if (transit == B_ENTERED_VIEW) {
|
|
fMouseInside = true;
|
|
_UpdateLinkColor();
|
|
} else if (transit == B_EXITED_VIEW) {
|
|
fMouseInside = false;
|
|
_UpdateLinkColor();
|
|
}
|
|
}
|
|
|
|
virtual void MouseDown(BPoint where)
|
|
{
|
|
if (fEnabled)
|
|
Invoke(Message());
|
|
}
|
|
|
|
void SetEnabled(bool enabled)
|
|
{
|
|
if (fEnabled != enabled) {
|
|
fEnabled = enabled;
|
|
_UpdateLinkColor();
|
|
}
|
|
}
|
|
|
|
private:
|
|
void _UpdateLinkColor()
|
|
{
|
|
if (fEnabled && fMouseInside) {
|
|
SetHighColor(fHoverColor);
|
|
BCursor cursor(B_CURSOR_ID_FOLLOW_LINK);
|
|
SetViewCursor(&cursor, true);
|
|
Invalidate();
|
|
} else {
|
|
SetHighColor(fNormalColor);
|
|
SetViewCursor(NULL);
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
private:
|
|
rgb_color fNormalColor;
|
|
rgb_color fHoverColor;
|
|
|
|
bool fEnabled;
|
|
bool fMouseInside;
|
|
};
|
|
|
|
|
|
// #pragma mark - rating stats
|
|
|
|
|
|
class RatingView : public BView {
|
|
public:
|
|
RatingView()
|
|
:
|
|
BView("package rating view", B_WILL_DRAW),
|
|
fStarBitmap(501),
|
|
fRating(-1.0f)
|
|
{
|
|
SetViewColor(B_TRANSPARENT_COLOR);
|
|
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
}
|
|
|
|
virtual ~RatingView()
|
|
{
|
|
}
|
|
|
|
virtual void AttachedToWindow()
|
|
{
|
|
BView* parent = Parent();
|
|
if (parent != NULL)
|
|
SetLowColor(parent->ViewColor());
|
|
}
|
|
|
|
virtual void Draw(BRect updateRect)
|
|
{
|
|
FillRect(updateRect, B_SOLID_LOW);
|
|
|
|
if (fRating < 0.0f)
|
|
return;
|
|
|
|
const BBitmap* star = fStarBitmap.Bitmap(SharedBitmap::SIZE_16);
|
|
if (star == NULL) {
|
|
fprintf(stderr, "No star icon found in application resources.\n");
|
|
return;
|
|
}
|
|
|
|
SetDrawingMode(B_OP_OVER);
|
|
|
|
float x = 0;
|
|
for (int i = 0; i < 5; i++) {
|
|
DrawBitmap(star, BPoint(x, 0));
|
|
x += 16 + 2;
|
|
}
|
|
|
|
if (fRating >= 5.0f)
|
|
return;
|
|
|
|
SetDrawingMode(B_OP_OVER);
|
|
|
|
BRect rect(Bounds());
|
|
rect.left = ceilf(rect.left + (fRating / 5.0f) * rect.Width());
|
|
|
|
rgb_color color = LowColor();
|
|
color.alpha = 190;
|
|
SetHighColor(color);
|
|
|
|
SetDrawingMode(B_OP_ALPHA);
|
|
FillRect(rect, B_SOLID_HIGH);
|
|
}
|
|
|
|
virtual BSize MinSize()
|
|
{
|
|
return BSize(16 * 5 + 2 * 4, 16 + 2);
|
|
}
|
|
|
|
virtual BSize PreferredSize()
|
|
{
|
|
return MinSize();
|
|
}
|
|
|
|
virtual BSize MaxSize()
|
|
{
|
|
return MinSize();
|
|
}
|
|
|
|
void SetRating(float rating)
|
|
{
|
|
fRating = rating;
|
|
Invalidate();
|
|
}
|
|
|
|
private:
|
|
SharedBitmap fStarBitmap;
|
|
float fRating;
|
|
};
|
|
|
|
|
|
class DiagramBarView : public BView {
|
|
public:
|
|
DiagramBarView()
|
|
:
|
|
BView("diagram bar view", B_WILL_DRAW),
|
|
fValue(0.0f)
|
|
{
|
|
SetViewColor(B_TRANSPARENT_COLOR);
|
|
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
SetHighColor(tint_color(LowColor(), B_DARKEN_2_TINT));
|
|
}
|
|
|
|
virtual ~DiagramBarView()
|
|
{
|
|
}
|
|
|
|
virtual void AttachedToWindow()
|
|
{
|
|
BView* parent = Parent();
|
|
if (parent != NULL) {
|
|
SetLowColor(parent->ViewColor());
|
|
SetHighColor(tint_color(LowColor(), B_DARKEN_2_TINT));
|
|
}
|
|
}
|
|
|
|
virtual void Draw(BRect updateRect)
|
|
{
|
|
FillRect(updateRect, B_SOLID_LOW);
|
|
|
|
if (fValue <= 0.0f)
|
|
return;
|
|
|
|
BRect rect(Bounds());
|
|
rect.right = ceilf(rect.left + fValue * rect.Width());
|
|
|
|
FillRect(rect, B_SOLID_HIGH);
|
|
}
|
|
|
|
virtual BSize MinSize()
|
|
{
|
|
return BSize(64, 10);
|
|
}
|
|
|
|
virtual BSize PreferredSize()
|
|
{
|
|
return MinSize();
|
|
}
|
|
|
|
virtual BSize MaxSize()
|
|
{
|
|
return BSize(64, 10);
|
|
}
|
|
|
|
void SetValue(float value)
|
|
{
|
|
if (fValue != value) {
|
|
fValue = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
private:
|
|
float fValue;
|
|
};
|
|
|
|
|
|
// #pragma mark - TitleView
|
|
|
|
|
|
enum {
|
|
MSG_PACKAGE_ACTION = 'pkga',
|
|
MSG_MOUSE_ENTERED_RATING = 'menr',
|
|
MSG_MOUSE_EXITED_RATING = 'mexr',
|
|
};
|
|
|
|
|
|
class TransitReportingButton : public BButton {
|
|
public:
|
|
TransitReportingButton(const char* name, const char* label,
|
|
BMessage* message)
|
|
:
|
|
BButton(name, label, message),
|
|
fTransitMessage(NULL)
|
|
{
|
|
}
|
|
|
|
virtual ~TransitReportingButton()
|
|
{
|
|
SetTransitMessage(NULL);
|
|
}
|
|
|
|
virtual void MouseMoved(BPoint point, uint32 transit,
|
|
const BMessage* dragMessage)
|
|
{
|
|
BButton::MouseMoved(point, transit, dragMessage);
|
|
|
|
if (fTransitMessage != NULL && transit == B_EXITED_VIEW)
|
|
Invoke(fTransitMessage);
|
|
}
|
|
|
|
void SetTransitMessage(BMessage* message)
|
|
{
|
|
if (fTransitMessage != message) {
|
|
delete fTransitMessage;
|
|
fTransitMessage = message;
|
|
}
|
|
}
|
|
|
|
private:
|
|
BMessage* fTransitMessage;
|
|
};
|
|
|
|
|
|
class TransitReportingRatingView : public RatingView, public BInvoker {
|
|
public:
|
|
TransitReportingRatingView(BMessage* transitMessage)
|
|
:
|
|
RatingView(),
|
|
fTransitMessage(transitMessage)
|
|
{
|
|
}
|
|
|
|
virtual ~TransitReportingRatingView()
|
|
{
|
|
delete fTransitMessage;
|
|
}
|
|
|
|
virtual void MouseMoved(BPoint point, uint32 transit,
|
|
const BMessage* dragMessage)
|
|
{
|
|
RatingView::MouseMoved(point, transit, dragMessage);
|
|
|
|
if (fTransitMessage != NULL && transit == B_ENTERED_VIEW)
|
|
Invoke(fTransitMessage);
|
|
}
|
|
|
|
private:
|
|
BMessage* fTransitMessage;
|
|
};
|
|
|
|
|
|
class TitleView : public BGroupView {
|
|
public:
|
|
TitleView()
|
|
:
|
|
BGroupView("title view", B_HORIZONTAL)
|
|
{
|
|
fIconView = new BitmapView("package icon view");
|
|
fTitleView = new BStringView("package title view", "");
|
|
fPublisherView = new BStringView("package publisher view", "");
|
|
|
|
// Title font
|
|
BFont font;
|
|
GetFont(&font);
|
|
font_family family;
|
|
font_style style;
|
|
font.SetSize(ceilf(font.Size() * 1.5f));
|
|
font.GetFamilyAndStyle(&family, &style);
|
|
font.SetFamilyAndStyle(family, "Bold");
|
|
fTitleView->SetFont(&font);
|
|
|
|
// Publisher font
|
|
GetFont(&font);
|
|
font.SetSize(std::max(9.0f, floorf(font.Size() * 0.92f)));
|
|
font.SetFamilyAndStyle(family, "Italic");
|
|
fPublisherView->SetFont(&font);
|
|
fPublisherView->SetHighColor(kLightBlack);
|
|
fPublisherView->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
|
|
B_ALIGN_VERTICAL_UNSET));
|
|
|
|
// slightly bigger font
|
|
GetFont(&font);
|
|
font.SetSize(ceilf(font.Size() * 1.2f));
|
|
|
|
// Version info
|
|
fVersionInfo = new BStringView("package version info", "");
|
|
fVersionInfo->SetFont(&font);
|
|
fVersionInfo->SetHighColor(kLightBlack);
|
|
|
|
// Rating view
|
|
fRatingView = new TransitReportingRatingView(
|
|
new BMessage(MSG_MOUSE_ENTERED_RATING));
|
|
|
|
fAvgRating = new BStringView("package average rating", "");
|
|
fAvgRating->SetFont(&font);
|
|
fAvgRating->SetHighColor(kLightBlack);
|
|
|
|
fVoteInfo = new BStringView("package vote info", "");
|
|
// small font
|
|
GetFont(&font);
|
|
font.SetSize(std::max(9.0f, floorf(font.Size() * 0.85f)));
|
|
fVoteInfo->SetFont(&font);
|
|
fVoteInfo->SetHighColor(kLightBlack);
|
|
|
|
// Rate button
|
|
fRateButton = new TransitReportingButton("rate",
|
|
B_TRANSLATE("Rate package" B_UTF8_ELLIPSIS),
|
|
new BMessage(MSG_RATE_PACKAGE));
|
|
fRateButton->SetTransitMessage(new BMessage(MSG_MOUSE_EXITED_RATING));
|
|
fRateButton->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
|
|
B_ALIGN_VERTICAL_CENTER));
|
|
|
|
// Rating group
|
|
BView* ratingStack = new BView("rating stack", 0);
|
|
fRatingLayout = new BCardLayout();
|
|
ratingStack->SetLayout(fRatingLayout);
|
|
ratingStack->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
|
|
ratingStack->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
|
|
BGroupView* ratingGroup = new BGroupView(B_HORIZONTAL,
|
|
B_USE_SMALL_SPACING);
|
|
BLayoutBuilder::Group<>(ratingGroup)
|
|
.Add(fRatingView)
|
|
.Add(fAvgRating)
|
|
.Add(fVoteInfo)
|
|
;
|
|
|
|
ratingStack->AddChild(ratingGroup);
|
|
ratingStack->AddChild(fRateButton);
|
|
fRatingLayout->SetVisibleItem((int32)0);
|
|
|
|
BLayoutBuilder::Group<>(this)
|
|
.Add(fIconView)
|
|
.AddGroup(B_VERTICAL, 1.0f, 2.2f)
|
|
.Add(fTitleView)
|
|
.Add(fPublisherView)
|
|
.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
|
|
.End()
|
|
.AddGlue(0.1f)
|
|
.Add(ratingStack, 0.8f)
|
|
.AddGlue(0.2f)
|
|
.AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING, 2.0f)
|
|
.Add(fVersionInfo)
|
|
.AddGlue()
|
|
.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
|
|
.End()
|
|
;
|
|
|
|
Clear();
|
|
}
|
|
|
|
virtual ~TitleView()
|
|
{
|
|
}
|
|
|
|
virtual void AttachedToWindow()
|
|
{
|
|
fRateButton->SetTarget(this);
|
|
fRatingView->SetTarget(this);
|
|
}
|
|
|
|
virtual void MessageReceived(BMessage* message)
|
|
{
|
|
switch (message->what) {
|
|
case MSG_RATE_PACKAGE:
|
|
// Forward to window (The button has us as target so
|
|
// we receive the message below.)
|
|
Window()->PostMessage(MSG_RATE_PACKAGE);
|
|
break;
|
|
|
|
case MSG_MOUSE_ENTERED_RATING:
|
|
fRatingLayout->SetVisibleItem(1);
|
|
break;
|
|
|
|
case MSG_MOUSE_EXITED_RATING:
|
|
fRatingLayout->SetVisibleItem((int32)0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SetPackage(const PackageInfo& package)
|
|
{
|
|
if (package.Icon().Get() != NULL)
|
|
fIconView->SetBitmap(package.Icon()->Bitmap(SharedBitmap::SIZE_32));
|
|
else
|
|
fIconView->SetBitmap(NULL);
|
|
|
|
fTitleView->SetText(package.Title());
|
|
|
|
BString publisher = package.Publisher().Name();
|
|
fPublisherView->SetText(publisher);
|
|
|
|
BString version = B_TRANSLATE("%Version%");
|
|
version.ReplaceAll("%Version%", package.Version());
|
|
fVersionInfo->SetText(version);
|
|
|
|
RatingSummary ratingSummary = package.CalculateRatingSummary();
|
|
|
|
fRatingView->SetRating(ratingSummary.averageRating);
|
|
|
|
if (ratingSummary.ratingCount > 0) {
|
|
BString avgRating;
|
|
avgRating.SetToFormat("%.1f", ratingSummary.averageRating);
|
|
fAvgRating->SetText(avgRating);
|
|
|
|
BString votes;
|
|
votes.SetToFormat("%d", ratingSummary.ratingCount);
|
|
|
|
BString voteInfo(B_TRANSLATE("(%Votes%)"));
|
|
voteInfo.ReplaceAll("%Votes%", votes);
|
|
|
|
fVoteInfo->SetText(voteInfo);
|
|
} else {
|
|
fAvgRating->SetText("");
|
|
fVoteInfo->SetText(B_TRANSLATE("n/a"));
|
|
}
|
|
|
|
InvalidateLayout();
|
|
Invalidate();
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
fIconView->SetBitmap(NULL);
|
|
fTitleView->SetText("");
|
|
fPublisherView->SetText("");
|
|
fVersionInfo->SetText("");
|
|
fRatingView->SetRating(-1.0f);
|
|
fAvgRating->SetText("");
|
|
fVoteInfo->SetText("");
|
|
}
|
|
|
|
private:
|
|
BitmapView* fIconView;
|
|
|
|
BStringView* fTitleView;
|
|
BStringView* fPublisherView;
|
|
|
|
BStringView* fVersionInfo;
|
|
|
|
BCardLayout* fRatingLayout;
|
|
|
|
TransitReportingRatingView* fRatingView;
|
|
BStringView* fAvgRating;
|
|
BStringView* fVoteInfo;
|
|
|
|
TransitReportingButton* fRateButton;
|
|
};
|
|
|
|
|
|
// #pragma mark - PackageActionView
|
|
|
|
|
|
class PackageActionView : public BView {
|
|
public:
|
|
PackageActionView(PackageActionHandler* handler)
|
|
:
|
|
BView("about view", B_WILL_DRAW),
|
|
fLayout(new BGroupLayout(B_HORIZONTAL)),
|
|
fPackageActionHandler(handler),
|
|
fStatusLabel(NULL),
|
|
fStatusBar(NULL)
|
|
{
|
|
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
|
|
SetLayout(fLayout);
|
|
fLayout->AddItem(BSpaceLayoutItem::CreateGlue());
|
|
}
|
|
|
|
virtual ~PackageActionView()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
virtual void MessageReceived(BMessage* message)
|
|
{
|
|
switch (message->what) {
|
|
case MSG_PACKAGE_ACTION:
|
|
{
|
|
int32 index;
|
|
if (message->FindInt32("index", &index) == B_OK) {
|
|
const PackageActionRef& action
|
|
= fPackageActions.ItemAt(index);
|
|
if (action.Get() != NULL) {
|
|
PackageActionList actions;
|
|
actions.Add(action);
|
|
status_t result = fPackageActionHandler
|
|
->SchedulePackageActions(actions);
|
|
if (result != B_OK) {
|
|
fprintf(stderr, "Failed to schedule action: "
|
|
"%s '%s'\n", action->Label(),
|
|
action->Package()->Title().String());
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
BView::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SetPackage(const PackageInfo& package)
|
|
{
|
|
if (package.State() == DOWNLOADING) {
|
|
AdoptDownloadProgress(package);
|
|
} else {
|
|
AdoptActions(package);
|
|
}
|
|
|
|
}
|
|
|
|
void AdoptActions(const PackageInfo& package)
|
|
{
|
|
PackageManager manager(
|
|
BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_HOME);
|
|
|
|
// TODO: if the given package is either a system package
|
|
// or a system dependency, show a message indicating that status
|
|
// so the user knows why no actions are presented
|
|
PackageActionList actions = manager.GetPackageActions(
|
|
const_cast<PackageInfo*>(&package),
|
|
fPackageActionHandler->GetModel());
|
|
|
|
bool clearNeeded = fStatusBar != NULL;
|
|
if (!clearNeeded) {
|
|
if (actions.CountItems() != fPackageActions.CountItems())
|
|
clearNeeded = true;
|
|
else {
|
|
for (int32 i = 0; i < actions.CountItems(); i++) {
|
|
if (actions.ItemAtFast(i)->Type()
|
|
!= fPackageActions.ItemAtFast(i)->Type()) {
|
|
clearNeeded = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fPackageActions = actions;
|
|
if (!clearNeeded)
|
|
return;
|
|
|
|
Clear();
|
|
|
|
// Add Buttons in reverse action order
|
|
for (int32 i = fPackageActions.CountItems() - 1; i >= 0; i--) {
|
|
const PackageActionRef& action = fPackageActions.ItemAtFast(i);
|
|
|
|
BMessage* message = new BMessage(MSG_PACKAGE_ACTION);
|
|
message->AddInt32("index", i);
|
|
|
|
BButton* button = new BButton(action->Label(), message);
|
|
fLayout->AddView(button);
|
|
button->SetTarget(this);
|
|
|
|
fButtons.AddItem(button);
|
|
}
|
|
}
|
|
|
|
void AdoptDownloadProgress(const PackageInfo& package)
|
|
{
|
|
if (fButtons.CountItems() > 0)
|
|
Clear();
|
|
|
|
if (fStatusBar == NULL) {
|
|
fStatusLabel = new BStringView("progress label",
|
|
B_TRANSLATE("Downloading:"));
|
|
fLayout->AddView(fStatusLabel);
|
|
|
|
fStatusBar = new BStatusBar("progress");
|
|
fStatusBar->SetMaxValue(100.0);
|
|
fStatusBar->SetExplicitMinSize(
|
|
BSize(StringWidth("XXX") * 5, B_SIZE_UNSET));
|
|
|
|
fLayout->AddView(fStatusBar);
|
|
}
|
|
|
|
fStatusBar->SetTo(package.DownloadProgress() * 100.0);
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
for (int32 i = fButtons.CountItems() - 1; i >= 0; i--) {
|
|
BButton* button = (BButton*)fButtons.ItemAtFast(i);
|
|
button->RemoveSelf();
|
|
delete button;
|
|
}
|
|
fButtons.MakeEmpty();
|
|
|
|
if (fStatusBar != NULL) {
|
|
fStatusBar->RemoveSelf();
|
|
delete fStatusBar;
|
|
fStatusBar = NULL;
|
|
}
|
|
if (fStatusLabel != NULL) {
|
|
fStatusLabel->RemoveSelf();
|
|
delete fStatusLabel;
|
|
fStatusLabel = NULL;
|
|
}
|
|
}
|
|
|
|
private:
|
|
BGroupLayout* fLayout;
|
|
PackageActionList fPackageActions;
|
|
PackageActionHandler* fPackageActionHandler;
|
|
BList fButtons;
|
|
|
|
BStringView* fStatusLabel;
|
|
BStatusBar* fStatusBar;
|
|
};
|
|
|
|
|
|
// #pragma mark - AboutView
|
|
|
|
|
|
enum {
|
|
MSG_EMAIL_PUBLISHER = 'emlp',
|
|
MSG_VISIT_PUBLISHER_WEBSITE = 'vpws',
|
|
};
|
|
|
|
|
|
class AboutView : public BView {
|
|
public:
|
|
AboutView()
|
|
:
|
|
BView("about view", 0),
|
|
fEmailIcon("text/x-email"),
|
|
fWebsiteIcon("text/html")
|
|
{
|
|
SetViewColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
|
|
kContentTint));
|
|
|
|
fDescriptionView = new MarkupTextView("description view");
|
|
fDescriptionView->SetLowColor(ViewColor());
|
|
fDescriptionView->SetInsets(be_plain_font->Size());
|
|
|
|
BScrollView* scrollView = new CustomScrollView(
|
|
"description scroll view", fDescriptionView);
|
|
|
|
BFont smallFont;
|
|
GetFont(&smallFont);
|
|
smallFont.SetSize(std::max(9.0f, ceilf(smallFont.Size() * 0.85f)));
|
|
|
|
// TODO: Clicking the screen shot view should open ShowImage with the
|
|
// the screen shot. This could be done by writing the screen shot to
|
|
// a temporary folder, launching ShowImage to display it, and writing
|
|
// all other screenshots associated with the package to the same folder
|
|
// so the user can use the ShowImage navigation to view the other
|
|
// screenshots.
|
|
fScreenshotView = new BitmapView("screenshot view");
|
|
fScreenshotView->SetExplicitMinSize(BSize(64.0f, 64.0f));
|
|
fScreenshotView->SetExplicitMaxSize(
|
|
BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
|
|
fScreenshotView->SetExplicitAlignment(
|
|
BAlignment(B_ALIGN_CENTER, B_ALIGN_TOP));
|
|
|
|
fEmailIconView = new BitmapView("email icon view");
|
|
fEmailLinkView = new LinkView("email link view", "",
|
|
new BMessage(MSG_EMAIL_PUBLISHER), kLightBlack);
|
|
fEmailLinkView->SetFont(&smallFont);
|
|
|
|
fWebsiteIconView = new BitmapView("website icon view");
|
|
fWebsiteLinkView = new LinkView("website link view", "",
|
|
new BMessage(MSG_VISIT_PUBLISHER_WEBSITE), kLightBlack);
|
|
fWebsiteLinkView->SetFont(&smallFont);
|
|
|
|
BGroupView* leftGroup = new BGroupView(B_VERTICAL,
|
|
B_USE_DEFAULT_SPACING);
|
|
leftGroup->SetViewColor(ViewColor());
|
|
|
|
BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0.0f)
|
|
// .Add(BSpaceLayoutItem::CreateHorizontalStrut(32.0f))
|
|
.AddGroup(leftGroup, 1.0f)
|
|
.Add(fScreenshotView)
|
|
.AddGroup(B_HORIZONTAL)
|
|
.AddGrid(B_USE_HALF_ITEM_SPACING, B_USE_HALF_ITEM_SPACING)
|
|
.Add(fEmailIconView, 0, 0)
|
|
.Add(fEmailLinkView, 1, 0)
|
|
.Add(fWebsiteIconView, 0, 1)
|
|
.Add(fWebsiteLinkView, 1, 1)
|
|
.End()
|
|
.AddGlue()
|
|
.End()
|
|
.SetInsets(B_USE_DEFAULT_SPACING)
|
|
.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
|
|
.End()
|
|
.Add(scrollView, 2.0f)
|
|
|
|
.SetExplicitMaxSize(BSize(B_SIZE_UNSET, B_SIZE_UNLIMITED))
|
|
.SetInsets(0.0f, -1.0f, -1.0f, -1.0f)
|
|
;
|
|
}
|
|
|
|
virtual ~AboutView()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
virtual void AttachedToWindow()
|
|
{
|
|
fWebsiteLinkView->SetTarget(this);
|
|
}
|
|
|
|
virtual void MessageReceived(BMessage* message)
|
|
{
|
|
switch (message->what) {
|
|
case MSG_EMAIL_PUBLISHER:
|
|
{
|
|
// TODO: Implement. If memory serves, there is a
|
|
// standard command line interface which mail apps should
|
|
// support, i.e. to open a compose window with the TO: field
|
|
// already set.
|
|
break;
|
|
}
|
|
|
|
case MSG_VISIT_PUBLISHER_WEBSITE:
|
|
{
|
|
BPrivate::Support::BUrl url(fWebsiteLinkView->Text());
|
|
url.OpenWithPreferredApplication();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
BView::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SetPackage(const PackageInfo& package)
|
|
{
|
|
fDescriptionView->SetText(package.ShortDescription(),
|
|
package.FullDescription());
|
|
|
|
fEmailIconView->SetBitmap(fEmailIcon.Bitmap(SharedBitmap::SIZE_16));
|
|
_SetContactInfo(fEmailLinkView, package.Publisher().Email());
|
|
fWebsiteIconView->SetBitmap(fWebsiteIcon.Bitmap(SharedBitmap::SIZE_16));
|
|
_SetContactInfo(fWebsiteLinkView, package.Publisher().Website());
|
|
|
|
const BBitmap* screenshot = NULL;
|
|
const BitmapList& screenShots = package.Screenshots();
|
|
if (screenShots.CountItems() > 0) {
|
|
const BitmapRef& bitmapRef = screenShots.ItemAtFast(0);
|
|
if (bitmapRef.Get() != NULL)
|
|
screenshot = bitmapRef->Bitmap(SharedBitmap::SIZE_ANY);
|
|
}
|
|
fScreenshotView->SetBitmap(screenshot);
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
fDescriptionView->SetText("");
|
|
fEmailIconView->SetBitmap(NULL);
|
|
fEmailLinkView->SetText("");
|
|
fWebsiteIconView->SetBitmap(NULL);
|
|
fWebsiteLinkView->SetText("");
|
|
|
|
fScreenshotView->SetBitmap(NULL);
|
|
}
|
|
|
|
private:
|
|
void _SetContactInfo(LinkView* view, const BString& string)
|
|
{
|
|
if (string.Length() > 0) {
|
|
view->SetText(string);
|
|
view->SetEnabled(true);
|
|
} else {
|
|
view->SetText(B_TRANSLATE("<no info>"));
|
|
view->SetEnabled(false);
|
|
}
|
|
}
|
|
|
|
private:
|
|
MarkupTextView* fDescriptionView;
|
|
|
|
BitmapView* fScreenshotView;
|
|
|
|
SharedBitmap fEmailIcon;
|
|
BitmapView* fEmailIconView;
|
|
LinkView* fEmailLinkView;
|
|
|
|
SharedBitmap fWebsiteIcon;
|
|
BitmapView* fWebsiteIconView;
|
|
LinkView* fWebsiteLinkView;
|
|
};
|
|
|
|
|
|
// #pragma mark - UserRatingsView
|
|
|
|
|
|
class RatingItemView : public BGroupView {
|
|
public:
|
|
RatingItemView(const UserRating& rating, const BitmapRef& voteUpIcon,
|
|
const BitmapRef& voteDownIcon)
|
|
:
|
|
BGroupView(B_HORIZONTAL, 0.0f)
|
|
{
|
|
SetViewColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
|
|
kContentTint));
|
|
|
|
fAvatarView = new BitmapView("avatar view");
|
|
if (rating.User().Avatar().Get() != NULL) {
|
|
fAvatarView->SetBitmap(
|
|
rating.User().Avatar()->Bitmap(SharedBitmap::SIZE_16));
|
|
}
|
|
fAvatarView->SetExplicitMinSize(BSize(16.0f, 16.0f));
|
|
|
|
fNameView = new BStringView("user name", rating.User().NickName());
|
|
BFont nameFont(be_bold_font);
|
|
nameFont.SetSize(std::max(9.0f, floorf(nameFont.Size() * 0.9f)));
|
|
fNameView->SetFont(&nameFont);
|
|
fNameView->SetHighColor(kLightBlack);
|
|
fNameView->SetExplicitMaxSize(
|
|
BSize(nameFont.StringWidth("xxxxxxxxxxxxxxxxxxxxxx"), B_SIZE_UNSET));
|
|
|
|
fRatingView = new RatingView();
|
|
fRatingView->SetRating(rating.Rating());
|
|
|
|
BString ratingLabel;
|
|
ratingLabel.SetToFormat("%.1f", rating.Rating());
|
|
fRatingLabelView = new BStringView("rating label", ratingLabel);
|
|
|
|
BString versionLabel(B_TRANSLATE("for %Version%"));
|
|
versionLabel.ReplaceAll("%Version%", rating.PackageVersion());
|
|
fPackageVersionView = new BStringView("package version",
|
|
versionLabel);
|
|
BFont versionFont(be_plain_font);
|
|
versionFont.SetSize(std::max(9.0f, floorf(versionFont.Size() * 0.85f)));
|
|
fPackageVersionView->SetFont(&versionFont);
|
|
fPackageVersionView->SetHighColor(kLightBlack);
|
|
|
|
// TODO: User rating IDs to identify which rating to vote up or down
|
|
BMessage* voteUpMessage = new BMessage(MSG_VOTE_UP);
|
|
voteUpMessage->AddInt32("rating id", -1);
|
|
BMessage* voteDownMessage = new BMessage(MSG_VOTE_DOWN);
|
|
voteDownMessage->AddInt32("rating id", -1);
|
|
|
|
fVoteUpIconView = new BitmapButton("vote up icon", voteUpMessage);
|
|
fUpVoteCountView = new BStringView("up vote count", "");
|
|
fVoteDownIconView = new BitmapButton("vote down icon", voteDownMessage);
|
|
fDownVoteCountView = new BStringView("up vote count", "");
|
|
|
|
fVoteUpIconView->SetBitmap(
|
|
voteUpIcon->Bitmap(SharedBitmap::SIZE_16));
|
|
fVoteDownIconView->SetBitmap(
|
|
voteDownIcon->Bitmap(SharedBitmap::SIZE_16));
|
|
|
|
fUpVoteCountView->SetFont(&versionFont);
|
|
fUpVoteCountView->SetHighColor(kLightBlack);
|
|
fDownVoteCountView->SetFont(&versionFont);
|
|
fDownVoteCountView->SetHighColor(kLightBlack);
|
|
|
|
BString voteCountLabel;
|
|
voteCountLabel.SetToFormat("%" B_PRId32, rating.UpVotes());
|
|
fUpVoteCountView->SetText(voteCountLabel);
|
|
voteCountLabel.SetToFormat("%" B_PRId32, rating.DownVotes());
|
|
fDownVoteCountView->SetText(voteCountLabel);
|
|
|
|
fTextView = new TextView("rating text");
|
|
fTextView->SetViewColor(ViewColor());
|
|
ParagraphStyle paragraphStyle(fTextView->ParagraphStyle());
|
|
paragraphStyle.SetJustify(true);
|
|
fTextView->SetParagraphStyle(paragraphStyle);
|
|
|
|
fTextView->SetText(rating.Comment());
|
|
|
|
BLayoutBuilder::Group<>(this)
|
|
.Add(fAvatarView, 0.2f)
|
|
.AddGroup(B_VERTICAL, 0.0f)
|
|
.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
|
|
.Add(fNameView)
|
|
.Add(fRatingView)
|
|
.Add(fRatingLabelView)
|
|
.AddGlue(0.1f)
|
|
.Add(fPackageVersionView)
|
|
.AddGlue(5.0f)
|
|
.AddGroup(B_HORIZONTAL, 0.0f, 0.0f)
|
|
.Add(fVoteUpIconView)
|
|
.Add(fUpVoteCountView)
|
|
.AddStrut(B_USE_HALF_ITEM_SPACING)
|
|
.Add(fVoteDownIconView)
|
|
.Add(fDownVoteCountView)
|
|
.End()
|
|
.End()
|
|
.Add(fTextView)
|
|
.End()
|
|
.SetInsets(B_USE_DEFAULT_SPACING)
|
|
;
|
|
}
|
|
|
|
private:
|
|
BitmapView* fAvatarView;
|
|
BStringView* fNameView;
|
|
RatingView* fRatingView;
|
|
BStringView* fRatingLabelView;
|
|
BStringView* fPackageVersionView;
|
|
|
|
BitmapView* fVoteUpIconView;
|
|
BStringView* fUpVoteCountView;
|
|
BitmapView* fVoteDownIconView;
|
|
BStringView* fDownVoteCountView;
|
|
|
|
TextView* fTextView;
|
|
};
|
|
|
|
|
|
class RatingContainerView : public BGroupView {
|
|
public:
|
|
RatingContainerView()
|
|
:
|
|
BGroupView(B_VERTICAL, 0.0)
|
|
{
|
|
SetViewColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
|
|
kContentTint));
|
|
AddChild(BSpaceLayoutItem::CreateGlue());
|
|
}
|
|
|
|
virtual BSize MinSize()
|
|
{
|
|
BSize minSize = BGroupView::MinSize();
|
|
return BSize(minSize.width, 80);
|
|
}
|
|
|
|
protected:
|
|
virtual void DoLayout()
|
|
{
|
|
BGroupView::DoLayout();
|
|
if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) {
|
|
BRect layoutArea = GroupLayout()->LayoutArea();
|
|
float layoutHeight = layoutArea.Height();
|
|
// Min size is not reliable with HasHeightForWidth() children,
|
|
// since it does not reflect how thos children are currently
|
|
// laid out, but what their theoretical minimum size would be.
|
|
|
|
BLayoutItem* lastItem = GroupLayout()->ItemAt(
|
|
GroupLayout()->CountItems() - 1);
|
|
if (lastItem != NULL) {
|
|
layoutHeight = lastItem->Frame().bottom;
|
|
}
|
|
|
|
float viewHeight = Bounds().Height();
|
|
|
|
float max = layoutHeight- viewHeight;
|
|
scrollBar->SetRange(0, max);
|
|
if (layoutHeight > 0)
|
|
scrollBar->SetProportion(viewHeight / layoutHeight);
|
|
else
|
|
scrollBar->SetProportion(1);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class RatingSummaryView : public BGridView {
|
|
public:
|
|
RatingSummaryView()
|
|
:
|
|
BGridView("rating summary view", B_USE_HALF_ITEM_SPACING, 0.0f)
|
|
{
|
|
SetViewColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
|
|
kContentTint - 0.1));
|
|
|
|
BLayoutBuilder::Grid<> layoutBuilder(this);
|
|
|
|
BFont smallFont;
|
|
GetFont(&smallFont);
|
|
smallFont.SetSize(std::max(9.0f, floorf(smallFont.Size() * 0.85f)));
|
|
|
|
for (int32 i = 0; i < 5; i++) {
|
|
BString label;
|
|
label.SetToFormat("%" B_PRId32, 5 - i);
|
|
fLabelViews[i] = new BStringView("", label);
|
|
fLabelViews[i]->SetFont(&smallFont);
|
|
fLabelViews[i]->SetHighColor(kLightBlack);
|
|
layoutBuilder.Add(fLabelViews[i], 0, i);
|
|
|
|
fDiagramBarViews[i] = new DiagramBarView();
|
|
layoutBuilder.Add(fDiagramBarViews[i], 1, i);
|
|
|
|
fCountViews[i] = new BStringView("", "");
|
|
fCountViews[i]->SetFont(&smallFont);
|
|
fCountViews[i]->SetHighColor(kLightBlack);
|
|
fCountViews[i]->SetAlignment(B_ALIGN_RIGHT);
|
|
layoutBuilder.Add(fCountViews[i], 2, i);
|
|
}
|
|
|
|
layoutBuilder.SetInsets(5);
|
|
}
|
|
|
|
void SetToSummary(const RatingSummary& summary) {
|
|
for (int32 i = 0; i < 5; i++) {
|
|
int32 count = summary.ratingCountByStar[4 - i];
|
|
|
|
BString label;
|
|
label.SetToFormat("%" B_PRId32, count);
|
|
fCountViews[i]->SetText(label);
|
|
|
|
if (summary.ratingCount > 0) {
|
|
fDiagramBarViews[i]->SetValue(
|
|
(float)count / summary.ratingCount);
|
|
} else
|
|
fDiagramBarViews[i]->SetValue(0.0f);
|
|
}
|
|
}
|
|
|
|
void Clear() {
|
|
for (int32 i = 0; i < 5; i++) {
|
|
fCountViews[i]->SetText("");
|
|
fDiagramBarViews[i]->SetValue(0.0f);
|
|
}
|
|
}
|
|
|
|
private:
|
|
BStringView* fLabelViews[5];
|
|
DiagramBarView* fDiagramBarViews[5];
|
|
BStringView* fCountViews[5];
|
|
};
|
|
|
|
|
|
class UserRatingsView : public BGroupView {
|
|
public:
|
|
UserRatingsView()
|
|
:
|
|
BGroupView("package ratings view", B_HORIZONTAL),
|
|
fThumbsUpIcon(BitmapRef(new SharedBitmap(502), true)),
|
|
fThumbsDownIcon(BitmapRef(new SharedBitmap(503), true))
|
|
{
|
|
SetViewColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
|
|
kContentTint));
|
|
|
|
fRatingSummaryView = new RatingSummaryView();
|
|
|
|
RatingContainerView* ratingsContainerView = new RatingContainerView();
|
|
fRatingContainerLayout = ratingsContainerView->GroupLayout();
|
|
|
|
BScrollView* scrollView = new RatingsScrollView(
|
|
"ratings scroll view", ratingsContainerView);
|
|
|
|
BLayoutBuilder::Group<>(this)
|
|
.AddGroup(B_VERTICAL)
|
|
.Add(fRatingSummaryView, 0.0f)
|
|
.AddGlue()
|
|
.SetInsets(0.0f, B_USE_DEFAULT_SPACING, 0.0f, 0.0f)
|
|
.End()
|
|
.Add(scrollView, 1.0f)
|
|
.SetInsets(B_USE_DEFAULT_SPACING, -1.0f, -1.0f, -1.0f)
|
|
;
|
|
}
|
|
|
|
virtual ~UserRatingsView()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void SetPackage(const PackageInfo& package)
|
|
{
|
|
ClearRatings();
|
|
|
|
// TODO: Re-use rating summary already used for TitleView...
|
|
fRatingSummaryView->SetToSummary(package.CalculateRatingSummary());
|
|
|
|
const UserRatingList& userRatings = package.UserRatings();
|
|
|
|
int count = userRatings.CountItems();
|
|
if (count == 0) {
|
|
BStringView* noRatingsView = new BStringView("no ratings",
|
|
B_TRANSLATE("No user ratings available."));
|
|
noRatingsView->SetAlignment(B_ALIGN_CENTER);
|
|
noRatingsView->SetHighColor(kLightBlack);
|
|
noRatingsView->SetExplicitMaxSize(
|
|
BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
|
|
fRatingContainerLayout->AddView(0, noRatingsView);
|
|
return;
|
|
}
|
|
|
|
// TODO: Sort by age or usefullness rating
|
|
// TODO: Optionally hide ratings that are not in the system language
|
|
|
|
for (int i = count - 1; i >= 0; i--) {
|
|
const UserRating& rating = userRatings.ItemAtFast(i);
|
|
RatingItemView* view = new RatingItemView(rating, fThumbsUpIcon,
|
|
fThumbsDownIcon);
|
|
fRatingContainerLayout->AddView(0, view);
|
|
}
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
fRatingSummaryView->Clear();
|
|
ClearRatings();
|
|
}
|
|
|
|
void ClearRatings()
|
|
{
|
|
for (int32 i = fRatingContainerLayout->CountItems() - 1;
|
|
BLayoutItem* item = fRatingContainerLayout->ItemAt(i); i--) {
|
|
BView* view = dynamic_cast<RatingItemView*>(item->View());
|
|
if (view == NULL)
|
|
view = dynamic_cast<BStringView*>(item->View());
|
|
if (view != NULL) {
|
|
view->RemoveSelf();
|
|
delete view;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
BGroupLayout* fRatingContainerLayout;
|
|
RatingSummaryView* fRatingSummaryView;
|
|
BitmapRef fThumbsUpIcon;
|
|
BitmapRef fThumbsDownIcon;
|
|
};
|
|
|
|
|
|
// #pragma mark - ChangelogView
|
|
|
|
|
|
class ChangelogView : public BView {
|
|
public:
|
|
ChangelogView()
|
|
:
|
|
BView("package changelog view", B_WILL_DRAW),
|
|
fLayout(new BGroupLayout(B_HORIZONTAL))
|
|
{
|
|
SetViewColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
|
|
kContentTint));
|
|
|
|
SetLayout(fLayout);
|
|
|
|
fTextView = new MarkupTextView("changelog view");
|
|
fTextView->SetLowColor(ViewColor());
|
|
fTextView->SetInsets(be_plain_font->Size());
|
|
|
|
BScrollView* scrollView = new CustomScrollView(
|
|
"changelog scroll view", fTextView);
|
|
|
|
BLayoutBuilder::Group<>(fLayout)
|
|
.Add(BSpaceLayoutItem::CreateHorizontalStrut(32.0f))
|
|
.Add(scrollView, 1.0f)
|
|
.SetInsets(B_USE_DEFAULT_SPACING, -1.0f, -1.0f, -1.0f)
|
|
;
|
|
}
|
|
|
|
virtual ~ChangelogView()
|
|
{
|
|
}
|
|
|
|
virtual void Draw(BRect updateRect)
|
|
{
|
|
}
|
|
|
|
void SetPackage(const PackageInfo& package)
|
|
{
|
|
const BString& changelog = package.Changelog();
|
|
if (changelog.Length() > 0)
|
|
fTextView->SetText(changelog);
|
|
else
|
|
fTextView->SetDisabledText(B_TRANSLATE("No changelog available."));
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
fTextView->SetText("");
|
|
}
|
|
|
|
private:
|
|
BGroupLayout* fLayout;
|
|
MarkupTextView* fTextView;
|
|
};
|
|
|
|
|
|
// #pragma mark - PagesView
|
|
|
|
|
|
class PagesView : public BTabView {
|
|
public:
|
|
PagesView()
|
|
:
|
|
BTabView("pages view", B_WIDTH_FROM_WIDEST),
|
|
fLayout(new BCardLayout())
|
|
{
|
|
SetBorder(B_NO_BORDER);
|
|
|
|
fAboutView = new AboutView();
|
|
fUserRatingsView = new UserRatingsView();
|
|
fChangelogView = new ChangelogView();
|
|
|
|
AddTab(fAboutView);
|
|
AddTab(fUserRatingsView);
|
|
AddTab(fChangelogView);
|
|
|
|
TabAt(0)->SetLabel(B_TRANSLATE("About"));
|
|
TabAt(1)->SetLabel(B_TRANSLATE("Ratings"));
|
|
TabAt(2)->SetLabel(B_TRANSLATE("Changelog"));
|
|
|
|
Select(0);
|
|
}
|
|
|
|
virtual ~PagesView()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void SetPackage(const PackageInfo& package)
|
|
{
|
|
Select(0);
|
|
fAboutView->SetPackage(package);
|
|
fUserRatingsView->SetPackage(package);
|
|
fChangelogView->SetPackage(package);
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
fAboutView->Clear();
|
|
fUserRatingsView->Clear();
|
|
fChangelogView->Clear();
|
|
}
|
|
|
|
private:
|
|
BCardLayout* fLayout;
|
|
|
|
AboutView* fAboutView;
|
|
UserRatingsView* fUserRatingsView;
|
|
ChangelogView* fChangelogView;
|
|
};
|
|
|
|
|
|
// #pragma mark - PackageInfoViewListener
|
|
|
|
|
|
enum {
|
|
MSG_UPDATE_PACKAGE = 'updp'
|
|
};
|
|
|
|
|
|
class PackageInfoView::Listener : public PackageInfoListener {
|
|
public:
|
|
Listener(PackageInfoView* view)
|
|
:
|
|
fView(view)
|
|
{
|
|
}
|
|
|
|
virtual ~Listener()
|
|
{
|
|
}
|
|
|
|
virtual void PackageChanged(const PackageInfoEvent& event)
|
|
{
|
|
BMessenger messenger(fView);
|
|
if (!messenger.IsValid())
|
|
return;
|
|
|
|
const PackageInfo& package = *event.Package().Get();
|
|
|
|
BMessage message(MSG_UPDATE_PACKAGE);
|
|
message.AddString("title", package.Title());
|
|
message.AddUInt32("changes", event.Changes());
|
|
|
|
messenger.SendMessage(&message);
|
|
}
|
|
|
|
void SetPackage(const PackageInfoRef& package)
|
|
{
|
|
if (fPackage == package)
|
|
return;
|
|
|
|
PackageInfoListenerRef listener(this);
|
|
|
|
if (fPackage.Get() != NULL)
|
|
fPackage->RemoveListener(listener);
|
|
|
|
fPackage = package;
|
|
|
|
if (fPackage.Get() != NULL)
|
|
fPackage->AddListener(listener);
|
|
}
|
|
|
|
const PackageInfoRef& Package() const
|
|
{
|
|
return fPackage;
|
|
}
|
|
|
|
private:
|
|
PackageInfoView* fView;
|
|
PackageInfoRef fPackage;
|
|
};
|
|
|
|
|
|
// #pragma mark - PackageInfoView
|
|
|
|
|
|
PackageInfoView::PackageInfoView(BLocker* modelLock,
|
|
PackageActionHandler* handler)
|
|
:
|
|
BView("package info view", 0),
|
|
fModelLock(modelLock),
|
|
fPackageListener(new(std::nothrow) Listener(this))
|
|
{
|
|
fCardLayout = new BCardLayout();
|
|
SetLayout(fCardLayout);
|
|
|
|
BGroupView* noPackageCard = new BGroupView("no package card", B_VERTICAL);
|
|
AddChild(noPackageCard);
|
|
|
|
BStringView* noPackageView = new BStringView("no package view",
|
|
B_TRANSLATE("Click a package to view information"));
|
|
noPackageView->SetHighColor(kLightBlack);
|
|
noPackageView->SetExplicitAlignment(BAlignment(
|
|
B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER));
|
|
|
|
BLayoutBuilder::Group<>(noPackageCard)
|
|
.Add(noPackageView)
|
|
.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED))
|
|
;
|
|
|
|
BGroupView* packageCard = new BGroupView("package card", B_VERTICAL);
|
|
AddChild(packageCard);
|
|
|
|
fCardLayout->SetVisibleItem((int32)0);
|
|
|
|
fTitleView = new TitleView();
|
|
fPackageActionView = new PackageActionView(handler);
|
|
fPackageActionView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
|
|
B_SIZE_UNSET));
|
|
fPagesView = new PagesView();
|
|
|
|
BLayoutBuilder::Group<>(packageCard)
|
|
.AddGroup(B_HORIZONTAL, 0.0f)
|
|
.Add(fTitleView, 6.0f)
|
|
.Add(fPackageActionView, 1.0f)
|
|
.SetInsets(
|
|
B_USE_DEFAULT_SPACING, 0.0f,
|
|
B_USE_DEFAULT_SPACING, 0.0f)
|
|
.End()
|
|
.Add(fPagesView)
|
|
;
|
|
|
|
Clear();
|
|
}
|
|
|
|
|
|
PackageInfoView::~PackageInfoView()
|
|
{
|
|
fPackageListener->SetPackage(PackageInfoRef(NULL));
|
|
delete fPackageListener;
|
|
}
|
|
|
|
|
|
void
|
|
PackageInfoView::AttachedToWindow()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
PackageInfoView::MessageReceived(BMessage* message)
|
|
{
|
|
switch (message->what) {
|
|
case MSG_UPDATE_PACKAGE:
|
|
{
|
|
if (fPackageListener->Package().Get() == NULL)
|
|
break;
|
|
|
|
BString title;
|
|
uint32 changes;
|
|
if (message->FindString("title", &title) != B_OK
|
|
|| message->FindUInt32("changes", &changes) != B_OK) {
|
|
break;
|
|
}
|
|
|
|
const PackageInfo& package = *fPackageListener->Package().Get();
|
|
if (package.Title() != title)
|
|
break;
|
|
|
|
BAutolock _(fModelLock);
|
|
|
|
if ((changes & PKG_CHANGED_DESCRIPTION) != 0
|
|
|| (changes & PKG_CHANGED_SCREENSHOTS) != 0) {
|
|
fPagesView->SetPackage(package);
|
|
}
|
|
|
|
if ((changes & PKG_CHANGED_RATINGS) != 0) {
|
|
fPagesView->SetPackage(package);
|
|
fTitleView->SetPackage(package);
|
|
}
|
|
|
|
if ((changes & PKG_CHANGED_STATE) != 0) {
|
|
fPackageActionView->SetPackage(package);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
BView::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
PackageInfoView::SetPackage(const PackageInfoRef& packageRef)
|
|
{
|
|
BAutolock _(fModelLock);
|
|
|
|
const PackageInfo& package = *packageRef.Get();
|
|
|
|
fTitleView->SetPackage(package);
|
|
fPackageActionView->SetPackage(package);
|
|
fPagesView->SetPackage(package);
|
|
|
|
fCardLayout->SetVisibleItem(1);
|
|
|
|
fPackageListener->SetPackage(packageRef);
|
|
}
|
|
|
|
|
|
void
|
|
PackageInfoView::Clear()
|
|
{
|
|
fTitleView->Clear();
|
|
fPackageActionView->Clear();
|
|
fPagesView->Clear();
|
|
|
|
fCardLayout->SetVisibleItem((int32)0);
|
|
|
|
BAutolock _(fModelLock);
|
|
|
|
fPackageListener->SetPackage(PackageInfoRef(NULL));
|
|
}
|
|
|