#include "CountView.h"
#include <Application.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <Locale.h>
#include <StringFormat.h>
#include "AutoLock.h"
#include "Bitmaps.h"
#include "ContainerWindow.h"
#include "DirMenu.h"
#include "PoseView.h"
#include "Utilities.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "CountView"
const bigtime_t kBarberPoleDelay = 500000;
static const float kMinFontSize = 8.0f;
BCountView::BCountView(BPoseView* view)
:
BView("CountVw", B_PULSE_NEEDED | B_WILL_DRAW),
fLastCount(-1),
fLastCountSelected(-1),
fPoseView(view),
fShowingBarberPole(false),
fBarberPoleMap(NULL),
fLastBarberPoleOffset(5),
fStartSpinningAfter(0),
fTypeAheadString(""),
fFilterString("")
{
GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, R_BarberPoleBitmap, &fBarberPoleMap);
SetFont(be_plain_font);
SetFontSize(std::max(kMinFontSize, ceilf(be_plain_font->Size() * 0.75f)));
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
SetLowUIColor(ViewUIColor());
}
BCountView::~BCountView()
{
delete fBarberPoleMap;
}
void
BCountView::TrySpinningBarberPole()
{
if (!fShowingBarberPole)
return;
if (fStartSpinningAfter && system_time() < fStartSpinningAfter)
return;
if (fStartSpinningAfter) {
fStartSpinningAfter = 0;
Invalidate(TextAndBarberPoleRect());
} else
Invalidate(BarberPoleInnerRect());
}
void
BCountView::Pulse()
{
TrySpinningBarberPole();
}
void
BCountView::EndBarberPole()
{
if (!fShowingBarberPole)
return;
fShowingBarberPole = false;
Invalidate();
}
void
BCountView::StartBarberPole()
{
AutoLock<BWindow> lock(Window());
if (fShowingBarberPole)
return;
fShowingBarberPole = true;
fStartSpinningAfter = system_time() + kBarberPoleDelay;
}
BRect
BCountView::BarberPoleInnerRect() const
{
BRect result = Bounds();
result.InsetBy(3, 4);
result.left = result.right - 7;
result.bottom = result.top + 7;
return result;
}
BRect
BCountView::BarberPoleOuterRect() const
{
BRect result(BarberPoleInnerRect());
result.InsetBy(-1, -1);
return result;
}
BRect
BCountView::TextInvalRect() const
{
BRect result = TextAndBarberPoleRect();
if (fShowingBarberPole)
result.right -= 10;
return result;
}
BRect
BCountView::TextAndBarberPoleRect() const
{
BRect result = Bounds();
result.InsetBy(be_control_look->ComposeSpacing(B_USE_SMALL_SPACING) / 2,
floorf(result.Height() * 0.25f));
return result;
}
void
BCountView::CheckCount()
{
bool invalidate = false;
if (fLastCount != fPoseView->CountItems()) {
fLastCount = fPoseView->CountItems();
invalidate = true;
}
if (fLastCountSelected != fPoseView->CountSelected()) {
fLastCountSelected = fPoseView->CountSelected();
invalidate = true;
}
if (invalidate)
Invalidate(TextInvalRect());
TrySpinningBarberPole();
}
void
BCountView::Draw(BRect updateRect)
{
BRect bounds(Bounds());
rgb_color color = ViewColor();
if (IsTypingAhead())
color = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
SetLowColor(color);
be_control_look->DrawBorder(this, bounds, updateRect,
color, B_PLAIN_BORDER, 0,
BControlLook::B_BOTTOM_BORDER | BControlLook::B_LEFT_BORDER);
be_control_look->DrawMenuBarBackground(this, bounds, updateRect, color);
BString itemString;
if (IsTypingAhead())
itemString << TypeAhead();
else if (IsFiltering()) {
BString lastCountStr;
fNumberFormat.Format(lastCountStr, fLastCount);
if (fLastCountSelected != 0) {
static BStringFormat selectedFilteredFormat(B_TRANSLATE_COMMENT(
"{0, plural, other{#/%total %filter}}",
"Number of selected items from a filtered set: \"10/30 view\""));
selectedFilteredFormat.Format(itemString, fLastCountSelected);
itemString.ReplaceFirst("%total", lastCountStr);
itemString.ReplaceFirst("%filter", Filter());
} else
itemString << lastCountStr << " " << Filter();
} else {
if (fLastCount == 0)
itemString << B_TRANSLATE("no items");
else if (fLastCountSelected == 0) {
static BStringFormat itemFormat(B_TRANSLATE_COMMENT(
"{0, plural, one{# item} other{# items}}",
"Number of selected items: \"1 item\" or \"2 items\""));
itemFormat.Format(itemString, fLastCount);
} else {
static BStringFormat selectedFormat(B_TRANSLATE_COMMENT(
"{0, plural, other{#/%total selected}}",
"Number of selected items out of a total: \"10/30 selected\""));
BString lastCountStr;
fNumberFormat.Format(lastCountStr, fLastCount);
selectedFormat.Format(itemString, fLastCountSelected);
itemString.ReplaceFirst("%total", lastCountStr);
}
}
BRect textRect(TextInvalRect());
TruncateString(&itemString, IsTypingAhead() ? B_TRUNCATE_BEGINNING
: IsFiltering() ? B_TRUNCATE_MIDDLE : B_TRUNCATE_END,
textRect.Width());
if (IsTypingAhead()) {
SetHighUIColor(B_DOCUMENT_TEXT_COLOR);
} else
SetHighUIColor(B_PANEL_TEXT_COLOR);
MovePenTo(textRect.LeftBottom());
DrawString(itemString.String());
bounds.top++;
rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT);
rgb_color shadow = tint_color(ViewColor(), B_DARKEN_2_TINT);
BeginLineArray(fShowingBarberPole && !fStartSpinningAfter ? 9 : 5);
if (!fShowingBarberPole || fStartSpinningAfter) {
EndLineArray();
return;
}
BRect barberPoleRect(BarberPoleOuterRect());
AddLine(barberPoleRect.LeftTop(), barberPoleRect.RightTop(), shadow);
AddLine(barberPoleRect.LeftTop(), barberPoleRect.LeftBottom(), shadow);
AddLine(barberPoleRect.LeftBottom(), barberPoleRect.RightBottom(), light);
AddLine(barberPoleRect.RightBottom(), barberPoleRect.RightTop(), light);
EndLineArray();
barberPoleRect.InsetBy(1, 1);
BRect destRect(fBarberPoleMap
? fBarberPoleMap->Bounds() : BRect(0, 0, 0, 0));
destRect.OffsetTo(barberPoleRect.LeftTop()
- BPoint(0, fLastBarberPoleOffset));
fLastBarberPoleOffset -= 1;
if (fLastBarberPoleOffset < 0)
fLastBarberPoleOffset = 5;
BRegion region;
region.Set(BarberPoleInnerRect());
ConstrainClippingRegion(®ion);
if (fBarberPoleMap)
DrawBitmap(fBarberPoleMap, destRect);
}
void
BCountView::MouseDown(BPoint)
{
BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
ThrowOnAssert(window != NULL);
window->Activate();
window->UpdateIfNeeded();
if (fPoseView->IsFilePanel() || fPoseView->TargetModel() == NULL)
return;
if (window->TargetModel()->IsRoot())
return;
BDirMenu menu(NULL, be_app, B_REFS_RECEIVED);
BEntry entry;
if (entry.SetTo(window->TargetModel()->EntryRef()) == B_OK)
menu.Populate(&entry, Window(), false, false, true, false, true);
else
menu.Populate(NULL, Window(), false, false, true, false, true);
BPoint point = Bounds().LeftBottom();
point.y += 3;
ConvertToScreen(&point);
BRect clickToOpenRect(Bounds());
ConvertToScreen(&clickToOpenRect);
menu.Go(point, true, true, clickToOpenRect);
}
void
BCountView::AttachedToWindow()
{
CheckCount();
}
void
BCountView::SetTypeAhead(const char* string)
{
fTypeAheadString = string;
Invalidate();
}
const char*
BCountView::TypeAhead() const
{
return fTypeAheadString.String();
}
bool
BCountView::IsTypingAhead() const
{
return fTypeAheadString.Length() != 0;
}
void
BCountView::AddFilterCharacter(const char* character)
{
fFilterString.AppendChars(character, 1);
Invalidate();
}
void
BCountView::RemoveFilterCharacter()
{
fFilterString.TruncateChars(fFilterString.CountChars() - 1);
Invalidate();
}
void
BCountView::CancelFilter()
{
fFilterString.Truncate(0);
Invalidate();
}
const char*
BCountView::Filter() const
{
return fFilterString.String();
}
bool
BCountView::IsFiltering() const
{
return fFilterString.Length() > 0;
}