SynchronizeChecklist.cpp 33 KB

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