1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00

Issue 912 – Thumbnail view in file panel (local)

https://winscp.net/tracker/912

Source commit: 4326a2efd8333867e5697a63f320f4cdf919708b
This commit is contained in:
Martin Prikryl
2024-06-26 14:34:28 +00:00
parent 2fea87d250
commit f880c68e23
20 changed files with 595 additions and 145 deletions

View File

@@ -159,6 +159,7 @@ __published:
__property MultiSelect;
__property TNotifyEvent OnDisplayProperties = { read = FOnDisplayProperties, write = FOnDisplayProperties };
__property ReadOnly;
__property DirViewStyle;
__property TNotifyEvent OnRead = { read = FOnRead, write = FOnRead };
// The only way to make Items stored automatically and survive handle recreation.

View File

@@ -9327,6 +9327,13 @@ TColor __fastcall TCustomScpExplorerForm::DisabledPanelColor()
return Result;
}
//---------------------------------------------------------------------------
void TCustomScpExplorerForm::UpdatePanelControls(TCustomDirView * ADirView, TCustomDriveView * ADriveView)
{
bool UseDarkTheme = WinConfiguration->UseDarkTheme();
ADirView->DarkMode = UseDarkTheme;
ADriveView->DarkMode = UseDarkTheme;
}
//---------------------------------------------------------------------------
void __fastcall TCustomScpExplorerForm::UpdateControls()
{
if (!FSessionChanging)
@@ -9371,9 +9378,8 @@ void __fastcall TCustomScpExplorerForm::UpdateControls()
QueueFileList->Font->Color = QueueView3->Font->Color;
QueueLabelUpdateStatus();
UpdatePanelControls(RemoteDirView, RemoteDriveView);
bool UseDarkTheme = WinConfiguration->UseDarkTheme();
RemoteDirView->DarkMode = UseDarkTheme;
RemoteDriveView->DarkMode = RemoteDirView->DarkMode;
if (FImmersiveDarkMode != UseDarkTheme)
{
UpdateDarkMode();
@@ -11671,11 +11677,11 @@ TListItem * __fastcall TCustomScpExplorerForm::SearchFile(const UnicodeString &
Matches = ContainsTextSemiCaseSensitive(ADirView->ItemFileName(Item), Text);
break;
case isAll:
int ColCount = (ADirView->ViewStyle == vsReport) ? ADirView->ColProperties->Count : 1;
int ColCount = (ADirView->DirViewStyle == dvsReport) ? ADirView->ColProperties->Count : 1;
int Index = 0;
while ((Index < ColCount) && !Matches)
{
bool Visible = (ADirView->ViewStyle == vsReport) ? ADirView->ColProperties->Visible[Index] : true;
bool Visible = (ADirView->DirViewStyle == dvsReport) ? ADirView->ColProperties->Visible[Index] : true;
if (Visible)
{
Matches = ContainsTextSemiCaseSensitive(ADirView->GetColumnText(Item, Index), Text);

View File

@@ -86,7 +86,6 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
ParentDoubleBuffered = False
PopupMenu = NonVisualDataModule.RemoteDirViewPopup
TabOrder = 0
ViewStyle = vsReport
OnColumnRightClick = DirViewColumnRightClick
OnEditing = DirViewEditing
OnEnter = RemoteDirViewEnter
@@ -118,6 +117,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
OnContextPopup = RemoteDirViewContextPopup
OnHistoryChange = DirViewHistoryChange
OnDisplayProperties = RemoteDirViewDisplayProperties
DirViewStyle = dvsReport
OnRead = RemoteDirViewRead
end
object ReconnectToolbar: TTBXToolbar

View File

@@ -592,7 +592,7 @@ protected:
TTBXStatusBar * StatusBar, const TStatusFileInfo & FileInfo);
void __fastcall FileStatusBarPanelClick(TTBXStatusPanel * Panel, TOperationSide Side);
virtual void __fastcall DoDirViewLoaded(TCustomDirView * Sender);
virtual void __fastcall UpdateControls();
virtual void UpdatePanelControls(TCustomDirView * ADirView, TCustomDriveView * ADriveView);
void __fastcall UpdateTransferList();
void __fastcall UpdateTransferLabel();
void __fastcall StartUpdates();
@@ -922,6 +922,7 @@ public:
void IncrementalSearchStart();
virtual void * SaveFocus();
virtual void RestoreFocus(void * Focus);
virtual void __fastcall UpdateControls();
__property bool ComponentVisible[Byte Component] = { read = GetComponentVisible, write = SetComponentVisible };
__property bool EnableFocusedOperation[TOperationSide Side] = { read = GetEnableFocusedOperation, index = 0 };

View File

@@ -266,14 +266,17 @@ void __fastcall TNonVisualDataModule::ExplorerActionsUpdate(
UPD(RemoteSelectAllAction2, DirView(osRemote)->FilesCount)
//style
UPDACT(CurrentCycleStyleAction,
CurrentCycleStyleAction->ImageIndex = 8 + (DirView(osCurrent)->ViewStyle + 1) % 4)
#define STYLEACTION(Style) UPDACT(Current ## Style ## Action, \
Current ## Style ## Action->Checked = (DirView(osCurrent)->ViewStyle == vs ## Style))
STYLEACTION(Icon)
STYLEACTION(SmallIcon)
STYLEACTION(List)
STYLEACTION(Report)
UPDACT(RemoteCycleStyleAction,
RemoteCycleStyleAction->ImageIndex = 8 + (DirView(osRemote)->DirViewStyle + 1) % 4)
#define STYLEACTION(SIDE, STYLE) UPDACT(SIDE ## STYLE ## Action, \
SIDE ## STYLE ## Action->Checked = (DirView(os ## SIDE)->DirViewStyle == dvs ## STYLE))
STYLEACTION(Remote, Icon)
STYLEACTION(Remote, SmallIcon)
STYLEACTION(Remote, List)
STYLEACTION(Remote, Report)
STYLEACTION(Remote, Thumbnail)
STYLEACTION(Local, Report)
STYLEACTION(Local, Thumbnail)
#undef STYLEACTION
// REMOTE+LOCAL
@@ -627,16 +630,20 @@ void __fastcall TNonVisualDataModule::ExplorerActionsExecute(
EXE(PasteAction3, ScpExplorer->PasteFromClipBoard())
// style
EXE(CurrentCycleStyleAction,
if (DirView(osCurrent)->ViewStyle == vsReport) DirView(osCurrent)->ViewStyle = vsIcon;
else DirView(osCurrent)->ViewStyle = (TViewStyle)(DirView(osCurrent)->ViewStyle + 1);
EXE(RemoteCycleStyleAction,
if (DirView(osRemote)->DirViewStyle == dvsReport) DirView(osRemote)->DirViewStyle = dvsIcon;
else DirView(osRemote)->DirViewStyle = (TDirViewStyle)(DirView(osRemote)->DirViewStyle + 1);
ScpExplorer->UpdateControls();
)
#define STYLEACTION(Style) EXE(Current ## Style ## Action, \
DirView(osCurrent)->ViewStyle = vs ## Style)
STYLEACTION(Icon)
STYLEACTION(SmallIcon)
STYLEACTION(List)
STYLEACTION(Report)
#define STYLEACTION(SIDE, STYLE) EXE(SIDE ## STYLE ## Action, \
DirView(os ## SIDE)->DirViewStyle = dvs ## STYLE; ScpExplorer->UpdateControls())
STYLEACTION(Remote, Icon)
STYLEACTION(Remote, SmallIcon)
STYLEACTION(Remote, List)
STYLEACTION(Remote, Report)
STYLEACTION(Remote, Thumbnail)
STYLEACTION(Local, Report)
STYLEACTION(Local, Thumbnail)
#undef STYLEACTION
#define PANEL_ACTIONS(SIDE) \

View File

@@ -343,40 +343,40 @@ object NonVisualDataModule: TNonVisualDataModule
ImageIndex = 6
ShortCut = 32805
end
object CurrentCycleStyleAction: TAction
Tag = 7
object RemoteCycleStyleAction: TAction
Tag = 15
Category = 'Style'
Caption = 'View'
HelpKeyword = 'ui_file_panel#view_style'
Hint = 'View|Cycle thru directory view styles'
ImageIndex = 8
end
object CurrentIconAction: TAction
Tag = 7
object RemoteIconAction: TAction
Tag = 15
Category = 'Style'
Caption = '&Large Icons'
HelpKeyword = 'ui_file_panel#view_style'
Hint = 'Large Icons|View large icons'
ImageIndex = 8
end
object CurrentSmallIconAction: TAction
Tag = 7
object RemoteSmallIconAction: TAction
Tag = 15
Category = 'Style'
Caption = '&Small Icons'
HelpKeyword = 'ui_file_panel#view_style'
Hint = 'Small Icons|View small icons'
ImageIndex = 9
end
object CurrentListAction: TAction
Tag = 7
object RemoteListAction: TAction
Tag = 15
Category = 'Style'
Caption = 'Lis&t'
HelpKeyword = 'ui_file_panel#view_style'
Hint = 'List|View list'
ImageIndex = 10
end
object CurrentReportAction: TAction
Tag = 7
object RemoteReportAction: TAction
Tag = 15
Category = 'Style'
Caption = '&Details'
HelpKeyword = 'ui_file_panel#view_style'
@@ -2465,6 +2465,28 @@ object NonVisualDataModule: TNonVisualDataModule
Caption = 'IncrementalSearchStartAction'
ShortCut = 16454
end
object RemoteThumbnailAction: TAction
Tag = 15
Category = 'Style'
Caption = '&Thumbnails'
HelpKeyword = 'ui_file_panel#view_style'
Hint = 'Thumbnails|View thumbnails'
end
object LocalReportAction: TAction
Tag = 15
Category = 'Style'
Caption = '&Details'
HelpKeyword = 'ui_file_panel#view_style'
Hint = 'Details|View details'
ImageIndex = 11
end
object LocalThumbnailAction: TAction
Tag = 15
Category = 'Style'
Caption = '&Thumbnails'
HelpKeyword = 'ui_file_panel#view_style'
Hint = 'Thumbnails|View thumbnails'
end
end
object ExplorerBarPopup: TTBXPopupMenu
Images = GlyphsModule.ExplorerImages

View File

@@ -81,11 +81,11 @@ __published: // IDE-managed Components
TAction *CurrentRenameAction;
TAction *CurrentDeleteAction;
TAction *CurrentCreateDirAction;
TAction *CurrentCycleStyleAction;
TAction *CurrentIconAction;
TAction *CurrentSmallIconAction;
TAction *CurrentReportAction;
TAction *CurrentListAction;
TAction *RemoteCycleStyleAction;
TAction *RemoteIconAction;
TAction *RemoteSmallIconAction;
TAction *RemoteReportAction;
TAction *RemoteListAction;
TAction *CurrentDeleteFocusedAction;
TAction *CurrentPropertiesFocusedAction;
TAction *CurrentPropertiesAction;
@@ -711,6 +711,9 @@ __published: // IDE-managed Components
TTBXSeparatorItem *TBXSeparatorItem23;
TTBXItem *TBXItem116;
TAction *IncrementalSearchStartAction;
TAction *RemoteThumbnailAction;
TAction *LocalReportAction;
TAction *LocalThumbnailAction;
void __fastcall ExplorerActionsUpdate(TBasicAction *Action, bool &Handled);
void __fastcall ExplorerActionsExecute(TBasicAction *Action, bool &Handled);
void __fastcall SessionIdleTimerTimer(TObject *Sender);

View File

@@ -165,6 +165,7 @@ void __fastcall TScpCommanderForm::RestorePanelParams(
const TScpCommanderPanelConfiguration & PanelConfiguration)
{
DirView->ColProperties->ParamsStr = PanelConfiguration.DirViewParams;
DirView->DirViewStyle = (TDirViewStyle)PanelConfiguration.ViewStyle;
StatusBar->Visible = PanelConfiguration.StatusBar;
DriveControl->Visible = PanelConfiguration.DriveView;
if (DriveControl->Align == alTop)
@@ -194,6 +195,7 @@ void __fastcall TScpCommanderForm::RestoreParams()
RestorePanelParams(LocalDirView, LocalDriveView, LocalStatusBar, WinConfiguration->ScpCommander.LocalPanel);
RestorePanelParams(RemoteDirView, RemoteDrivePanel, RemoteStatusBar, WinConfiguration->ScpCommander.RemotePanel);
OtherLocalDirView->ColProperties->ParamsStr = WinConfiguration->ScpCommander.OtherLocalPanelDirViewParams;
OtherLocalDirView->DirViewStyle = (TDirViewStyle)WinConfiguration->ScpCommander.OtherLocalPanelViewStyle;
FPanelsRestored = true;
// just to make sure
@@ -207,6 +209,7 @@ void __fastcall TScpCommanderForm::StorePanelParams(
TScpCommanderPanelConfiguration & PanelConfiguration)
{
PanelConfiguration.DirViewParams = DirView->ColProperties->ParamsStr;
PanelConfiguration.ViewStyle = DirView->DirViewStyle;
PanelConfiguration.StatusBar = StatusBar->Visible;
PanelConfiguration.DriveView = DriveControl->Visible;
if (DriveControl->Align == alTop)
@@ -242,6 +245,7 @@ void __fastcall TScpCommanderForm::StoreParams()
StorePanelParams(LocalDirView, LocalDriveView, LocalStatusBar, CommanderConfiguration.LocalPanel);
StorePanelParams(RemoteDirView, RemoteDrivePanel, RemoteStatusBar, CommanderConfiguration.RemotePanel);
CommanderConfiguration.OtherLocalPanelDirViewParams = OtherLocalDirView->ColProperties->ParamsStr;
CommanderConfiguration.OtherLocalPanelViewStyle = reinterpret_cast<TDirViewStyle>(OtherLocalDirView->DirViewStyle);
CommanderConfiguration.LocalPanel.LastPath = LocalDirView->Path;
CommanderConfiguration.OtherLocalPanelLastPath = OtherLocalDirView->Path;
@@ -1024,6 +1028,12 @@ void __fastcall TScpCommanderForm::SetToolbar2ItemAction(TTBXItem * Item, TBasic
}
}
//---------------------------------------------------------------------------
void TScpCommanderForm::UpdatePanelControls(TCustomDirView * ADirView, TCustomDriveView * ADriveView)
{
TCustomScpExplorerForm::UpdatePanelControls(ADirView, ADriveView);
ADirView->AddParentDir = (ADirView->DirViewStyle == dvsReport);
}
//---------------------------------------------------------------------------
void __fastcall TScpCommanderForm::UpdateControls()
{
// Before TCustomScpExplorerForm disables them (when disconnecting)
@@ -1057,10 +1067,8 @@ void __fastcall TScpCommanderForm::UpdateControls()
// command line combo width needs to be updated as caption width has probably changed
ToolBarResize(CommandLineToolbar);
}
LocalDirView->DarkMode = WinConfiguration->UseDarkTheme();
LocalDriveView->DarkMode = LocalDirView->DarkMode;
OtherLocalDirView->DarkMode = LocalDirView->DarkMode;
OtherLocalDriveView->DarkMode = LocalDirView->DarkMode;
UpdatePanelControls(LocalDirView, LocalDriveView);
UpdatePanelControls(OtherLocalDirView, OtherLocalDriveView);
LocalDirView->Color = PanelColor();
LocalDriveView->Color = LocalDirView->Color;
OtherLocalDirView->Color = LocalDirView->Color;
@@ -1070,6 +1078,9 @@ void __fastcall TScpCommanderForm::UpdateControls()
OtherLocalDirView->Font->Color = LocalDirView->Font->Color;
OtherLocalDriveView->Font->Color = LocalDirView->Font->Color;
LocalColumnsSubmenuItem->Enabled = (DirView(osLocal)->DirViewStyle == dvsReport);
RemoteColumnsSubmenuItem->Enabled = (DirView(osRemote)->DirViewStyle == dvsReport);
if (IsLocalBrowserMode())
{
CurrentCopyItem->Visible = false;

View File

@@ -93,6 +93,19 @@ inherited ScpCommanderForm: TScpCommanderForm
end
object TBXSeparatorItem4: TTBXSeparatorItem
end
object TBXSubmenuItem17: TTBXSubmenuItem
Caption = '&View'
HelpKeyword = 'ui_file_panel#view_style'
Hint = 'Change directory view style'
object TBXItem270: TTBXItem
Action = NonVisualDataModule.LocalReportAction
end
object TBXSeparatorItem76: TTBXSeparatorItem
end
object TBXItem271: TTBXItem
Action = NonVisualDataModule.LocalThumbnailAction
end
end
object TBXSubmenuItem3: TTBXSubmenuItem
Caption = '&Sort'
HelpKeyword = 'ui_file_panel#sorting_files'
@@ -1195,7 +1208,6 @@ inherited ScpCommanderForm: TScpCommanderForm
ParentDoubleBuffered = False
PopupMenu = NonVisualDataModule.RemoteDirViewPopup
TabOrder = 1
ViewStyle = vsReport
OnColumnRightClick = DirViewColumnRightClick
OnEditing = DirViewEditing
OnEnter = OtherLocalDirViewEnter
@@ -1224,6 +1236,7 @@ inherited ScpCommanderForm: TScpCommanderForm
OnPathChange = OtherLocalDirViewPathChange
OnBusy = DirViewBusy
OnChangeFocus = DirViewChangeFocus
DirViewStyle = dvsReport
end
end
inherited RemoteDrivePanel: TPanel
@@ -1600,7 +1613,6 @@ inherited ScpCommanderForm: TScpCommanderForm
ParentDoubleBuffered = False
PopupMenu = NonVisualDataModule.LocalDirViewPopup
TabOrder = 1
ViewStyle = vsReport
OnColumnRightClick = DirViewColumnRightClick
OnEditing = DirViewEditing
OnEnter = LocalDirViewEnter
@@ -1630,6 +1642,7 @@ inherited ScpCommanderForm: TScpCommanderForm
OnPathChange = LocalDirViewPathChange
OnBusy = DirViewBusy
OnChangeFocus = DirViewChangeFocus
DirViewStyle = dvsReport
end
object LocalTopDock: TTBXDock
Left = 0
@@ -1686,6 +1699,19 @@ inherited ScpCommanderForm: TScpCommanderForm
object TBXItem164: TTBXItem
Action = NonVisualDataModule.LocalTreeAction
end
object TBXSubmenuItem4: TTBXSubmenuItem
Caption = 'Change directory view style'
ImageIndex = 11
Options = [tboDropdownArrow]
object TBXItem269: TTBXItem
Action = NonVisualDataModule.LocalReportAction
end
object TBXSeparatorItem75: TTBXSeparatorItem
end
object TBXItem268: TTBXItem
Action = NonVisualDataModule.LocalThumbnailAction
end
end
end
object LocalPathToolbar: TTBXToolbar
Left = 0
@@ -1805,7 +1831,7 @@ inherited ScpCommanderForm: TScpCommanderForm
end
end
object LocalSelectionToolbar: TTBXToolbar
Left = 217
Left = 244
Top = 27
Caption = 'Local Selection'
DockPos = 217

View File

@@ -468,6 +468,14 @@ __published:
TTBXItem *TBXItem266;
TTBXSeparatorItem *TBXSeparatorItem74;
TTBXItem *TBXItem267;
TTBXSubmenuItem *TBXSubmenuItem4;
TTBXItem *TBXItem269;
TTBXSeparatorItem *TBXSeparatorItem75;
TTBXItem *TBXItem268;
TTBXSubmenuItem *TBXSubmenuItem17;
TTBXItem *TBXItem270;
TTBXSeparatorItem *TBXSeparatorItem76;
TTBXItem *TBXItem271;
void __fastcall SplitterMoved(TObject *Sender);
void __fastcall SplitterCanResize(TObject *Sender, int &NewSize,
bool &Accept);
@@ -592,7 +600,7 @@ protected:
virtual void __fastcall ConfigurationChanged();
virtual bool __fastcall GetHasDirView(TOperationSide Side);
virtual TCustomDirView * GetCurrentLocalBrowser();
virtual void __fastcall UpdateControls();
virtual void UpdatePanelControls(TCustomDirView * ADirView, TCustomDriveView * ADriveView);
virtual void __fastcall FileOperationProgress(
TFileOperationProgressType & ProgressData);
virtual void __fastcall DoOpenDirectoryDialog(TOpenDirectoryMode Mode,
@@ -695,6 +703,7 @@ public:
virtual void ResetLayoutColumns(TOperationSide Side);
virtual void * SaveFocus();
virtual void RestoreFocus(void * Focus);
virtual void __fastcall UpdateControls();
__property double LeftPanelWidth = { read = GetLeftPanelWidth, write = SetLeftPanelWidth };
};

View File

@@ -86,7 +86,7 @@ void __fastcall TScpExplorerForm::RestoreParams()
bool HadHandleAllocated = RemoteDirView->HandleAllocated();
RemoteDirView->UnixColProperties->ParamsStr = WinConfiguration->ScpExplorer.DirViewParams;
RemoteDirView->UnixColProperties->ExtVisible = false; // just to make sure
RemoteDirView->ViewStyle = (TViewStyle)WinConfiguration->ScpExplorer.ViewStyle;
RemoteDirView->DirViewStyle = (TDirViewStyle)WinConfiguration->ScpExplorer.ViewStyle;
if (HadHandleAllocated)
{
// This is here to make our persistence checks in VerifyControl pass,
@@ -121,7 +121,7 @@ void __fastcall TScpExplorerForm::StoreParams()
WinConfiguration->ScpExplorer.WindowParams = StoreForm(this);
WinConfiguration->ScpExplorer.DirViewParams = RemoteDirView->UnixColProperties->ParamsStr;
WinConfiguration->ScpExplorer.ViewStyle = RemoteDirView->ViewStyle;
WinConfiguration->ScpExplorer.ViewStyle = RemoteDirView->DirViewStyle;
WinConfiguration->ScpExplorer.DriveView = RemoteDrivePanel->Visible;
WinConfiguration->ScpExplorer.DriveViewWidth = RemoteDrivePanel->Width;
WinConfiguration->ScpExplorer.DriveViewWidthPixelsPerInch = GetControlPixelsPerInch(this);
@@ -444,5 +444,5 @@ void __fastcall TScpExplorerForm::UpdateControls()
ActiveControl = RemoteDirView;
}
ColumndsSubmenuItem->Enabled = (RemoteDirView->ViewStyle == vsReport);
ColumndsSubmenuItem->Enabled = (RemoteDirView->DirViewStyle == dvsReport);
}

View File

@@ -460,16 +460,16 @@ inherited ScpExplorerForm: TScpExplorerForm
object TBXSeparatorItem3: TTBXSeparatorItem
end
object TBXItem16: TTBXItem
Action = NonVisualDataModule.CurrentIconAction
Action = NonVisualDataModule.RemoteIconAction
end
object TBXItem17: TTBXItem
Action = NonVisualDataModule.CurrentSmallIconAction
Action = NonVisualDataModule.RemoteSmallIconAction
end
object TBXItem18: TTBXItem
Action = NonVisualDataModule.CurrentListAction
Action = NonVisualDataModule.RemoteListAction
end
object TBXItem19: TTBXItem
Action = NonVisualDataModule.CurrentReportAction
Action = NonVisualDataModule.RemoteReportAction
end
object TBXSeparatorItem4: TTBXSeparatorItem
end
@@ -833,19 +833,19 @@ inherited ScpExplorerForm: TScpExplorerForm
object TBXSeparatorItem36: TTBXSeparatorItem
end
object TBXSubmenuItem3: TTBXSubmenuItem
Action = NonVisualDataModule.CurrentCycleStyleAction
Action = NonVisualDataModule.RemoteCycleStyleAction
DropdownCombo = True
object TBXItem72: TTBXItem
Action = NonVisualDataModule.CurrentIconAction
Action = NonVisualDataModule.RemoteIconAction
end
object TBXItem73: TTBXItem
Action = NonVisualDataModule.CurrentSmallIconAction
Action = NonVisualDataModule.RemoteSmallIconAction
end
object TBXItem74: TTBXItem
Action = NonVisualDataModule.CurrentListAction
Action = NonVisualDataModule.RemoteListAction
end
object TBXItem75: TTBXItem
Action = NonVisualDataModule.CurrentReportAction
Action = NonVisualDataModule.RemoteReportAction
end
end
object TBXSubmenuItem24: TTBXSubmenuItem

View File

@@ -373,7 +373,6 @@ protected:
virtual void __fastcall ToolbarItemResize(TTBXCustomDropDownItem * Item, int Width);
virtual bool __fastcall UpdateToolbarDisplayMode();
virtual void __fastcall UpdateImages();
virtual void __fastcall UpdateControls();
public:
__fastcall TScpExplorerForm(TComponent* Owner);
@@ -386,6 +385,7 @@ public:
virtual UnicodeString __fastcall DefaultDownloadTargetDirectory();
virtual bool SupportedSession(TSessionData * SessionData);
virtual void ResetLayoutColumns(TOperationSide Side);
virtual void __fastcall UpdateControls();
};
//---------------------------------------------------------------------------
#endif

View File

@@ -17,6 +17,7 @@ uses
const
clDefaultItemColor = -(COLOR_ENDCOLORS + 1);
WM_USER_RENAME = WM_USER + 57;
WM_USER_INVALIDATEITEM = WM_USER + $2000 + 16;
oiNoOverlay = $00;
oiDirUp = $01;
oiLink = $02;
@@ -88,6 +89,8 @@ type
TDVHistoryGoEvent = procedure(Sender: TCustomDirView; Index: Integer; var Cancel: Boolean) of object;
TCompareCriteria = (ccTime, ccSize);
TCompareCriterias = set of TCompareCriteria;
// First four must match TViewStyle
TDirViewStyle = (dvsIcon, dvsSmallIcon, dvsList, dvsReport, dvsThumbnail);
TWMXMouse = packed record
Msg: Cardinal;
@@ -149,6 +152,8 @@ type
FHistoryPaths: TStrings;
FOverlaySmallImages: TImageList;
FOverlayLargeImages: TImageList;
FThumbnailShellImages: TImageList;
FThumbnailImages: TImageList;
FMaxHistoryCount: Integer;
FPathLabel: TCustomPathLabel;
FOnUpdateStatusBar: TDirViewUpdateStatusBarEvent;
@@ -173,6 +178,8 @@ type
FDoubleBufferedScrollingWorkaround: Boolean;
FOnBusy: TDirViewBusy;
FOnChangeFocus: TDirViewChangeFocusEvent;
FFallbackThumbnail: array[Boolean] of TBitmap;
FFallbackThumbnailSize: TSize;
procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
procedure WMNotify(var Msg: TWMNotify); message WM_NOTIFY;
@@ -190,6 +197,7 @@ type
procedure CMDPIChanged(var Message: TMessage); message CM_DPICHANGED;
procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
procedure CNKeyDown(var Message: TWMKeyDown); message CN_KEYDOWN;
procedure WMUserInvalidateItem(var Message: TMessage); message WM_USER_INVALIDATEITEM;
procedure DumbCustomDrawItem(Sender: TCustomListView; Item: TListItem;
State: TCustomDrawState; var DefaultDraw: Boolean);
@@ -201,6 +209,9 @@ type
function GetHistoryPath(Index: Integer): string;
function GetSelectedNamesSaved: Boolean;
function GetDirViewStyle: TDirViewStyle;
procedure SetDirViewStyle(Value: TDirViewStyle);
function GetTargetPopupMenu: Boolean;
function GetUseDragImages: Boolean;
procedure SetMaxHistoryCount(Value: Integer);
@@ -223,6 +234,7 @@ type
FInvalidNameChars: string;
FDragDrive: string;
FAnnouncedState: TObject;
FThumbnail: Boolean;
procedure AddToDragFileList(FileList: TFileList; Item: TListItem); virtual;
function CanEdit(Item: TListItem): Boolean; override;
@@ -276,6 +288,10 @@ type
function DoItemColor(Item: TListItem): TColor;
function ItemColor(Item: TListItem): TColor; virtual;
function ItemImageIndex(Item: TListItem; Cache: Boolean): Integer; virtual; abstract;
function ItemThumbnail(Item: TListItem; Size: TSize): TBitmap; virtual;
procedure FreeThumbnails;
function FallbackThumbnail(Dir: Boolean; Size: TSize): TBitmap;
procedure DrawThumbnail(Item: TListItem; DC: HDC);
// ItemIsDirectory and ItemFullFileName is in public block
function ItemIsRecycleBin(Item: TListItem): Boolean; virtual;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
@@ -299,6 +315,7 @@ type
function ItemIsFile(Item: TListItem): Boolean; virtual; abstract;
function ItemMatchesFilter(Item: TListItem; const Filter: TFileFilter): Boolean; virtual; abstract;
function ItemOverlayIndexes(Item: TListItem): Word; virtual;
function IsItemVisible(Item: TListItem): Boolean;
procedure LimitHistorySize;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure PathChanged; virtual;
@@ -342,6 +359,8 @@ type
procedure DoUpdateStatusBar(Force: Boolean = False);
procedure DoCustomDrawItem(Item: TListItem; Stage: TCustomDrawStage);
procedure ItemCalculatedSizeUpdated(Item: TListItem; OldSize, NewSize: Int64);
function GetThumbnail(Path: string; Size: TSize): TBitmap;
procedure InvalidateItem(Item: TListItem);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
@@ -436,6 +455,7 @@ type
property NaturalOrderNumericalSorting: Boolean read FNaturalOrderNumericalSorting write SetNaturalOrderNumericalSorting;
property AlwaysSortDirectoriesByName: Boolean read FAlwaysSortDirectoriesByName write SetAlwaysSortDirectoriesByName;
property DarkMode: Boolean read FDarkMode write SetDarkMode;
property DirViewStyle: TDirViewStyle read GetDirViewStyle write SetDirViewStyle;
property OnContextPopup;
property OnStartLoading: TNotifyEvent read FOnStartLoading write FOnStartLoading;
@@ -548,7 +568,11 @@ const
implementation
uses
Math, DirViewColProperties, UITypes, Types, OperationWithTimeout, Winapi.UxTheme, Vcl.Themes;
Math, DirViewColProperties, UITypes, Types, OperationWithTimeout, Winapi.UxTheme, Vcl.Themes, System.IOUtils;
type
PRGBQuadArray = ^TRGBQuadArray; // From graphics.pas
TRGBQuadArray = array[Byte] of TRGBQuad; // From graphics.pas
const
ResDirUp = 'DIRUP%2.2d';
@@ -957,6 +981,47 @@ begin
else inherited;
end;
procedure TCustomDirView.DrawThumbnail(Item: TListItem; DC: HDC);
var
Rect: TRect;
Thumbnail: TBitmap;
Size: TSize;
Left, Top: Integer;
BlendFunction: TBlendFunction;
ThumbnailDC: HDC;
begin
Rect := Item.DisplayRect(drIcon);
// For thumbnails: The larger side (e.g. height for portrait oriented images) is rescaled to Size.Width
// For icons: The "generated" images is exactly as requested
Size.Height := MulDiv(Min(Rect.Width, Rect.Height), 9, 10);
Size.Width := Size.Height;
Thumbnail := ItemThumbnail(Item, Size);
if not Assigned(Thumbnail) then
Thumbnail := FallbackThumbnail(ItemIsDirectory(Item), Size);
if Assigned(Thumbnail) then
begin
Left := Rect.Left + ((Rect.Width - Thumbnail.Width) div 2);
Top := Rect.Bottom - Thumbnail.Height - MulDiv(Rect.Height, 1, 20); // Bottom-aligned, as Explorer does
// https://stackoverflow.com/q/24595717/850848
// https://stackoverflow.com/q/10028531/850848#10044325
ThumbnailDC := CreateCompatibleDC(0);
try
SelectObject(ThumbnailDC, Thumbnail.Handle);
BlendFunction.BlendOp := AC_SRC_OVER;
BlendFunction.BlendFlags := 0;
BlendFunction.SourceConstantAlpha := 255;
BlendFunction.AlphaFormat := AC_SRC_ALPHA;
AlphaBlend(DC, Left, Top, Thumbnail.Width, Thumbnail.Height, ThumbnailDC, 0, 0, Thumbnail.Width, Thumbnail.Height, BlendFunction);
finally
DeleteDC(ThumbnailDC);
end;
end;
end;
procedure TCustomDirView.CNNotify(var Message: TWMNotify);
procedure DrawOverlayImage(DC: HDC; Image: Integer; Item: TListItem);
@@ -1072,22 +1137,39 @@ begin
begin
Nmcd := @PNMLVCustomDraw(Message.NMHdr).nmcd;
try
Message.Result := Message.Result or CDRF_NOTIFYPOSTPAINT;
if (Nmcd.dwDrawStage = CDDS_ITEMPOSTPAINT) and
((Nmcd.dwDrawStage and CDDS_SUBITEM) = 0) then
if (Nmcd.dwDrawStage and CDDS_SUBITEM) = 0 then
begin
Item := Items[Nmcd.dwItemSpec];
Assert(Assigned(Item));
OverlayIndexes := ItemOverlayIndexes(Item);
OverlayIndex := 1;
while OverlayIndexes > 0 do
if (Nmcd.dwDrawStage and CDDS_ITEMPREPAINT) = CDDS_ITEMPREPAINT then
begin
if (OverlayIndex and OverlayIndexes) <> 0 then
Message.Result := Message.Result or CDRF_NOTIFYPOSTPAINT;
end
else
if ((Nmcd.dwDrawStage and CDDS_ITEMPOSTPAINT) = CDDS_ITEMPOSTPAINT) and
(Nmcd.rc.Width > 0) then // We get called many times with empty rect while view is recreating (e.g. when switching to thumnails mode)
begin
Item := Items[Nmcd.dwItemSpec];
if IsItemVisible(Item) then // particularly the thumbnail drawing is expensive
begin
DrawOverlayImage(Nmcd.hdc, OverlayIndex, Item);
Dec(OverlayIndexes, OverlayIndex);
if FThumbnail then
begin
DrawThumbnail(Item, Nmcd.hdc);
end
else
begin
OverlayIndexes := ItemOverlayIndexes(Item);
OverlayIndex := 1;
while OverlayIndexes > 0 do
begin
if (OverlayIndex and OverlayIndexes) <> 0 then
begin
DrawOverlayImage(Nmcd.hdc, OverlayIndex, Item);
Dec(OverlayIndexes, OverlayIndex);
end;
OverlayIndex := OverlayIndex shl 1;
end;
end;
end;
OverlayIndex := OverlayIndex shl 1;
end;
end;
except
@@ -1186,9 +1268,31 @@ begin
end;
procedure TCustomDirView.NeedImageLists(Recreate: Boolean);
var
ALargeImages: TImageList;
ThumbnailSize: Integer;
begin
SmallImages := NeedImageList(ilsSmall, Recreate, FOverlaySmallImages);
LargeImages := NeedImageList(ilsLarge, Recreate, FOverlayLargeImages);
ALargeImages := NeedImageList(ilsLarge, Recreate, FOverlayLargeImages);
if FThumbnail then
begin
ThumbnailSize := ScaleByPixelsPerInch(128, Self);
// ShellImageListForSize would normally prefer smaller icons (for row sizing purposes).
// But for thumbnails, we prefer larger version as will will scale it when painting.
// The *2 is hackish way to achieve that.
FThumbnailShellImages := ShellImageListForSize(ThumbnailSize * 2);
if (not Assigned(FThumbnailImages)) or (FThumbnailImages.Width <> ThumbnailSize) then
begin
if Assigned(FThumbnailImages) then
FreeAndNil(FThumbnailImages);
// Dummy image list, whose sole purpose it to autosize the items in the view
FThumbnailImages := TImageList.CreateSize(ThumbnailSize, ThumbnailSize);
end;
LargeImages := FThumbnailImages
end
else LargeImages := ALargeImages;
end;
procedure TCustomDirView.CMDPIChanged(var Message: TMessage);
@@ -1222,6 +1326,7 @@ procedure TCustomDirView.FreeImageLists;
begin
FreeAndNil(FOverlaySmallImages);
FreeAndNil(FOverlayLargeImages);
FreeAndNil(FThumbnailImages);
SmallImages := nil;
LargeImages := nil;
@@ -1383,6 +1488,7 @@ begin
FreeAndNil(FDragDropFilesEx);
FreeImageLists;
FreeThumbnails;
inherited;
end;
@@ -1465,6 +1571,104 @@ begin
Result := clDefaultItemColor;
end;
function TCustomDirView.ItemThumbnail(Item: TListItem; Size: TSize): TBitmap;
begin
Result := nil;
end;
function TCustomDirView.GetThumbnail(Path: string; Size: TSize): TBitmap;
var
ImageFactory: IShellItemImageFactory;
X, Y: Integer;
Row: PRGBQuadArray;
Pixel: PRGBQuad;
Alpha: Byte;
Handle: HBITMAP;
begin
Result := nil;
SHCreateItemFromParsingName(PChar(Path), nil, IShellItemImageFactory, ImageFactory);
if Assigned(ImageFactory) then
begin
if Succeeded(ImageFactory.GetImage(Size, SIIGBF_RESIZETOFIT, Handle)) then
begin
Result := TBitmap.Create;
try
Result.Handle := Handle;
Result.PixelFormat := pf32bit;
for Y := 0 to Result.Height - 1 do
begin
Row := Result.ScanLine[Y];
for X := 0 to Result.Width - 1 do
begin
Pixel := @Row[X];
Alpha := Pixel.rgbReserved;
Pixel.rgbBlue := (Pixel.rgbBlue * Alpha) div 255;
Pixel.rgbGreen := (Pixel.rgbGreen * Alpha) div 255;
Pixel.rgbRed := (Pixel.rgbRed * Alpha) div 255;
end;
end;
except
Result.Free;
raise;
end;
end;
ImageFactory := nil; // Redundant?
end;
end;
procedure TCustomDirView.FreeThumbnails;
begin
FreeAndNil(FFallbackThumbnail[True]);
FreeAndNil(FFallbackThumbnail[False]);
end;
function TCustomDirView.FallbackThumbnail(Dir: Boolean; Size: TSize): TBitmap;
var
FallbackPath: string;
Existed: Boolean;
Index: Integer;
begin
Result := nil;
try
if FFallbackThumbnailSize <> Size then
begin
FreeThumbnails;
end;
if not Assigned(FFallbackThumbnail[Dir]) then
begin
Index := 1;
repeat
FallbackPath := TPath.Combine(TempDir, 'default.' + IntToStr(Index) + '.thumbnailimage');
Existed := FileExists(FallbackPath) or DirectoryExists(FallbackPath);
if not Existed then
begin
if Dir then
CreateDir(FallbackPath)
else
TFile.WriteAllText(FallbackPath, '');
end;
Inc(Index);
until not Existed;
FFallbackThumbnailSize := Size;
FFallbackThumbnail[Dir] := GetThumbnail(FallbackPath, Size);
if Existed then
begin
if Dir then
RemoveDir(FallbackPath)
else
DeleteFile(FallbackPath);
end;
end;
Result := FFallbackThumbnail[Dir];
except
end;
end;
function TCustomDirView.GetFilesMarkedSize: Int64;
begin
if SelCount > 0 then Result := FilesSelSize
@@ -1485,6 +1689,11 @@ begin
OnGetOverlay(Self, Item, Result);
end;
function TCustomDirView.IsItemVisible(Item: TListItem): Boolean;
begin
Result := (ListView_IsItemVisible(Handle, Item.Index) <> 0);
end;
procedure TCustomDirView.WMKeyDown(var Message: TWMKeyDown);
begin
if DoubleBuffered and (Message.CharCode in [VK_PRIOR, VK_NEXT]) and
@@ -3482,6 +3691,54 @@ begin
UpdateStatusBar;
end;
function TCustomDirView.GetDirViewStyle: TDirViewStyle;
begin
if (ViewStyle = vsIcon) and FThumbnail then Result := dvsThumbnail
else Result := TDirViewStyle(ViewStyle);
end;
procedure TCustomDirView.SetDirViewStyle(Value: TDirViewStyle);
var
NewViewStyle: TViewStyle;
begin
if DirViewStyle <> Value then
begin
FThumbnail := (Value = dvsThumbnail);
// Create thumbnail images before recreating the view
NeedImageLists(False);
if FThumbnail then NewViewStyle := vsIcon
else NewViewStyle := TViewStyle(Value);
if ViewStyle <> NewViewStyle then
begin
ViewStyle := NewViewStyle;
end
else
begin
// Changing ViewStyle recreates the view, we want to be consistent.
if not (csLoading in ComponentState) then
RecreateWnd;
end;
end;
end;
procedure TCustomDirView.InvalidateItem(Item: TListItem);
var
R: TRect;
begin
R := Item.DisplayRect(drBounds);
// alternative to TListItem.Update (which causes flicker)
InvalidateRect(Handle, @R, True);
end;
procedure TCustomDirView.WMUserInvalidateItem(var Message: TMessage);
var
Index: Integer;
begin
Index := Integer(Message.WParam);
if (Index >= 0) and (Index < Items.Count) then
InvalidateItem(Items[Index]);
end;
initialization
DropSourceControl := nil;

View File

@@ -74,6 +74,8 @@ type
FileExt: string;
TypeName: string;
ImageIndex: Integer;
Thumbnail: TBitmap;
ThumbnailSize: TSize;
Size: Int64;
Attr: LongWord;
FileTime: TFileTime;
@@ -94,6 +96,7 @@ type
private
FOwner: TDirView;
FSyncIcon: Integer;
FSyncThumbnail: TBitmap;
FCurrentIndex: Integer;
FCurrentFilePath: string;
FCurrentItemData: TFileRec;
@@ -101,6 +104,9 @@ type
protected
constructor Create(Owner: TDirView);
procedure Execute; override;
public
destructor Destroy; override;
end;
// WORKAROUND: TQueue<Integer>.Create fails when TDirView is created in IDE, while TQueue<TIconUpdateSchedule>.Create works
@@ -200,6 +206,7 @@ type
function GetIsRoot: Boolean; override;
procedure InternalEdit(const HItem: TLVItem); override;
function ItemColor(Item: TListItem): TColor; override;
function ItemThumbnail(Item: TListItem; Size: TSize): TBitmap; override;
function ItemFileExt(Item: TListItem): string;
function ItemFileNameOnly(Item: TListItem): string;
function ItemImageIndex(Item: TListItem; Cache: Boolean): Integer; override;
@@ -211,9 +218,11 @@ type
procedure LoadFiles; override;
procedure PerformItemDragDropOperation(Item: TListItem; Effect: Integer; Paste: Boolean); override;
procedure SortItems; override;
function ThumbnailNeeded(ItemData: PFileRec): Boolean;
procedure DoFetchIconUpdate;
procedure DoUpdateIcon;
procedure StartFileDeleteThread;
procedure IconUpdateEnqueue(ListItem: TListItem);
function IconUpdatePeek: Integer;
procedure IconUpdateDequeue;
procedure WMDestroy(var Msg: TWMDestroy); message WM_DESTROY;
@@ -363,6 +372,7 @@ type
property ColumnClick;
property MultiSelect;
property ReadOnly;
property DirViewStyle;
// The only way to make Items stored automatically and survive handle recreation.
// Though we should implement custom persisting to avoid publishing this
@@ -395,6 +405,9 @@ uses
ShellDialogs, IEDriveInfo,
FileChanges, Math, PasTools, StrUtils, Types, UITypes;
var
ThumbnailNotNeeded: TSize;
var
DaylightHack: Boolean;
@@ -617,6 +630,12 @@ begin
FOwner := Owner;
end; {TIconUpdateThread.Create}
destructor TIconUpdateThread.Destroy;
begin
FreeAndNil(FSyncThumbnail);
inherited;
end;
procedure TIconUpdateThread.Execute;
var
IsSpecialExt: Boolean;
@@ -631,19 +650,32 @@ begin
if (FCurrentIndex >= 0) and
(not Terminated) then
begin
PrevPIDL := FCurrentItemData.PIDL;
try
IsSpecialExt := MatchesFileExt(FCurrentItemData.FileExt, SpecialExtensions);
FOwner.DoFetchIcon(FCurrentFilePath, IsSpecialExt, False, @FCurrentItemData, FSyncIcon, TypeName);
except
{Capture exceptions generated by the shell}
FSyncIcon := UnknownFileIcon;
end;
if Assigned(FCurrentItemData.PIDL) and
(PrevPIDL <> FCurrentItemData.PIDL) then
FSyncIcon := -1;
if Assigned(FSyncThumbnail) then
FreeAndNil(FSyncThumbnail);
if FCurrentItemData.IconEmpty then
begin
FreePIDL(FCurrentItemData.PIDL);
PrevPIDL := FCurrentItemData.PIDL;
try
IsSpecialExt := MatchesFileExt(FCurrentItemData.FileExt, SpecialExtensions);
FOwner.DoFetchIcon(FCurrentFilePath, IsSpecialExt, False, @FCurrentItemData, FSyncIcon, TypeName);
except
{Capture exceptions generated by the shell}
FSyncIcon := UnknownFileIcon;
end;
if Assigned(FCurrentItemData.PIDL) and
(PrevPIDL <> FCurrentItemData.PIDL) then
begin
FreePIDL(FCurrentItemData.PIDL);
end;
end;
if FOwner.ThumbnailNeeded(@FCurrentItemData) then
begin
FSyncThumbnail := FOwner.GetThumbnail(FCurrentFilePath, FCurrentItemData.ThumbnailSize);
end;
if not Terminated then
begin
Synchronize(FOwner.DoUpdateIcon);
@@ -907,6 +939,8 @@ begin
{$WARNINGS ON}
Empty := True;
IconEmpty := True;
Thumbnail := nil;
ThumbnailSize := ThumbnailNotNeeded;
if Size > 0 then Inc(FFilesSize, Size);
PIDL := nil;
CalculatedSize := -1;
@@ -955,6 +989,8 @@ begin
{$WARNINGS ON}
Empty := True;
IconEmpty := False;
Thumbnail := nil;
ThumbnailSize := ThumbnailNotNeeded;
PIDL := nil;
ImageIndex := StdDirIcon;
@@ -1332,7 +1368,6 @@ var
SaveCursor: TCursor;
FSize: Int64;
FocusedIsVisible: Boolean;
R: TRect;
begin
if (not Loading) and LoadEnabled then
begin
@@ -1347,29 +1382,7 @@ begin
end
else
begin
if Assigned(ItemFocused) then
begin
R := ItemFocused.DisplayRect(drBounds);
// btw, we use vsReport only, nothing else was tested
// (We can use ListView_IsItemVisible here)
Assert(ViewStyle = vsReport);
case ViewStyle of
vsReport:
FocusedIsVisible := (TopItem.Index <= ItemFocused.Index) and
(ItemFocused.Index < TopItem.Index + VisibleRowCount);
vsList:
// do not know how to implement that
FocusedIsVisible := False;
else // vsIcon and vsSmallIcon
FocusedIsVisible :=
IntersectRect(R,
Classes.Rect(ViewOrigin, Point(ViewOrigin.X + ClientWidth, ViewOrigin.Y + ClientHeight)),
ItemFocused.DisplayRect(drBounds));
end;
end
else FocusedIsVisible := False; // shut up
FocusedIsVisible := Assigned(ItemFocused) and IsItemVisible(ItemFocused);
SaveCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
@@ -1446,9 +1459,7 @@ begin
FileTime := SRec.FindData.ftLastWriteTime;
{$WARNINGS ON}
end;
// alternative to TListItem.Update (which causes flicker)
R := Items[iIndex].DisplayRect(drBounds);
InvalidateRect(Handle, @R, True);
InvalidateItem(Items[iIndex]);
AnyUpdate := True;
end;
FItems.Add(Srec.Name);
@@ -2299,7 +2310,6 @@ var
Value: string;
ASize: Int64;
FetchIcon: Boolean;
Schedule: TIconUpdateSchedule;
begin
Assert(Assigned(ListItem) and Assigned(ListItem.Data));
with PFileRec(ListItem.Data)^, DispInfo do
@@ -2320,13 +2330,9 @@ begin
GetDisplayData(ListItem, True);
{Set IconUpdatethread :}
if IconEmpty and UseIconUpdateThread and (not FIsRecycleBin) and
(not FIconUpdateSet.ContainsKey(ListItem.Index)) then
if IconEmpty and UseIconUpdateThread and (not FIsRecycleBin) then
begin
FIconUpdateSet.Add(ListItem.Index, False);
Schedule.Index := ListItem.Index;
FIconUpdateQueue.Enqueue(Schedule);
StartIconUpdateThread;
IconUpdateEnqueue(ListItem);
end;
if (DispInfo.Mask and LVIF_TEXT) <> 0 then
@@ -2381,6 +2387,33 @@ begin
Result := clDefaultItemColor;
end;
function TDirView.ItemThumbnail(Item: TListItem; Size: TSize): TBitmap;
var
ItemData: PFileRec;
begin
if not UseIconUpdateThread then
begin
// not supported without update thread
Result := nil;
end
else
begin
ItemData := PFileRec(Item.Data);
if (not Assigned(ItemData.Thumbnail)) or
(ItemData.ThumbnailSize <> Size) then
begin
FreeAndNil(ItemData.Thumbnail);
ItemData.ThumbnailSize := Size;
Result := nil;
IconUpdateEnqueue(Item);
end
else
begin
Result := ItemData.Thumbnail;
end;
end;
end;
procedure TDirView.StartFileDeleteThread;
var
Files: TStringList;
@@ -2394,6 +2427,20 @@ begin
end;
end;
procedure TDirView.IconUpdateEnqueue(ListItem: TListItem);
var
Schedule: TIconUpdateSchedule;
begin
if not FIconUpdateSet.ContainsKey(ListItem.Index) then
begin
FIconUpdateSet.Add(ListItem.Index, False);
Schedule.Index := ListItem.Index;
FIconUpdateQueue.Enqueue(Schedule);
Assert(FIconUpdateSet.Count = FIconUpdateQueue.Count + FIconUpdateQueueDeferred.Count);
end;
StartIconUpdateThread;
end;
function TDirView.IconUpdatePeek: Integer;
begin
if FIconUpdateQueue.Count > 0 then
@@ -2411,12 +2458,19 @@ begin
else
FIconUpdateQueueDeferred.Dequeue;
FIconUpdateSet.Remove(FIconUpdateThread.FCurrentIndex);
Assert(FIconUpdateSet.Count = FIconUpdateQueue.Count + FIconUpdateQueueDeferred.Count);
end;
function TDirView.ThumbnailNeeded(ItemData: PFileRec): Boolean;
begin
Result := (not Assigned(ItemData.Thumbnail)) and (ItemData.ThumbnailSize <> ThumbnailNotNeeded);
end;
procedure TDirView.DoFetchIconUpdate;
var
Item: TListItem;
ItemData: PFileRec;
Invisible: Boolean;
begin
FIconUpdateThread.FCurrentIndex := IconUpdatePeek;
if FIconUpdateThread.FCurrentIndex < 0 then
@@ -2432,27 +2486,50 @@ begin
IconUpdateDequeue;
end
else
// Deprioritizing unvisible item taken from priority queue
if (FIconUpdateQueue.Count > 0) and
IsVista and (ListView_IsItemVisible(Handle, FIconUpdateThread.FCurrentIndex) = 0) then
begin
FIconUpdateQueueDeferred.Enqueue(FIconUpdateQueue.Dequeue);
end
else
begin
Item := Items[FIconUpdateThread.FCurrentIndex];
Assert(Assigned(Item));
Assert(Assigned(Item.Data));
ItemData := PFileRec(Item.Data);
if not ItemData.IconEmpty then
Invisible := IsVista and (not IsItemVisible(Item));
if (Invisible or (not FThumbnail)) and
(not Assigned(ItemData.Thumbnail)) then
begin
ItemData.ThumbnailSize := ThumbnailNotNeeded; // not needed anymore
end;
// Deprioritizing unvisible item taken from priority queue.
// As we ask Window to cache the image index (LVIF_DI_SETITEM), we won't get asked again,
// so we have to retrieve the image eventually (but not thumbnail)
if (FIconUpdateQueue.Count > 0) and
Invisible then
begin
Assert(not ThumbnailNeeded(ItemData));
if ItemData.IconEmpty then
begin
FIconUpdateQueueDeferred.Enqueue(FIconUpdateQueue.Dequeue);
end
else
begin
IconUpdateDequeue;
end;
FIconUpdateThread.FCurrentIndex := -1;
IconUpdateDequeue;
end
else
begin
FIconUpdateThread.FCurrentFilePath := ItemFullFileName(Item);
FIconUpdateThread.FCurrentItemData := ItemData^;
if ItemData.IconEmpty or
ThumbnailNeeded(ItemData) then
begin
FIconUpdateThread.FCurrentFilePath := ItemFullFileName(Item);
FIconUpdateThread.FCurrentItemData := ItemData^;
end
else
begin
IconUpdateDequeue;
FIconUpdateThread.FCurrentIndex := -1;
end;
end;
end;
end;
@@ -2498,6 +2575,16 @@ begin
end;
end;
ItemData^.IconEmpty := False;
if Assigned(FIconUpdateThread.FSyncThumbnail) then
begin
ItemData^.Thumbnail := FIconUpdateThread.FSyncThumbnail;
FIconUpdateThread.FSyncThumbnail := nil;
// It can happen that this is called while the very item is being painted,
// (particularly ImageFactory.GetImage pumps the queue).
// Calling InvalidateRect directly would get ignored then.
PostMessage(WindowHandle, WM_USER_INVALIDATEITEM, Item.Index, 0);
end;
end;
end;
end;
@@ -2801,6 +2888,7 @@ begin
SetLength(TypeName, 0);
SetLength(DisplayName, 0);
if Assigned(PIDL) then FreePIDL(PIDL);
FreeAndNil(Thumbnail);
Dispose(PFileRec(Item.Data));
Item.Data := nil;
end;

View File

@@ -147,7 +147,6 @@ type
property ShowHint;
property TabOrder;
property TabStop default True;
property ViewStyle;
property Visible;
property OnChange;
property OnChanging;

View File

@@ -64,6 +64,7 @@ type
TImageListSize = (ilsSmall, ilsLarge);
procedure NeedShellImageLists;
function ShellImageListForSize(Width: Integer): TImageList;
function ShellImageListForControl(Control: TControl; Size: TImageListSize): TImageList;
function ControlHasRecreationPersistenceData(Control: TControl): Boolean;
@@ -504,6 +505,7 @@ end;
var
ShellImageLists: TDictionary<Integer, TImageList> = nil;
// This should be replaced with IShellItemImageFactory, as already used for thumbnails
procedure InitializeShellImageLists;
type
TSHGetImageList = function (iImageList: integer; const riid: TGUID; var ppv: Pointer): hResult; stdcall;
@@ -551,23 +553,15 @@ begin
end;
end;
function ShellImageListForControl(Control: TControl; Size: TImageListSize): TImageList;
function ShellImageListForSize(Width: Integer): TImageList;
var
ImageListPair: TPair<Integer, TImageList>;
Width, ImageListWidth: Integer;
ImageListWidth: Integer;
Diff, BestDiff: Integer;
begin
// Delay load image lists, not to waste resources in console/scripting mode
NeedShellImageLists;
case Size of
ilsSmall: Width := 16;
ilsLarge: Width := 32;
else Width := 0; Assert(False);
end;
Width := ScaleByPixelsPerInch(Width, Control);
Result := nil;
BestDiff := -1;
for ImageListPair in ShellImageLists do
@@ -580,6 +574,7 @@ begin
else
begin
// Prefer smaller images over larger, so for 150%, we use 100% images, not 200%
// (a larger icon would make the item row higher)
Diff := ImageListWidth - Width + 1;
end;
@@ -591,6 +586,22 @@ begin
end;
end;
function ShellImageListForControl(Control: TControl; Size: TImageListSize): TImageList;
var
Width: Integer;
begin
case Size of
ilsSmall: Width := 16;
ilsLarge: Width := 32;
else Width := 0; Assert(False);
end;
Width := ScaleByPixelsPerInch(Width, Control);
Result := ShellImageListForSize(Width);
end;
type
TListViewHelper = class helper for TCustomListView
public

View File

@@ -18,6 +18,7 @@
// WM_USER_SHCHANGENOTIFY + 13 (packages/filemng/DriveView.pas)
// WM_PASTE_FILES + 14 (forms/CustomScpExplorer.cpp)
#define WM_IS_HIDDEN (WM_WINSCP_USER + 15)
// WM_USER_INVALIDATEITEM + 16 (packages/filemng/DirView.pas)
//---------------------------------------------------------------------------
#define C(Property) (Property != rhc.Property) ||
struct TSynchronizeChecklistConfiguration

View File

@@ -723,7 +723,7 @@ void __fastcall TWinConfiguration::Default()
FScpExplorer.SessionsTabs = true;
FScpExplorer.StatusBar = true;
FScpExplorer.LastLocalTargetDirectory = GetPersonalFolder();
FScpExplorer.ViewStyle = 0; /* vsIcon */
FScpExplorer.ViewStyle = dvsIcon;
FScpExplorer.ShowFullAddress = true;
FScpExplorer.DriveView = true;
FScpExplorer.DriveViewWidth = 180;
@@ -771,6 +771,7 @@ void __fastcall TWinConfiguration::Default()
FScpCommander.ExplorerKeyboardShortcuts = false;
FScpCommander.SystemContextMenu = false;
FScpCommander.RemotePanel.DirViewParams = ScpCommanderRemotePanelDirViewParamsDefault;
FScpCommander.RemotePanel.ViewStyle = dvsReport;
FScpCommander.RemotePanel.StatusBar = true;
FScpCommander.RemotePanel.DriveView = false;
FScpCommander.RemotePanel.DriveViewHeight = 100;
@@ -779,6 +780,7 @@ void __fastcall TWinConfiguration::Default()
FScpCommander.RemotePanel.DriveViewWidthPixelsPerInch = USER_DEFAULT_SCREEN_DPI;
FScpCommander.RemotePanel.LastPath = UnicodeString();
FScpCommander.LocalPanel.DirViewParams = ScpCommanderLocalPanelDirViewParamsDefault;
FScpCommander.LocalPanel.ViewStyle = dvsReport;
FScpCommander.LocalPanel.StatusBar = true;
FScpCommander.LocalPanel.DriveView = false;
FScpCommander.LocalPanel.DriveViewHeight = 100;
@@ -787,6 +789,7 @@ void __fastcall TWinConfiguration::Default()
FScpCommander.LocalPanel.DriveViewWidthPixelsPerInch = USER_DEFAULT_SCREEN_DPI;
FScpCommander.LocalPanel.LastPath = UnicodeString();
FScpCommander.OtherLocalPanelDirViewParams = FScpCommander.LocalPanel.DirViewParams;
FScpCommander.OtherLocalPanelViewStyle = FScpCommander.LocalPanel.ViewStyle;
FScpCommander.OtherLocalPanelLastPath = UnicodeString();
FBookmarks->Clear();
@@ -1229,6 +1232,7 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool & SessionList)
); \
BLOCK(L"Interface\\Commander\\LocalPanel", CANCREATE, \
KEY(String, ScpCommander.LocalPanel.DirViewParams); \
KEY(Integer, ScpCommander.LocalPanel.ViewStyle); \
KEY(Bool, ScpCommander.LocalPanel.StatusBar); \
KEY(Bool, ScpCommander.LocalPanel.DriveView); \
KEY(Integer, ScpCommander.LocalPanel.DriveViewHeight); \
@@ -1239,6 +1243,7 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool & SessionList)
); \
BLOCK(L"Interface\\Commander\\RemotePanel", CANCREATE, \
KEY(String, ScpCommander.RemotePanel.DirViewParams); \
KEY(Integer, ScpCommander.RemotePanel.ViewStyle); \
KEY(Bool, ScpCommander.RemotePanel.StatusBar); \
KEY(Bool, ScpCommander.RemotePanel.DriveView); \
KEY(Integer, ScpCommander.RemotePanel.DriveViewHeight); \
@@ -1248,8 +1253,9 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool & SessionList)
KEY(String, ScpCommander.RemotePanel.LastPath); \
); \
BLOCK(L"Interface\\Commander\\OtherLocalPanel", CANCREATE, \
KEYEX(String, ScpCommander.OtherLocalPanelDirViewParams, L"DirViewParams"); \
KEYEX(String, ScpCommander.OtherLocalPanelLastPath, L"LastPath"); \
KEYEX(String, ScpCommander.OtherLocalPanelDirViewParams, L"DirViewParams"); \
KEYEX(Integer, ScpCommander.OtherLocalPanelViewStyle, L"ViewStyle"); \
KEYEX(String, ScpCommander.OtherLocalPanelLastPath, L"LastPath"); \
); \
BLOCK(L"Security", CANCREATE, \
KEY(Bool, FUseMasterPassword); \

View File

@@ -35,6 +35,7 @@ struct TScpExplorerConfiguration {
//---------------------------------------------------------------------------
struct TScpCommanderPanelConfiguration {
UnicodeString DirViewParams;
int ViewStyle;
bool StatusBar;
bool DriveView;
int DriveViewHeight;
@@ -43,7 +44,7 @@ struct TScpCommanderPanelConfiguration {
int DriveViewWidthPixelsPerInch;
UnicodeString LastPath;
bool __fastcall operator !=(TScpCommanderPanelConfiguration & rhc)
{ return C(DirViewParams) C(StatusBar)
{ return C(DirViewParams) C(ViewStyle) C(StatusBar)
C(DriveView) C(DriveViewHeight) C(DriveViewHeightPixelsPerInch)
C(DriveViewWidth) C(DriveViewWidthPixelsPerInch) C(LastPath) 0; };
};
@@ -67,6 +68,7 @@ struct TScpCommanderConfiguration {
bool ExplorerKeyboardShortcuts;
bool SystemContextMenu;
UnicodeString OtherLocalPanelDirViewParams;
int OtherLocalPanelViewStyle;
UnicodeString OtherLocalPanelLastPath;
bool __fastcall operator !=(TScpCommanderConfiguration & rhc)
{ return C(WindowParams) C(LocalPanelWidth) C(ToolbarsLayout) C(ToolbarsButtons)
@@ -75,7 +77,7 @@ struct TScpCommanderConfiguration {
C(NortonLikeMode) C(PreserveLocalDirectory)
C(CompareBySize) C(CompareByTime) C(SwappedPanels)
C(TreeOnLeft) C(ExplorerKeyboardShortcuts) C(SystemContextMenu)
C(OtherLocalPanelDirViewParams) C(OtherLocalPanelLastPath) 0; };
C(OtherLocalPanelDirViewParams) C(OtherLocalPanelViewStyle) C(OtherLocalPanelLastPath) 0; };
TCompareCriterias __fastcall CompareCriterias()
{