SynchronizeChecklist.cpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126
  1. //---------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <Common.h>
  5. #include "WinInterface.h"
  6. #include "SynchronizeChecklist.h"
  7. #include <Terminal.h>
  8. #include <TextsWin.h>
  9. #include <CoreMain.h>
  10. #include <VCLCommon.h>
  11. #include <Tools.h>
  12. #include <BaseUtils.hpp>
  13. #include <Math.hpp>
  14. #include <WinConfiguration.h>
  15. #include <GUITools.h>
  16. //---------------------------------------------------------------------
  17. #pragma link "IEListView"
  18. #pragma link "NortonLikeListView"
  19. #pragma link "PngImageList"
  20. #ifndef NO_RESOURCES
  21. #pragma resource "*.dfm"
  22. #endif
  23. //---------------------------------------------------------------------
  24. const int ImageColumnIndex = 4;
  25. //---------------------------------------------------------------------
  26. bool __fastcall DoSynchronizeChecklistDialog(TSynchronizeChecklist * Checklist,
  27. TSynchronizeMode Mode, int Params,
  28. const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
  29. TCustomCommandMenuEvent OnCustomCommandMenu, TFullSynchronizeEvent OnSynchronize, void * Token)
  30. {
  31. std::unique_ptr<TSynchronizeChecklistDialog> Dialog(
  32. new TSynchronizeChecklistDialog(Application, Mode, Params, LocalDirectory, RemoteDirectory, OnCustomCommandMenu, OnSynchronize, Token));
  33. return Dialog->Execute(Checklist);
  34. }
  35. //---------------------------------------------------------------------
  36. __fastcall TSynchronizeChecklistDialog::TSynchronizeChecklistDialog(
  37. TComponent * AOwner, TSynchronizeMode Mode, int Params,
  38. const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
  39. TCustomCommandMenuEvent OnCustomCommandMenu, TFullSynchronizeEvent OnSynchronize, void * Token)
  40. : TForm(AOwner)
  41. {
  42. FFormRestored = false;
  43. FMode = Mode;
  44. FParams = Params;
  45. FLocalDirectory = ExcludeTrailingBackslash(LocalDirectory);
  46. FRemoteDirectory = UnixExcludeTrailingBackslash(RemoteDirectory);
  47. FOnCustomCommandMenu = OnCustomCommandMenu;
  48. DebugAssert(OnSynchronize != NULL);
  49. FOnSynchronize = OnSynchronize;
  50. FToken = Token;
  51. UseSystemSettings(this);
  52. UseDesktopFont(ListView);
  53. UseDesktopFont(StatusBar);
  54. FChecklist = NULL;
  55. FChangingItem = NULL;
  56. FChangingItemIgnore = false;
  57. FChangingItemMass = false;
  58. FGeneralHint = StatusBar->Hint;
  59. FSynchronizing = false;
  60. SelectScaledImageList(ActionImages);
  61. FOrigListViewWindowProc = ListView->WindowProc;
  62. ListView->WindowProc = ListViewWindowProc;
  63. UpdateImages();
  64. CustomCommandsAction->Visible = (FOnCustomCommandMenu != NULL);
  65. // button visibility cannot be bound to action visibility
  66. CustomCommandsButton2->Visible = CustomCommandsAction->Visible;
  67. MenuButton(CustomCommandsButton2);
  68. }
  69. //---------------------------------------------------------------------
  70. __fastcall TSynchronizeChecklistDialog::~TSynchronizeChecklistDialog()
  71. {
  72. ListView->WindowProc = FOrigListViewWindowProc;
  73. }
  74. //---------------------------------------------------------------------
  75. bool __fastcall TSynchronizeChecklistDialog::Execute(TSynchronizeChecklist * Checklist)
  76. {
  77. FChecklist = Checklist;
  78. bool Result = (ShowModal() == DefaultResult(this));
  79. if (Result)
  80. {
  81. TSynchronizeChecklistConfiguration FormConfiguration =
  82. CustomWinConfiguration->SynchronizeChecklist;
  83. FormConfiguration.ListParams = ListView->ColProperties->ParamsStr;
  84. UnicodeString WindowParams = FormConfiguration.WindowParams;
  85. // if there is no main window, keep previous "custom pos" indication,
  86. bool CustomPos = (StrToIntDef(CutToChar(WindowParams, L';', true), 0) != 0);
  87. if (!IsMainFormLike(this))
  88. {
  89. CustomPos = (Application->MainForm->BoundsRect != BoundsRect);
  90. }
  91. FormConfiguration.WindowParams =
  92. FORMAT(L"%d;%s", ((CustomPos ? 1 : 0), StoreForm(this)));
  93. CustomWinConfiguration->SynchronizeChecklist = FormConfiguration;
  94. }
  95. if (FException.get() != NULL)
  96. {
  97. RethrowException(FException.get());
  98. }
  99. return Result;
  100. }
  101. //---------------------------------------------------------------------
  102. void __fastcall TSynchronizeChecklistDialog::UpdateControls()
  103. {
  104. Caption = FormatFormCaption(this, LoadStr(FSynchronizing ? SYNCHRONIZE_PROGRESS_SYNCHRONIZE2 : SYNCHRONIZE_CHECKLIST_CAPTION));
  105. StatusBar->Invalidate();
  106. bool AllChecked = true;
  107. bool AllUnchecked = true;
  108. bool AnyBoth = false;
  109. bool AnyNonBoth = false;
  110. TListItem * Item = ListView->Selected;
  111. while (Item != NULL)
  112. {
  113. TSynchronizeChecklist::TAction Action = GetChecklistItemAction(GetChecklistItem(Item));
  114. if ((Action == TSynchronizeChecklist::saUploadUpdate) ||
  115. (Action == TSynchronizeChecklist::saDownloadUpdate))
  116. {
  117. AnyBoth = true;
  118. }
  119. else
  120. {
  121. AnyNonBoth = true;
  122. }
  123. if (Item->Checked)
  124. {
  125. AllUnchecked = false;
  126. }
  127. else
  128. {
  129. AllChecked = false;
  130. }
  131. Item = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected);
  132. }
  133. EnableControl(OkButton, (FChecked[0] > 0) && !FSynchronizing);
  134. EnableControl(CancelButton, !FSynchronizing);
  135. EnableControl(HelpButton, !FSynchronizing);
  136. CheckAction->Enabled = !AllChecked && !FSynchronizing;
  137. UncheckAction->Enabled = !AllUnchecked && !FSynchronizing;
  138. CheckAllAction->Enabled = (FChecked[0] < FTotals[0]) && !FSynchronizing;
  139. UncheckAllAction->Enabled = (FChecked[0] > 0) && !FSynchronizing;
  140. CustomCommandsAction->Enabled = AnyBoth && !AnyNonBoth && DebugAlwaysTrue(!FSynchronizing);
  141. ReverseAction->Enabled = (ListView->SelCount > 0) && DebugAlwaysTrue(!FSynchronizing);
  142. SelectAllAction->Enabled = (ListView->SelCount < ListView->Items->Count) && !FSynchronizing;
  143. }
  144. //---------------------------------------------------------------------------
  145. bool __fastcall TSynchronizeChecklistDialog::GetWindowParams(UnicodeString & WindowParams)
  146. {
  147. WindowParams = CustomWinConfiguration->SynchronizeChecklist.WindowParams;
  148. bool CustomPos = (StrToIntDef(CutToChar(WindowParams, L';', true), 0) != 0);
  149. return CustomPos || IsMainFormLike(this);
  150. }
  151. //---------------------------------------------------------------------------
  152. void __fastcall TSynchronizeChecklistDialog::CreateParams(TCreateParams & Params)
  153. {
  154. UnicodeString WindowParams;
  155. if (GetWindowParams(WindowParams))
  156. {
  157. // This is only to set correct TForm::Position. Actual bounds are set later after DPI scaling
  158. RestoreForm(WindowParams, this, true);
  159. }
  160. TForm::CreateParams(Params);
  161. }
  162. //---------------------------------------------------------------------------
  163. void __fastcall TSynchronizeChecklistDialog::HelpButtonClick(TObject * /*Sender*/)
  164. {
  165. FormHelp(this);
  166. }
  167. //---------------------------------------------------------------------------
  168. void __fastcall TSynchronizeChecklistDialog::AddSubItem(TListItem * Item, int & Index, const UnicodeString & S)
  169. {
  170. if (Index < Item->SubItems->Count)
  171. {
  172. Item->SubItems->Strings[Index] = S;
  173. }
  174. else
  175. {
  176. DebugAssert(Index == Item->SubItems->Count);
  177. Item->SubItems->Add(S);
  178. }
  179. Index++;
  180. }
  181. //---------------------------------------------------------------------------
  182. void __fastcall TSynchronizeChecklistDialog::LoadItem(TListItem * Item)
  183. {
  184. UnicodeString S;
  185. const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item);
  186. if (ChecklistItem->ImageIndex >= 0)
  187. {
  188. Item->ImageIndex = ChecklistItem->ImageIndex;
  189. }
  190. else
  191. {
  192. Item->ImageIndex = FakeFileImageIndex(ChecklistItem->GetFileName(),
  193. FLAGMASK(ChecklistItem->IsDirectory, FILE_ATTRIBUTE_DIRECTORY));
  194. }
  195. S = ChecklistItem->GetFileName();
  196. if (ChecklistItem->IsDirectory)
  197. {
  198. S = IncludeTrailingBackslash(S);
  199. }
  200. Item->Caption = S;
  201. int Index = 0;
  202. TSynchronizeChecklist::TAction Action = GetChecklistItemAction(ChecklistItem);
  203. if (Action == TSynchronizeChecklist::saDeleteRemote)
  204. {
  205. AddSubItem(Item, Index, L"");
  206. AddSubItem(Item, Index, L"");
  207. AddSubItem(Item, Index, L"");
  208. }
  209. else
  210. {
  211. S = ChecklistItem->Local.Directory;
  212. if (AnsiSameText(FLocalDirectory, S.SubString(1, FLocalDirectory.Length())))
  213. {
  214. S[1] = L'.';
  215. S.Delete(2, FLocalDirectory.Length() - 1);
  216. }
  217. else
  218. {
  219. DebugFail();
  220. }
  221. AddSubItem(Item, Index, S);
  222. if (Action == TSynchronizeChecklist::saDownloadNew)
  223. {
  224. AddSubItem(Item, Index, L"");
  225. AddSubItem(Item, Index, L"");
  226. }
  227. else
  228. {
  229. if (ChecklistItem->IsDirectory)
  230. {
  231. AddSubItem(Item, Index, L"");
  232. }
  233. else
  234. {
  235. AddSubItem(Item, Index,
  236. FormatPanelBytes(ChecklistItem->Local.Size, WinConfiguration->FormatSizeBytes));
  237. }
  238. AddSubItem(Item, Index,
  239. UserModificationStr(ChecklistItem->Local.Modification,
  240. ChecklistItem->Local.ModificationFmt));
  241. }
  242. }
  243. AddSubItem(Item, Index, L"");
  244. DebugAssert(Index == ImageColumnIndex);
  245. if (Action == TSynchronizeChecklist::saDeleteLocal)
  246. {
  247. AddSubItem(Item, Index, L"");
  248. AddSubItem(Item, Index, L"");
  249. AddSubItem(Item, Index, L"");
  250. }
  251. else
  252. {
  253. S = ChecklistItem->Remote.Directory;
  254. if (AnsiSameText(FRemoteDirectory, S.SubString(1, FRemoteDirectory.Length())))
  255. {
  256. S[1] = L'.';
  257. S.Delete(2, FRemoteDirectory.Length() - 1);
  258. }
  259. else
  260. {
  261. DebugFail();
  262. }
  263. AddSubItem(Item, Index, S);
  264. if (Action == TSynchronizeChecklist::saUploadNew)
  265. {
  266. AddSubItem(Item, Index, L"");
  267. AddSubItem(Item, Index, L"");
  268. }
  269. else
  270. {
  271. if (ChecklistItem->IsDirectory)
  272. {
  273. AddSubItem(Item, Index, L"");
  274. }
  275. else
  276. {
  277. AddSubItem(Item, Index,
  278. FormatPanelBytes(ChecklistItem->Remote.Size, WinConfiguration->FormatSizeBytes));
  279. }
  280. AddSubItem(Item, Index, UserModificationStr(ChecklistItem->Remote.Modification,
  281. ChecklistItem->Remote.ModificationFmt));
  282. }
  283. }
  284. }
  285. //---------------------------------------------------------------------------
  286. void __fastcall TSynchronizeChecklistDialog::LoadList()
  287. {
  288. memset(&FTotals, 0, sizeof(FTotals));
  289. memset(&FChecked, 0, sizeof(FChecked));
  290. memset(&FCheckedSize, 0, sizeof(FCheckedSize));
  291. FTotals[0] = FChecklist->Count;
  292. ListView->Items->BeginUpdate();
  293. try
  294. {
  295. ListView->Items->Clear();
  296. for (int Index = 0; Index < FChecklist->Count; Index++)
  297. {
  298. const TSynchronizeChecklist::TItem * ChecklistItem =
  299. FChecklist->Item[ListView->Items->Count];
  300. FChangingItemIgnore = true;
  301. try
  302. {
  303. TListItem * Item = ListView->Items->Add();
  304. TSynchronizeChecklist::TAction Action = ChecklistItem->Action;
  305. FActions.insert(std::make_pair(ChecklistItem, Action));
  306. Item->Data = const_cast<TSynchronizeChecklist::TItem *>(ChecklistItem);
  307. Item->Checked = ChecklistItem->Checked;
  308. LoadItem(Item);
  309. }
  310. __finally
  311. {
  312. FChangingItemIgnore = false;
  313. }
  314. int ActionIndex = int(GetChecklistItemAction(ChecklistItem));
  315. FTotals[ActionIndex]++;
  316. if (ChecklistItem->Checked)
  317. {
  318. FChecked[ActionIndex]++;
  319. FChecked[0]++;
  320. __int64 ItemSize = GetItemSize(ChecklistItem);
  321. FCheckedSize[ActionIndex] += ItemSize;
  322. FCheckedSize[0] += ItemSize;
  323. }
  324. }
  325. }
  326. __finally
  327. {
  328. ListView->Items->EndUpdate();
  329. }
  330. ListView->AlphaSort();
  331. UpdateControls();
  332. }
  333. //---------------------------------------------------------------------------
  334. bool __fastcall TSynchronizeChecklistDialog::IsItemSizeIrrelevant(TSynchronizeChecklist::TAction Action)
  335. {
  336. switch (Action)
  337. {
  338. case TSynchronizeChecklist::saNone:
  339. case TSynchronizeChecklist::saDeleteRemote:
  340. case TSynchronizeChecklist::saDeleteLocal:
  341. return true;
  342. default:
  343. return false;
  344. }
  345. }
  346. //---------------------------------------------------------------------------
  347. __int64 __fastcall TSynchronizeChecklistDialog::GetItemSize(const TSynchronizeChecklist::TItem * Item)
  348. {
  349. TSynchronizeChecklist::TAction Action = GetChecklistItemAction(Item);
  350. if (IsItemSizeIrrelevant(Action))
  351. {
  352. return 0;
  353. }
  354. else
  355. {
  356. switch (Action)
  357. {
  358. case TSynchronizeChecklist::saUploadNew:
  359. case TSynchronizeChecklist::saUploadUpdate:
  360. return Item->Local.Size;
  361. case TSynchronizeChecklist::saDownloadNew:
  362. case TSynchronizeChecklist::saDownloadUpdate:
  363. return Item->Remote.Size;
  364. default:
  365. DebugFail();
  366. return 0;
  367. }
  368. }
  369. }
  370. //---------------------------------------------------------------------------
  371. void __fastcall TSynchronizeChecklistDialog::FormShow(TObject * /*Sender*/)
  372. {
  373. // Moved here from CreateParams (see also TEditorForm::CreateParams), because there it breaks per-monitor DPI.
  374. // For example BoundsRect is matched to the main form too soon, so it gets rescaled later.
  375. // Also it happens before constructor, what causes UseDesktopFont-flagged controls to rescale twice.
  376. // But Position is already set in the CreateParams, as it cannot be set here anymore.
  377. if (!FFormRestored)
  378. {
  379. FFormRestored = True;
  380. UnicodeString WindowParams;
  381. if (GetWindowParams(WindowParams))
  382. {
  383. RestoreForm(WindowParams, this);
  384. }
  385. else
  386. {
  387. BoundsRect = Application->MainForm->BoundsRect;
  388. }
  389. }
  390. ListView->ColProperties->ParamsStr = CustomWinConfiguration->SynchronizeChecklist.ListParams;
  391. LoadList();
  392. UpdateStatusBarSize();
  393. }
  394. //---------------------------------------------------------------------------
  395. TRect __fastcall TSynchronizeChecklistDialog::GetColumnHeaderRect(int Index)
  396. {
  397. HWND HeaderHandle = ListView_GetHeader(ListView->Handle);
  398. TRect R;
  399. Header_GetItemRect(HeaderHandle, Index, &R);
  400. // Can be simplified using GetScrollPos
  401. TScrollInfo ScrollInfo;
  402. ZeroMemory(&ScrollInfo, sizeof(ScrollInfo));
  403. ScrollInfo.cbSize = sizeof(ScrollInfo);
  404. ScrollInfo.fMask = SIF_POS;
  405. GetScrollInfo(ListView->Handle, SB_HORZ, &ScrollInfo);
  406. R.Left -= ScrollInfo.nPos;
  407. R.Right -= ScrollInfo.nPos;
  408. return R;
  409. }
  410. //---------------------------------------------------------------------------
  411. void __fastcall TSynchronizeChecklistDialog::ListViewWindowProc(TMessage & Message)
  412. {
  413. if (Message.Msg == CN_NOTIFY)
  414. {
  415. TWMNotify & NotifyMessage = reinterpret_cast<TWMNotify &>(Message);
  416. if (NotifyMessage.NMHdr->code == NM_CUSTOMDRAW)
  417. {
  418. // workaround
  419. // Due to a bug in VCL, OnAdvancedCustomDrawSubItem is not called for any
  420. // other stage except for cdPrePaint. So we must call it ourselves.
  421. TNMLVCustomDraw * CustomDraw =
  422. reinterpret_cast<TNMLVCustomDraw *>(NotifyMessage.NMHdr);
  423. if (FLAGSET(CustomDraw->nmcd.dwDrawStage, CDDS_ITEM) &&
  424. FLAGSET(CustomDraw->nmcd.dwDrawStage, CDDS_SUBITEM) &&
  425. FLAGSET(CustomDraw->nmcd.dwDrawStage, CDDS_ITEMPOSTPAINT) &&
  426. (CustomDraw->iSubItem == ImageColumnIndex) &&
  427. (ActionImages->Width <= ListView->Columns->Items[CustomDraw->iSubItem]->Width))
  428. {
  429. TListItem * Item = ListView->Items->Item[CustomDraw->nmcd.dwItemSpec];
  430. const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item);
  431. TRect HeaderR = GetColumnHeaderRect(CustomDraw->iSubItem);
  432. TRect R = Item->DisplayRect(drBounds);
  433. R.Left = HeaderR.Left + (HeaderR.Width() - ActionImages->Width) / 2;
  434. R.Right = HeaderR.Right;
  435. // workaround
  436. // doing this from ListViewAdvancedCustomDraw corrupts list view on Windows 7
  437. ImageList_Draw(reinterpret_cast<HIMAGELIST>(ActionImages->Handle),
  438. int(GetChecklistItemAction(ChecklistItem)), CustomDraw->nmcd.hdc,
  439. R.Left, ((R.Top + R.Bottom - ActionImages->Height) / 2), ILD_TRANSPARENT);
  440. }
  441. }
  442. }
  443. FOrigListViewWindowProc(Message);
  444. }
  445. //---------------------------------------------------------------------------
  446. void __fastcall TSynchronizeChecklistDialog::ListViewAdvancedCustomDrawSubItem(
  447. TCustomListView * /*Sender*/, TListItem * /*Item*/, int /*SubItem*/,
  448. TCustomDrawState /*State*/, TCustomDrawStage /*Stage*/, bool & /*DefaultDraw*/)
  449. {
  450. // this is just fake handler that makes the list view request custom draw notification above
  451. }
  452. //---------------------------------------------------------------------------
  453. void __fastcall TSynchronizeChecklistDialog::StatusBarDrawPanel(
  454. TStatusBar * StatusBar, TStatusPanel * Panel, const TRect & Rect)
  455. {
  456. bool Possible;
  457. int ActionIndex = Panel->Index;
  458. TSynchronizeChecklist::TAction Action = TSynchronizeChecklist::TAction(ActionIndex);
  459. if (FTotals[ActionIndex] > 0)
  460. {
  461. // if direction is overriden to an action that is otherwise not possible
  462. // in given synchronization mode, we still want to show the stats
  463. Possible = true;
  464. }
  465. else
  466. {
  467. switch (Action)
  468. {
  469. case TSynchronizeChecklist::saNone:
  470. Possible = true;
  471. break;
  472. case TSynchronizeChecklist::saUploadNew:
  473. Possible = ((FMode == smRemote) || (FMode == smBoth)) &&
  474. FLAGCLEAR(FParams, TTerminal::spTimestamp);
  475. break;
  476. case TSynchronizeChecklist::saDownloadNew:
  477. Possible = ((FMode == smLocal) || (FMode == smBoth)) &&
  478. FLAGCLEAR(FParams, TTerminal::spTimestamp);
  479. break;
  480. case TSynchronizeChecklist::saUploadUpdate:
  481. Possible =
  482. ((FMode == smRemote) || (FMode == smBoth)) &&
  483. (FLAGCLEAR(FParams, TTerminal::spNotByTime) || FLAGSET(FParams, TTerminal::spBySize));
  484. break;
  485. case TSynchronizeChecklist::saDownloadUpdate:
  486. Possible =
  487. ((FMode == smLocal) || (FMode == smBoth)) &&
  488. (FLAGCLEAR(FParams, TTerminal::spNotByTime) || FLAGSET(FParams, TTerminal::spBySize));
  489. break;
  490. case TSynchronizeChecklist::saDeleteRemote:
  491. Possible = (FMode == smRemote) &&
  492. FLAGCLEAR(FParams, TTerminal::spTimestamp);
  493. break;
  494. case TSynchronizeChecklist::saDeleteLocal:
  495. Possible = (FMode == smLocal) &&
  496. FLAGCLEAR(FParams, TTerminal::spTimestamp);
  497. break;
  498. default:
  499. DebugFail();
  500. Possible = false;
  501. break;
  502. }
  503. }
  504. UnicodeString PanelText;
  505. if (Possible)
  506. {
  507. PanelText = FORMAT(LoadStrPart(SYNCHRONIZE_SELECTED_ACTIONS, 1),
  508. (FormatNumber(FChecked[ActionIndex]),
  509. FormatNumber(FTotals[ActionIndex])));
  510. if ((FChecked[ActionIndex] > 0) &&
  511. ((ActionIndex == 0) || !IsItemSizeIrrelevant(Action)))
  512. {
  513. PanelText += FORMAT(L" (%s)", (FormatBytes(FCheckedSize[ActionIndex])));
  514. }
  515. }
  516. else
  517. {
  518. PanelText = LoadStrPart(SYNCHRONIZE_SELECTED_ACTIONS, 2);
  519. }
  520. int TextHeight = StatusBar->Canvas->TextHeight(PanelText);
  521. int X = Rect.Left + ActionImages->Width + 4;
  522. int Y = (Rect.Top + Rect.Bottom - TextHeight) / 2;
  523. StatusBar->Canvas->TextRect(Rect, X, Y, PanelText);
  524. X = Rect.Left + 1;
  525. Y = ((Rect.Top + Rect.Bottom - ActionImages->Height) / 2);
  526. int ImageIndex = ActionIndex;
  527. ActionImages->Draw(StatusBar->Canvas, X, Y, ImageIndex, Possible);
  528. }
  529. //---------------------------------------------------------------------------
  530. int __fastcall TSynchronizeChecklistDialog::PanelCount()
  531. {
  532. // last "panel" is technical
  533. return StatusBar->Panels->Count - 1;
  534. }
  535. //---------------------------------------------------------------------------
  536. int __fastcall TSynchronizeChecklistDialog::PanelAt(int X)
  537. {
  538. int Result = 0;
  539. while ((X > StatusBar->Panels->Items[Result]->Width) &&
  540. (Result < PanelCount()))
  541. {
  542. X -= StatusBar->Panels->Items[Result]->Width;
  543. Result++;
  544. }
  545. return ((Result < StatusBar->Panels->Count - 1) ? Result : -1);
  546. }
  547. //---------------------------------------------------------------------------
  548. void __fastcall TSynchronizeChecklistDialog::StatusBarMouseMove(
  549. TObject * /*Sender*/, TShiftState /*Shift*/, int X, int /*Y*/)
  550. {
  551. UnicodeString Hint;
  552. int IPanel = PanelAt(X);
  553. if (IPanel >= 0)
  554. {
  555. Hint = StatusBar->Panels->Items[IPanel]->Text;
  556. if (IPanel > 0)
  557. {
  558. Hint = FORMAT(L"%s\n%s", (Hint, FGeneralHint));
  559. }
  560. }
  561. if (Hint != StatusBar->Hint)
  562. {
  563. Application->CancelHint();
  564. StatusBar->Hint = Hint;
  565. }
  566. }
  567. //---------------------------------------------------------------------------
  568. void __fastcall TSynchronizeChecklistDialog::ListViewChange(
  569. TObject * /*Sender*/, TListItem * Item, TItemChange Change)
  570. {
  571. if ((Change == ctState) && (FChangingItem == Item) && (FChangingItem != NULL))
  572. {
  573. if (!FChangingItemIgnore)
  574. {
  575. DebugAssert(Item->Data != NULL);
  576. if ((FChangingItemChecked != Item->Checked) && (Item->Data != NULL))
  577. {
  578. const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item);
  579. int ActionIndex = int(GetChecklistItemAction(ChecklistItem));
  580. int Diff = Item->Checked ? 1 : -1;
  581. FChecked[ActionIndex] += Diff;
  582. FChecked[0] += Diff;
  583. __int64 ItemSize = GetItemSize(ChecklistItem);
  584. if (!Item->Checked)
  585. {
  586. ItemSize = -ItemSize;
  587. }
  588. FCheckedSize[ActionIndex] += ItemSize;
  589. FCheckedSize[0] += ItemSize;
  590. if (!FChangingItemMass)
  591. {
  592. UpdateControls();
  593. }
  594. }
  595. }
  596. FChangingItem = NULL;
  597. }
  598. }
  599. //---------------------------------------------------------------------------
  600. void __fastcall TSynchronizeChecklistDialog::ListViewChanging(
  601. TObject * /*Sender*/, TListItem * Item, TItemChange Change,
  602. bool & /*AllowChange*/)
  603. {
  604. if (Change == ctState)
  605. {
  606. FChangingItem = Item;
  607. FChangingItemChecked = Item->Checked;
  608. }
  609. else
  610. {
  611. DebugAssert(FChangingItem == NULL);
  612. FChangingItem = NULL;
  613. }
  614. }
  615. //---------------------------------------------------------------------------
  616. void __fastcall TSynchronizeChecklistDialog::CheckAll(bool Check)
  617. {
  618. FChangingItemMass = true;
  619. try
  620. {
  621. for (int Index = 0; Index < ListView->Items->Count; Index++)
  622. {
  623. ListView->Items->Item[Index]->Checked = Check;
  624. }
  625. }
  626. __finally
  627. {
  628. FChangingItemMass = false;
  629. }
  630. UpdateControls();
  631. }
  632. //---------------------------------------------------------------------------
  633. void __fastcall TSynchronizeChecklistDialog::CheckAllActionExecute(TObject * /*Sender*/)
  634. {
  635. CheckAll(true);
  636. }
  637. //---------------------------------------------------------------------------
  638. void __fastcall TSynchronizeChecklistDialog::UncheckAllActionExecute(TObject * /*Sender*/)
  639. {
  640. CheckAll(false);
  641. }
  642. //---------------------------------------------------------------------------
  643. void __fastcall TSynchronizeChecklistDialog::Check(bool Check)
  644. {
  645. FChangingItemMass = true;
  646. try
  647. {
  648. TListItem * Item = ListView->Selected;
  649. while (Item != NULL)
  650. {
  651. Item->Checked = Check;
  652. Item = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected);
  653. }
  654. }
  655. __finally
  656. {
  657. FChangingItemMass = false;
  658. }
  659. UpdateControls();
  660. }
  661. //---------------------------------------------------------------------------
  662. void __fastcall TSynchronizeChecklistDialog::CheckActionExecute(TObject * /*Sender*/)
  663. {
  664. Check(true);
  665. }
  666. //---------------------------------------------------------------------------
  667. void __fastcall TSynchronizeChecklistDialog::UncheckActionExecute(TObject * /*Sender*/)
  668. {
  669. Check(false);
  670. }
  671. //---------------------------------------------------------------------------
  672. void __fastcall TSynchronizeChecklistDialog::ListViewSelectItem(
  673. TObject * /*Sender*/, TListItem * /*Item*/, bool /*Selected*/)
  674. {
  675. // Delayed update of button status in case many items are being selected at once
  676. // Also change of selection causes buttons to flash, as for short period of time,
  677. // no item is selected
  678. UpdateTimer->Enabled = true;
  679. }
  680. //---------------------------------------------------------------------------
  681. void __fastcall TSynchronizeChecklistDialog::UpdateTimerTimer(
  682. TObject * /*Sender*/)
  683. {
  684. UpdateTimer->Enabled = false;
  685. UpdateControls();
  686. }
  687. //---------------------------------------------------------------------------
  688. TListItem * __fastcall TSynchronizeChecklistDialog::SelectAll(bool Select, int Action,
  689. bool OnlyTheAction)
  690. {
  691. TListItem * Result = NULL;
  692. for (int Index = 0; Index < ListView->Items->Count; Index++)
  693. {
  694. TListItem * Item = ListView->Items->Item[Index];
  695. if (Action == 0)
  696. {
  697. Item->Selected = Select;
  698. if (Result == NULL)
  699. {
  700. Result = Item;
  701. }
  702. }
  703. else
  704. {
  705. const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item);
  706. bool WantedAction = (int(GetChecklistItemAction(ChecklistItem)) == Action);
  707. if (WantedAction || !OnlyTheAction)
  708. {
  709. Item->Selected = Select && WantedAction;
  710. if (WantedAction && (Result == NULL))
  711. {
  712. Result = Item;
  713. }
  714. }
  715. }
  716. }
  717. return Result;
  718. }
  719. //---------------------------------------------------------------------------
  720. void __fastcall TSynchronizeChecklistDialog::SelectAllActionExecute(
  721. TObject * /*Sender*/)
  722. {
  723. SelectAll(true);
  724. }
  725. //---------------------------------------------------------------------------
  726. void __fastcall TSynchronizeChecklistDialog::StatusBarMouseDown(
  727. TObject * /*Sender*/, TMouseButton /*Button*/, TShiftState Shift, int X,
  728. int /*Y*/)
  729. {
  730. int IPanel = PanelAt(X);
  731. if (IPanel >= 0)
  732. {
  733. TListItem * Item = SelectAll(true, IPanel, Shift.Contains(ssCtrl));
  734. if (Item != NULL)
  735. {
  736. Item->MakeVisible(false);
  737. Item->Focused = true;
  738. ListView->SetFocus();
  739. }
  740. }
  741. }
  742. //---------------------------------------------------------------------------
  743. int __fastcall TSynchronizeChecklistDialog::CompareNumber(__int64 Value1,
  744. __int64 Value2)
  745. {
  746. int Result;
  747. if (Value1 < Value2)
  748. {
  749. Result = -1;
  750. }
  751. else if (Value1 == Value2)
  752. {
  753. Result = 0;
  754. }
  755. else
  756. {
  757. Result = 1;
  758. }
  759. return Result;
  760. }
  761. //---------------------------------------------------------------------------
  762. void __fastcall TSynchronizeChecklistDialog::ListViewCompare(
  763. TObject * /*Sender*/, TListItem * Item1, TListItem * Item2, int /*Data*/,
  764. int & Compare)
  765. {
  766. const TSynchronizeChecklist::TItem * ChecklistItem1 = GetChecklistItem(Item1);
  767. const TSynchronizeChecklist::TItem * ChecklistItem2 = GetChecklistItem(Item2);
  768. TIEListViewColProperties * ColProperties =
  769. dynamic_cast<TIEListViewColProperties *>(ListView->ColProperties);
  770. switch (ColProperties->SortColumn)
  771. {
  772. case 0: // name
  773. Compare = AnsiCompareText(ChecklistItem1->GetFileName(), ChecklistItem2->GetFileName());
  774. break;
  775. // sorting by local and remote dir is the same
  776. case 1: // local dir
  777. case 5: // remote dir
  778. Compare = 0; // default sorting
  779. break;
  780. case 2: // local size
  781. Compare = CompareNumber(ChecklistItem1->Local.Size, ChecklistItem2->Local.Size);
  782. break;
  783. case 3: // local changed
  784. Compare = CompareFileTime(ChecklistItem1->Local.Modification,
  785. ChecklistItem2->Local.Modification);
  786. break;
  787. case ImageColumnIndex: // action
  788. Compare = CompareNumber(GetChecklistItemAction(ChecklistItem1), GetChecklistItemAction(ChecklistItem2));
  789. break;
  790. case 6: // remote size
  791. Compare = CompareNumber(ChecklistItem1->Remote.Size, ChecklistItem2->Remote.Size);
  792. break;
  793. case 7: // remote changed
  794. Compare = CompareFileTime(ChecklistItem1->Remote.Modification,
  795. ChecklistItem2->Remote.Modification);
  796. break;
  797. }
  798. if (Compare == 0)
  799. {
  800. if (!ChecklistItem1->Local.Directory.IsEmpty())
  801. {
  802. Compare = AnsiCompareText(ChecklistItem1->Local.Directory, ChecklistItem2->Local.Directory);
  803. }
  804. else
  805. {
  806. DebugAssert(!ChecklistItem1->Remote.Directory.IsEmpty());
  807. Compare = AnsiCompareText(ChecklistItem1->Remote.Directory, ChecklistItem2->Remote.Directory);
  808. }
  809. if (Compare == 0)
  810. {
  811. Compare = AnsiCompareText(ChecklistItem1->GetFileName(), ChecklistItem2->GetFileName());
  812. }
  813. }
  814. if (!ColProperties->SortAscending)
  815. {
  816. Compare = -Compare;
  817. }
  818. }
  819. //---------------------------------------------------------------------------
  820. void __fastcall TSynchronizeChecklistDialog::ListViewSecondaryColumnHeader(
  821. TCustomIEListView * /*Sender*/, int Index, int & SecondaryColumn)
  822. {
  823. // "remote dir" column is sorting alias for "local dir" column
  824. if (Index == 5)
  825. {
  826. SecondaryColumn = 1;
  827. }
  828. else
  829. {
  830. SecondaryColumn = -1;
  831. }
  832. }
  833. //---------------------------------------------------------------------------
  834. void __fastcall TSynchronizeChecklistDialog::ListViewContextPopup(
  835. TObject * Sender, TPoint & MousePos, bool & Handled)
  836. {
  837. // to update source popup menu before TBX menu is created
  838. UpdateControls();
  839. MenuPopup(Sender, MousePos, Handled);
  840. }
  841. //---------------------------------------------------------------------------
  842. void __fastcall TSynchronizeChecklistDialog::CustomCommandsActionExecute(
  843. TObject * /*Sender*/)
  844. {
  845. TStrings * LocalFileList = new TStringList();
  846. TStrings * RemoteFileList = new TStringList();
  847. try
  848. {
  849. TListItem * Item = ListView->Selected;
  850. DebugAssert(Item != NULL);
  851. while (Item != NULL)
  852. {
  853. const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item);
  854. DebugAssert((GetChecklistItemAction(ChecklistItem) == TSynchronizeChecklist::saUploadUpdate) ||
  855. (GetChecklistItemAction(ChecklistItem) == TSynchronizeChecklist::saDownloadUpdate));
  856. DebugAssert(ChecklistItem->RemoteFile != NULL);
  857. UnicodeString LocalPath =
  858. IncludeTrailingBackslash(ChecklistItem->Local.Directory) +
  859. ChecklistItem->Local.FileName;
  860. LocalFileList->Add(LocalPath);
  861. UnicodeString RemotePath =
  862. UnixIncludeTrailingBackslash(ChecklistItem->Remote.Directory) +
  863. ChecklistItem->Remote.FileName;
  864. RemoteFileList->AddObject(RemotePath, ChecklistItem->RemoteFile);
  865. Item = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected);
  866. }
  867. }
  868. catch(...)
  869. {
  870. delete LocalFileList;
  871. delete RemoteFileList;
  872. throw;
  873. }
  874. DebugAssert(FOnCustomCommandMenu != NULL);
  875. FOnCustomCommandMenu(CustomCommandsAction, LocalFileList, RemoteFileList);
  876. }
  877. //---------------------------------------------------------------------------
  878. void __fastcall TSynchronizeChecklistDialog::UpdateStatusBarSize()
  879. {
  880. int PanelWidth = Min(StatusBar->Width / PanelCount(), ScaleByTextHeight(this, 160));
  881. for (int Index = 0; Index < PanelCount(); Index++)
  882. {
  883. StatusBar->Panels->Items[Index]->Width = PanelWidth;
  884. }
  885. }
  886. //---------------------------------------------------------------------------
  887. void __fastcall TSynchronizeChecklistDialog::StatusBarResize(TObject * /*Sender*/)
  888. {
  889. UpdateStatusBarSize();
  890. }
  891. //---------------------------------------------------------------------------
  892. const TSynchronizeChecklist::TItem * TSynchronizeChecklistDialog::GetChecklistItem(
  893. TListItem * Item)
  894. {
  895. return static_cast<const TSynchronizeChecklist::TItem *>(Item->Data);
  896. }
  897. //---------------------------------------------------------------------------
  898. TSynchronizeChecklist::TAction & TSynchronizeChecklistDialog::GetChecklistItemAction(
  899. const TSynchronizeChecklist::TItem * ChecklistItem)
  900. {
  901. TActions::iterator i = FActions.find(ChecklistItem);
  902. if (i == FActions.end())
  903. {
  904. throw EInvalidOperation(L"");
  905. }
  906. return i->second;
  907. }
  908. //---------------------------------------------------------------------------
  909. void __fastcall TSynchronizeChecklistDialog::ReverseActionExecute(TObject * /*Sender*/)
  910. {
  911. TAutoFlag Flag(FChangingItemMass);
  912. TListItem * Item = ListView->Selected;
  913. while (Item != NULL)
  914. {
  915. const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item);
  916. TSynchronizeChecklist::TAction & Action = GetChecklistItemAction(ChecklistItem);
  917. TSynchronizeChecklist::TAction NewAction = TSynchronizeChecklist::Reverse(Action);
  918. if (DebugAlwaysTrue(Action != NewAction))
  919. {
  920. int ActionIndex = int(Action);
  921. FTotals[ActionIndex]--;
  922. if (Item->Checked)
  923. {
  924. FChecked[ActionIndex]--;
  925. __int64 ItemSize = GetItemSize(ChecklistItem);
  926. FCheckedSize[ActionIndex] -= ItemSize;
  927. FCheckedSize[0] -= ItemSize;
  928. }
  929. Action = NewAction;
  930. ActionIndex = int(Action);
  931. FTotals[ActionIndex]++;
  932. if (Item->Checked)
  933. {
  934. FChecked[ActionIndex]++;
  935. // item size may differ with action (0 for delete, but non-0 for new file transfer)
  936. __int64 ItemSize = GetItemSize(ChecklistItem);
  937. FCheckedSize[ActionIndex] += ItemSize;
  938. FCheckedSize[0] += ItemSize;
  939. }
  940. LoadItem(Item);
  941. }
  942. Item = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected);
  943. }
  944. Flag.Release();
  945. UpdateControls();
  946. }
  947. //---------------------------------------------------------------------------
  948. void __fastcall TSynchronizeChecklistDialog::ListViewClick(TObject * /*Sender*/)
  949. {
  950. TKeyboardState KeyState;
  951. GetKeyboardState(KeyState);
  952. TShiftState ShiftState = KeyboardStateToShiftState(KeyState);
  953. // when selecting, do not reverse, even when user clicked on action column
  954. if (!ShiftState.Contains(ssShift) && !ShiftState.Contains(ssCtrl))
  955. {
  956. TPoint P = ListView->ScreenToClient(Mouse->CursorPos);
  957. TRect R = GetColumnHeaderRect(ImageColumnIndex);
  958. if ((R.Left <= P.x) && (P.x <= R.Right))
  959. {
  960. // If no item was selected before the click, the action is not enabled yet here,
  961. // and Execute would be noop, force update
  962. UpdateControls();
  963. ReverseAction->Execute();
  964. }
  965. }
  966. }
  967. //---------------------------------------------------------------------------
  968. void __fastcall TSynchronizeChecklistDialog::Dispatch(void * Message)
  969. {
  970. TMessage * M = reinterpret_cast<TMessage*>(Message);
  971. if (M->Msg == WM_SYSCOMMAND)
  972. {
  973. if (!HandleMinimizeSysCommand(*M))
  974. {
  975. TForm::Dispatch(Message);
  976. }
  977. }
  978. else if (M->Msg == CM_DPICHANGED)
  979. {
  980. CMDpiChanged(*M);
  981. }
  982. else
  983. {
  984. TForm::Dispatch(Message);
  985. }
  986. }
  987. //---------------------------------------------------------------------------
  988. void __fastcall TSynchronizeChecklistDialog::UpdateImages()
  989. {
  990. ListView->SmallImages = ShellImageListForControl(this, ilsSmall);
  991. }
  992. //---------------------------------------------------------------------------
  993. void __fastcall TSynchronizeChecklistDialog::CMDpiChanged(TMessage & Message)
  994. {
  995. TForm::Dispatch(&Message);
  996. UpdateImages();
  997. }
  998. //---------------------------------------------------------------------------
  999. void __fastcall TSynchronizeChecklistDialog::ProcessedItem(const void * Token)
  1000. {
  1001. TTokens::const_iterator I = FTokens.find(Token);
  1002. if (DebugAlwaysTrue(I != FTokens.end()))
  1003. {
  1004. TListItem * Item = I->second;
  1005. DebugAssert(Item->Checked);
  1006. Item->Checked = false;
  1007. Item->MakeVisible(false);
  1008. }
  1009. }
  1010. //---------------------------------------------------------------------------
  1011. void __fastcall TSynchronizeChecklistDialog::OkButtonClick(TObject * /*Sender*/)
  1012. {
  1013. ListView->SelectAll(smNone);
  1014. FTokens.clear();
  1015. for (int Index = 0; Index < ListView->Items->Count; Index++)
  1016. {
  1017. TListItem * Item = ListView->Items->Item[Index];
  1018. const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item);
  1019. FChecklist->Update(ChecklistItem, Item->Checked, GetChecklistItemAction(ChecklistItem));
  1020. FTokens.insert(std::make_pair(ChecklistItem, Item));
  1021. if (ChecklistItem->RemoteFile != NULL)
  1022. {
  1023. FTokens.insert(std::make_pair(ChecklistItem->RemoteFile, Item));
  1024. }
  1025. }
  1026. TAutoFlag Flag(FSynchronizing);
  1027. UpdateControls();
  1028. try
  1029. {
  1030. FOnSynchronize(FToken, ProcessedItem);
  1031. }
  1032. catch (Exception & E)
  1033. {
  1034. FException.reset(CloneException(&E));
  1035. }
  1036. }
  1037. //---------------------------------------------------------------------------