d797e0629c
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41801 a95241bf-73f2-0310-859d-f6bbb57e9c96
983 lines
24 KiB
C++
983 lines
24 KiB
C++
/*
|
|
Open Tracker License
|
|
|
|
Terms and Conditions
|
|
|
|
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is furnished to do
|
|
so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice applies to all licensees
|
|
and shall be included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Be Incorporated shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings in
|
|
this Software without prior written authorization from Be Incorporated.
|
|
|
|
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
|
|
of Be Incorporated in the United States and other countries. Other brand product
|
|
names are registered trademarks or trademarks of their respective holders.
|
|
All rights reserved.
|
|
*/
|
|
|
|
/*! A subclass of BWindow that is used to display the status of the Tracker
|
|
operations (copying, deleting, etc.).
|
|
*/
|
|
|
|
|
|
#include <Application.h>
|
|
#include <Button.h>
|
|
#include <Catalog.h>
|
|
#include <ControlLook.h>
|
|
#include <Debug.h>
|
|
#include <Locale.h>
|
|
#include <MessageFilter.h>
|
|
#include <StringView.h>
|
|
#include <String.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "AutoLock.h"
|
|
#include "Bitmaps.h"
|
|
#include "Commands.h"
|
|
#include "StatusWindow.h"
|
|
#include "StringForSize.h"
|
|
#include "DeskWindow.h"
|
|
|
|
|
|
const float kDefaultStatusViewHeight = 50;
|
|
const bigtime_t kMaxUpdateInterval = 100000LL;
|
|
const bigtime_t kSpeedReferenceInterval = 2000000LL;
|
|
const bigtime_t kShowSpeedInterval = 8000000LL;
|
|
const bigtime_t kShowEstimatedFinishInterval = 4000000LL;
|
|
const BRect kStatusRect(200, 200, 550, 200);
|
|
|
|
static bigtime_t sLastEstimatedFinishSpeedToggleTime = -1;
|
|
static bool sShowSpeed = true;
|
|
static const time_t kSecondsPerDay = 24 * 60 * 60;
|
|
|
|
class TCustomButton : public BButton {
|
|
public:
|
|
TCustomButton(BRect frame, uint32 command);
|
|
virtual void Draw(BRect updateRect);
|
|
private:
|
|
typedef BButton _inherited;
|
|
};
|
|
|
|
|
|
class BStatusMouseFilter : public BMessageFilter {
|
|
public:
|
|
BStatusMouseFilter();
|
|
virtual filter_result Filter(BMessage* message, BHandler** target);
|
|
};
|
|
|
|
|
|
namespace BPrivate {
|
|
BStatusWindow *gStatusWindow = NULL;
|
|
}
|
|
|
|
|
|
BStatusMouseFilter::BStatusMouseFilter()
|
|
:
|
|
BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE, B_MOUSE_DOWN)
|
|
{
|
|
}
|
|
|
|
|
|
filter_result
|
|
BStatusMouseFilter::Filter(BMessage* message, BHandler** target)
|
|
{
|
|
// If the target is the status bar, make sure the message goes to the
|
|
// parent view instead.
|
|
if ((*target)->Name() != NULL
|
|
&& strcmp((*target)->Name(), "StatusBar") == 0) {
|
|
BView* view = dynamic_cast<BView*>(*target);
|
|
if (view != NULL)
|
|
view = view->Parent();
|
|
if (view != NULL)
|
|
*target = view;
|
|
}
|
|
|
|
return B_DISPATCH_MESSAGE;
|
|
}
|
|
|
|
|
|
TCustomButton::TCustomButton(BRect frame, uint32 what)
|
|
:
|
|
BButton(frame, "", "", new BMessage(what), B_FOLLOW_LEFT | B_FOLLOW_TOP,
|
|
B_WILL_DRAW)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
TCustomButton::Draw(BRect updateRect)
|
|
{
|
|
_inherited::Draw(updateRect);
|
|
|
|
if (Message()->what == kStopButton) {
|
|
updateRect = Bounds();
|
|
updateRect.InsetBy(9, 8);
|
|
SetHighColor(0, 0, 0);
|
|
if (Value() == B_CONTROL_ON)
|
|
updateRect.OffsetBy(1, 1);
|
|
FillRect(updateRect);
|
|
} else {
|
|
updateRect = Bounds();
|
|
updateRect.InsetBy(9, 7);
|
|
BRect rect(updateRect);
|
|
rect.right -= 3;
|
|
|
|
updateRect.left += 3;
|
|
updateRect.OffsetBy(1, 0);
|
|
SetHighColor(0, 0, 0);
|
|
if (Value() == B_CONTROL_ON) {
|
|
updateRect.OffsetBy(1, 1);
|
|
rect.OffsetBy(1, 1);
|
|
}
|
|
FillRect(updateRect);
|
|
FillRect(rect);
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
class StatusBackgroundView : public BView {
|
|
public:
|
|
StatusBackgroundView(BRect frame)
|
|
: BView(frame, "BackView", B_FOLLOW_ALL, B_WILL_DRAW | B_PULSE_NEEDED)
|
|
{
|
|
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
}
|
|
|
|
virtual void Pulse()
|
|
{
|
|
bigtime_t now = system_time();
|
|
if (sShowSpeed
|
|
&& sLastEstimatedFinishSpeedToggleTime + kShowSpeedInterval
|
|
<= now) {
|
|
sShowSpeed = false;
|
|
sLastEstimatedFinishSpeedToggleTime = now;
|
|
} else if (!sShowSpeed
|
|
&& sLastEstimatedFinishSpeedToggleTime
|
|
+ kShowEstimatedFinishInterval <= now) {
|
|
sShowSpeed = true;
|
|
sLastEstimatedFinishSpeedToggleTime = now;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
// #pragma mark - BStatusWindow
|
|
|
|
|
|
#undef B_TRANSLATE_CONTEXT
|
|
#define B_TRANSLATE_CONTEXT "StatusWindow"
|
|
|
|
|
|
BStatusWindow::BStatusWindow()
|
|
:
|
|
BWindow(kStatusRect, B_TRANSLATE("Tracker status"), B_TITLED_WINDOW,
|
|
B_NOT_CLOSABLE | B_NOT_RESIZABLE | B_NOT_ZOOMABLE, B_ALL_WORKSPACES),
|
|
fRetainDesktopFocus(false)
|
|
{
|
|
SetSizeLimits(0, 100000, 0, 100000);
|
|
fMouseDownFilter = new BStatusMouseFilter();
|
|
AddCommonFilter(fMouseDownFilter);
|
|
|
|
BView* view = new StatusBackgroundView(Bounds());
|
|
AddChild(view);
|
|
|
|
SetPulseRate(1000000);
|
|
|
|
Hide();
|
|
Show();
|
|
}
|
|
|
|
|
|
BStatusWindow::~BStatusWindow()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
BStatusWindow::CreateStatusItem(thread_id thread, StatusWindowState type)
|
|
{
|
|
AutoLock<BWindow> lock(this);
|
|
|
|
BRect rect(Bounds());
|
|
if (BStatusView* lastView = fViewList.LastItem())
|
|
rect.top = lastView->Frame().bottom + 1;
|
|
else {
|
|
// This is the first status item, reset speed/estimated finish toggle.
|
|
sShowSpeed = true;
|
|
sLastEstimatedFinishSpeedToggleTime = system_time();
|
|
}
|
|
rect.bottom = rect.top + kDefaultStatusViewHeight - 1;
|
|
|
|
BStatusView* view = new BStatusView(rect, thread, type);
|
|
// the BStatusView will resize itself if needed in its constructor
|
|
ChildAt(0)->AddChild(view);
|
|
fViewList.AddItem(view);
|
|
|
|
ResizeTo(Bounds().Width(), view->Frame().bottom);
|
|
|
|
// find out if the desktop is the active window
|
|
// if the status window is the only thing to take over active state and
|
|
// desktop was active to begin with, return focus back to desktop
|
|
// when we are done
|
|
bool desktopActive = false;
|
|
{
|
|
AutoLock<BLooper> lock(be_app);
|
|
int32 count = be_app->CountWindows();
|
|
for (int32 index = 0; index < count; index++) {
|
|
if (dynamic_cast<BDeskWindow *>(be_app->WindowAt(index))
|
|
&& be_app->WindowAt(index)->IsActive()) {
|
|
desktopActive = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IsHidden()) {
|
|
fRetainDesktopFocus = desktopActive;
|
|
Minimize(false);
|
|
Show();
|
|
} else
|
|
fRetainDesktopFocus &= desktopActive;
|
|
}
|
|
|
|
|
|
void
|
|
BStatusWindow::InitStatusItem(thread_id thread, int32 totalItems,
|
|
off_t totalSize, const entry_ref* destDir, bool showCount)
|
|
{
|
|
AutoLock<BWindow> lock(this);
|
|
|
|
int32 numItems = fViewList.CountItems();
|
|
for (int32 index = 0; index < numItems; index++) {
|
|
BStatusView* view = fViewList.ItemAt(index);
|
|
if (view->Thread() == thread) {
|
|
view->InitStatus(totalItems, totalSize, destDir, showCount);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
BStatusWindow::UpdateStatus(thread_id thread, const char* curItem,
|
|
off_t itemSize, bool optional)
|
|
{
|
|
AutoLock<BWindow> lock(this);
|
|
|
|
int32 numItems = fViewList.CountItems();
|
|
for (int32 index = 0; index < numItems; index++) {
|
|
BStatusView* view = fViewList.ItemAt(index);
|
|
if (view->Thread() == thread) {
|
|
view->UpdateStatus(curItem, itemSize, optional);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
BStatusWindow::RemoveStatusItem(thread_id thread)
|
|
{
|
|
AutoLock<BWindow> lock(this);
|
|
BStatusView* winner = NULL;
|
|
|
|
int32 numItems = fViewList.CountItems();
|
|
int32 index;
|
|
for (index = 0; index < numItems; index++) {
|
|
BStatusView* view = fViewList.ItemAt(index);
|
|
if (view->Thread() == thread) {
|
|
winner = view;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (winner != NULL) {
|
|
// The height by which the other views will have to be moved (in pixel
|
|
// count).
|
|
float height = winner->Bounds().Height() + 1;
|
|
fViewList.RemoveItem(winner);
|
|
winner->RemoveSelf();
|
|
delete winner;
|
|
|
|
if (--numItems == 0 && !IsHidden()) {
|
|
BDeskWindow* desktop = NULL;
|
|
if (fRetainDesktopFocus) {
|
|
AutoLock<BLooper> lock(be_app);
|
|
int32 count = be_app->CountWindows();
|
|
for (int32 index = 0; index < count; index++) {
|
|
desktop = dynamic_cast<BDeskWindow*>(
|
|
be_app->WindowAt(index));
|
|
if (desktop != NULL)
|
|
break;
|
|
}
|
|
}
|
|
Hide();
|
|
if (desktop != NULL) {
|
|
// desktop was active when we first started,
|
|
// make it active again
|
|
desktop->Activate();
|
|
}
|
|
}
|
|
|
|
for (; index < numItems; index++)
|
|
fViewList.ItemAt(index)->MoveBy(0, -height);
|
|
|
|
ResizeTo(Bounds().Width(), Bounds().Height() - height);
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
BStatusWindow::CheckCanceledOrPaused(thread_id thread)
|
|
{
|
|
bool wasCanceled = false;
|
|
bool isPaused = false;
|
|
|
|
BStatusView* view = NULL;
|
|
|
|
AutoLock<BWindow> lock(this);
|
|
// check if cancel or pause hit
|
|
for (int32 index = fViewList.CountItems() - 1; index >= 0; index--) {
|
|
view = fViewList.ItemAt(index);
|
|
if (view && view->Thread() == thread) {
|
|
isPaused = view->IsPaused();
|
|
wasCanceled = view->WasCanceled();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (wasCanceled || !isPaused)
|
|
return wasCanceled;
|
|
|
|
if (isPaused && view != NULL) {
|
|
// say we are paused
|
|
view->Invalidate();
|
|
thread_id thread = view->Thread();
|
|
|
|
lock.Unlock();
|
|
|
|
// and suspend ourselves
|
|
// we will get resumed from BStatusView::MessageReceived
|
|
ASSERT(find_thread(NULL) == thread);
|
|
suspend_thread(thread);
|
|
}
|
|
|
|
return wasCanceled;
|
|
}
|
|
|
|
|
|
bool
|
|
BStatusWindow::AttemptToQuit()
|
|
{
|
|
// called when tracker is quitting
|
|
// try to cancel all the move/copy/empty trash threads in a nice way
|
|
// by issuing cancels
|
|
int32 count = fViewList.CountItems();
|
|
|
|
if (count == 0)
|
|
return true;
|
|
|
|
for (int32 index = 0; index < count; index++)
|
|
fViewList.ItemAt(index)->SetWasCanceled();
|
|
|
|
// maybe next time everything will have been canceled
|
|
return false;
|
|
}
|
|
|
|
|
|
void
|
|
BStatusWindow::WindowActivated(bool state)
|
|
{
|
|
if (!state)
|
|
fRetainDesktopFocus = false;
|
|
|
|
return _inherited::WindowActivated(state);
|
|
}
|
|
|
|
|
|
// #pragma mark - BStatusView
|
|
|
|
|
|
BStatusView::BStatusView(BRect bounds, thread_id thread,
|
|
StatusWindowState type)
|
|
:
|
|
BView(bounds, "StatusView", B_FOLLOW_NONE, B_WILL_DRAW),
|
|
fType(type),
|
|
fBitmap(NULL),
|
|
fThread(thread)
|
|
{
|
|
Init();
|
|
|
|
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
SetLowColor(ViewColor());
|
|
SetHighColor(20, 20, 20);
|
|
SetDrawingMode(B_OP_OVER);
|
|
|
|
const float buttonWidth = 22;
|
|
const float buttonHeight = 20;
|
|
|
|
BRect rect(bounds);
|
|
rect.OffsetTo(B_ORIGIN);
|
|
rect.left += 40;
|
|
rect.right -= buttonWidth * 2 + 12;
|
|
rect.top += 6;
|
|
rect.bottom = rect.top + 15;
|
|
|
|
BString caption;
|
|
int32 id = 0;
|
|
|
|
switch (type) {
|
|
case kCopyState:
|
|
caption = B_TRANSLATE("Preparing to copy items" B_UTF8_ELLIPSIS);
|
|
id = R_CopyStatusBitmap;
|
|
break;
|
|
|
|
case kMoveState:
|
|
caption = B_TRANSLATE("Preparing to move items" B_UTF8_ELLIPSIS);
|
|
id = R_MoveStatusBitmap;
|
|
break;
|
|
|
|
case kCreateLinkState:
|
|
caption = B_TRANSLATE("Preparing to create links" B_UTF8_ELLIPSIS);
|
|
id = R_MoveStatusBitmap;
|
|
break;
|
|
|
|
case kTrashState:
|
|
caption = B_TRANSLATE("Preparing to empty Trash" B_UTF8_ELLIPSIS);
|
|
id = R_TrashStatusBitmap;
|
|
break;
|
|
|
|
case kVolumeState:
|
|
caption = B_TRANSLATE("Searching for disks to mount" B_UTF8_ELLIPSIS);
|
|
break;
|
|
|
|
case kDeleteState:
|
|
caption = B_TRANSLATE("Preparing to delete items" B_UTF8_ELLIPSIS);
|
|
id = R_TrashStatusBitmap;
|
|
break;
|
|
|
|
case kRestoreFromTrashState:
|
|
caption = B_TRANSLATE("Preparing to restore items" B_UTF8_ELLIPSIS);
|
|
break;
|
|
|
|
default:
|
|
TRESPASS();
|
|
break;
|
|
}
|
|
|
|
if (caption.Length() != 0) {
|
|
fStatusBar = new BStatusBar(rect, "StatusBar", caption.String());
|
|
fStatusBar->SetBarHeight(12);
|
|
float width, height;
|
|
fStatusBar->GetPreferredSize(&width, &height);
|
|
fStatusBar->ResizeTo(fStatusBar->Frame().Width(), height);
|
|
AddChild(fStatusBar);
|
|
|
|
// Figure out how much room we need to display the additional status
|
|
// message below the bar
|
|
font_height fh;
|
|
GetFontHeight(&fh);
|
|
BRect f = fStatusBar->Frame();
|
|
// Height is 3 x the "room from the top" + bar height + room for
|
|
// string.
|
|
ResizeTo(Bounds().Width(), f.top + f.Height() + fh.leading + fh.ascent
|
|
+ fh.descent + f.top);
|
|
}
|
|
|
|
if (id != 0)
|
|
GetTrackerResources()->GetBitmapResource(B_MESSAGE_TYPE, id, &fBitmap);
|
|
|
|
rect = Bounds();
|
|
rect.left = rect.right - buttonWidth * 2 - 7;
|
|
rect.right = rect.left + buttonWidth;
|
|
rect.top = floorf((rect.top + rect.bottom) / 2 + 0.5) - buttonHeight / 2;
|
|
rect.bottom = rect.top + buttonHeight;
|
|
|
|
fPauseButton = new TCustomButton(rect, kPauseButton);
|
|
fPauseButton->ResizeTo(buttonWidth, buttonHeight);
|
|
AddChild(fPauseButton);
|
|
|
|
rect.OffsetBy(buttonWidth + 2, 0);
|
|
fStopButton = new TCustomButton(rect, kStopButton);
|
|
fStopButton->ResizeTo(buttonWidth, buttonHeight);
|
|
AddChild(fStopButton);
|
|
}
|
|
|
|
|
|
BStatusView::~BStatusView()
|
|
{
|
|
delete fBitmap;
|
|
}
|
|
|
|
|
|
void
|
|
BStatusView::Init()
|
|
{
|
|
fDestDir = "";
|
|
fCurItem = 0;
|
|
fPendingStatusString[0] = '\0';
|
|
fWasCanceled = false;
|
|
fIsPaused = false;
|
|
fLastUpdateTime = 0;
|
|
fBytesPerSecond = 0.0;
|
|
for (size_t i = 0; i < kBytesPerSecondSlots; i++)
|
|
fBytesPerSecondSlot[i] = 0.0;
|
|
fCurrentBytesPerSecondSlot = 0;
|
|
fItemSize = 0;
|
|
fSizeProcessed = 0;
|
|
fLastSpeedReferenceSize = 0;
|
|
fEstimatedFinishReferenceSize = 0;
|
|
|
|
fProcessStartTime = fLastSpeedReferenceTime = fEstimatedFinishReferenceTime
|
|
= system_time();
|
|
}
|
|
|
|
|
|
void
|
|
BStatusView::InitStatus(int32 totalItems, off_t totalSize,
|
|
const entry_ref* destDir, bool showCount)
|
|
{
|
|
Init();
|
|
fTotalSize = totalSize;
|
|
fShowCount = showCount;
|
|
|
|
BEntry entry;
|
|
char name[B_FILE_NAME_LENGTH];
|
|
if (destDir && (entry.SetTo(destDir) == B_OK)) {
|
|
entry.GetName(name);
|
|
fDestDir = name;
|
|
}
|
|
|
|
BString buffer;
|
|
if (totalItems > 0) {
|
|
char totalStr[32];
|
|
buffer.SetTo(B_TRANSLATE("of %items"));
|
|
snprintf(totalStr, sizeof(totalStr), "%ld", totalItems);
|
|
buffer.ReplaceFirst("%items", totalStr);
|
|
}
|
|
|
|
switch (fType) {
|
|
case kCopyState:
|
|
fStatusBar->Reset(B_TRANSLATE("Copying: "), buffer.String());
|
|
break;
|
|
|
|
case kCreateLinkState:
|
|
fStatusBar->Reset(B_TRANSLATE("Creating links: "), buffer.String());
|
|
break;
|
|
|
|
case kMoveState:
|
|
fStatusBar->Reset(B_TRANSLATE("Moving: "), buffer.String());
|
|
break;
|
|
|
|
case kTrashState:
|
|
fStatusBar->Reset(B_TRANSLATE("Emptying Trash" B_UTF8_ELLIPSIS " "),
|
|
buffer.String());
|
|
break;
|
|
|
|
case kDeleteState:
|
|
fStatusBar->Reset(B_TRANSLATE("Deleting: "), buffer.String());
|
|
break;
|
|
|
|
case kRestoreFromTrashState:
|
|
fStatusBar->Reset(B_TRANSLATE("Restoring: "), buffer.String());
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
fStatusBar->SetMaxValue(1);
|
|
// SetMaxValue has to be here because Reset changes it to 100
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
void
|
|
BStatusView::Draw(BRect updateRect)
|
|
{
|
|
if (fBitmap) {
|
|
BPoint location;
|
|
location.x = (fStatusBar->Frame().left - fBitmap->Bounds().Width()) / 2;
|
|
location.y = (Bounds().Height()- fBitmap->Bounds().Height()) / 2;
|
|
DrawBitmap(fBitmap, location);
|
|
}
|
|
|
|
BRect bounds(Bounds());
|
|
be_control_look->DrawRaisedBorder(this, bounds, updateRect, ViewColor());
|
|
|
|
SetHighColor(0, 0, 0);
|
|
|
|
BPoint tp = fStatusBar->Frame().LeftBottom();
|
|
font_height fh;
|
|
GetFontHeight(&fh);
|
|
tp.y += ceilf(fh.leading) + ceilf(fh.ascent);
|
|
if (IsPaused()) {
|
|
DrawString(B_TRANSLATE("Paused: click to resume or stop"), tp);
|
|
return;
|
|
}
|
|
|
|
BFont font;
|
|
GetFont(&font);
|
|
float normalFontSize = font.Size();
|
|
float smallFontSize = max_c(normalFontSize * 0.8f, 8.0f);
|
|
float availableSpace = fStatusBar->Frame().Width();
|
|
availableSpace -= be_control_look->DefaultLabelSpacing();
|
|
// subtract to provide some room between our two strings
|
|
|
|
float destinationStringWidth = 0.f;
|
|
BString destinationString(_DestinationString(&destinationStringWidth));
|
|
availableSpace -= destinationStringWidth;
|
|
|
|
float statusStringWidth = 0.f;
|
|
BString statusString(_StatusString(availableSpace, smallFontSize,
|
|
&statusStringWidth));
|
|
|
|
if (statusStringWidth > availableSpace) {
|
|
TruncateString(&destinationString, B_TRUNCATE_MIDDLE,
|
|
availableSpace + destinationStringWidth - statusStringWidth);
|
|
}
|
|
|
|
BPoint textPoint = fStatusBar->Frame().LeftBottom();
|
|
textPoint.y += ceilf(fh.leading) + ceilf(fh.ascent);
|
|
|
|
if (destinationStringWidth > 0) {
|
|
DrawString(destinationString.String(), textPoint);
|
|
}
|
|
|
|
SetHighColor(tint_color(LowColor(), B_DARKEN_4_TINT));
|
|
font.SetSize(smallFontSize);
|
|
SetFont(&font, B_FONT_SIZE);
|
|
|
|
textPoint.x = fStatusBar->Frame().right - statusStringWidth;
|
|
DrawString(statusString.String(), textPoint);
|
|
|
|
font.SetSize(normalFontSize);
|
|
SetFont(&font, B_FONT_SIZE);
|
|
}
|
|
|
|
|
|
BString
|
|
BStatusView::_DestinationString(float* _width)
|
|
{
|
|
if (fDestDir.Length() > 0) {
|
|
BString buffer(B_TRANSLATE("To: %dir"));
|
|
buffer.ReplaceFirst("%dir", fDestDir);
|
|
|
|
*_width = ceilf(StringWidth(buffer.String()));
|
|
return buffer;
|
|
} else {
|
|
*_width = 0;
|
|
return BString();
|
|
}
|
|
}
|
|
|
|
|
|
BString
|
|
BStatusView::_StatusString(float availableSpace, float fontSize, float* _width)
|
|
{
|
|
BFont font;
|
|
GetFont(&font);
|
|
float oldSize = font.Size();
|
|
font.SetSize(fontSize);
|
|
SetFont(&font, B_FONT_SIZE);
|
|
|
|
BString status;
|
|
if (sShowSpeed) {
|
|
status = _SpeedStatusString(availableSpace, _width);
|
|
} else
|
|
status = _TimeStatusString(availableSpace, _width);
|
|
|
|
font.SetSize(oldSize);
|
|
SetFont(&font, B_FONT_SIZE);
|
|
return status;
|
|
}
|
|
|
|
|
|
BString
|
|
BStatusView::_SpeedStatusString(float availableSpace, float* _width)
|
|
{
|
|
BString string(_FullSpeedString());
|
|
*_width = StringWidth(string.String());
|
|
if (*_width > availableSpace) {
|
|
string.SetTo(_ShortSpeedString());
|
|
*_width = StringWidth(string.String());
|
|
}
|
|
*_width = ceilf(*_width);
|
|
return string;
|
|
}
|
|
|
|
|
|
BString
|
|
BStatusView::_FullSpeedString()
|
|
{
|
|
BString buffer;
|
|
if (fBytesPerSecond != 0.0) {
|
|
char sizeBuffer[128];
|
|
buffer.SetTo(B_TRANSLATE(
|
|
"(%SizeProcessed of %TotalSize, %BytesPerSecond/s)"));
|
|
buffer.ReplaceFirst("%SizeProcessed",
|
|
string_for_size((double)fSizeProcessed, sizeBuffer,
|
|
sizeof(sizeBuffer)));
|
|
buffer.ReplaceFirst("%TotalSize",
|
|
string_for_size((double)fTotalSize, sizeBuffer,
|
|
sizeof(sizeBuffer)));
|
|
buffer.ReplaceFirst("%BytesPerSecond",
|
|
string_for_size(fBytesPerSecond, sizeBuffer, sizeof(sizeBuffer)));
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
|
|
BString
|
|
BStatusView::_ShortSpeedString()
|
|
{
|
|
BString buffer;
|
|
if (fBytesPerSecond != 0.0) {
|
|
char sizeBuffer[128];
|
|
buffer << B_TRANSLATE("%BytesPerSecond/s");
|
|
buffer.ReplaceFirst("%BytesPerSecond",
|
|
string_for_size(fBytesPerSecond, sizeBuffer, sizeof(sizeBuffer)));
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
|
|
BString
|
|
BStatusView::_TimeStatusString(float availableSpace, float* _width)
|
|
{
|
|
double totalBytesPerSecond = (double)(fSizeProcessed
|
|
- fEstimatedFinishReferenceSize)
|
|
* 1000000LL / (system_time() - fEstimatedFinishReferenceTime);
|
|
double secondsRemaining = (fTotalSize - fSizeProcessed)
|
|
/ totalBytesPerSecond;
|
|
time_t now = (time_t)real_time_clock();
|
|
time_t finishTime = (time_t)(now + secondsRemaining);
|
|
|
|
tm _time;
|
|
tm* time = localtime_r(&finishTime, &_time);
|
|
int32 year = time->tm_year + 1900;
|
|
|
|
char timeText[32];
|
|
// TODO: Localization of time string...
|
|
if (now < finishTime - kSecondsPerDay) {
|
|
// process is going to take more than a day!
|
|
snprintf(timeText, sizeof(timeText), "%0*d:%0*d %0*d/%0*d/%ld",
|
|
2, time->tm_hour, 2, time->tm_min,
|
|
2, time->tm_mon + 1, 2, time->tm_mday, year);
|
|
} else {
|
|
snprintf(timeText, sizeof(timeText), "%0*d:%0*d",
|
|
2, time->tm_hour, 2, time->tm_min);
|
|
}
|
|
|
|
finishTime -= now;
|
|
BString string(_FullTimeRemainingString(finishTime, timeText));
|
|
*_width = StringWidth(string.String());
|
|
if (*_width > availableSpace) {
|
|
string.SetTo(_ShortTimeRemainingString(timeText));
|
|
*_width = StringWidth(string.String());
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
|
|
BString
|
|
BStatusView::_ShortTimeRemainingString(const char* timeText)
|
|
{
|
|
BString buffer;
|
|
|
|
// complete string too wide, try with shorter version
|
|
buffer.SetTo(B_TRANSLATE("(Finish: %time)"));
|
|
buffer.ReplaceFirst("%time", timeText);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
BString
|
|
BStatusView::_FullTimeRemainingString(time_t finishTime, const char* timeText)
|
|
{
|
|
BString buffer;
|
|
char finishStr[32];
|
|
if (finishTime > kSecondsPerDay) {
|
|
buffer.SetTo(B_TRANSLATE("(Finish: %time - Over %finishtime "
|
|
"days left)"));
|
|
snprintf(finishStr, sizeof(finishStr), "%ld",
|
|
finishTime / kSecondsPerDay);
|
|
} else if (finishTime > 60 * 60) {
|
|
buffer.SetTo(B_TRANSLATE("(Finish: %time - Over %finishtime "
|
|
"hours left)"));
|
|
snprintf(finishStr, sizeof(finishStr), "%ld",
|
|
finishTime / (60 * 60));
|
|
} else if (finishTime > 60) {
|
|
buffer.SetTo(B_TRANSLATE("(Finish: %time - %finishtime minutes "
|
|
"left)"));
|
|
snprintf(finishStr, sizeof(finishStr), "%ld", finishTime / 60);
|
|
} else {
|
|
buffer.SetTo(B_TRANSLATE("(Finish: %time - %finishtime seconds "
|
|
"left)"));
|
|
snprintf(finishStr, sizeof(finishStr), "%ld", finishTime);
|
|
}
|
|
|
|
buffer.ReplaceFirst("%time", timeText);
|
|
buffer.ReplaceFirst("%finishtime", finishStr);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
void
|
|
BStatusView::AttachedToWindow()
|
|
{
|
|
fPauseButton->SetTarget(this);
|
|
fStopButton->SetTarget(this);
|
|
}
|
|
|
|
|
|
void
|
|
BStatusView::MessageReceived(BMessage *message)
|
|
{
|
|
switch (message->what) {
|
|
case kPauseButton:
|
|
fIsPaused = !fIsPaused;
|
|
fPauseButton->SetValue(fIsPaused ? B_CONTROL_ON : B_CONTROL_OFF);
|
|
if (fBytesPerSecond != 0.0) {
|
|
fBytesPerSecond = 0.0;
|
|
for (size_t i = 0; i < kBytesPerSecondSlots; i++)
|
|
fBytesPerSecondSlot[i] = 0.0;
|
|
Invalidate();
|
|
}
|
|
if (!fIsPaused) {
|
|
fEstimatedFinishReferenceTime = system_time();
|
|
fEstimatedFinishReferenceSize = fSizeProcessed;
|
|
|
|
// force window update
|
|
Invalidate();
|
|
|
|
// let 'er rip
|
|
resume_thread(Thread());
|
|
}
|
|
break;
|
|
|
|
case kStopButton:
|
|
fWasCanceled = true;
|
|
if (fIsPaused) {
|
|
// resume so that the copy loop gets a chance to finish up
|
|
fIsPaused = false;
|
|
|
|
// force window update
|
|
Invalidate();
|
|
|
|
// let 'er rip
|
|
resume_thread(Thread());
|
|
}
|
|
break;
|
|
|
|
default:
|
|
_inherited::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
BStatusView::UpdateStatus(const char *curItem, off_t itemSize, bool optional)
|
|
{
|
|
if (!fShowCount) {
|
|
fStatusBar->Update((float)fItemSize / fTotalSize);
|
|
fItemSize = 0;
|
|
return;
|
|
}
|
|
|
|
if (curItem != NULL)
|
|
fCurItem++;
|
|
|
|
fItemSize += itemSize;
|
|
fSizeProcessed += itemSize;
|
|
|
|
bigtime_t currentTime = system_time();
|
|
if (!optional || ((currentTime - fLastUpdateTime) > kMaxUpdateInterval)) {
|
|
if (curItem != NULL || fPendingStatusString[0]) {
|
|
// forced update or past update time
|
|
|
|
BString buffer;
|
|
buffer << fCurItem << " ";
|
|
|
|
// if we don't have curItem, take the one from the stash
|
|
const char *statusItem = curItem != NULL
|
|
? curItem : fPendingStatusString;
|
|
|
|
fStatusBar->Update((float)fItemSize / fTotalSize, statusItem,
|
|
buffer.String());
|
|
|
|
// we already displayed this item, clear the stash
|
|
fPendingStatusString[0] = '\0';
|
|
|
|
fLastUpdateTime = currentTime;
|
|
} else {
|
|
// don't have a file to show, just update the bar
|
|
fStatusBar->Update((float)fItemSize / fTotalSize);
|
|
}
|
|
|
|
if (currentTime
|
|
>= fLastSpeedReferenceTime + kSpeedReferenceInterval) {
|
|
// update current speed every kSpeedReferenceInterval
|
|
fCurrentBytesPerSecondSlot
|
|
= (fCurrentBytesPerSecondSlot + 1) % kBytesPerSecondSlots;
|
|
fBytesPerSecondSlot[fCurrentBytesPerSecondSlot]
|
|
= (double)(fSizeProcessed - fLastSpeedReferenceSize)
|
|
* 1000000LL / (currentTime - fLastSpeedReferenceTime);
|
|
fLastSpeedReferenceSize = fSizeProcessed;
|
|
fLastSpeedReferenceTime = currentTime;
|
|
fBytesPerSecond = 0.0;
|
|
size_t count = 0;
|
|
for (size_t i = 0; i < kBytesPerSecondSlots; i++) {
|
|
if (fBytesPerSecondSlot[i] != 0.0) {
|
|
fBytesPerSecond += fBytesPerSecondSlot[i];
|
|
count++;
|
|
}
|
|
}
|
|
if (count > 0)
|
|
fBytesPerSecond /= count;
|
|
Invalidate();
|
|
}
|
|
|
|
fItemSize = 0;
|
|
} else if (curItem != NULL) {
|
|
// stash away the name of the item we are currently processing
|
|
// so we can show it when the time comes
|
|
strncpy(fPendingStatusString, curItem, 127);
|
|
fPendingStatusString[127] = '0';
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
BStatusView::SetWasCanceled()
|
|
{
|
|
fWasCanceled = true;
|
|
}
|
|
|