Progress.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. //---------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <Common.h>
  5. #include <CoreMain.h>
  6. #include <TextsWin.h>
  7. #include <HelpWin.h>
  8. #include <WinInterface.h>
  9. #include <VCLCommon.h>
  10. #include <CustomWinConfiguration.h>
  11. #include <GUITools.h>
  12. #include <BaseUtils.hpp>
  13. #include <DateUtils.hpp>
  14. #include "Progress.h"
  15. //---------------------------------------------------------------------
  16. #pragma link "HistoryComboBox"
  17. #pragma link "PathLabel"
  18. #ifndef NO_RESOURCES
  19. #pragma resource "*.dfm"
  20. #endif
  21. //---------------------------------------------------------------------
  22. UnicodeString __fastcall TProgressForm::OperationName(TFileOperation Operation)
  23. {
  24. static const int Captions[] = { PROGRESS_COPY, PROGRESS_MOVE, PROGRESS_DELETE,
  25. PROGRESS_SETPROPERTIES, 0, PROGRESS_CUSTOM_COMAND, PROGRESS_CALCULATE_SIZE,
  26. PROGRESS_REMOTE_MOVE, PROGRESS_REMOTE_COPY, PROGRESS_GETPROPERTIES,
  27. PROGRESS_CALCULATE_CHECKSUM };
  28. assert((unsigned int)Operation >= 1 && ((unsigned int)Operation - 1) < LENOF(Captions));
  29. return LoadStr(Captions[(int)Operation - 1]);
  30. }
  31. //---------------------------------------------------------------------
  32. __fastcall TProgressForm::TProgressForm(TComponent* AOwner)
  33. : FData(), TForm(AOwner)
  34. {
  35. FLastOperation = foNone;
  36. FLastTotalSizeSet = false;
  37. FDataReceived = false;
  38. FAsciiTransferChanged = false;
  39. FResumeStatusChanged = false;
  40. FCancel = csContinue;
  41. FMinimizedByMe = false;
  42. FUpdateCounter = 0;
  43. FLastUpdate = 0;
  44. FDeleteToRecycleBin = false;
  45. FReadOnly = false;
  46. FShowAsModalStorage = NULL;
  47. UseSystemSettings(this);
  48. ResetOnceDoneOperation();
  49. if (CustomWinConfiguration->OperationProgressOnTop)
  50. {
  51. FOperationProgress = TopProgress;
  52. FFileProgress = BottomProgress;
  53. }
  54. else
  55. {
  56. FOperationProgress = BottomProgress;
  57. FFileProgress = TopProgress;
  58. }
  59. if (!IsGlobalMinimizeHandler())
  60. {
  61. SetGlobalMinimizeHandler(GlobalMinimize);
  62. };
  63. }
  64. //---------------------------------------------------------------------------
  65. __fastcall TProgressForm::~TProgressForm()
  66. {
  67. // to prevent raising assertion (e.g. IsProgress == True)
  68. FData.Clear();
  69. if (GetGlobalMinimizeHandler() == GlobalMinimize)
  70. {
  71. SetGlobalMinimizeHandler(NULL);
  72. }
  73. if (IsApplicationMinimized() && FMinimizedByMe)
  74. {
  75. ShowNotification(NULL, LoadStr(BALLOON_OPERATION_COMPLETE), qtInformation);
  76. }
  77. ReleaseAsModal(this, FShowAsModalStorage);
  78. }
  79. //---------------------------------------------------------------------
  80. void __fastcall TProgressForm::UpdateControls()
  81. {
  82. assert((FData.Operation >= foCopy) && (FData.Operation <= foCalculateChecksum) &&
  83. FData.Operation != foRename );
  84. CancelButton->Enabled = !FReadOnly;
  85. OnceDoneOperationCombo->Enabled =
  86. !FReadOnly && (FData.Operation != foCalculateSize) &&
  87. (FData.Operation != foGetProperties) &&
  88. (FData.Operation != foCalculateChecksum);
  89. OnceDoneOperationLabel->Enabled = OnceDoneOperationCombo->Enabled;
  90. bool TransferOperation =
  91. ((FData.Operation == foCopy) || (FData.Operation == foMove));
  92. if (FData.Operation != FLastOperation)
  93. {
  94. bool AVisible;
  95. THandle ShellModule;
  96. try
  97. {
  98. AVisible = true;
  99. switch (FData.Operation) {
  100. case foCopy:
  101. case foMove:
  102. case foRemoteMove:
  103. case foRemoteCopy:
  104. if (FData.Count == 1) Animate->CommonAVI = aviCopyFile;
  105. else Animate->CommonAVI = aviCopyFiles;
  106. break;
  107. case foDelete:
  108. Animate->CommonAVI = (DeleteToRecycleBin ? aviRecycleFile : aviDeleteFile);
  109. break;
  110. case foSetProperties:
  111. case foGetProperties:
  112. ShellModule = SafeLoadLibrary(L"shell32.dll");
  113. if (!ShellModule)
  114. {
  115. Abort();
  116. }
  117. // workaround, VCL is not able to set both ResId and ResHandle otherwise
  118. Animate->Active = false;
  119. Animate->ResHandle = 0;
  120. Animate->ComponentState << csLoading;
  121. Animate->ResId = 165;
  122. Animate->ResHandle = ShellModule;
  123. Animate->ComponentState >> csLoading;
  124. Animate->Active = true;
  125. break;
  126. default:
  127. assert(FData.Operation == foCustomCommand ||
  128. FData.Operation == foCalculateSize ||
  129. FData.Operation == foCalculateChecksum);
  130. Animate->CommonAVI = aviNone;
  131. AVisible = false;
  132. }
  133. }
  134. catch (...)
  135. {
  136. AVisible = false;
  137. };
  138. int Delta = 0;
  139. if (AVisible && !Animate->Visible) Delta = Animate->Height;
  140. else
  141. if (!AVisible && Animate->Visible) Delta = -Animate->Height;
  142. MainPanel->Top = MainPanel->Top + Delta;
  143. TransferPanel->Top = TransferPanel->Top + Delta;
  144. SpeedPanel->Top = SpeedPanel->Top + Delta;
  145. Animate->Visible = AVisible;
  146. Animate->Active = AVisible;
  147. if (TransferOperation && !TransferPanel->Visible) Delta += TransferPanel->Height;
  148. else
  149. if (!TransferOperation && TransferPanel->Visible) Delta += -TransferPanel->Height;
  150. TransferPanel->Visible = TransferOperation;
  151. SpeedPanel->Visible = TransferOperation;
  152. ClientHeight = ClientHeight + Delta;
  153. Caption = OperationName(FData.Operation);
  154. TargetLabel->Visible = TransferOperation;
  155. TargetPathLabel->Visible = TransferOperation;
  156. TargetPathLabel->UnixPath = (FData.Side == osLocal);
  157. FileLabel->UnixPath = (FData.Side == osRemote);
  158. FLastOperation = FData.Operation;
  159. FLastTotalSizeSet = !FData.TotalSizeSet;
  160. };
  161. if (FLastTotalSizeSet != FData.TotalSizeSet)
  162. {
  163. StartTimeLabelLabel->Visible = !FData.TotalSizeSet;
  164. StartTimeLabel->Visible = !FData.TotalSizeSet;
  165. TimeLeftLabelLabel->Visible = FData.TotalSizeSet;
  166. TimeLeftLabel->Visible = FData.TotalSizeSet;
  167. FLastTotalSizeSet = FData.TotalSizeSet;
  168. }
  169. if ((FData.Side == osRemote) || !FData.Temp)
  170. {
  171. FileLabel->Caption = FData.FileName;
  172. }
  173. else
  174. {
  175. FileLabel->Caption = ExtractFileName(FData.FileName);
  176. }
  177. int OverallProgress = FData.OverallProgress();
  178. FOperationProgress->Position = OverallProgress;
  179. FOperationProgress->Hint = FORMAT(L"%d%%", (OverallProgress));
  180. Caption = FORMAT(L"%d%% %s", (OverallProgress, OperationName(FData.Operation)));
  181. if (TransferOperation)
  182. {
  183. if ((FData.Side == osLocal) || !FData.Temp)
  184. {
  185. TargetPathLabel->Caption = FData.Directory;
  186. }
  187. else
  188. {
  189. TargetPathLabel->Caption = LoadStr(PROGRESS_TEMP_DIR);
  190. }
  191. StartTimeLabel->Caption = FData.StartTime.TimeString();
  192. if (FData.TotalSizeSet)
  193. {
  194. TimeLeftLabel->Caption = FormatDateTimeSpan(Configuration->TimeFormat,
  195. FData.TotalTimeLeft());
  196. }
  197. TimeElapsedLabel->Caption = FormatDateTimeSpan(Configuration->TimeFormat, FData.TimeElapsed());
  198. BytesTransferedLabel->Caption = FormatBytes(FData.TotalTransfered);
  199. CPSLabel->Caption = FORMAT(L"%s/s", (FormatBytes(FData.CPS())));
  200. FFileProgress->Position = FData.TransferProgress();
  201. FFileProgress->Hint = FORMAT(L"%d%%", (FFileProgress->Position));
  202. }
  203. }
  204. //---------------------------------------------------------------------
  205. void __fastcall TProgressForm::SetProgressData(TFileOperationProgressType & AData)
  206. {
  207. bool InstantUpdate = false;
  208. // workaround: to force displaing first file data immediatelly,
  209. // otherwise form dialog uses to be blank for first second
  210. // (until UpdateTimerTimer)
  211. if (FileLabel->Caption.IsEmpty() && !AData.FileName.IsEmpty())
  212. {
  213. InstantUpdate = true;
  214. }
  215. if (!FAsciiTransferChanged && FData.AsciiTransfer != AData.AsciiTransfer)
  216. {
  217. FAsciiTransferChanged = true;
  218. InstantUpdate = true;
  219. }
  220. if (!FResumeStatusChanged && FData.ResumeStatus != AData.ResumeStatus)
  221. {
  222. FResumeStatusChanged = true;
  223. InstantUpdate = true;
  224. }
  225. FData = AData;
  226. if (!FDataReceived)
  227. {
  228. FDataReceived = true;
  229. // CPS limit is set set only once from TFileOperationProgressType::Start
  230. FCPSLimit = AData.CPSLimit;
  231. SpeedCombo->Text = SetSpeedLimit(FCPSLimit);
  232. ShowAsModal(this, FShowAsModalStorage);
  233. }
  234. if (InstantUpdate)
  235. {
  236. UpdateControls();
  237. Application->ProcessMessages();
  238. }
  239. TDateTime N = Now();
  240. static double UpdateInterval = static_cast<double>(OneSecond*5); // 1/5 sec
  241. if ((FUpdateCounter % 5 == 0) ||
  242. (double(N) - double(FLastUpdate) > UpdateInterval))
  243. {
  244. FLastUpdate = N;
  245. FUpdateCounter = 0;
  246. Application->ProcessMessages();
  247. }
  248. FUpdateCounter++;
  249. AData.CPSLimit = FCPSLimit;
  250. }
  251. //---------------------------------------------------------------------------
  252. void __fastcall TProgressForm::UpdateTimerTimer(TObject * /*Sender*/)
  253. {
  254. if (FDataReceived) UpdateControls();
  255. }
  256. //---------------------------------------------------------------------------
  257. void __fastcall TProgressForm::FormShow(TObject * /*Sender*/)
  258. {
  259. UpdateTimer->Enabled = true;
  260. SpeedCombo->Items = CustomWinConfiguration->History[L"SpeedLimit"];
  261. if (FDataReceived) UpdateControls();
  262. FLastUpdate = 0;
  263. }
  264. //---------------------------------------------------------------------------
  265. void __fastcall TProgressForm::FormHide(TObject * /*Sender*/)
  266. {
  267. // This is to counter the "infinite" timestamp in
  268. // TTerminalManager::ApplicationShowHint.
  269. // Because if form disappears on its own, hint is not hidden.
  270. Application->CancelHint();
  271. CustomWinConfiguration->History[L"SpeedLimit"] = SpeedCombo->Items;
  272. UpdateTimer->Enabled = false;
  273. }
  274. //---------------------------------------------------------------------------
  275. void __fastcall TProgressForm::CancelButtonClick(TObject * /*Sender*/)
  276. {
  277. CancelOperation();
  278. }
  279. //---------------------------------------------------------------------------
  280. void __fastcall TProgressForm::MinimizeButtonClick(TObject * Sender)
  281. {
  282. GetGlobalMinimizeHandler()(Sender);
  283. }
  284. //---------------------------------------------------------------------------
  285. void __fastcall TProgressForm::CancelOperation()
  286. {
  287. // partially duplicated in TWinSCPFileSystem::CancelConfiguration (far\WinSCPFileSystem)
  288. assert(FDataReceived);
  289. if (!FData.Suspended)
  290. {
  291. // mostly useless, as suspend is called over copy of actual progress data
  292. FData.Suspend();
  293. UpdateControls();
  294. try
  295. {
  296. TCancelStatus ACancel;
  297. int Result;
  298. if (FData.TransferingFile &&
  299. (FData.TimeExpected() > GUIConfiguration->IgnoreCancelBeforeFinish))
  300. {
  301. Result = MessageDialog(LoadStr(CANCEL_OPERATION_FATAL), qtWarning,
  302. qaYes | qaNo | qaCancel, HELP_PROGRESS_CANCEL);
  303. }
  304. else
  305. {
  306. Result = MessageDialog(LoadStr(CANCEL_OPERATION), qtConfirmation,
  307. qaOK | qaCancel, HELP_PROGRESS_CANCEL);
  308. }
  309. switch (Result) {
  310. case qaYes:
  311. ACancel = csCancelTransfer; break;
  312. case qaOK:
  313. case qaNo:
  314. ACancel = csCancel; break;
  315. default:
  316. ACancel = csContinue; break;
  317. }
  318. if (FCancel < ACancel)
  319. {
  320. FCancel = ACancel;
  321. }
  322. }
  323. __finally
  324. {
  325. FData.Resume();
  326. }
  327. }
  328. }
  329. //---------------------------------------------------------------------------
  330. void __fastcall TProgressForm::MinimizeApp()
  331. {
  332. Application->Minimize();
  333. FMinimizedByMe = true;
  334. }
  335. //---------------------------------------------------------------------------
  336. void __fastcall TProgressForm::GlobalMinimize(TObject * /*Sender*/)
  337. {
  338. MinimizeApp();
  339. }
  340. //---------------------------------------------------------------------------
  341. void __fastcall TProgressForm::SetOnceDoneOperation(TOnceDoneOperation value)
  342. {
  343. int Index = 0;
  344. switch (value)
  345. {
  346. case odoIdle:
  347. Index = 0;
  348. break;
  349. case odoDisconnect:
  350. Index = 1;
  351. break;
  352. case odoShutDown:
  353. Index = 2;
  354. break;
  355. default:
  356. assert(false);
  357. }
  358. OnceDoneOperationCombo->ItemIndex = Index;
  359. OnceDoneOperationComboSelect(NULL);
  360. }
  361. //---------------------------------------------------------------------------
  362. bool __fastcall TProgressForm::GetAllowMinimize()
  363. {
  364. return MinimizeButton->Visible;
  365. }
  366. //---------------------------------------------------------------------------
  367. void __fastcall TProgressForm::SetAllowMinimize(bool value)
  368. {
  369. MinimizeButton->Visible = value;
  370. }
  371. //---------------------------------------------------------------------------
  372. void __fastcall TProgressForm::SetReadOnly(bool value)
  373. {
  374. if (FReadOnly != value)
  375. {
  376. FReadOnly = value;
  377. if (!value)
  378. {
  379. ResetOnceDoneOperation();
  380. }
  381. UpdateControls();
  382. }
  383. }
  384. //---------------------------------------------------------------------------
  385. void __fastcall TProgressForm::ApplyCPSLimit()
  386. {
  387. try
  388. {
  389. FCPSLimit = GetSpeedLimit(SpeedCombo->Text);
  390. }
  391. catch(...)
  392. {
  393. SpeedCombo->SetFocus();
  394. throw;
  395. }
  396. SpeedCombo->Text = SetSpeedLimit(FCPSLimit);
  397. // visualize application
  398. SpeedCombo->SelectAll();
  399. CancelButton->SetFocus();
  400. }
  401. //---------------------------------------------------------------------------
  402. void __fastcall TProgressForm::SpeedComboExit(TObject * /*Sender*/)
  403. {
  404. SpeedCombo->Text = SetSpeedLimit(FCPSLimit);
  405. }
  406. //---------------------------------------------------------------------------
  407. void __fastcall TProgressForm::SpeedComboSelect(TObject * /*Sender*/)
  408. {
  409. ApplyCPSLimit();
  410. }
  411. //---------------------------------------------------------------------------
  412. void __fastcall TProgressForm::SpeedComboKeyPress(TObject * /*Sender*/,
  413. wchar_t & Key)
  414. {
  415. // using OnKeyPress instead of OnKeyDown to catch "enter" prevents
  416. // system beep for unhandled key
  417. if (Key == L'\r')
  418. {
  419. Key = L'\0';
  420. ApplyCPSLimit();
  421. }
  422. }
  423. //---------------------------------------------------------------------------
  424. void __fastcall TProgressForm::ResetOnceDoneOperation()
  425. {
  426. OnceDoneOperationCombo->ItemIndex = 0;
  427. OnceDoneOperationComboSelect(NULL);
  428. }
  429. //---------------------------------------------------------------------------
  430. void __fastcall TProgressForm::OnceDoneOperationComboSelect(TObject * /*Sender*/)
  431. {
  432. switch (OnceDoneOperationCombo->ItemIndex)
  433. {
  434. case 0:
  435. FOnceDoneOperation = odoIdle;
  436. break;
  437. case 1:
  438. FOnceDoneOperation = odoDisconnect;
  439. break;
  440. case 2:
  441. FOnceDoneOperation = odoShutDown;
  442. break;
  443. default:
  444. assert(false);
  445. }
  446. }
  447. //---------------------------------------------------------------------------
  448. void __fastcall TProgressForm::OnceDoneOperationComboCloseUp(TObject * /*Sender*/)
  449. {
  450. CancelButton->SetFocus();
  451. }
  452. //---------------------------------------------------------------------------
  453. #pragma warn -8080