//--------------------------------------------------------------------- #include #pragma hdrstop #include #include "Progress.h" //--------------------------------------------------------------------- #pragma link "PathLabel" #pragma link "PngImageList" #pragma link "TB2Dock" #pragma link "TB2Item" #pragma link "TB2Toolbar" #pragma link "TBX" #pragma link "TB2ExtItems" #pragma link "TBXExtItems" #pragma resource "*.dfm" //--------------------------------------------------------------------- static bool IsIndeterminate(const TSynchronizeProgress * SynchronizeProgress, const TFileOperationProgressType * ProgressData) { bool Result; // TSynchronizeProgress has its own way how to take atomic operations into account if (SynchronizeProgress != NULL) { Result = TFileOperationProgressType::IsIndeterminateOperation(ProgressData->Operation); } else { Result = ProgressData->IsIndeterminate(); } return Result; } //--------------------------------------------------------------------- UnicodeString __fastcall TProgressForm::ProgressStr( const TSynchronizeProgress * SynchronizeProgress, const TFileOperationProgressType * ProgressData) { static const int Captions[] = { 0, 0, PROGRESS_DELETE, PROGRESS_SETPROPERTIES, 0, PROGRESS_CUSTOM_COMAND, PROGRESS_CALCULATE_SIZE, PROGRESS_REMOTE_MOVE, PROGRESS_REMOTE_COPY, PROGRESS_GETPROPERTIES, PROGRESS_CALCULATE_CHECKSUM, PROGRESS_LOCK, PROGRESS_UNLOCK }; DebugAssert((unsigned int)ProgressData->Operation >= 1 && ((unsigned int)ProgressData->Operation - 1) < LENOF(Captions)); int Id; if (ProgressData->IsTransfer()) { Id = (ProgressData->Side == osLocal) ? PROGRESS_UPLOAD : PROGRESS_DOWNLOAD; } else { Id = Captions[(int)ProgressData->Operation - 1]; DebugAssert(Id != 0); } UnicodeString Result = LoadStr(Id); if (SynchronizeProgress != NULL) { Result = LoadStr(SYNCHRONIZE_PROGRESS_SYNCHRONIZE2) + TitleSeparator + Result; } if (!IsIndeterminate(SynchronizeProgress, ProgressData)) { int OverallProgress; if (SynchronizeProgress != NULL) { OverallProgress = SynchronizeProgress->Progress(ProgressData); } else { OverallProgress = ProgressData->OverallProgress(); } Result = FORMAT(L"%d%% %s", (OverallProgress, Result)); } return Result; } //--------------------------------------------------------------------- __fastcall TProgressForm::TProgressForm( TComponent * AOwner, bool AllowMoveToQueue, bool AllowSkip, TSynchronizeProgress * SynchronizeProgress) : TForm(AOwner), FData() { FLastOperation = foNone; FLastSide = (TOperationSide)-1; FLastTotalSizeSet = false; FDataGot = false; FDataReceived = false; FCancel = csContinue; FMoveToQueue = false; FMinimizedByMe = false; FUpdateCounter = 0; FDeleteLocalToRecycleBin = false; FDeleteRemoteToRecycleBin = false; FReadOnly = false; FShowAsModalStorage = NULL; FStarted = Now(); FModalBeginHooked = false; FModalLevel = -1; FPendingSkip = false; FSynchronizeProgress = SynchronizeProgress; FAllowSkip = AllowSkip; FWheelDelta = 0; UseSystemSettings(this); FOnceDoneItems.Add(odoIdle, IdleOnceDoneItem); FOnceDoneItems.Add(odoDisconnect, DisconnectOnceDoneItem); FOnceDoneItems.Add(odoSuspend, SuspendOnceDoneItem); FOnceDoneItems.Add(odoShutDown, ShutDownOnceDoneItem); ResetOnceDoneOperation(); HideComponentsPanel(this); SelectScaledImageList(ImageList); SetGlobalMinimizeHandler(this, GlobalMinimize); MoveToQueueItem->Visible = AllowMoveToQueue; } //--------------------------------------------------------------------------- __fastcall TProgressForm::~TProgressForm() { // to prevent raising assertion (e.g. IsProgress == True) FData.Clear(); ClearGlobalMinimizeHandler(GlobalMinimize); if (IsApplicationMinimized() && FMinimizedByMe) { ShowNotification( NULL, MainInstructions(LoadStr(BALLOON_OPERATION_COMPLETE)), qtInformation); } ReleaseAsModal(this, FShowAsModalStorage); } //--------------------------------------------------------------------- UnicodeString __fastcall TProgressForm::ProgressStr() { return FProgressStr; } //--------------------------------------------------------------------- void __fastcall TProgressForm::UpdateControls() { DebugAssert((FData.Operation >= foCopy) && (FData.Operation <= foUnlock) && (FData.Operation != foRename)); bool TransferOperation = FData.IsTransfer(); bool Indeterminate = IsIndeterminate(SynchronizeProgress, &FData); CancelItem->Enabled = !FReadOnly && (FCancel < csCancel); SkipItem->Visible = TransferOperation && FAllowSkip; SkipItem->Enabled = !FReadOnly && (FCancel < csCancelFile) && !FPendingSkip; MoveToQueueItem->Enabled = !FMoveToQueue && (FCancel == csContinue) && !FPendingSkip; bool HideOnceDone = FReadOnly || (FData.Operation == foCalculateSize) || (FData.Operation == foGetProperties) || (FData.Operation == foCalculateChecksum) || ((FData.Operation == foDelete) && (FData.Side == osLocal)); CycleOnceDoneItem->Visible = !HideOnceDone; CycleOnceDoneItem->ImageIndex = CurrentOnceDoneItem()->ImageIndex; SpeedComboBoxItem->Visible = TransferOperation; if ((FData.Operation != FLastOperation) || (FData.Side != FLastSide)) { UnicodeString Animation; UnicodeString CancelCaption = Vcl_Consts_SMsgDlgCancel; int MoveToQueueImageIndex = -1; FPendingSkip = false; // just in case int MoveTransferToQueueImageIndex; if (FData.Side == osRemote) { MoveTransferToQueueImageIndex = 7; } else { MoveTransferToQueueImageIndex = 8; } switch (FData.Operation) { case foCopy: if (FData.Side == osRemote) { Animation = L"CopyRemote"; } else { Animation = L"CopyLocal"; } MoveToQueueImageIndex = MoveTransferToQueueImageIndex; break; case foMove: if (FData.Side == osRemote) { Animation = L"MoveRemote"; } else { Animation = L"MoveLocal"; } MoveToQueueImageIndex = MoveTransferToQueueImageIndex; break; case foDelete: Animation = ((FData.Side == osRemote) ? DeleteRemoteToRecycleBin : DeleteLocalToRecycleBin) ? L"Recycle" : L"Delete"; MoveToQueueImageIndex = 10; break; case foCalculateSize: Animation = L"CalculateSize"; CancelCaption = LoadStr(SKIP_BUTTON); MoveToQueueImageIndex = MoveTransferToQueueImageIndex; break; case foSetProperties: Animation = "SetProperties"; break; case foRemoteCopy: Animation = "DuplicateLtoR"; break; case foRemoteMove: Animation = "MoveLtoR"; break; default: DebugAssert( (FData.Operation == foCustomCommand) || (FData.Operation == foGetProperties) || (FData.Operation == foCalculateChecksum) || (FData.Operation == foLock) || (FData.Operation == foUnlock)); break; } CancelItem->Caption = CancelCaption; OperationProgress->Style = Indeterminate ? pbstMarquee : pbstNormal; if (SynchronizeProgress != NULL) { Animation = L"SynchronizeDirectories"; } FFrameAnimation.Init(AnimationPaintBox, Animation); FFrameAnimation.Start(); int Delta = 0; if (TransferOperation && !TransferPanel->Visible) Delta += TransferPanel->Height; else if (!TransferOperation && TransferPanel->Visible) Delta += -TransferPanel->Height; TransferPanel->Visible = TransferOperation; ClientHeight = ClientHeight + Delta; TargetLabel->Visible = TransferOperation; TargetPathLabel->Visible = TransferOperation; TargetPathLabel->UnixPath = (FData.Side == osLocal); FileLabel->UnixPath = (FData.Side == osRemote); PathLabel->Caption = LoadStr((FData.Operation == foCalculateSize) ? PROGRESS_PATH_LABEL : PROGRESS_FILE_LABEL); DebugAssert(!MoveToQueueItem->Visible || (MoveToQueueImageIndex >= 0)); MoveToQueueItem->ImageIndex = MoveToQueueImageIndex; FLastOperation = FData.Operation; FLastSide = FData.Side; FLastTotalSizeSet = !FData.TotalSizeSet; } if (FLastTotalSizeSet != FData.TotalSizeSet) { StartTimeLabelLabel->Visible = !FData.TotalSizeSet; StartTimeLabel->Visible = !FData.TotalSizeSet; TimeLeftLabelLabel->Visible = FData.TotalSizeSet; TimeLeftLabel->Visible = FData.TotalSizeSet; FLastTotalSizeSet = FData.TotalSizeSet; } UnicodeString FileCaption; if ((FData.Operation == foCalculateSize) && DebugAlwaysTrue(!FData.Temp)) { if (FData.Side == osRemote) { FileCaption = UnixExtractFileDir(FData.FullFileName); } else { FileCaption = ExtractFileDir(FData.FullFileName); } } else if ((FData.Side == osRemote) || !FData.Temp) { FileCaption = FData.FileName; } else { FileCaption = ExtractFileName(FData.FileName); } if (FileLabel->Caption != FileCaption) { FileLabel->Caption = FileCaption; FPendingSkip = false; } int OverallProgress; // as a side effect this prevents calling TSynchronizeProgress::Progress when we do not know total size yet // (what would cache wrong values forever) if (Indeterminate) { OverallProgress = -1; } else { if (SynchronizeProgress != NULL) { OverallProgress = SynchronizeProgress->Progress(&FData); } else { OverallProgress = FData.OverallProgress(); } } OperationProgress->Position = std::max(0, OverallProgress); OperationProgress->Hint = (OverallProgress < 0) ? UnicodeString() : FORMAT(L"%d%%", (OverallProgress)); FProgressStr = ProgressStr(SynchronizeProgress, &FData); Caption = FormatFormCaption(this, FProgressStr); if (TransferOperation) { if ((FData.Side == osLocal) || !FData.Temp) { TargetPathLabel->Caption = FData.Directory; } else { TargetPathLabel->Caption = LoadStr(PROGRESS_TEMP_DIR); } StartTimeLabel->Caption = FData.StartTime.TimeString(); if (FData.TotalSizeSet) { UnicodeString TimeLeftCaption; bool CanShow = CanShowTimeEstimate(FData.StartTime); if (CanShow) { TDateTime TimeLeft; if (SynchronizeProgress != NULL) { TimeLeft = SynchronizeProgress->TimeLeft(&FData); } else { TimeLeft = FData.TotalTimeLeft(); } TimeLeftCaption = FormatDateTimeSpan(TimeLeft); } else { TimeLeftCaption = LoadStr(PROGRESS_TIME_LEFT_CALCULATING); } TimeLeftLabel->Caption = TimeLeftCaption; } TDateTime Elapsed = FData.TimeElapsed(); TimeElapsedLabel->Caption = FormatDateTimeSpan(Elapsed); BytesTransferredLabel->Caption = FormatBytes(FData.TotalTransferred); int CPS = FData.CPS(); CPSLabel->Caption = FORMAT(L"%s/s", (FormatBytes(CPS))); FileProgress->Position = FData.TransferProgress(); FileProgress->Hint = FORMAT(L"%d%%", (FileProgress->Position)); } SizeToolbar(this, Toolbar, Dock, ToolbarPanel); } //--------------------------------------------------------------------- void TProgressForm::SizeToolbar(TForm * Form, TTBXToolbar * Toolbar, TTBXDock * Dock, TPanel * ToolbarPanel) { // particularly to adjust to LargerToolbar Dock->ClientHeight = Toolbar->Height; int Delta = Dock->Height - ToolbarPanel->ClientHeight; if (Delta != 0) { ToolbarPanel->ClientHeight += Delta; DebugAssert(ToolbarPanel->Anchors.Contains(akBottom)); ToolbarPanel->Top -= Delta; // to counter akBottom anchoring Form->ClientHeight += Delta; } } //--------------------------------------------------------------------- static __int64 DelayStartInterval = MSecsPerSec / 2; static __int64 UpdateInterval = 1 * MSecsPerSec; //--------------------------------------------------------------------- bool __fastcall TProgressForm::ReceiveData(bool Force, int ModalLevelOffset) { bool Result = false; if (FDataGot && !FDataReceived) { // CPS limit is set set only once from TFileOperationProgressType::Start. // Needs to be set even when data are not accepted yet, otherwise we would // write default value to FData in TProgressForm::SetProgressData FCPSLimit = FData.CPSLimit; // Never popup over dialog that appeared later than we started // (this can happen from UpdateTimerTimer when application is // restored while overwrite confirmation dialog [or any other] // is already shown). // TODO We should probably take as-modal windows into account too // (for extreme cases like restoring while reconnecting [as-modal TAuthenticateForm]). if ((FModalLevel < 0) || (Application->ModalLevel + ModalLevelOffset <= FModalLevel)) { // Delay showing the progress until the application is restored, // otherwise the form popups up unminimized. // See solution in TMessageForm::CMShowingChanged. if (!IsApplicationMinimized() && (Force || (MilliSecondsBetween(Now(), FStarted) > DelayStartInterval))) { FDataReceived = true; SpeedComboBoxItem->Text = SetSpeedLimit(FCPSLimit); ShowAsModal(this, FShowAsModalStorage); // particularly needed for the case, when we are showing the form delayed // because application was minimized when operation started Result = true; } else if (!FModalBeginHooked && DebugAlwaysTrue(FModalLevel < 0)) { // record state as of time, the window should be shown, // had not we implemented delayed show ApplicationEvents->OnModalBegin = ApplicationModalBegin; FModalBeginHooked = true; FModalLevel = Application->ModalLevel; } } } return Result; } //--------------------------------------------------------------------------- void __fastcall TProgressForm::ApplicationModalBegin(TObject * /*Sender*/) { // Popup before any modal dialog shows (typically overwrite confirmation, // as that popups nearly instantly, i.e. less than DelayStartInterval). // The Application->ModalLevel is already incremented, but we should treat it as // if it were not as the dialog is not created yet (so we can popup if we are not yet). ReceiveData(true, -1); } //--------------------------------------------------------------------- void __fastcall TProgressForm::SetProgressData(TFileOperationProgressType & AData) { bool InstantUpdate = false; // workaround: to force displaing first file data immediately, // otherwise form dialog uses to be blank for first second // (until UpdateTimerTimer) if (FileLabel->Caption.IsEmpty() && !AData.FileName.IsEmpty()) { InstantUpdate = true; } FData.AssignButKeepSuspendState(AData); FDataGot = true; if (!UpdateTimer->Enabled) { UpdateTimer->Interval = static_cast(DelayStartInterval); UpdateTimer->Enabled = true; FSinceLastUpdate = 0; } if (ReceiveData(false, 0)) { InstantUpdate = true; } if (InstantUpdate) { UpdateControls(); Application->ProcessMessages(); } if (ProcessGUI(FUpdateCounter % 5 == 0)) { FUpdateCounter = 0; } FUpdateCounter++; AData.SetCPSLimit(FCPSLimit); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::UpdateTimerTimer(TObject * /*Sender*/) { // popup the progress window at least here, if SetProgressData is // not being called (typically this happens when using custom command // that launches long-lasting external process, such as visual diff) ReceiveData(false, 0); if (UpdateTimer->Interval == DelayStartInterval) { UpdateTimer->Interval = static_cast(GUIUpdateInterval); } if (FDataReceived) { FSinceLastUpdate += UpdateTimer->Interval; if (FSinceLastUpdate >= UpdateInterval) { UpdateControls(); FSinceLastUpdate = 0; } } } //--------------------------------------------------------------------------- void __fastcall TProgressForm::FormShow(TObject * /*Sender*/) { CopySpeedLimits(CustomWinConfiguration->History[L"SpeedLimit"], SpeedComboBoxItem->Strings); ReceiveData(false, 0); if (FDataReceived) { UpdateControls(); } // HACK: In command-line run (/upload), FormShow gets called twice, // leading to duplicate hook and memory leak. Make sure we unhook, just in case. // Calling unhook without hooking first is noop. UnhookFormActivation(this); HookFormActivation(this); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::FormHide(TObject * /*Sender*/) { UnhookFormActivation(this); // This is to counter the "infinite" timestamp in // TTerminalManager::ApplicationShowHint. // Because if form disappears on its own, hint is not hidden. Application->CancelHint(); CustomWinConfiguration->History[L"SpeedLimit"] = SpeedComboBoxItem->Strings; UpdateTimer->Enabled = false; } //--------------------------------------------------------------------------- void __fastcall TProgressForm::CancelItemClick(TObject * /*Sender*/) { CancelOperation(); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::Minimize(TObject * Sender) { CallGlobalMinimizeHandler(Sender); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::MinimizeItemClick(TObject * Sender) { Minimize(Sender); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::CancelOperation() { DebugAssert(FDataReceived); if (!FData.Suspended) { // mostly useless, as suspend is called over copy of actual progress data FData.Suspend(); UpdateControls(); try { TCancelStatus ACancel; if (FData.TransferringFile && (FData.TimeExpected() > GUIConfiguration->IgnoreCancelBeforeFinish)) { int Result = MessageDialog(LoadStr(CANCEL_OPERATION_FATAL2), qtWarning, qaYes | qaNo | qaCancel, HELP_PROGRESS_CANCEL); switch (Result) { case qaYes: ACancel = csCancelTransfer; break; case qaNo: ACancel = csCancel; break; default: ACancel = csContinue; break; } } else { ACancel = csCancel; } SetCancelLower(ACancel); } __finally { FData.Resume(); } } } //--------------------------------------------------------------------------- void __fastcall TProgressForm::GlobalMinimize(TObject * /*Sender*/) { ApplicationMinimize(); FMinimizedByMe = true; } //--------------------------------------------------------------------------- TTBCustomItem * __fastcall TProgressForm::CurrentOnceDoneItem() { TOnceDoneItems::const_iterator Iterator = FOnceDoneItems.begin(); while (Iterator != FOnceDoneItems.end()) { if (Iterator->second->Checked) { return Iterator->second; } Iterator++; } DebugFail(); return NULL; } //--------------------------------------------------------------------------- TOnceDoneOperation __fastcall TProgressForm::GetOnceDoneOperation() { return FOnceDoneItems.LookupFirst(CurrentOnceDoneItem()); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SetOnceDoneItem(TTBCustomItem * Item) { TTBCustomItem * Current = CurrentOnceDoneItem(); if (Current != Item) { Current->Checked = false; Item->Checked = true; // Not until we have any data to update. // Happens when set to odoDisconnect in command-line upload/download // mode from TCustomScpExplorerForm::FileOperationProgress. if (FDataGot) { UpdateControls(); } } } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SetOnceDoneOperation(TOnceDoneOperation value) { SetOnceDoneItem(FOnceDoneItems.LookupSecond(value)); } //--------------------------------------------------------------------------- bool __fastcall TProgressForm::GetAllowMinimize() { return MinimizeItem->Visible; } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SetAllowMinimize(bool value) { MinimizeItem->Visible = value; } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SetReadOnly(bool value) { if (FReadOnly != value) { FReadOnly = value; if (!value) { ResetOnceDoneOperation(); } UpdateControls(); } } //--------------------------------------------------------------------------- void __fastcall TProgressForm::ResetOnceDoneOperation() { SetOnceDoneOperation(odoIdle); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::CMDialogKey(TCMDialogKey & Message) { if (Message.CharCode == VK_TAB) { Toolbar->KeyboardOpen(L'\0', false); Message.Result = 1; } else { TForm::Dispatch(&Message); } } //--------------------------------------------------------------------------- void __fastcall TProgressForm::Dispatch(void * AMessage) { TMessage & Message = *reinterpret_cast(AMessage); if (Message.Msg == WM_CLOSE) { CancelOperation(); } else if (Message.Msg == CM_DIALOGKEY) { CMDialogKey(reinterpret_cast(Message)); } else if (Message.Msg == WM_MANAGES_CAPTION) { Message.Result = 1; } else { TForm::Dispatch(AMessage); } } //--------------------------------------------------------------------------- void __fastcall TProgressForm::MoveToQueueItemClick(TObject * /*Sender*/) { FMoveToQueue = true; UpdateControls(); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::OnceDoneItemClick(TObject * Sender) { SetOnceDoneItem(dynamic_cast(Sender)); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::CycleOnceDoneItemClick(TObject * /*Sender*/) { TTBCustomItem * Item = CurrentOnceDoneItem(); int Index = Item->Parent->IndexOf(Item); DebugAssert(Index >= 0); if (Index < Item->Parent->Count - 1) { Index++; } else { Index = 0; } SetOnceDoneItem(Item->Parent->Items[Index]); } //--------------------------------------------------------------------------- UnicodeString __fastcall TProgressForm::ItemSpeed(const UnicodeString & Text, TTBXComboBoxItem * Item) { // Keep in sync with TNonVisualDataModule::QueueItemSpeed FCPSLimit = GetSpeedLimit(Text); UnicodeString Result = SetSpeedLimit(FCPSLimit); SaveToHistory(Item->Strings, Result); CustomWinConfiguration->History[L"SpeedLimit"] = Item->Strings; FWheelDelta = 0; return Result; } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SpeedComboBoxItemAcceptText(TObject * Sender, UnicodeString & NewText, bool & /*Accept*/) { TTBXComboBoxItem * Item = dynamic_cast(Sender); NewText = ItemSpeed(NewText, Item); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SpeedComboBoxItemItemClick(TObject * Sender) { TTBXComboBoxItem * Item = dynamic_cast(Sender); Item->Text = ItemSpeed(Item->Text, Item); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SpeedComboBoxItemAdjustImageIndex( TTBXComboBoxItem * Sender, const UnicodeString /*AText*/, int /*AIndex*/, int & ImageIndex) { // Use fixed image (do not change image by item index) ImageIndex = Sender->ImageIndex; } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SpeedComboBoxItemClick(TObject * Sender) { ClickToolbarItem(DebugNotNull(dynamic_cast(Sender)), false); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::ClearCancel() { if (DebugAlwaysTrue(FCancel == csCancelFile)) { FPendingSkip = true; } FCancel = csContinue; UpdateControls(); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SetCancelLower(TCancelStatus ACancel) { if (FCancel < ACancel) { FCancel = ACancel; UpdateControls(); } } //--------------------------------------------------------------------------- void __fastcall TProgressForm::SkipItemClick(TObject * /*Sender*/) { SetCancelLower(csCancelFile); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::MouseWheelHandler(TMessage & Message) { int X = GET_X_LPARAM(Message.LParam); int Y = GET_Y_LPARAM(Message.LParam); TPoint P = Toolbar->ScreenToClient(TPoint(X, Y)); if (Toolbar->ClientRect.Contains(P)) { TTBItemViewer * Viewer = Toolbar->View->Find(SpeedComboBoxItem); if (Viewer->BoundsRect.Contains(P)) { int Delta = GET_WHEEL_DELTA_WPARAM(Message.WParam); FWheelDelta += Delta; unsigned long CurrentSpeed = GetSpeedLimit(SpeedComboBoxItem->Text); unsigned long Speed = CurrentSpeed; unsigned int CPS = FData.CPS(); int Step = 4 * WHEEL_DELTA; if (FWheelDelta > 0) { while (FWheelDelta > Step) { if (Speed > 0) { Speed *= 2; if (Speed > std::max(MaxSpeed, static_cast(CPS) * 2)) { Speed = 0; } } FWheelDelta -= Step; } } else if (FWheelDelta < 0) { while (FWheelDelta < -Step) { if (Speed == 0) { Speed = 8; while (Speed * 2 < CPS) { Speed *= 2; } } else { if (Speed > MinSpeed) { Speed /= 2; } } FWheelDelta += Step; } } if (Speed != CurrentSpeed) { TTBEditItemViewer * EditViewer = dynamic_cast(Viewer); if (EditViewer->EditControl != NULL) { EditViewer->View->CancelMode(); } FCPSLimit = Speed; SpeedComboBoxItem->Text = SetSpeedLimit(Speed); } Message.Result = 1; } } TForm::MouseWheelHandler(Message); } //--------------------------------------------------------------------------- void __fastcall TProgressForm::CreateWnd() { TForm::CreateWnd(); ApplyColorMode(this); Toolbar->Color = Color; } //---------------------------------------------------------------------------