//--------------------------------------------------------------------- #include #pragma hdrstop #include #include "WinInterface.h" #include "SynchronizeChecklist.h" #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------- #pragma link "IEListView" #pragma link "NortonLikeListView" #pragma link "PngImageList" #ifndef NO_RESOURCES #pragma resource "*.dfm" #endif //--------------------------------------------------------------------- const int ImageColumnIndex = 4; //--------------------------------------------------------------------- bool __fastcall DoSynchronizeChecklistDialog(TSynchronizeChecklist * Checklist, TSynchronizeMode Mode, int Params, const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory, TCustomCommandMenuEvent OnCustomCommandMenu, TFullSynchronizeEvent OnSynchronize, void * Token) { std::unique_ptr Dialog( new TSynchronizeChecklistDialog(Application, Mode, Params, LocalDirectory, RemoteDirectory, OnCustomCommandMenu, OnSynchronize, Token)); return Dialog->Execute(Checklist); } //--------------------------------------------------------------------- __fastcall TSynchronizeChecklistDialog::TSynchronizeChecklistDialog( TComponent * AOwner, TSynchronizeMode Mode, int Params, const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory, TCustomCommandMenuEvent OnCustomCommandMenu, TFullSynchronizeEvent OnSynchronize, void * Token) : TForm(AOwner) { FFormRestored = false; FMode = Mode; FParams = Params; FLocalDirectory = ExcludeTrailingBackslash(LocalDirectory); FRemoteDirectory = UnixExcludeTrailingBackslash(RemoteDirectory); FOnCustomCommandMenu = OnCustomCommandMenu; DebugAssert(OnSynchronize != NULL); FOnSynchronize = OnSynchronize; FToken = Token; UseSystemSettings(this); UseDesktopFont(ListView); UseDesktopFont(StatusBar); FChecklist = NULL; FChangingItem = NULL; FChangingItemIgnore = false; FChangingItemMass = false; FGeneralHint = StatusBar->Hint; FSynchronizing = false; SelectScaledImageList(ActionImages); FOrigListViewWindowProc = ListView->WindowProc; ListView->WindowProc = ListViewWindowProc; UpdateImages(); CustomCommandsAction->Visible = (FOnCustomCommandMenu != NULL); // button visibility cannot be bound to action visibility CustomCommandsButton2->Visible = CustomCommandsAction->Visible; MenuButton(CustomCommandsButton2); } //--------------------------------------------------------------------- __fastcall TSynchronizeChecklistDialog::~TSynchronizeChecklistDialog() { ListView->WindowProc = FOrigListViewWindowProc; } //--------------------------------------------------------------------- bool __fastcall TSynchronizeChecklistDialog::Execute(TSynchronizeChecklist * Checklist) { FChecklist = Checklist; bool Result = (ShowModal() == DefaultResult(this)); if (Result) { TSynchronizeChecklistConfiguration FormConfiguration = CustomWinConfiguration->SynchronizeChecklist; FormConfiguration.ListParams = ListView->ColProperties->ParamsStr; UnicodeString WindowParams = FormConfiguration.WindowParams; // if there is no main window, keep previous "custom pos" indication, bool CustomPos = (StrToIntDef(CutToChar(WindowParams, L';', true), 0) != 0); if (!IsMainFormLike(this)) { CustomPos = (Application->MainForm->BoundsRect != BoundsRect); } FormConfiguration.WindowParams = FORMAT(L"%d;%s", ((CustomPos ? 1 : 0), StoreForm(this))); CustomWinConfiguration->SynchronizeChecklist = FormConfiguration; } if (FException.get() != NULL) { RethrowException(FException.get()); } return Result; } //--------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::UpdateControls() { Caption = FormatFormCaption(this, LoadStr(FSynchronizing ? SYNCHRONIZE_PROGRESS_SYNCHRONIZE2 : SYNCHRONIZE_CHECKLIST_CAPTION)); StatusBar->Invalidate(); bool AllChecked = true; bool AllUnchecked = true; bool AnyBoth = false; bool AnyNonBoth = false; TListItem * Item = ListView->Selected; while (Item != NULL) { TSynchronizeChecklist::TAction Action = GetChecklistItemAction(GetChecklistItem(Item)); if ((Action == TSynchronizeChecklist::saUploadUpdate) || (Action == TSynchronizeChecklist::saDownloadUpdate)) { AnyBoth = true; } else { AnyNonBoth = true; } if (Item->Checked) { AllUnchecked = false; } else { AllChecked = false; } Item = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected); } EnableControl(OkButton, (FChecked[0] > 0) && !FSynchronizing); EnableControl(CancelButton, !FSynchronizing); EnableControl(HelpButton, !FSynchronizing); CheckAction->Enabled = !AllChecked && !FSynchronizing; UncheckAction->Enabled = !AllUnchecked && !FSynchronizing; CheckAllAction->Enabled = (FChecked[0] < FTotals[0]) && !FSynchronizing; UncheckAllAction->Enabled = (FChecked[0] > 0) && !FSynchronizing; CustomCommandsAction->Enabled = AnyBoth && !AnyNonBoth && DebugAlwaysTrue(!FSynchronizing); ReverseAction->Enabled = (ListView->SelCount > 0) && DebugAlwaysTrue(!FSynchronizing); SelectAllAction->Enabled = (ListView->SelCount < ListView->Items->Count) && !FSynchronizing; } //--------------------------------------------------------------------------- bool __fastcall TSynchronizeChecklistDialog::GetWindowParams(UnicodeString & WindowParams) { WindowParams = CustomWinConfiguration->SynchronizeChecklist.WindowParams; bool CustomPos = (StrToIntDef(CutToChar(WindowParams, L';', true), 0) != 0); return CustomPos || IsMainFormLike(this); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::CreateParams(TCreateParams & Params) { UnicodeString WindowParams; if (GetWindowParams(WindowParams)) { // This is only to set correct TForm::Position. Actual bounds are set later after DPI scaling RestoreForm(WindowParams, this, true); } TForm::CreateParams(Params); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::HelpButtonClick(TObject * /*Sender*/) { FormHelp(this); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::AddSubItem(TListItem * Item, int & Index, const UnicodeString & S) { if (Index < Item->SubItems->Count) { Item->SubItems->Strings[Index] = S; } else { DebugAssert(Index == Item->SubItems->Count); Item->SubItems->Add(S); } Index++; } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::LoadItem(TListItem * Item) { UnicodeString S; const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item); if (ChecklistItem->ImageIndex >= 0) { Item->ImageIndex = ChecklistItem->ImageIndex; } else { Item->ImageIndex = FakeFileImageIndex(ChecklistItem->GetFileName(), FLAGMASK(ChecklistItem->IsDirectory, FILE_ATTRIBUTE_DIRECTORY)); } S = ChecklistItem->GetFileName(); if (ChecklistItem->IsDirectory) { S = IncludeTrailingBackslash(S); } Item->Caption = S; int Index = 0; TSynchronizeChecklist::TAction Action = GetChecklistItemAction(ChecklistItem); if (Action == TSynchronizeChecklist::saDeleteRemote) { AddSubItem(Item, Index, L""); AddSubItem(Item, Index, L""); AddSubItem(Item, Index, L""); } else { S = ChecklistItem->Local.Directory; if (AnsiSameText(FLocalDirectory, S.SubString(1, FLocalDirectory.Length()))) { S[1] = L'.'; S.Delete(2, FLocalDirectory.Length() - 1); } else { DebugFail(); } AddSubItem(Item, Index, S); if (Action == TSynchronizeChecklist::saDownloadNew) { AddSubItem(Item, Index, L""); AddSubItem(Item, Index, L""); } else { if (ChecklistItem->IsDirectory) { AddSubItem(Item, Index, L""); } else { AddSubItem(Item, Index, FormatPanelBytes(ChecklistItem->Local.Size, WinConfiguration->FormatSizeBytes)); } AddSubItem(Item, Index, UserModificationStr(ChecklistItem->Local.Modification, ChecklistItem->Local.ModificationFmt)); } } AddSubItem(Item, Index, L""); DebugAssert(Index == ImageColumnIndex); if (Action == TSynchronizeChecklist::saDeleteLocal) { AddSubItem(Item, Index, L""); AddSubItem(Item, Index, L""); AddSubItem(Item, Index, L""); } else { S = ChecklistItem->Remote.Directory; if (AnsiSameText(FRemoteDirectory, S.SubString(1, FRemoteDirectory.Length()))) { S[1] = L'.'; S.Delete(2, FRemoteDirectory.Length() - 1); } else { DebugFail(); } AddSubItem(Item, Index, S); if (Action == TSynchronizeChecklist::saUploadNew) { AddSubItem(Item, Index, L""); AddSubItem(Item, Index, L""); } else { if (ChecklistItem->IsDirectory) { AddSubItem(Item, Index, L""); } else { AddSubItem(Item, Index, FormatPanelBytes(ChecklistItem->Remote.Size, WinConfiguration->FormatSizeBytes)); } AddSubItem(Item, Index, UserModificationStr(ChecklistItem->Remote.Modification, ChecklistItem->Remote.ModificationFmt)); } } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::LoadList() { memset(&FTotals, 0, sizeof(FTotals)); memset(&FChecked, 0, sizeof(FChecked)); memset(&FCheckedSize, 0, sizeof(FCheckedSize)); FTotals[0] = FChecklist->Count; ListView->Items->BeginUpdate(); try { ListView->Items->Clear(); for (int Index = 0; Index < FChecklist->Count; Index++) { const TSynchronizeChecklist::TItem * ChecklistItem = FChecklist->Item[ListView->Items->Count]; FChangingItemIgnore = true; try { TListItem * Item = ListView->Items->Add(); TSynchronizeChecklist::TAction Action = ChecklistItem->Action; FActions.insert(std::make_pair(ChecklistItem, Action)); Item->Data = const_cast(ChecklistItem); Item->Checked = ChecklistItem->Checked; LoadItem(Item); } __finally { FChangingItemIgnore = false; } int ActionIndex = int(GetChecklistItemAction(ChecklistItem)); FTotals[ActionIndex]++; if (ChecklistItem->Checked) { FChecked[ActionIndex]++; FChecked[0]++; __int64 ItemSize = GetItemSize(ChecklistItem); FCheckedSize[ActionIndex] += ItemSize; FCheckedSize[0] += ItemSize; } } } __finally { ListView->Items->EndUpdate(); } ListView->AlphaSort(); UpdateControls(); } //--------------------------------------------------------------------------- bool __fastcall TSynchronizeChecklistDialog::IsItemSizeIrrelevant(TSynchronizeChecklist::TAction Action) { switch (Action) { case TSynchronizeChecklist::saNone: case TSynchronizeChecklist::saDeleteRemote: case TSynchronizeChecklist::saDeleteLocal: return true; default: return false; } } //--------------------------------------------------------------------------- __int64 __fastcall TSynchronizeChecklistDialog::GetItemSize(const TSynchronizeChecklist::TItem * Item) { TSynchronizeChecklist::TAction Action = GetChecklistItemAction(Item); if (IsItemSizeIrrelevant(Action)) { return 0; } else { switch (Action) { case TSynchronizeChecklist::saUploadNew: case TSynchronizeChecklist::saUploadUpdate: return Item->Local.Size; case TSynchronizeChecklist::saDownloadNew: case TSynchronizeChecklist::saDownloadUpdate: return Item->Remote.Size; default: DebugFail(); return 0; } } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::FormShow(TObject * /*Sender*/) { // Moved here from CreateParams (see also TEditorForm::CreateParams), because there it breaks per-monitor DPI. // For example BoundsRect is matched to the main form too soon, so it gets rescaled later. // Also it happens before constructor, what causes UseDesktopFont-flagged controls to rescale twice. // But Position is already set in the CreateParams, as it cannot be set here anymore. if (!FFormRestored) { FFormRestored = True; UnicodeString WindowParams; if (GetWindowParams(WindowParams)) { RestoreForm(WindowParams, this); } else { BoundsRect = Application->MainForm->BoundsRect; } } ListView->ColProperties->ParamsStr = CustomWinConfiguration->SynchronizeChecklist.ListParams; LoadList(); UpdateStatusBarSize(); } //--------------------------------------------------------------------------- TRect __fastcall TSynchronizeChecklistDialog::GetColumnHeaderRect(int Index) { HWND HeaderHandle = ListView_GetHeader(ListView->Handle); TRect R; Header_GetItemRect(HeaderHandle, Index, &R); // Can be simplified using GetScrollPos TScrollInfo ScrollInfo; ZeroMemory(&ScrollInfo, sizeof(ScrollInfo)); ScrollInfo.cbSize = sizeof(ScrollInfo); ScrollInfo.fMask = SIF_POS; GetScrollInfo(ListView->Handle, SB_HORZ, &ScrollInfo); R.Left -= ScrollInfo.nPos; R.Right -= ScrollInfo.nPos; return R; } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ListViewWindowProc(TMessage & Message) { if (Message.Msg == CN_NOTIFY) { TWMNotify & NotifyMessage = reinterpret_cast(Message); if (NotifyMessage.NMHdr->code == NM_CUSTOMDRAW) { // workaround // Due to a bug in VCL, OnAdvancedCustomDrawSubItem is not called for any // other stage except for cdPrePaint. So we must call it ourselves. TNMLVCustomDraw * CustomDraw = reinterpret_cast(NotifyMessage.NMHdr); if (FLAGSET(CustomDraw->nmcd.dwDrawStage, CDDS_ITEM) && FLAGSET(CustomDraw->nmcd.dwDrawStage, CDDS_SUBITEM) && FLAGSET(CustomDraw->nmcd.dwDrawStage, CDDS_ITEMPOSTPAINT) && (CustomDraw->iSubItem == ImageColumnIndex) && (ActionImages->Width <= ListView->Columns->Items[CustomDraw->iSubItem]->Width)) { TListItem * Item = ListView->Items->Item[CustomDraw->nmcd.dwItemSpec]; const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item); TRect HeaderR = GetColumnHeaderRect(CustomDraw->iSubItem); TRect R = Item->DisplayRect(drBounds); R.Left = HeaderR.Left + (HeaderR.Width() - ActionImages->Width) / 2; R.Right = HeaderR.Right; // workaround // doing this from ListViewAdvancedCustomDraw corrupts list view on Windows 7 ImageList_Draw(reinterpret_cast(ActionImages->Handle), int(GetChecklistItemAction(ChecklistItem)), CustomDraw->nmcd.hdc, R.Left, ((R.Top + R.Bottom - ActionImages->Height) / 2), ILD_TRANSPARENT); } } } FOrigListViewWindowProc(Message); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ListViewAdvancedCustomDrawSubItem( TCustomListView * /*Sender*/, TListItem * /*Item*/, int /*SubItem*/, TCustomDrawState /*State*/, TCustomDrawStage /*Stage*/, bool & /*DefaultDraw*/) { // this is just fake handler that makes the list view request custom draw notification above } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::StatusBarDrawPanel( TStatusBar * StatusBar, TStatusPanel * Panel, const TRect & Rect) { bool Possible; int ActionIndex = Panel->Index; TSynchronizeChecklist::TAction Action = TSynchronizeChecklist::TAction(ActionIndex); if (FTotals[ActionIndex] > 0) { // if direction is overriden to an action that is otherwise not possible // in given synchronization mode, we still want to show the stats Possible = true; } else { switch (Action) { case TSynchronizeChecklist::saNone: Possible = true; break; case TSynchronizeChecklist::saUploadNew: Possible = ((FMode == smRemote) || (FMode == smBoth)) && FLAGCLEAR(FParams, TTerminal::spTimestamp); break; case TSynchronizeChecklist::saDownloadNew: Possible = ((FMode == smLocal) || (FMode == smBoth)) && FLAGCLEAR(FParams, TTerminal::spTimestamp); break; case TSynchronizeChecklist::saUploadUpdate: Possible = ((FMode == smRemote) || (FMode == smBoth)) && (FLAGCLEAR(FParams, TTerminal::spNotByTime) || FLAGSET(FParams, TTerminal::spBySize)); break; case TSynchronizeChecklist::saDownloadUpdate: Possible = ((FMode == smLocal) || (FMode == smBoth)) && (FLAGCLEAR(FParams, TTerminal::spNotByTime) || FLAGSET(FParams, TTerminal::spBySize)); break; case TSynchronizeChecklist::saDeleteRemote: Possible = (FMode == smRemote) && FLAGCLEAR(FParams, TTerminal::spTimestamp); break; case TSynchronizeChecklist::saDeleteLocal: Possible = (FMode == smLocal) && FLAGCLEAR(FParams, TTerminal::spTimestamp); break; default: DebugFail(); Possible = false; break; } } UnicodeString PanelText; if (Possible) { PanelText = FORMAT(LoadStrPart(SYNCHRONIZE_SELECTED_ACTIONS, 1), (FormatNumber(FChecked[ActionIndex]), FormatNumber(FTotals[ActionIndex]))); if ((FChecked[ActionIndex] > 0) && ((ActionIndex == 0) || !IsItemSizeIrrelevant(Action))) { PanelText += FORMAT(L" (%s)", (FormatBytes(FCheckedSize[ActionIndex]))); } } else { PanelText = LoadStrPart(SYNCHRONIZE_SELECTED_ACTIONS, 2); } int TextHeight = StatusBar->Canvas->TextHeight(PanelText); int X = Rect.Left + ActionImages->Width + 4; int Y = (Rect.Top + Rect.Bottom - TextHeight) / 2; StatusBar->Canvas->TextRect(Rect, X, Y, PanelText); X = Rect.Left + 1; Y = ((Rect.Top + Rect.Bottom - ActionImages->Height) / 2); int ImageIndex = ActionIndex; ActionImages->Draw(StatusBar->Canvas, X, Y, ImageIndex, Possible); } //--------------------------------------------------------------------------- int __fastcall TSynchronizeChecklistDialog::PanelCount() { // last "panel" is technical return StatusBar->Panels->Count - 1; } //--------------------------------------------------------------------------- int __fastcall TSynchronizeChecklistDialog::PanelAt(int X) { int Result = 0; while ((X > StatusBar->Panels->Items[Result]->Width) && (Result < PanelCount())) { X -= StatusBar->Panels->Items[Result]->Width; Result++; } return ((Result < StatusBar->Panels->Count - 1) ? Result : -1); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::StatusBarMouseMove( TObject * /*Sender*/, TShiftState /*Shift*/, int X, int /*Y*/) { UnicodeString Hint; int IPanel = PanelAt(X); if (IPanel >= 0) { Hint = StatusBar->Panels->Items[IPanel]->Text; if (IPanel > 0) { Hint = FORMAT(L"%s\n%s", (Hint, FGeneralHint)); } } if (Hint != StatusBar->Hint) { Application->CancelHint(); StatusBar->Hint = Hint; } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ListViewChange( TObject * /*Sender*/, TListItem * Item, TItemChange Change) { if ((Change == ctState) && (FChangingItem == Item) && (FChangingItem != NULL)) { if (!FChangingItemIgnore) { DebugAssert(Item->Data != NULL); if ((FChangingItemChecked != Item->Checked) && (Item->Data != NULL)) { const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item); int ActionIndex = int(GetChecklistItemAction(ChecklistItem)); int Diff = Item->Checked ? 1 : -1; FChecked[ActionIndex] += Diff; FChecked[0] += Diff; __int64 ItemSize = GetItemSize(ChecklistItem); if (!Item->Checked) { ItemSize = -ItemSize; } FCheckedSize[ActionIndex] += ItemSize; FCheckedSize[0] += ItemSize; if (!FChangingItemMass) { UpdateControls(); } } } FChangingItem = NULL; } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ListViewChanging( TObject * /*Sender*/, TListItem * Item, TItemChange Change, bool & /*AllowChange*/) { if (Change == ctState) { FChangingItem = Item; FChangingItemChecked = Item->Checked; } else { DebugAssert(FChangingItem == NULL); FChangingItem = NULL; } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::CheckAll(bool Check) { FChangingItemMass = true; try { for (int Index = 0; Index < ListView->Items->Count; Index++) { ListView->Items->Item[Index]->Checked = Check; } } __finally { FChangingItemMass = false; } UpdateControls(); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::CheckAllActionExecute(TObject * /*Sender*/) { CheckAll(true); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::UncheckAllActionExecute(TObject * /*Sender*/) { CheckAll(false); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::Check(bool Check) { FChangingItemMass = true; try { TListItem * Item = ListView->Selected; while (Item != NULL) { Item->Checked = Check; Item = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected); } } __finally { FChangingItemMass = false; } UpdateControls(); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::CheckActionExecute(TObject * /*Sender*/) { Check(true); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::UncheckActionExecute(TObject * /*Sender*/) { Check(false); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ListViewSelectItem( TObject * /*Sender*/, TListItem * /*Item*/, bool /*Selected*/) { // Delayed update of button status in case many items are being selected at once // Also change of selection causes buttons to flash, as for short period of time, // no item is selected UpdateTimer->Enabled = true; } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::UpdateTimerTimer( TObject * /*Sender*/) { UpdateTimer->Enabled = false; UpdateControls(); } //--------------------------------------------------------------------------- TListItem * __fastcall TSynchronizeChecklistDialog::SelectAll(bool Select, int Action, bool OnlyTheAction) { TListItem * Result = NULL; for (int Index = 0; Index < ListView->Items->Count; Index++) { TListItem * Item = ListView->Items->Item[Index]; if (Action == 0) { Item->Selected = Select; if (Result == NULL) { Result = Item; } } else { const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item); bool WantedAction = (int(GetChecklistItemAction(ChecklistItem)) == Action); if (WantedAction || !OnlyTheAction) { Item->Selected = Select && WantedAction; if (WantedAction && (Result == NULL)) { Result = Item; } } } } return Result; } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::SelectAllActionExecute( TObject * /*Sender*/) { SelectAll(true); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::StatusBarMouseDown( TObject * /*Sender*/, TMouseButton /*Button*/, TShiftState Shift, int X, int /*Y*/) { int IPanel = PanelAt(X); if (IPanel >= 0) { TListItem * Item = SelectAll(true, IPanel, Shift.Contains(ssCtrl)); if (Item != NULL) { Item->MakeVisible(false); Item->Focused = true; ListView->SetFocus(); } } } //--------------------------------------------------------------------------- int __fastcall TSynchronizeChecklistDialog::CompareNumber(__int64 Value1, __int64 Value2) { int Result; if (Value1 < Value2) { Result = -1; } else if (Value1 == Value2) { Result = 0; } else { Result = 1; } return Result; } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ListViewCompare( TObject * /*Sender*/, TListItem * Item1, TListItem * Item2, int /*Data*/, int & Compare) { const TSynchronizeChecklist::TItem * ChecklistItem1 = GetChecklistItem(Item1); const TSynchronizeChecklist::TItem * ChecklistItem2 = GetChecklistItem(Item2); TIEListViewColProperties * ColProperties = dynamic_cast(ListView->ColProperties); switch (ColProperties->SortColumn) { case 0: // name Compare = AnsiCompareText(ChecklistItem1->GetFileName(), ChecklistItem2->GetFileName()); break; // sorting by local and remote dir is the same case 1: // local dir case 5: // remote dir Compare = 0; // default sorting break; case 2: // local size Compare = CompareNumber(ChecklistItem1->Local.Size, ChecklistItem2->Local.Size); break; case 3: // local changed Compare = CompareFileTime(ChecklistItem1->Local.Modification, ChecklistItem2->Local.Modification); break; case ImageColumnIndex: // action Compare = CompareNumber(GetChecklistItemAction(ChecklistItem1), GetChecklistItemAction(ChecklistItem2)); break; case 6: // remote size Compare = CompareNumber(ChecklistItem1->Remote.Size, ChecklistItem2->Remote.Size); break; case 7: // remote changed Compare = CompareFileTime(ChecklistItem1->Remote.Modification, ChecklistItem2->Remote.Modification); break; } if (Compare == 0) { if (!ChecklistItem1->Local.Directory.IsEmpty()) { Compare = AnsiCompareText(ChecklistItem1->Local.Directory, ChecklistItem2->Local.Directory); } else { DebugAssert(!ChecklistItem1->Remote.Directory.IsEmpty()); Compare = AnsiCompareText(ChecklistItem1->Remote.Directory, ChecklistItem2->Remote.Directory); } if (Compare == 0) { Compare = AnsiCompareText(ChecklistItem1->GetFileName(), ChecklistItem2->GetFileName()); } } if (!ColProperties->SortAscending) { Compare = -Compare; } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ListViewSecondaryColumnHeader( TCustomIEListView * /*Sender*/, int Index, int & SecondaryColumn) { // "remote dir" column is sorting alias for "local dir" column if (Index == 5) { SecondaryColumn = 1; } else { SecondaryColumn = -1; } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ListViewContextPopup( TObject * Sender, TPoint & MousePos, bool & Handled) { // to update source popup menu before TBX menu is created UpdateControls(); MenuPopup(Sender, MousePos, Handled); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::CustomCommandsActionExecute( TObject * /*Sender*/) { TStrings * LocalFileList = new TStringList(); TStrings * RemoteFileList = new TStringList(); try { TListItem * Item = ListView->Selected; DebugAssert(Item != NULL); while (Item != NULL) { const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item); DebugAssert((GetChecklistItemAction(ChecklistItem) == TSynchronizeChecklist::saUploadUpdate) || (GetChecklistItemAction(ChecklistItem) == TSynchronizeChecklist::saDownloadUpdate)); DebugAssert(ChecklistItem->RemoteFile != NULL); UnicodeString LocalPath = IncludeTrailingBackslash(ChecklistItem->Local.Directory) + ChecklistItem->Local.FileName; LocalFileList->Add(LocalPath); UnicodeString RemotePath = UnixIncludeTrailingBackslash(ChecklistItem->Remote.Directory) + ChecklistItem->Remote.FileName; RemoteFileList->AddObject(RemotePath, ChecklistItem->RemoteFile); Item = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected); } } catch(...) { delete LocalFileList; delete RemoteFileList; throw; } DebugAssert(FOnCustomCommandMenu != NULL); FOnCustomCommandMenu(CustomCommandsAction, LocalFileList, RemoteFileList); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::UpdateStatusBarSize() { int PanelWidth = Min(StatusBar->Width / PanelCount(), ScaleByTextHeight(this, 160)); for (int Index = 0; Index < PanelCount(); Index++) { StatusBar->Panels->Items[Index]->Width = PanelWidth; } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::StatusBarResize(TObject * /*Sender*/) { UpdateStatusBarSize(); } //--------------------------------------------------------------------------- const TSynchronizeChecklist::TItem * TSynchronizeChecklistDialog::GetChecklistItem( TListItem * Item) { return static_cast(Item->Data); } //--------------------------------------------------------------------------- TSynchronizeChecklist::TAction & TSynchronizeChecklistDialog::GetChecklistItemAction( const TSynchronizeChecklist::TItem * ChecklistItem) { TActions::iterator i = FActions.find(ChecklistItem); if (i == FActions.end()) { throw EInvalidOperation(L""); } return i->second; } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ReverseActionExecute(TObject * /*Sender*/) { TAutoFlag Flag(FChangingItemMass); TListItem * Item = ListView->Selected; while (Item != NULL) { const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item); TSynchronizeChecklist::TAction & Action = GetChecklistItemAction(ChecklistItem); TSynchronizeChecklist::TAction NewAction = TSynchronizeChecklist::Reverse(Action); if (DebugAlwaysTrue(Action != NewAction)) { int ActionIndex = int(Action); FTotals[ActionIndex]--; if (Item->Checked) { FChecked[ActionIndex]--; __int64 ItemSize = GetItemSize(ChecklistItem); FCheckedSize[ActionIndex] -= ItemSize; FCheckedSize[0] -= ItemSize; } Action = NewAction; ActionIndex = int(Action); FTotals[ActionIndex]++; if (Item->Checked) { FChecked[ActionIndex]++; // item size may differ with action (0 for delete, but non-0 for new file transfer) __int64 ItemSize = GetItemSize(ChecklistItem); FCheckedSize[ActionIndex] += ItemSize; FCheckedSize[0] += ItemSize; } LoadItem(Item); } Item = ListView->GetNextItem(Item, sdAll, TItemStates() << isSelected); } Flag.Release(); UpdateControls(); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ListViewClick(TObject * /*Sender*/) { TKeyboardState KeyState; GetKeyboardState(KeyState); TShiftState ShiftState = KeyboardStateToShiftState(KeyState); // when selecting, do not reverse, even when user clicked on action column if (!ShiftState.Contains(ssShift) && !ShiftState.Contains(ssCtrl)) { TPoint P = ListView->ScreenToClient(Mouse->CursorPos); TRect R = GetColumnHeaderRect(ImageColumnIndex); if ((R.Left <= P.x) && (P.x <= R.Right)) { // If no item was selected before the click, the action is not enabled yet here, // and Execute would be noop, force update UpdateControls(); ReverseAction->Execute(); } } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::Dispatch(void * Message) { TMessage * M = reinterpret_cast(Message); if (M->Msg == WM_SYSCOMMAND) { if (!HandleMinimizeSysCommand(*M)) { TForm::Dispatch(Message); } } else if (M->Msg == CM_DPICHANGED) { CMDpiChanged(*M); } else { TForm::Dispatch(Message); } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::UpdateImages() { ListView->SmallImages = ShellImageListForControl(this, ilsSmall); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::CMDpiChanged(TMessage & Message) { TForm::Dispatch(&Message); UpdateImages(); } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::ProcessedItem(const void * Token) { TTokens::const_iterator I = FTokens.find(Token); if (DebugAlwaysTrue(I != FTokens.end())) { TListItem * Item = I->second; DebugAssert(Item->Checked); Item->Checked = false; Item->MakeVisible(false); } } //--------------------------------------------------------------------------- void __fastcall TSynchronizeChecklistDialog::OkButtonClick(TObject * /*Sender*/) { ListView->SelectAll(smNone); FTokens.clear(); for (int Index = 0; Index < ListView->Items->Count; Index++) { TListItem * Item = ListView->Items->Item[Index]; const TSynchronizeChecklist::TItem * ChecklistItem = GetChecklistItem(Item); FChecklist->Update(ChecklistItem, Item->Checked, GetChecklistItemAction(ChecklistItem)); FTokens.insert(std::make_pair(ChecklistItem, Item)); if (ChecklistItem->RemoteFile != NULL) { FTokens.insert(std::make_pair(ChecklistItem->RemoteFile, Item)); } } TAutoFlag Flag(FSynchronizing); UpdateControls(); try { FOnSynchronize(FToken, ProcessedItem); } catch (Exception & E) { FException.reset(CloneException(&E)); } } //---------------------------------------------------------------------------