1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/windows/QueueController.cpp
Martin Prikryl 8b438ccd30 Refactoring
Source commit: ccadcd5e989d237f44bf93203541a0cc0b7f3019
2026-02-04 19:42:06 +01:00

708 lines
20 KiB
C++

//---------------------------------------------------------------------------
#include <WinPCH.h>
#pragma hdrstop
#include <Queue.h>
#include "QueueController.h"
//---------------------------------------------------------------------------
__fastcall TQueueController::TQueueController(TCustomListView * ListView)
{
FListView = static_cast<TListView *>(ListView);
DebugAssert(FListView != NULL);
DebugAssert(FListView->OnDblClick == NULL);
FListView->OnDblClick = QueueViewDblClick;
DebugAssert(FListView->OnKeyDown == NULL);
FListView->OnKeyDown = QueueViewKeyDown;
DebugAssert(FListView->OnCustomDrawItem == NULL);
FListView->OnCustomDrawItem = QueueViewCustomDrawItem;
FQueueStatus = NULL;
FOnChange = NULL;
RememberConfiguration();
}
//---------------------------------------------------------------------------
__fastcall TQueueController::~TQueueController()
{
DebugAssert(FListView->OnDblClick == QueueViewDblClick);
FListView->OnDblClick = NULL;
DebugAssert(FListView->OnKeyDown == QueueViewKeyDown);
FListView->OnKeyDown = NULL;
DebugAssert(FListView->OnCustomDrawItem == QueueViewCustomDrawItem);
FListView->OnCustomDrawItem = NULL;
}
//---------------------------------------------------------------------------
TQueueItemProxy * __fastcall TQueueController::QueueViewItemToQueueItem(TListItem * Item)
{
// previously this method was based on ActiveCount and DoneCount,
// as if we were inconfident about validity of Item->Data pointers,
// not sure why
return static_cast<TQueueItemProxy *>(Item->Data);
}
//---------------------------------------------------------------------------
TQueueOperation __fastcall TQueueController::DefaultOperation()
{
TQueueItemProxy * QueueItem;
if (FListView->ItemFocused != NULL)
{
QueueItem = QueueViewItemToQueueItem(FListView->ItemFocused);
switch (QueueItem->Status)
{
case TQueueItem::qsPending:
return qoItemExecute;
case TQueueItem::qsQuery:
return qoItemQuery;
case TQueueItem::qsError:
return qoItemError;
case TQueueItem::qsPrompt:
return qoItemPrompt;
case TQueueItem::qsProcessing:
return qoItemPause;
case TQueueItem::qsPaused:
return qoItemResume;
}
}
return qoNone;
}
//---------------------------------------------------------------------------
bool __fastcall TQueueController::AllowOperation(
TQueueOperation Operation, unsigned long * Param)
{
TQueueItemProxy * QueueItem = NULL;
if (FListView->ItemFocused != NULL)
{
QueueItem = QueueViewItemToQueueItem(FListView->ItemFocused);
}
switch (Operation)
{
case qoItemUserAction:
return (QueueItem != NULL) && TQueueItem::IsUserActionStatus(QueueItem->Status);
case qoItemQuery:
return (QueueItem != NULL) && (QueueItem->Status == TQueueItem::qsQuery);
case qoItemError:
return (QueueItem != NULL) && (QueueItem->Status == TQueueItem::qsError);
case qoItemPrompt:
return (QueueItem != NULL) && (QueueItem->Status == TQueueItem::qsPrompt);
case qoItemDelete:
return (QueueItem != NULL);
case qoItemExecute:
return (QueueItem != NULL) && (QueueItem->Status == TQueueItem::qsPending);
case qoItemUp:
return (QueueItem != NULL) &&
(QueueItem->Status == TQueueItem::qsPending) &&
// it's not first pending item,
// this is based on assumption that pending items occupy single line always
(FListView->ItemFocused->Index > 0) &&
(QueueViewItemToQueueItem(FListView->Items->Item[FListView->ItemFocused->Index - 1])->Status == TQueueItem::qsPending);
case qoItemDown:
return (QueueItem != NULL) &&
(QueueItem->Status == TQueueItem::qsPending) &&
(FListView->ItemFocused->Index < (FListView->Items->Count - 1));
case qoItemPause:
return (QueueItem != NULL) &&
(QueueItem->Status == TQueueItem::qsProcessing);
case qoItemResume:
return (QueueItem != NULL) &&
(QueueItem->Status == TQueueItem::qsPaused);
case qoItemSpeed:
{
bool Result =
(QueueItem != NULL) && (QueueItem->Status != TQueueItem::qsDone) &&
TFileOperationProgressType::IsTransferOperation(QueueItem->Info->Operation);
if (Result && (Param != NULL))
{
Result = QueueItem->GetCPSLimit(*Param);
}
return Result;
}
case qoPauseAll:
case qoResumeAll:
{
TQueueItem::TStatus Status =
(Operation == qoPauseAll) ? TQueueItem::qsProcessing : TQueueItem::qsPaused;
bool Result = false;
// can be NULL when action update is triggered while disconnecting
if (FQueueStatus != NULL)
{
for (int i = FQueueStatus->DoneCount; !Result && (i < FQueueStatus->DoneAndActiveCount); i++)
{
QueueItem = FQueueStatus->Items[i];
Result = (QueueItem->Status == Status);
}
}
return Result;
}
case qoDeleteAllDone:
return (FQueueStatus != NULL) && (FQueueStatus->DoneCount > 0);
case qoDeleteAll:
return (FQueueStatus != NULL) && (FQueueStatus->Count > 0);
default:
DebugFail();
return false;
}
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::ExecuteOperation(TQueueOperation Operation,
unsigned long Param)
{
TQueueItemProxy * QueueItem = NULL;
if (FListView->ItemFocused != NULL)
{
QueueItem = QueueViewItemToQueueItem(FListView->ItemFocused);
}
switch (Operation)
{
case qoItemUserAction:
case qoItemQuery:
case qoItemError:
case qoItemPrompt:
if (QueueItem != NULL)
{
QueueItem->ProcessUserAction();
}
break;
case qoItemExecute:
if (QueueItem != NULL)
{
QueueItem->ExecuteNow();
}
break;
case qoItemUp:
case qoItemDown:
if (QueueItem != NULL)
{
QueueItem->Move(Operation == qoItemUp);
}
break;
case qoItemDelete:
if (QueueItem != NULL)
{
QueueItem->Delete();
}
break;
case qoItemPause:
if (QueueItem != NULL)
{
QueueItem->Pause();
}
break;
case qoItemResume:
if (QueueItem != NULL)
{
QueueItem->Resume();
}
break;
case qoItemSpeed:
if (QueueItem != NULL)
{
QueueItem->SetCPSLimit(Param);
}
break;
case qoPauseAll:
case qoResumeAll:
{
for (int i = FQueueStatus->DoneCount; i < FQueueStatus->DoneAndActiveCount; i++)
{
QueueItem = FQueueStatus->Items[i];
if ((Operation == qoPauseAll) && (QueueItem->Status == TQueueItem::qsProcessing))
{
QueueItem->Pause();
}
else if ((Operation == qoResumeAll) && (QueueItem->Status == TQueueItem::qsPaused))
{
QueueItem->Resume();
}
}
}
break;
case qoDeleteAllDone:
case qoDeleteAll:
{
int Count = (Operation == qoDeleteAll) ? FQueueStatus->Count : FQueueStatus->DoneCount;
for (int i = 0; i < Count; i++)
{
QueueItem = FQueueStatus->Items[i];
QueueItem->Delete();
}
}
break;
default:
DebugFail();
break;
}
}
//---------------------------------------------------------------------------
static UnicodeString GetTime(TFileOperationProgressType * ProgressData)
{
UnicodeString Result;
if (ProgressData->TotalSizeSet)
{
Result = FormatDateTimeSpan(ProgressData->TotalTimeLeft());
}
else
{
Result = FormatDateTimeSpan(ProgressData->TimeElapsed());
}
return Result;
}
//---------------------------------------------------------------------------
static UnicodeString FormatProgress(int Progress)
{
return FORMAT(L"%d%%", (Progress));
}
//---------------------------------------------------------------------------
static UnicodeString FormatOverallProgress(TFileOperationProgressType * ProgressData)
{
return FormatProgress(ProgressData->OverallProgress());
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::FillQueueViewItem(TListItem * Item,
TQueueItemProxy * QueueItem, bool Detail, bool OnlyLine)
{
DebugAssert(!Detail || (QueueItem->Status != TQueueItem::qsPending));
DebugAssert((Item->Data == NULL) || (Item->Data == QueueItem));
Item->Data = QueueItem;
UnicodeString ProgressStr;
int Image = -1;
switch (QueueItem->Status)
{
case TQueueItem::qsDone:
ProgressStr = LoadStr(QUEUE_DONE);
break;
case TQueueItem::qsPending:
ProgressStr = LoadStr(QUEUE_PENDING);
break;
case TQueueItem::qsConnecting:
ProgressStr = LoadStr(QUEUE_CONNECTING);
break;
case TQueueItem::qsQuery:
ProgressStr = LoadStr(QUEUE_QUERY);
Image = 4;
break;
case TQueueItem::qsError:
ProgressStr = LoadStr(QUEUE_ERROR);
Image = 5;
break;
case TQueueItem::qsPrompt:
ProgressStr = LoadStr(QUEUE_PROMPT);
Image = 6;
break;
case TQueueItem::qsPaused:
ProgressStr = LoadStr(QUEUE_PAUSED);
Image = 7;
break;
}
bool BlinkHide = QueueItemNeedsFrequentRefresh(QueueItem) &&
!QueueItem->ProcessingUserAction &&
((GetTickCount() % MSecsPerSec) >= (MSecsPerSec/2));
int State = -1;
UnicodeString Values[6];
TFileOperationProgressType * ProgressData = QueueItem->ProgressData;
TQueueItem::TInfo * Info = QueueItem->Info;
if (SimpleOperation(QueueItem))
{
DebugAssert(!Detail && Info->Primary);
State = 8;
if ((ProgressData != NULL) && (QueueItem->Status != TQueueItem::qsDone))
{
Values[0] = ProgressData->FileName;
Values[3] = GetTime(ProgressData);
if (ProgressStr.IsEmpty())
{
if (ProgressData->Count > 1)
{
ProgressStr = FormatOverallProgress(ProgressData);
}
else
{
ProgressStr = LoadStr(QUEUE_DELETING);
}
}
}
else
{
Values[0] = Info->Source;
}
Values[5] = ProgressStr;
}
else if (!Detail && Info->Primary)
{
switch (Info->Operation)
{
case foCopy:
State = ((Info->Side == osLocal) ? 2 : 0);
break;
case foMove:
State = ((Info->Side == osLocal) ? 3 : 1);
break;
default:
DebugFail();
}
if (!OnlyLine)
{
Image = -1;
ProgressStr = L"";
}
// If both are empty, it's bootstrap item => do not show anything
if (!Info->Source.IsEmpty() || !Info->Destination.IsEmpty())
{
// cannot use ProgressData->Temp as it is set only after the transfer actually starts
Values[0] = Info->Source.IsEmpty() ? LoadStr(PROGRESS_TEMP_DIR) : Info->Source;
Values[1] = Info->Destination.IsEmpty() ? LoadStr(PROGRESS_TEMP_DIR) : Info->Destination;
}
__int64 TotalTransferred = QueueItem->TotalTransferred;
if (TotalTransferred >= 0)
{
Values[2] =
FormatPanelBytes(TotalTransferred, WinConfiguration->FormatSizeBytes);
}
if (ProgressData != NULL)
{
if (ProgressData->Operation == Info->Operation)
{
if (QueueItem->Status != TQueueItem::qsDone)
{
UnicodeString Time = GetTime(ProgressData);
unsigned int CPS = ProgressData->CPS();
Values[3] = Time;
Values[4] = FORMAT(L"%s/s", (FormatBytes(CPS)));
}
if (ProgressStr.IsEmpty())
{
ProgressStr = FormatOverallProgress(ProgressData);
}
}
else if (ProgressData->Operation == foCalculateSize)
{
ProgressStr = LoadStr(QUEUE_LISTING);
}
}
Values[5] = ProgressStr;
}
else
{
if (ProgressData != NULL)
{
UnicodeString FileName;
if ((Info->Side == osRemote) || !ProgressData->Temp)
{
FileName = ProgressData->FileName;
}
else
{
FileName = ExtractFileName(ProgressData->FileName);
}
Values[0] = FileName;
if (ProgressData->Operation == Info->Operation)
{
__int64 TransferredSize = ProgressData->TransferredSize;
Values[2] = FormatPanelBytes(TransferredSize, WinConfiguration->FormatSizeBytes);
if (ProgressStr.IsEmpty())
{
int Progress = ProgressData->TransferProgress();
ProgressStr = FormatProgress(Progress);
}
}
}
Values[5] = ProgressStr;
}
Item->StateIndex = (!BlinkHide ? State : -1);
Item->ImageIndex = (!BlinkHide ? Image : -1);
for (int Index = 0; Index < static_cast<int>(std::size(Values)); Index++)
{
if (Index < Item->SubItems->Count)
{
Item->SubItems->Strings[Index] = Values[Index];
}
else
{
Item->SubItems->Add(Values[Index]);
}
}
}
//---------------------------------------------------------------------------
TListItem * __fastcall TQueueController::InsertItemFor(TQueueItemProxy * QueueItem, int Index)
{
TListItem * Item;
if (Index == FListView->Items->Count)
{
Item = FListView->Items->Add();
}
else if (FListView->Items->Item[Index]->Data != QueueItem)
{
Item = FListView->Items->Insert(Index);
}
else
{
Item = FListView->Items->Item[Index];
DebugAssert(Item->Data == QueueItem);
}
return Item;
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::UpdateQueueStatus(
TTerminalQueueStatus * QueueStatus)
{
FQueueStatus = QueueStatus;
if (FQueueStatus != NULL)
{
TQueueItemProxy * QueueItem;
TListItem * Item;
int Index = 0;
for (int ItemIndex = 0; ItemIndex < FQueueStatus->Count; ItemIndex++)
{
QueueItem = FQueueStatus->Items[ItemIndex];
int Index2 = Index;
while ((Index2 < FListView->Items->Count) &&
(FListView->Items->Item[Index2]->Data != QueueItem))
{
Index2++;
}
if (Index2 < FListView->Items->Count)
{
while (Index < Index2)
{
FListView->Items->Delete(Index);
Index2--;
}
}
Item = InsertItemFor(QueueItem, Index);
bool HasDetailsLine = UseDetailsLine(ItemIndex, QueueItem);
FillQueueViewItem(Item, QueueItem, false, !HasDetailsLine);
Index++;
DebugAssert((QueueItem->Status != TQueueItem::qsPending) ==
(ItemIndex < FQueueStatus->DoneAndActiveCount));
if (HasDetailsLine)
{
Item = InsertItemFor(QueueItem, Index);
FillQueueViewItem(Item, QueueItem, true, false);
Index++;
}
}
while (Index < FListView->Items->Count)
{
FListView->Items->Delete(Index);
}
}
else
{
FListView->Items->Clear();
}
DoChange();
}
//---------------------------------------------------------------------------
bool TQueueController::SimpleOperation(TQueueItemProxy * QueueItem)
{
return (QueueItem->Info->Operation == foDelete); // basically any non-transfer, but we support delete now only
}
//---------------------------------------------------------------------------
bool __fastcall TQueueController::UseDetailsLine(int ItemIndex, TQueueItemProxy * QueueItem)
{
return
(ItemIndex >= FQueueStatus->DoneCount) &&
(ItemIndex < FQueueStatus->DoneAndActiveCount) &&
QueueItem->Info->Primary &&
!QueueItem->Info->SingleFile &&
!SimpleOperation(QueueItem) &&
((QueueItem->ProgressData == NULL) || !QueueItem->ProgressData->Done);
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::RefreshQueueItem(TQueueItemProxy * QueueItem)
{
TListItem * NextListItem = NULL;
TListItem * ListItem;
ListItem = FListView->FindData(0, QueueItem, true, false);
DebugAssert(ListItem != NULL);
int Index = ListItem->Index;
if (Index + 1 < FListView->Items->Count)
{
NextListItem = FListView->Items->Item[Index + 1];
if (NextListItem->Data != QueueItem)
{
NextListItem = NULL;
}
}
bool HasDetailsLine = UseDetailsLine(QueueItem->Index, QueueItem);
FillQueueViewItem(ListItem, QueueItem, false, !HasDetailsLine);
if (HasDetailsLine)
{
if (NextListItem == NULL)
{
NextListItem = FListView->Items->Insert(Index + 1);
}
FillQueueViewItem(NextListItem, QueueItem, true, false);
}
else
{
if (NextListItem != NULL)
{
NextListItem->Delete();
}
}
DoChange();
}
//---------------------------------------------------------------------------
bool __fastcall TQueueController::QueueItemNeedsFrequentRefresh(
TQueueItemProxy * QueueItem)
{
return
(TQueueItem::IsUserActionStatus(QueueItem->Status) ||
(QueueItem->Status == TQueueItem::qsPaused));
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::DoChange()
{
if (FOnChange != NULL)
{
FOnChange(NULL);
}
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::QueueViewDblClick(TObject * /*Sender*/)
{
TQueueOperation Operation = DefaultOperation();
if (Operation != qoNone)
{
ExecuteOperation(Operation);
}
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::QueueViewKeyDown(TObject * /*Sender*/,
WORD & Key, TShiftState /*Shift*/)
{
if (Key == VK_RETURN)
{
TQueueOperation Operation = DefaultOperation();
if (Operation != qoNone)
{
ExecuteOperation(Operation);
}
Key = 0;
}
else if (Key == VK_DELETE)
{
ExecuteOperation(qoItemDelete);
Key = 0;
}
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::QueueViewCustomDrawItem(TCustomListView * Sender,
TListItem * Item, TCustomDrawState /*State*/, bool & /*DefaultDraw*/)
{
TQueueItemProxy * QueueItem = QueueViewItemToQueueItem(Item);
if (QueueItem->Status == TQueueItem::qsDone)
{
Sender->Canvas->Font->Color = clGrayText;
}
}
//---------------------------------------------------------------------------
bool __fastcall TQueueController::GetEmpty()
{
return (FQueueStatus == NULL) || (FQueueStatus->Count == 0);
}
//---------------------------------------------------------------------------
void __fastcall TQueueController::RememberConfiguration()
{
FFormatSizeBytes = WinConfiguration->FormatSizeBytes;
}
//---------------------------------------------------------------------------
bool __fastcall TQueueController::NeedRefresh()
{
bool Result = (WinConfiguration->FormatSizeBytes != FFormatSizeBytes);
RememberConfiguration();
return Result;
}
//---------------------------------------------------------------------------
TQueueItemProxy * __fastcall TQueueController::GetFocusedPrimaryItem()
{
TQueueItemProxy * Result = NULL;
TListItem * PrimaryItemOfFocused = FListView->ItemFocused;
if (PrimaryItemOfFocused != NULL)
{
while (!QueueViewItemToQueueItem(PrimaryItemOfFocused)->Info->Primary &&
DebugAlwaysTrue(PrimaryItemOfFocused->Index > 0))
{
PrimaryItemOfFocused = FListView->Items->Item[PrimaryItemOfFocused->Index - 1];
}
Result = QueueViewItemToQueueItem(PrimaryItemOfFocused);
}
return Result;
}