//--------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include #include #include #include "QueueController.h" #include //--------------------------------------------------------------------------- #pragma package(smart_init) //--------------------------------------------------------------------------- __fastcall TQueueController::TQueueController(TCustomListView * ListView) { FListView = static_cast(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(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 GetOverallProgress(TFileOperationProgressType * ProgressData) { return FORMAT(L"%d%%", (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 = GetOverallProgress(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) { Values[3] = GetTime(ProgressData); Values[4] = FORMAT(L"%s/s", (FormatBytes(ProgressData->CPS()))); } if (ProgressStr.IsEmpty()) { ProgressStr = GetOverallProgress(ProgressData); } } else if (ProgressData->Operation == foCalculateSize) { ProgressStr = LoadStr(QUEUE_LISTING); } } Values[5] = ProgressStr; } else { if (ProgressData != NULL) { if ((Info->Side == osRemote) || !ProgressData->Temp) { Values[0] = ProgressData->FileName; } else { Values[0] = ExtractFileName(ProgressData->FileName); } if (ProgressData->Operation == Info->Operation) { Values[2] = FormatPanelBytes(ProgressData->TransferredSize, WinConfiguration->FormatSizeBytes); if (ProgressStr.IsEmpty()) { ProgressStr = FORMAT(L"%d%%", (ProgressData->TransferProgress())); } } } Values[5] = ProgressStr; } Item->StateIndex = (!BlinkHide ? State : -1); Item->ImageIndex = (!BlinkHide ? Image : -1); for (size_t Index = 0; Index < LENOF(Values); Index++) { if (Index < static_cast(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; }