| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854 | //---------------------------------------------------------------------#include <vcl.h>#pragma hdrstop#include <Common.h>#include <CoreMain.h>#include <TextsWin.h>#include <HelpWin.h>#include <WinInterface.h>#include <VCLCommon.h>#include <CustomWinConfiguration.h>#include <GUITools.h>#include <BaseUtils.hpp>#include <DateUtils.hpp>#include <Consts.hpp>#include <HistoryComboBox.hpp>#include <windowsx.h>#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 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)    : FData(), TForm(AOwner){  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;      if (CanShowTimeEstimate(FData.StartTime))      {        TDateTime TimeLeft;        if (SynchronizeProgress != NULL)        {          TimeLeft = SynchronizeProgress->TimeLeft(&FData);        }        else        {          TimeLeft = FData.TotalTimeLeft();        }        TimeLeftCaption = FormatDateTimeSpan(Configuration->TimeFormat, TimeLeft);      }      else      {        TimeLeftCaption = LoadStr(PROGRESS_TIME_LEFT_CALCULATING);      }      TimeLeftLabel->Caption = TimeLeftCaption;    }    TimeElapsedLabel->Caption = FormatDateTimeSpan(Configuration->TimeFormat, FData.TimeElapsed());    BytesTransferredLabel->Caption = FormatBytes(FData.TotalTransferred);    CPSLabel->Caption = FORMAT(L"%s/s", (FormatBytes(FData.CPS())));    FileProgress->Position = FData.TransferProgress();    FileProgress->Hint = FORMAT(L"%d%%", (FileProgress->Position));  }}//---------------------------------------------------------------------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<int>(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<int>(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<TMessage *>(AMessage);  if (Message.Msg == WM_CLOSE)  {    CancelOperation();  }  else if (Message.Msg == CM_DIALOGKEY)  {    CMDialogKey(reinterpret_cast<TCMDialogKey &>(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<TTBCustomItem *>(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<TTBXComboBoxItem *>(Sender);  NewText = ItemSpeed(NewText, Item);}//---------------------------------------------------------------------------void __fastcall TProgressForm::SpeedComboBoxItemItemClick(TObject * Sender){  TTBXComboBoxItem * Item = dynamic_cast<TTBXComboBoxItem *>(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<TTBCustomItem *>(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<unsigned long>(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<TTBEditItemViewer *>(Viewer);        if (EditViewer->EditControl != NULL)        {          EditViewer->View->CancelMode();        }        FCPSLimit = Speed;        SpeedComboBoxItem->Text = SetSpeedLimit(Speed);      }      Message.Result = 1;    }  }  TForm::MouseWheelHandler(Message);}//---------------------------------------------------------------------------
 |