mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-28 10:02:14 +00:00
4243 lines
118 KiB
ObjectPascal
4243 lines
118 KiB
ObjectPascal
unit uColumnsFileView;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Graphics, Controls, Forms, StdCtrls, ExtCtrls, Grids,
|
|
LMessages, LCLIntf, LCLType, Menus,
|
|
uDragDropEx,
|
|
uPathLabel,
|
|
uFile,
|
|
uFileProperty,
|
|
uFileView,
|
|
uFileSource,
|
|
uDisplayFile,
|
|
uColumns,
|
|
uFileSorting,
|
|
uXmlConfig,
|
|
uClassesEx,
|
|
uTypes,
|
|
uFileViewWorker
|
|
;
|
|
|
|
//{$DEFINE timeFileView}
|
|
|
|
type
|
|
|
|
{ Columns sorting }
|
|
|
|
PColumnsSorting = ^TColumnsSorting;
|
|
TColumnsSorting = record
|
|
Column : Integer;
|
|
SortDirection : TSortDirection;
|
|
end;
|
|
|
|
PFileListSorting = ^TColumnsSortings;
|
|
TColumnsSortings = class(TList)
|
|
public
|
|
Destructor Destroy; override;
|
|
function Clone: TColumnsSortings;
|
|
procedure AddSorting(iColumn : Integer; SortDirection : TSortDirection);
|
|
procedure Clear; override;
|
|
function GetSortingDirection(iColumn : Integer) : TSortDirection;
|
|
end;
|
|
|
|
TColumnsFileView = class;
|
|
|
|
{ TDrawGridEx }
|
|
|
|
TDrawGridEx = class(TDrawGrid)
|
|
private
|
|
// Used to register as a drag and drop source and target.
|
|
DragDropSource: uDragDropEx.TDragDropSource;
|
|
DragDropTarget: uDragDropEx.TDragDropTarget;
|
|
|
|
StartDrag: Boolean;
|
|
DragStartPoint: TPoint;
|
|
DragRowIndex,
|
|
DropRowIndex,
|
|
HintRowIndex: Integer;
|
|
LastMouseButton: TMouseButton; // Mouse button that initiated dragging
|
|
SelectionStartIndex: Cardinal;
|
|
FMouseDown: Boolean; // Used to check if button-up was received after button-down
|
|
// or after dropping something after dragging with right mouse button
|
|
|
|
ColumnsView: TColumnsFileView;
|
|
|
|
// Updates the drop row index, which is used to draw a rectangle
|
|
// on directories during drag&drop operations.
|
|
procedure ChangeDropRowIndex(NewIndex: Integer);
|
|
|
|
// Simulates releasing mouse button that started a dragging operation,
|
|
// but was released in another window or another application.
|
|
procedure ClearMouseButtonAfterDrag;
|
|
|
|
// If internal dragging is currently in effect, this function
|
|
// stops internal dragging and starts external.
|
|
procedure TransformDraggingToExternal(ScreenPoint: TPoint);
|
|
|
|
{ Events for drag&drop from external applications }
|
|
function OnExDragBegin: Boolean;
|
|
function OnExDragEnd : Boolean;
|
|
function OnExDragEnter(var DropEffect: TDropEffect; ScreenPoint: TPoint):Boolean;
|
|
function OnExDragOver(var DropEffect: TDropEffect; ScreenPoint: TPoint):Boolean;
|
|
function OnExDrop(const FileNamesList: TStringList; DropEffect: TDropEffect; ScreenPoint: TPoint):Boolean;
|
|
function OnExDragLeave:Boolean;
|
|
|
|
protected
|
|
|
|
procedure MouseMove(Shift: TShiftState; X,Y: Integer); override;
|
|
procedure MouseUp(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
|
|
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X,Y: Integer); override;
|
|
|
|
procedure InitializeWnd; override;
|
|
procedure FinalizeWnd; override;
|
|
|
|
procedure DrawCell(aCol, aRow: Integer; aRect: TRect;
|
|
aState: TGridDrawState); override;
|
|
|
|
public
|
|
{$IFDEF LCLGTK2}
|
|
fLastDoubleClickTime : TDateTime;
|
|
|
|
function TooManyDoubleClicks: Boolean;
|
|
{$ENDIF}
|
|
constructor Create(AOwner: TComponent; AParent: TWinControl); reintroduce;
|
|
|
|
procedure UpdateView;
|
|
|
|
function MouseOnGrid(X, Y: LongInt): Boolean;
|
|
|
|
// Returns height of all the header rows.
|
|
function GetHeaderHeight: Integer;
|
|
|
|
// Adapted from TCustomGrid.GetVisibleGrid only for visible rows.
|
|
function GetVisibleRows: TRange;
|
|
end;
|
|
|
|
{ TColumnsFileView }
|
|
|
|
TColumnsFileView = class(TFileView)
|
|
|
|
private
|
|
FColumnsSorting: TColumnsSortings;
|
|
FSelection: TStringListEx;
|
|
FLastActiveRow: Integer; //<en Last active row
|
|
FUpdatingGrid: Boolean;
|
|
FLastMark: String;
|
|
FLastSelectionStartRow: Integer;
|
|
FLastSelectionState: Boolean;
|
|
FSearchDirect,
|
|
FNext,
|
|
FPrevious : Boolean;
|
|
|
|
pnlFooter: TPanel;
|
|
lblInfo: TLabel;
|
|
pnlHeader: TPanel;
|
|
pmColumnsMenu: TPopupMenu;
|
|
pnAltSearch: TPanel;
|
|
pnlFilter: TPanel;
|
|
edtSearch: TEdit;
|
|
edtFilter: TEdit;
|
|
btnCloseFilter: TButton;
|
|
edtPath: TEdit;
|
|
edtRename: TEdit;
|
|
lblPath: TPathLabel;
|
|
lblAddress: TPathLabel;
|
|
dgPanel: TDrawGridEx;
|
|
tmContextMenu: TTimer;
|
|
tmClearGrid: TTimer;
|
|
|
|
function GetGridHorzLine: Boolean;
|
|
function GetGridVertLine: Boolean;
|
|
procedure SetGridHorzLine(const AValue: Boolean);
|
|
procedure SetGridVertLine(const AValue: Boolean);
|
|
function GetColumnsClass: TPanelColumnsClass;
|
|
function GetVisibleFilesIndexes: TRange;
|
|
|
|
{en
|
|
Sets last active file by row nr in the grid.
|
|
}
|
|
procedure SetLastActiveFile(RowNr: Integer);
|
|
{en
|
|
Sets a file as active if the file currently exists in the grid.
|
|
@returns(@true if the file was found and selected.)
|
|
}
|
|
function SetActiveFileNow(aFilePath: String): Boolean;
|
|
function StartDragEx(MouseButton: TMouseButton; ScreenStartPoint: TPoint): Boolean;
|
|
|
|
procedure UpdateAddressLabel;
|
|
procedure UpdatePathLabel;
|
|
procedure UpdateInfoPanel;
|
|
procedure UpdateColCount(NewColCount: Integer);
|
|
procedure SetRowCount(Count: Integer);
|
|
procedure SetColumnsWidths;
|
|
procedure RedrawGrid;
|
|
{en
|
|
Redraw row containing DisplayFile if it is visible.
|
|
}
|
|
procedure RedrawFile(DisplayFile: TDisplayFile);
|
|
|
|
procedure MakeVisible(iRow: Integer);
|
|
procedure MakeSelectedVisible;
|
|
procedure SelectFile(AFile: TDisplayFile);
|
|
procedure SelectRange(iRow: PtrInt);
|
|
procedure MarkFile(AFile: TDisplayFile; bMarked: Boolean);
|
|
procedure MarkAllFiles(bMarked: Boolean);
|
|
procedure InvertFileSelection(AFile: TDisplayFile);
|
|
procedure MarkGroup(const sMask: String; bSelect: Boolean);
|
|
procedure InvertAll;
|
|
procedure MarkAll;
|
|
procedure UnMarkAll;
|
|
procedure MarkMinus;
|
|
procedure MarkPlus;
|
|
procedure MarkShiftPlus;
|
|
procedure MarkShiftMinus;
|
|
procedure SaveSelection;
|
|
procedure RestoreSelection;
|
|
|
|
{en
|
|
Updates GUI after the display file list has changed.
|
|
}
|
|
procedure DisplayFileListHasChanged;
|
|
{en
|
|
Format and cache all columns strings.
|
|
}
|
|
procedure MakeColumnsStrings;
|
|
procedure MakeColumnsStrings(AFile: TDisplayFile);
|
|
procedure EnsureDisplayProperties;
|
|
procedure UpdateFile(const UpdatedFile: TDisplayFile;
|
|
const UserData: Pointer);
|
|
procedure CalcSpaceUpdateFile(const UpdatedFile: TDisplayFile;
|
|
const UserData: Pointer);
|
|
|
|
{en
|
|
Prepares sortings for later use in Sort function.
|
|
This function must be called from main thread.
|
|
}
|
|
function PrepareSortings: TFileSortings;
|
|
{en
|
|
Translates file sorting by functions to sorting by columns.
|
|
}
|
|
procedure SetColumnsSorting(const ASortings: TFileSortings);
|
|
|
|
{en
|
|
Checks which file properties are needed for displaying.
|
|
}
|
|
function GetFilePropertiesNeeded: TFilePropertiesTypes;
|
|
|
|
procedure ShowRenameFileEdit(const sFileName:String);
|
|
procedure ShowPathEdit;
|
|
procedure ShowSearchPanel(Char : TUTF8Char = #0);
|
|
procedure CloseSearchPanel;
|
|
procedure ShowFilterPanel(Char : TUTF8Char = #0);
|
|
procedure FilterPanelVisible;
|
|
procedure CloseFilterPanel;
|
|
|
|
procedure CalculateSpaceOfAllDirectories;
|
|
procedure CalculateSpace(AFile: TDisplayFile);
|
|
procedure CalculateSpace(var AFileList: TFVWorkerFileList);
|
|
|
|
function DimColor(AColor: TColor): TColor;
|
|
|
|
// -- Events --------------------------------------------------------------
|
|
|
|
procedure edtPathExit(Sender: TObject);
|
|
procedure edtSearchExit(Sender: TObject);
|
|
procedure edtRenameExit(Sender: TObject);
|
|
procedure edtFilterEnter(Sender: TObject);
|
|
procedure edtFilterExit(Sender: TObject);
|
|
|
|
procedure edtSearchChange(Sender: TObject);
|
|
procedure edtSearchKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
|
procedure edtPathKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
|
procedure edtRenameKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
|
procedure edtFilterChange(Sender: TObject);
|
|
procedure edtFilterKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
|
|
|
procedure btnCloseFilterClick(Sender: TObject);
|
|
|
|
procedure dgPanelEnter(Sender: TObject);
|
|
procedure dgPanelExit(Sender: TObject);
|
|
procedure dgPanelDblClick(Sender: TObject);
|
|
procedure dgPanelKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
procedure dgPanelKeyUp(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
procedure dgPanelMouseLeave(Sender: TObject);
|
|
procedure dgPanelMouseDown(Sender: TObject; Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
procedure dgPanelMouseMove(Sender: TObject; Shift: TShiftState;
|
|
X, Y: Integer);
|
|
procedure dgPanelMouseUp(Sender: TObject; Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
procedure dgPanelStartDrag(Sender: TObject; var DragObject: TDragObject);
|
|
procedure dgPanelDragOver(Sender, Source: TObject; X, Y: Integer;
|
|
State: TDragState; var Accept: Boolean);
|
|
procedure dgPanelDragDrop(Sender, Source: TObject; X, Y: Integer);
|
|
procedure dgPanelEndDrag(Sender, Target: TObject; X, Y: Integer);
|
|
procedure dgPanelHeaderClick(Sender: TObject;IsColumn: Boolean; index: Integer);
|
|
procedure dgPanelMouseWheelUp(Sender: TObject; Shift: TShiftState;
|
|
MousePos: TPoint; var Handled: Boolean);
|
|
procedure dgPanelMouseWheelDown(Sender: TObject; Shift: TShiftState;
|
|
MousePos: TPoint; var Handled: Boolean);
|
|
procedure dgPanelSelection(Sender: TObject; aCol, aRow: Integer);
|
|
procedure dgPanelShowHint(Sender: TObject; HintInfo: PHintInfo);
|
|
procedure dgPanelTopLeftChanged(Sender: TObject);
|
|
procedure dgPanelResize(Sender: TObject);
|
|
procedure tmContextMenuTimer(Sender: TObject);
|
|
procedure tmClearGridTimer(Sender: TObject);
|
|
procedure lblPathClick(Sender: TObject);
|
|
procedure lblPathMouseUp(Sender: TObject; Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
procedure pnlHeaderResize(Sender: TObject);
|
|
procedure ColumnsMenuClick(Sender: TObject);
|
|
|
|
procedure UTF8KeyPressEvent(Sender: TObject; var UTF8Key: TUTF8Char);
|
|
|
|
protected
|
|
procedure CreateDefault(AOwner: TWinControl); override;
|
|
|
|
procedure BeforeMakeFileList; override;
|
|
procedure AfterMakeFileList; override;
|
|
{en
|
|
Changes drawing colors depending on if this panel is active.
|
|
}
|
|
procedure SetActive(bActive: Boolean); override;
|
|
procedure SetSorting(const NewSortings: TFileSortings); override;
|
|
|
|
procedure AfterChangePath; override;
|
|
function GetActiveDisplayFile: TDisplayFile; override;
|
|
|
|
procedure WorkerStarting(const Worker: TFileViewWorker); override;
|
|
procedure WorkerFinished(const Worker: TFileViewWorker); override;
|
|
|
|
public
|
|
ActiveColm: String;
|
|
ActiveColmSlave: TPanelColumnsClass;
|
|
isSlave:boolean;
|
|
//---------------------
|
|
|
|
constructor Create(AOwner: TWinControl; AFileSource: IFileSource; APath: String); override;
|
|
constructor Create(AOwner: TWinControl; AFileView: TFileView); override;
|
|
constructor Create(AOwner: TWinControl; AConfig: TIniFileEx; ASectionName: String; ATabIndex: Integer); override;
|
|
constructor Create(AOwner: TWinControl; AConfig: TXmlConfig; ANode: TXmlNode); override;
|
|
|
|
destructor Destroy; override;
|
|
|
|
function Clone(NewParent: TWinControl): TColumnsFileView; override;
|
|
procedure CloneTo(FileView: TFileView); override;
|
|
|
|
procedure AddFileSource(aFileSource: IFileSource; aPath: String); override;
|
|
procedure RemoveCurrentFileSource; override;
|
|
|
|
procedure LoadConfiguration(Section: String; TabIndex: Integer); override;
|
|
procedure SaveConfiguration(Section: String; TabIndex: Integer); override;
|
|
procedure LoadConfiguration(AConfig: TXmlConfig; ANode: TXmlNode); override;
|
|
procedure SaveConfiguration(AConfig: TXmlConfig; ANode: TXmlNode); override;
|
|
|
|
function Focused: Boolean; override;
|
|
procedure SetFocus; override;
|
|
|
|
procedure SetActiveFile(aFilePath: String); override;
|
|
|
|
procedure UpdateColumnsView;
|
|
procedure UpdateView; override;
|
|
|
|
procedure DoDragDropOperation(Operation: TDragDropOperation;
|
|
var DropParams: TDropParams); override;
|
|
|
|
property GridVertLine: Boolean read GetGridVertLine write SetGridVertLine;
|
|
property GridHorzLine: Boolean read GetGridHorzLine write SetGridHorzLine;
|
|
|
|
published // commands
|
|
procedure cm_MarkInvert(param: string='');
|
|
procedure cm_MarkMarkAll(param: string='');
|
|
procedure cm_MarkUnmarkAll(param: string='');
|
|
procedure cm_MarkPlus(param: string='');
|
|
procedure cm_MarkMinus(param: string='');
|
|
procedure cm_MarkCurrentExtension(param: string='');
|
|
procedure cm_UnmarkCurrentExtension(param: string='');
|
|
procedure cm_SaveSelection(param: string='');
|
|
procedure cm_RestoreSelection(param: string='');
|
|
procedure cm_SaveSelectionToFile(param: string='');
|
|
procedure cm_LoadSelectionFromFile(param: string='');
|
|
procedure cm_LoadSelectionFromClip(param: string='');
|
|
procedure cm_QuickSearch(param: string='');
|
|
procedure cm_QuickFilter(param: string='');
|
|
procedure cm_Open(param: string='');
|
|
procedure cm_CountDirContent(param: string='');
|
|
procedure cm_RenameOnly(param: string='');
|
|
procedure cm_ContextMenu(param: string='');
|
|
procedure cm_EditPath(param: string='');
|
|
procedure cm_GoToFirstFile(param: string='');
|
|
procedure cm_GoToLastFile(param: string='');
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
LCLProc, uMasks, Clipbrd, uLng, uShowMsg, uGlobs, uPixmapManager,
|
|
uDCUtils, uOSUtils, math, fMain, fMaskInputDlg, uSearchTemplate,
|
|
uInfoToolTip, dmCommonData,
|
|
uFileSourceProperty,
|
|
uFileSourceOperationTypes,
|
|
uFileSystemFileSource,
|
|
fColumnsSetConf,
|
|
uKeyboard,
|
|
uFileSourceUtil,
|
|
uFileFunctions
|
|
{$IF DEFINED(LCLGTK)}
|
|
, GtkProc // for ReleaseMouseCapture
|
|
, GTKGlobals // for DblClickTime
|
|
{$ENDIF}
|
|
{$IF DEFINED(LCLGTK2)}
|
|
, Gtk2Proc // for ReleaseMouseCapture
|
|
, GTK2Globals // for DblClickTime
|
|
{$ENDIF}
|
|
;
|
|
|
|
{$IFDEF timeFileView}
|
|
var
|
|
startTime: TDateTime;
|
|
{$ENDIF}
|
|
|
|
function TColumnsFileView.Focused: Boolean;
|
|
begin
|
|
Result := Assigned(dgPanel) and dgPanel.Focused;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetFocus;
|
|
begin
|
|
// CanFocus checks parent controls, but not parent form.
|
|
if GetParentForm(Self).CanFocus and dgPanel.CanFocus then
|
|
dgPanel.SetFocus;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetActive(bActive: Boolean);
|
|
begin
|
|
inherited SetActive(bActive);
|
|
|
|
lblAddress.SetActive(bActive);
|
|
lblPath.SetActive(bActive);
|
|
|
|
dgPanel.Color := DimColor(gBackColor);
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetSorting(const NewSortings: TFileSortings);
|
|
begin
|
|
SetColumnsSorting(NewSortings);
|
|
inherited SetSorting(PrepareSortings); // NewSortings
|
|
TFileSorter.Sort(FFileSourceFiles, Sorting);
|
|
ReDisplayFileList;
|
|
end;
|
|
|
|
procedure TColumnsFileView.LoadConfiguration(Section: String; TabIndex: Integer);
|
|
var
|
|
ColumnsClass: TPanelColumnsClass;
|
|
SortCount: Integer;
|
|
SortColumn: Integer;
|
|
SortDirection: TSortDirection;
|
|
i: Integer;
|
|
sIndex: String;
|
|
begin
|
|
sIndex := IntToStr(TabIndex);
|
|
|
|
ActiveColm := gIni.ReadString(Section, sIndex + '_columnsset', 'Default');
|
|
|
|
// Load sorting options.
|
|
FColumnsSorting.Clear;
|
|
ColumnsClass := GetColumnsClass;
|
|
SortCount := gIni.ReadInteger(Section, sIndex + '_sortcount', 0);
|
|
for i := 0 to SortCount - 1 do
|
|
begin
|
|
SortColumn := gIni.ReadInteger(Section, sIndex + '_sortcolumn' + IntToStr(i), -1);
|
|
if (SortColumn >= 0) and (SortColumn < ColumnsClass.ColumnsCount) then
|
|
begin
|
|
SortDirection := TSortDirection(gIni.ReadInteger(Section, sIndex + '_sortdirection' + IntToStr(i), Integer(sdNone)));
|
|
FColumnsSorting.AddSorting(SortColumn, SortDirection);
|
|
end;
|
|
end;
|
|
inherited SetSorting(PrepareSortings);
|
|
end;
|
|
|
|
procedure TColumnsFileView.SaveConfiguration(Section: String; TabIndex: Integer);
|
|
var
|
|
SortingColumn: PColumnsSorting;
|
|
sIndex: String;
|
|
i: Integer;
|
|
begin
|
|
sIndex := IntToStr(TabIndex);
|
|
|
|
gIni.WriteString(Section, sIndex + '_columnsset', ActiveColm);
|
|
|
|
// Save sorting options.
|
|
gIni.WriteInteger(Section, sIndex + '_sortcount', FColumnsSorting.Count);
|
|
for i := 0 to FColumnsSorting.Count - 1 do
|
|
begin
|
|
SortingColumn := PColumnsSorting(FColumnsSorting.Items[i]);
|
|
|
|
gIni.WriteInteger(Section, sIndex + '_sortcolumn' + IntToStr(i),
|
|
SortingColumn^.Column);
|
|
gIni.WriteInteger(Section, sIndex + '_sortdirection' + IntToStr(i),
|
|
Integer(SortingColumn^.SortDirection));
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.LoadConfiguration(AConfig: TXmlConfig; ANode: TXmlNode);
|
|
var
|
|
ColumnsClass: TPanelColumnsClass;
|
|
SortColumn: Integer;
|
|
SortDirection: TSortDirection;
|
|
ColumnsViewNode: TXmlNode;
|
|
begin
|
|
inherited LoadConfiguration(AConfig, ANode);
|
|
|
|
// Try to read new view-specific node.
|
|
ColumnsViewNode := AConfig.FindNode(ANode, 'ColumnsView');
|
|
if Assigned(ColumnsViewNode) then
|
|
ANode := ColumnsViewNode;
|
|
|
|
ActiveColm := AConfig.GetValue(ANode, 'ColumnsSet', 'Default');
|
|
|
|
// Load sorting options.
|
|
FColumnsSorting.Clear;
|
|
ColumnsClass := GetColumnsClass;
|
|
ANode := ANode.FindNode('Sorting');
|
|
if Assigned(ANode) then
|
|
begin
|
|
ANode := ANode.FirstChild;
|
|
while Assigned(ANode) do
|
|
begin
|
|
if ANode.CompareName('Sort') = 0 then
|
|
begin
|
|
if AConfig.TryGetValue(ANode, 'Column', SortColumn) and
|
|
(SortColumn >= 0) and (SortColumn < ColumnsClass.ColumnsCount) then
|
|
begin
|
|
SortDirection := TSortDirection(AConfig.GetValue(ANode, 'Direction', Integer(sdNone)));
|
|
FColumnsSorting.AddSorting(SortColumn, SortDirection);
|
|
end
|
|
else
|
|
DebugLn('Invalid entry in configuration: ' + AConfig.GetPathFromNode(ANode) + '.');
|
|
end;
|
|
ANode := ANode.NextSibling;
|
|
end;
|
|
inherited SetSorting(PrepareSortings);
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SaveConfiguration(AConfig: TXmlConfig; ANode: TXmlNode);
|
|
var
|
|
SortingColumn: PColumnsSorting;
|
|
i: Integer;
|
|
SubNode: TXmlNode;
|
|
begin
|
|
inherited SaveConfiguration(AConfig, ANode);
|
|
|
|
AConfig.SetAttr(ANode, 'Type', 'columns');
|
|
ANode := AConfig.FindNode(ANode, 'ColumnsView', True);
|
|
AConfig.ClearNode(ANode);
|
|
|
|
AConfig.SetValue(ANode, 'ColumnsSet', ActiveColm);
|
|
ANode := AConfig.FindNode(ANode, 'Sorting', True);
|
|
|
|
// Save sorting options.
|
|
for i := 0 to FColumnsSorting.Count - 1 do
|
|
begin
|
|
SortingColumn := PColumnsSorting(FColumnsSorting.Items[i]);
|
|
SubNode := AConfig.AddNode(ANode, 'Sort');
|
|
AConfig.AddValue(SubNode, 'Column', SortingColumn^.Column);
|
|
AConfig.AddValue(SubNode, 'Direction', Integer(SortingColumn^.SortDirection));
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SelectFile(AFile: TDisplayFile);
|
|
begin
|
|
InvertFileSelection(AFile);
|
|
UpdateInfoPanel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MarkFile(AFile: TDisplayFile; bMarked: Boolean);
|
|
begin
|
|
if IsItemValid(AFile) then
|
|
AFile.Selected := bMarked;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MarkAllFiles(bMarked: Boolean);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to FFiles.Count - 1 do
|
|
MarkFile(FFiles[i], bMarked);
|
|
end;
|
|
|
|
procedure TColumnsFileView.InvertFileSelection(AFile: TDisplayFile);
|
|
begin
|
|
if Assigned(AFile) then
|
|
MarkFile(AFile, not AFile.Selected);
|
|
end;
|
|
|
|
procedure TColumnsFileView.InvertAll;
|
|
var
|
|
i:Integer;
|
|
begin
|
|
for i := 0 to FFiles.Count-1 do
|
|
InvertFileSelection(FFiles[i]);
|
|
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
|
|
function TColumnsFileView.StartDragEx(MouseButton: TMouseButton; ScreenStartPoint: TPoint): Boolean;
|
|
var
|
|
fileNamesList: TStringList;
|
|
draggedFileItem: TDisplayFile;
|
|
i: Integer;
|
|
begin
|
|
Result := False;
|
|
|
|
if Assigned(dgPanel.DragDropSource) and (dgPanel.DragRowIndex >= dgPanel.FixedRows) then
|
|
begin
|
|
draggedFileItem := FFiles[dgPanel.DragRowIndex - dgPanel.FixedRows]; // substract fixed rows (header)
|
|
|
|
fileNamesList := TStringList.Create;
|
|
try
|
|
if IsItemValid(draggedFileItem) = True then
|
|
begin
|
|
for i := 0 to FFiles.Count-1 do
|
|
begin
|
|
if FFiles[i].Selected then
|
|
fileNamesList.Add(FFiles[i].FSFile.FullPath);
|
|
end;
|
|
|
|
// If there were no files selected add the dragged file.
|
|
if fileNamesList.Count = 0 then
|
|
fileNamesList.Add(draggedFileItem.FSFile.FullPath);
|
|
|
|
// Initiate external drag&drop operation.
|
|
Result := dgPanel.DragDropSource.DoDragDrop(fileNamesList, MouseButton, ScreenStartPoint);
|
|
|
|
// Refresh source file panel after drop to (possibly) another application
|
|
// (files could have been moved for example).
|
|
// 'draggedFileItem' is invalid after this.
|
|
Reload;
|
|
end;
|
|
|
|
finally
|
|
FreeAndNil(fileNamesList);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SelectRange(iRow: PtrInt);
|
|
var
|
|
ARow, AFromRow, AToRow: Integer;
|
|
AFile: TDisplayFile;
|
|
begin
|
|
if iRow < 0 then
|
|
iRow:= dgPanel.Row;
|
|
|
|
if(FLastSelectionStartRow < 0) then
|
|
begin
|
|
AFromRow := Min(dgPanel.Row, iRow) - dgPanel.FixedRows;
|
|
AToRow := Max(dgPanel.Row, iRow) - dgPanel.FixedRows;
|
|
FLastSelectionStartRow := dgPanel.Row;
|
|
end
|
|
else
|
|
begin
|
|
AFromRow := Min(FLastSelectionStartRow, iRow) - dgPanel.FixedRows; // substract fixed rows (header)
|
|
AToRow := Max(FLastSelectionStartRow, iRow) - dgPanel.FixedRows;
|
|
end;
|
|
|
|
MarkAllFiles(False);
|
|
for ARow := AFromRow to AToRow do
|
|
begin
|
|
AFile := FFiles[ARow];
|
|
MarkFile(AFile, True);
|
|
end;
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelMouseDown(Sender: TObject;
|
|
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
iRow, iCol : Integer;
|
|
AFile: TDisplayFile;
|
|
begin
|
|
if (Y < dgPanel.GetHeaderHeight) then Exit; // if is header
|
|
|
|
SetFocus;
|
|
|
|
// history navigation for mice with extra buttons
|
|
case Button of
|
|
mbExtra1:
|
|
begin
|
|
Actions.cm_ViewHistoryPrev();
|
|
Exit;
|
|
end;
|
|
mbExtra2:
|
|
begin
|
|
Actions.cm_ViewHistoryNext();
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
if IsEmpty then Exit;
|
|
|
|
if dgPanel.MouseOnGrid(X, Y) then
|
|
begin
|
|
dgPanel.MouseToCell(X, Y, iCol, iRow);
|
|
|
|
if iRow < dgPanel.FixedRows then // clicked on header
|
|
Exit;
|
|
|
|
dgPanel.LastMouseButton:= Button;
|
|
|
|
case Button of
|
|
mbRight: begin
|
|
dgPanel.Row := iRow;
|
|
|
|
if (gMouseSelectionEnabled) and (gMouseSelectionButton = 1) then
|
|
begin
|
|
AFile := FFiles[iRow - dgPanel.FixedRows]; // substract fixed rows (header)
|
|
|
|
if Assigned(AFile) then
|
|
begin
|
|
dgPanel.SelectionStartIndex:=iRow;
|
|
tmContextMenu.Enabled:= True; // start context menu timer
|
|
FLastSelectionState:= not AFile.Selected;
|
|
MarkFile(AFile, FLastSelectionState);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
mbLeft: begin
|
|
if (dgPanel.Row < 0) or (dgPanel.Row >= dgPanel.RowCount) then
|
|
begin
|
|
dgPanel.Row := iRow;
|
|
end
|
|
else if gMouseSelectionEnabled then
|
|
begin
|
|
if ssCtrl in Shift then
|
|
begin
|
|
// if there is no selected files then select also previous file
|
|
if not HasSelectedFiles then
|
|
begin
|
|
AFile := FFiles[dgPanel.Row - dgPanel.FixedRows]; // substract fixed rows (header)
|
|
if Assigned(AFile) then
|
|
begin
|
|
MarkFile(AFile, True);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
end;
|
|
AFile := FFiles[iRow - dgPanel.FixedRows]; // substract fixed rows (header)
|
|
if Assigned(AFile) then
|
|
begin
|
|
InvertFileSelection(AFile);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
end
|
|
else if ssShift in Shift then
|
|
begin
|
|
SelectRange(iRow);
|
|
end
|
|
else if (gMouseSelectionButton = 0) then
|
|
begin
|
|
AFile := FFiles[iRow - dgPanel.FixedRows]; // substract fixed rows (header)
|
|
if Assigned(AFile) and not AFile.Selected then
|
|
begin
|
|
MarkAllFiles(False);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
end;
|
|
end;//of mouse selection handler
|
|
end;
|
|
else
|
|
dgPanel.Row := iRow;
|
|
Exit;
|
|
end;
|
|
end
|
|
else // if mouse on empty space
|
|
begin
|
|
if (Button = mbRight) and (gMouseSelectionEnabled) and (gMouseSelectionButton = 1) then
|
|
tmContextMenu.Enabled:= True; // start context menu timer
|
|
end;
|
|
|
|
{ Dragging }
|
|
|
|
if (not dgPanel.Dragging) and // we could be in dragging mode already (started by a different button)
|
|
(dgPanel.MouseOnGrid(X, Y)) then // check if there is an item under the mouse cursor
|
|
begin
|
|
// indicate that drag start at next mouse move event
|
|
dgPanel.StartDrag:= True;
|
|
dgPanel.DragStartPoint.X := X;
|
|
dgPanel.DragStartPoint.Y := Y;
|
|
dgPanel.DragRowIndex := iRow;
|
|
uDragDropEx.TransformDragging := False;
|
|
uDragDropEx.AllowTransformToInternal := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelMouseMove(Sender: TObject;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
AFile: TDisplayFile;
|
|
iCol, iRow: Integer;
|
|
i, SelStartIndex, SelEndIndex: Cardinal;
|
|
begin
|
|
// if right mouse button selection enabled
|
|
if dgPanel.FMouseDown and (dgPanel.LastMouseButton = mbRight) and
|
|
gMouseSelectionEnabled and (gMouseSelectionButton = 1) then
|
|
begin
|
|
dgPanel.MouseToCell(X, Y, iCol, iRow);
|
|
if iRow < dgPanel.FixedRows then Exit; // move on header
|
|
if dgPanel.Row <> iRow then // if new row index
|
|
begin
|
|
tmContextMenu.Enabled:= False; // stop context menu timer
|
|
if dgPanel.SelectionStartIndex < iRow then begin
|
|
SelStartIndex := dgPanel.SelectionStartIndex;
|
|
SelEndIndex := iRow;
|
|
end else begin
|
|
SelStartIndex := iRow;
|
|
SelEndIndex := dgPanel.SelectionStartIndex;
|
|
end;
|
|
dgPanel.Row:= iRow;
|
|
for i := SelStartIndex to SelEndIndex do begin
|
|
AFile := FFiles[i - dgPanel.FixedRows]; // substract fixed rows (header)
|
|
if Assigned(AFile) then
|
|
begin
|
|
MarkFile(AFile, FLastSelectionState);
|
|
dgPanel.InvalidateRow(i);
|
|
end;
|
|
end;
|
|
UpdateInfoPanel;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ Show context or columns menu on right click }
|
|
{ Is called manually from TDrawGridEx.MouseUp }
|
|
procedure TColumnsFileView.dgPanelMouseUp(Sender: TObject; Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
I : Integer;
|
|
Point:TPoint;
|
|
MI:TMenuItem;
|
|
Background: Boolean;
|
|
begin
|
|
if Button = mbRight then
|
|
begin
|
|
{ If right click on header }
|
|
if (Y < dgPanel.GetHeaderHeight) then
|
|
begin
|
|
//Load Columns into menu
|
|
pmColumnsMenu.Items.Clear;
|
|
if ColSet.Items.Count>0 then
|
|
begin
|
|
For I:=0 to ColSet.Items.Count-1 do
|
|
begin
|
|
MI:=TMenuItem.Create(pmColumnsMenu);
|
|
MI.Tag:=I;
|
|
MI.Caption:=ColSet.Items[I];
|
|
MI.OnClick:=@ColumnsMenuClick;
|
|
pmColumnsMenu.Items.Add(MI);
|
|
end;
|
|
end;
|
|
|
|
//-
|
|
MI:=TMenuItem.Create(pmColumnsMenu);
|
|
MI.Caption:='-';
|
|
pmColumnsMenu.Items.Add(MI);
|
|
//Configure this custom columns
|
|
MI:=TMenuItem.Create(pmColumnsMenu);
|
|
MI.Tag:=1000;
|
|
MI.Caption:=rsMenuConfigureThisCustomColumn;
|
|
MI.OnClick:=@ColumnsMenuClick;
|
|
pmColumnsMenu.Items.Add(MI);
|
|
//Configure custom columns
|
|
MI:=TMenuItem.Create(pmColumnsMenu);
|
|
MI.Tag:=1001;
|
|
MI.Caption:=rsMenuConfigureCustomColumns;
|
|
MI.OnClick:=@ColumnsMenuClick;
|
|
pmColumnsMenu.Items.Add(MI);
|
|
|
|
Point:=(Sender as TDrawGrid).ClientToScreen(Classes.Point(0,0));
|
|
Point.Y:=Point.Y+(Sender as TDrawGridEx).GetHeaderHeight;
|
|
Point.X:=Point.X+X-50;
|
|
pmColumnsMenu.PopUp(Point.X,Point.Y);
|
|
end
|
|
|
|
{ If right click on file/directory }
|
|
else if ((gMouseSelectionButton<>1) or not gMouseSelectionEnabled) then
|
|
begin
|
|
Background:= not (Sender as TDrawGridEx).MouseOnGrid(X, Y);
|
|
Actions.DoContextMenu(Self, Mouse.CursorPos.x, Mouse.CursorPos.y, Background);
|
|
end
|
|
else if (gMouseSelectionEnabled and (gMouseSelectionButton = 1)) then
|
|
begin
|
|
tmContextMenu.Enabled:= False; // stop context menu timer
|
|
end;
|
|
end
|
|
{ Open folder in new tab on middle click }
|
|
else if (Button = mbMiddle) and (Y > dgPanel.GetHeaderHeight) then
|
|
begin
|
|
Actions.cm_OpenDirInNewTab();
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelStartDrag(Sender: TObject; var DragObject: TDragObject);
|
|
begin
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelDragOver(Sender, Source: TObject; X, Y: Integer;
|
|
State: TDragState; var Accept: Boolean);
|
|
var
|
|
iRow, Dummy: Integer;
|
|
AFile: TDisplayFile = nil;
|
|
SourcePanel: TColumnsFileView = nil;
|
|
TargetPanel: TColumnsFileView = nil;
|
|
SourceDir, TargetDir: String;
|
|
begin
|
|
Accept := False;
|
|
|
|
if (not (Source is TDrawGridEx)) or (not (Sender is TDrawGridEx)) then
|
|
Exit;
|
|
|
|
// Always allow dropping into an empty panel.
|
|
// And it is also allowed to drop onto header in case all visible items
|
|
// are directories and the user wants to drop into panel's current directory.
|
|
if IsEmpty or (Y < dgPanel.GetHeaderHeight) then
|
|
begin
|
|
dgPanel.ChangeDropRowIndex(-1);
|
|
Accept:= True;
|
|
Exit;
|
|
end;
|
|
|
|
SourcePanel := ((Source as TDrawGridEx).Parent) as TColumnsFileView;
|
|
TargetPanel := ((Sender as TDrawGridEx).Parent) as TColumnsFileView;
|
|
|
|
SourceDir := SourcePanel.CurrentPath;
|
|
TargetDir := TargetPanel.CurrentPath;
|
|
|
|
dgPanel.MouseToCell(X, Y, Dummy, iRow);
|
|
|
|
if iRow >= dgPanel.FixedRows then
|
|
AFile := FFiles[iRow - dgPanel.FixedRows]; // substract fixed rows (header)
|
|
|
|
if Assigned(AFile) and
|
|
(AFile.FSFile.IsDirectory or AFile.FSFile.IsLinkToDirectory) and
|
|
(dgPanel.MouseOnGrid(X, Y))
|
|
then
|
|
begin
|
|
if State = dsDragLeave then
|
|
// Mouse is leaving the control or drop will occur immediately.
|
|
// Don't draw DropRow rectangle.
|
|
dgPanel.ChangeDropRowIndex(-1)
|
|
else
|
|
dgPanel.ChangeDropRowIndex(iRow);
|
|
|
|
if Sender = Source then
|
|
begin
|
|
if not ((iRow = dgPanel.DragRowIndex) or (AFile.Selected = True)) then
|
|
Accept := True;
|
|
end
|
|
else
|
|
begin
|
|
if Assigned(SourcePanel) and Assigned(TargetPanel) then
|
|
begin
|
|
if AFile.FSFile.Name = '..' then
|
|
TargetDir := GetParentDir(TargetDir)
|
|
else
|
|
TargetDir := TargetDir + AFile.FSFile.Name + DirectorySeparator;
|
|
|
|
if SourceDir <> TargetDir then Accept := True;
|
|
end
|
|
else
|
|
Accept := True;
|
|
end;
|
|
end
|
|
else if (Sender <> Source) then
|
|
begin
|
|
dgPanel.ChangeDropRowIndex(-1);
|
|
|
|
if Assigned(SourcePanel) and Assigned(TargetPanel) then
|
|
begin
|
|
if SourcePanel.CurrentPath <> TargetPanel.CurrentPath then
|
|
Accept := True;
|
|
end
|
|
else
|
|
Accept := True;
|
|
end
|
|
else
|
|
begin
|
|
dgPanel.ChangeDropRowIndex(-1);
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelDragDrop(Sender, Source: TObject; X, Y: Integer);
|
|
var
|
|
SourcePanel: TColumnsFileView;
|
|
SourceFiles: TFiles;
|
|
DropParams: TDropParams;
|
|
begin
|
|
if (Sender is TDrawGridEx) and (Source is TDrawGridEx) then
|
|
begin
|
|
SourcePanel := ((Source as TDrawGridEx).Parent) as TColumnsFileView;
|
|
|
|
// Get file names from source panel.
|
|
SourceFiles := SourcePanel.CloneSelectedFiles;
|
|
try
|
|
// Drop onto target panel.
|
|
with Sender as TDrawGridEx do
|
|
begin
|
|
DropParams := TDropParams.Create(
|
|
SourceFiles, // Will be freed automatically.
|
|
GetDropEffectByKeyAndMouse(GetKeyShiftState,
|
|
(Source as TDrawGridEx).LastMouseButton),
|
|
ClientToScreen(Classes.Point(X, Y)),
|
|
True,
|
|
SourcePanel,
|
|
Self, Self.CurrentPath);
|
|
|
|
frmMain.DropFiles(DropParams);
|
|
ChangeDropRowIndex(-1);
|
|
end;
|
|
except
|
|
FreeAndNil(SourceFiles);
|
|
raise;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelEndDrag(Sender, Target: TObject; X, Y: Integer);
|
|
begin
|
|
// If cancelled by the user, DragManager does not send drag-leave event
|
|
// to the target, so we must clear the DropRow in both panels.
|
|
{
|
|
frmMain.FrameLeft.dgPanel.ChangeDropRowIndex(-1);
|
|
frmMain.FrameRight.dgPanel.ChangeDropRowIndex(-1);
|
|
}
|
|
if uDragDropEx.TransformDragging = False then
|
|
dgPanel.ClearMouseButtonAfterDrag;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelHeaderClick(Sender: TObject;
|
|
IsColumn: Boolean; Index: Integer);
|
|
var
|
|
ShiftState : TShiftState;
|
|
SortingDirection : TSortDirection = sdAscending;
|
|
begin
|
|
if not IsColumn then Exit;
|
|
|
|
ShiftState := GetKeyShiftState;
|
|
if not ((ssShift in ShiftState) or (ssCtrl in ShiftState)) then
|
|
begin
|
|
SortingDirection := FColumnsSorting.GetSortingDirection(Index);
|
|
if SortingDirection = sdNone then
|
|
SortingDirection := sdAscending
|
|
else
|
|
SortingDirection := ReverseSortDirection(SortingDirection);
|
|
FColumnsSorting.Clear;
|
|
end;
|
|
|
|
FColumnsSorting.AddSorting(Index, SortingDirection);
|
|
inherited SetSorting(PrepareSortings);
|
|
TFileSorter.Sort(FFileSourceFiles, Sorting);
|
|
ReDisplayFileList;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelMouseWheelUp(Sender: TObject;
|
|
Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Handled:= True;
|
|
case gScrollMode of
|
|
smLineByLine:
|
|
for I:= 1 to gWheelScrollLines do
|
|
dgPanel.Perform(LM_VSCROLL, SB_LINEUP, 0);
|
|
smPageByPage:
|
|
dgPanel.Perform(LM_VSCROLL, SB_PAGEUP, 0);
|
|
else
|
|
Handled:= False;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelMouseWheelDown(Sender: TObject;
|
|
Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Handled:= True;
|
|
case gScrollMode of
|
|
smLineByLine:
|
|
for I:= 1 to gWheelScrollLines do
|
|
dgPanel.Perform(LM_VSCROLL, SB_LINEDOWN, 0);
|
|
smPageByPage:
|
|
dgPanel.Perform(LM_VSCROLL, SB_PAGEDOWN, 0);
|
|
else
|
|
Handled:= False;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelSelection(Sender: TObject; aCol, aRow: Integer);
|
|
var
|
|
aFile: TFile = nil;
|
|
begin
|
|
if (FLastActiveRow <> aRow) and (not FUpdatingGrid) then
|
|
begin
|
|
SetLastActiveFile(aRow);
|
|
FLastActiveRow:= aRow;
|
|
|
|
if Assigned(OnChangeActiveFile) then
|
|
begin
|
|
aFile := CloneActiveFile;
|
|
try
|
|
OnChangeActiveFile(Self, aFile);
|
|
finally
|
|
FreeAndNil(aFile);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelShowHint(Sender: TObject; HintInfo: PHintInfo);
|
|
var
|
|
AFile: TDisplayFile;
|
|
sHint: UTF8String;
|
|
begin
|
|
if HintInfo^.HintStr = EmptyStr then Exit; // don't show
|
|
with dgPanel do
|
|
AFile := FFiles[HintRowIndex - FixedRows];
|
|
if not AFile.FSFile.IsDirectory then
|
|
begin
|
|
sHint:= GetFileInfoToolTip(FileSource, AFile.FSFile);
|
|
with HintInfo^ do
|
|
if (sHint = EmptyStr) and (HintStr = #32) then // no tooltip
|
|
HintStr:= EmptyStr
|
|
else if (sHint <> EmptyStr) then // has tooltip
|
|
begin
|
|
if HintStr = #32 then // without name
|
|
HintStr:= sHint
|
|
else
|
|
HintStr:= HintStr + LineEnding + sHint;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelTopLeftChanged(Sender: TObject);
|
|
begin
|
|
EnsureDisplayProperties;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelResize(Sender: TObject);
|
|
begin
|
|
EnsureDisplayProperties;
|
|
end;
|
|
|
|
procedure TColumnsFileView.tmContextMenuTimer(Sender: TObject);
|
|
var
|
|
AFile: TDisplayFile;
|
|
iRow, iCol: Integer;
|
|
MousePoint: TPoint;
|
|
Background: Boolean;
|
|
begin
|
|
dgPanel.FMouseDown:= False;
|
|
tmContextMenu.Enabled:= False; // stop context menu timer
|
|
// show context menu
|
|
MousePoint:= dgPanel.ScreenToClient(Mouse.CursorPos);
|
|
Background:= not dgPanel.MouseOnGrid(MousePoint.x, MousePoint.y);
|
|
Actions.DoContextMenu(Self, Mouse.CursorPos.x, Mouse.CursorPos.y, Background);
|
|
if not Background then
|
|
begin
|
|
// get current row
|
|
dgPanel.MouseToCell(MousePoint.x, MousePoint.y, iCol, iRow);
|
|
if iRow < dgPanel.FixedRows then Exit;
|
|
AFile := FFiles[iRow - dgPanel.FixedRows]; // get current file
|
|
MarkFile(AFile, False); // unselect file
|
|
dgPanel.InvalidateRow(iRow); // invalidate current row
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.tmClearGridTimer(Sender: TObject);
|
|
begin
|
|
tmClearGrid.Enabled := False;
|
|
|
|
if not Assigned(FFileSourceFiles) or (FFileSourceFiles.Count = 0) then
|
|
begin
|
|
SetRowCount(0);
|
|
RedrawGrid;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtSearchKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
begin
|
|
case Key of
|
|
VK_DOWN:
|
|
begin
|
|
fSearchDirect := True;
|
|
fNext := True;
|
|
Key := 0;
|
|
edtSearchChange(Sender);
|
|
end;
|
|
|
|
VK_UP:
|
|
begin
|
|
fSearchDirect := False;
|
|
fPrevious := True;
|
|
Key := 0;
|
|
edtSearchChange(Sender);
|
|
end;
|
|
|
|
VK_TAB:
|
|
begin
|
|
CloseSearchPanel;
|
|
SetFocus;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_ESCAPE:
|
|
begin
|
|
Key := 0;
|
|
CloseSearchPanel;
|
|
SetFocus;
|
|
end;
|
|
|
|
VK_RETURN,
|
|
VK_SELECT:
|
|
begin
|
|
Key := 0;
|
|
CloseSearchPanel;
|
|
SetFocus;
|
|
|
|
{LaBero begin}
|
|
{en
|
|
Execute/open selected file/directory
|
|
if the user press ENTER during QuickSearch
|
|
}
|
|
ChooseFile(GetActiveDisplayFile);
|
|
{LaBero end}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.AfterChangePath;
|
|
begin
|
|
inherited;
|
|
|
|
FUpdatingGrid := True;
|
|
dgPanel.Row := 0;
|
|
FUpdatingGrid := False;
|
|
|
|
MakeFileSourceFileList;
|
|
UpdatePathLabel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.ShowRenameFileEdit(const sFileName:String);
|
|
begin
|
|
frmMain.EnableHotkeys(False);
|
|
|
|
edtRename.Width := dgPanel.ColWidths[0]+dgPanel.ColWidths[1]-16;
|
|
edtRename.Top := (dgPanel.CellRect(0,dgPanel.Row).Top-2);
|
|
if gShowIcons <> sim_none then
|
|
edtRename.Left:= gIconsSize + 3
|
|
else
|
|
edtRename.Left:= 2;
|
|
edtRename.Height:=dgpanel.DefaultRowHeight+4;
|
|
edtRename.Hint:=sFileName;
|
|
edtRename.Text:=ExtractFileName(sFileName);
|
|
edtRename.Visible:=True;
|
|
edtRename.SetFocus;
|
|
if gRenameSelOnlyName then
|
|
begin
|
|
{$IFDEF LCLGTK2}
|
|
edtRename.SelStart:=1;
|
|
{$ENDIF}
|
|
edtRename.SelStart:=0;
|
|
edtRename.SelLength:= UTF8Length(edtRename.Text)-UTF8Length(ExtractFileExt(edtRename.Text));
|
|
end
|
|
else
|
|
edtRename.SelectAll;
|
|
end;
|
|
|
|
procedure TColumnsFileView.ShowPathEdit;
|
|
begin
|
|
frmMain.EnableHotkeys(False);
|
|
|
|
with lblPath do
|
|
begin
|
|
edtPath.SetBounds(Left, Top, Width, Height);
|
|
edtPath.Text := CurrentPath;
|
|
edtPath.Visible := True;
|
|
edtPath.SetFocus;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.UpdateAddressLabel;
|
|
begin
|
|
if CurrentAddress = '' then
|
|
begin
|
|
lblAddress.Visible := False;
|
|
end
|
|
else
|
|
begin
|
|
lblAddress.Top:= 0;
|
|
lblAddress.Visible := True;
|
|
lblAddress.Caption := CurrentAddress;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.UpdatePathLabel;
|
|
begin
|
|
lblPath.Caption := MinimizeFilePath(CurrentPath, lblPath.Canvas, lblPath.Width);
|
|
end;
|
|
|
|
function TColumnsFileView.PrepareSortings: TFileSortings;
|
|
var
|
|
ColumnsClass: TPanelColumnsClass;
|
|
i: Integer;
|
|
pSortingColumn : PColumnsSorting;
|
|
Column: TPanelColumn;
|
|
begin
|
|
Result := nil;
|
|
|
|
ColumnsClass := GetColumnsClass;
|
|
if ColumnsClass.ColumnsCount = 0 then
|
|
Exit;
|
|
|
|
for i := 0 to FColumnsSorting.Count - 1 do
|
|
begin
|
|
pSortingColumn := PColumnsSorting(FColumnsSorting[i]);
|
|
|
|
if (pSortingColumn^.Column >= 0) and
|
|
(pSortingColumn^.Column < ColumnsClass.ColumnsCount) then
|
|
begin
|
|
Column := ColumnsClass.GetColumnItem(pSortingColumn^.Column);
|
|
AddSorting(Result, Column.GetColumnFunctions, pSortingColumn^.SortDirection);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetColumnsSorting(const ASortings: TFileSortings);
|
|
|
|
var
|
|
Columns: TPanelColumnsClass;
|
|
|
|
function AddColumnsSorting(ASortFunction: TFileFunction; ASortDirection: TSortDirection): Boolean;
|
|
var
|
|
k, l: Integer;
|
|
ColumnFunctions: TFileFunctions;
|
|
begin
|
|
for k := 0 to Columns.Count - 1 do
|
|
begin
|
|
ColumnFunctions := Columns.GetColumnItem(k).GetColumnFunctions;
|
|
for l := 0 to Length(ColumnFunctions) - 1 do
|
|
if ColumnFunctions[l] = ASortFunction then
|
|
begin
|
|
FColumnsSorting.AddSorting(k, ASortDirection);
|
|
Exit(True);
|
|
end;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
var
|
|
i, j: Integer;
|
|
begin
|
|
FColumnsSorting.Clear;
|
|
Columns := GetColumnsClass;
|
|
for i := 0 to Length(ASortings) - 1 do
|
|
begin
|
|
for j := 0 to Length(ASortings[i].SortFunctions) - 1 do
|
|
begin
|
|
// Search for the column containing the sort function and add sorting
|
|
// by that column. If function is Name and it is not found try searching
|
|
// for NameNoExtension + Extension.
|
|
if (not AddColumnsSorting(ASortings[i].SortFunctions[j], ASortings[i].SortDirection)) and
|
|
(ASortings[i].SortFunctions[j] = fsfName) then
|
|
begin
|
|
if AddColumnsSorting(fsfNameNoExtension, ASortings[i].SortDirection) then
|
|
AddColumnsSorting(fsfExtension, ASortings[i].SortDirection);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TColumnsFileView.GetFilePropertiesNeeded: TFilePropertiesTypes;
|
|
var
|
|
i, j: Integer;
|
|
ColumnsClass: TPanelColumnsClass;
|
|
Column: TPanelColumn;
|
|
FileFunctionsUsed: TFileFunctions;
|
|
begin
|
|
// By default always use some properties.
|
|
Result := [fpName,
|
|
fpSize, // For info panel (total size, selected size)
|
|
fpAttributes, // For distinguishing directories
|
|
fpLink, // For distinguishing directories (link to dir) and link icons
|
|
fpModificationTime // For selecting/coloring files (by SearchTemplate)
|
|
];
|
|
|
|
ColumnsClass := GetColumnsClass;
|
|
|
|
// Scan through all columns.
|
|
for i := 0 to ColumnsClass.Count - 1 do
|
|
begin
|
|
Column := ColumnsClass.GetColumnItem(i);
|
|
FileFunctionsUsed := Column.GetColumnFunctions;
|
|
if Length(FileFunctionsUsed) > 0 then
|
|
begin
|
|
// Scan through all functions in the column.
|
|
for j := Low(FileFunctionsUsed) to High(FileFunctionsUsed) do
|
|
begin
|
|
// Add file properties needed to display the function.
|
|
Result := Result + TFileFunctionToProperty[FileFunctionsUsed[j]];
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.UpdateColCount(NewColCount: Integer);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if dgPanel.Columns.Count <> NewColCount then
|
|
begin
|
|
while dgPanel.Columns.Count < NewColCount do
|
|
dgPanel.Columns.Add;
|
|
while dgPanel.Columns.Count > NewColCount do
|
|
dgPanel.Columns.Delete(0);
|
|
for i := 0 to FFiles.Count - 1 do
|
|
while FFiles[i].DisplayStrings.Count < NewColCount do
|
|
FFiles[i].DisplayStrings.Add(EmptyStr);
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetRowCount(Count: Integer);
|
|
begin
|
|
FUpdatingGrid := True;
|
|
dgPanel.RowCount := dgPanel.FixedRows + Count;
|
|
FUpdatingGrid := False;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetColumnsWidths;
|
|
var
|
|
x: Integer;
|
|
ColumnsClass: TPanelColumnsClass;
|
|
begin
|
|
// setup column widths
|
|
ColumnsClass := GetColumnsClass;
|
|
|
|
UpdateColCount(ColumnsClass.ColumnsCount);
|
|
for x:= 0 to ColumnsClass.ColumnsCount - 1 do
|
|
with dgPanel.Columns.Items[x] do
|
|
begin
|
|
if not ((x = 0) and gAutoFillColumns and (gAutoSizeColumn = 0)) then
|
|
SizePriority:= 0;
|
|
Width:= ColumnsClass.GetColumnWidth(x);
|
|
Title.Caption:= ColumnsClass.GetColumnTitle(x);
|
|
end;
|
|
end;
|
|
|
|
function TColumnsFileView.DimColor(AColor: TColor): TColor;
|
|
begin
|
|
if (not Active) and (gInactivePanelBrightness < 100) then
|
|
Result := ModColor(AColor, gInactivePanelBrightness)
|
|
else
|
|
Result := AColor;
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtPathExit(Sender: TObject);
|
|
begin
|
|
edtPath.Visible := False;
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtSearchExit(Sender: TObject);
|
|
begin
|
|
// sometimes must be search panel closed this way
|
|
CloseSearchPanel;
|
|
RedrawGrid;
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtRenameExit(Sender: TObject);
|
|
begin
|
|
edtRename.Visible := False;
|
|
UnMarkAll;
|
|
|
|
// dgPanelEnter don't called automatically (bug?)
|
|
dgPanelEnter(dgPanel);
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtFilterEnter(Sender: TObject);
|
|
begin
|
|
SetActive(True);
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtFilterExit(Sender: TObject);
|
|
begin
|
|
if edtFilter.Text = '' then
|
|
pnlFilter.Visible := False;
|
|
|
|
SetActive(False);
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtSearchChange(Sender: TObject);
|
|
var
|
|
I, iPos, iEnd : Integer;
|
|
Result : Boolean;
|
|
sSearchName,
|
|
sSearchNameNoExt,
|
|
sSearchExt : UTF8String;
|
|
begin
|
|
if (edtSearch.Text='') or IsEmpty then Exit;
|
|
//DebugLn('edtSearchChange: '+ edtSearch.Text);
|
|
|
|
sSearchName := UTF8LowerCase(edtSearch.Text);
|
|
|
|
if Pos('.', sSearchName) <> 0 then
|
|
begin
|
|
sSearchNameNoExt := ExtractOnlyFileName(sSearchName);
|
|
sSearchExt := ExtractFileExt(sSearchName);
|
|
if not gQuickSearchMatchBeginning then
|
|
sSearchNameNoExt := '*' + sSearchNameNoExt;
|
|
if not gQuickSearchMatchEnding then
|
|
sSearchNameNoExt := sSearchNameNoExt + '*';
|
|
sSearchName := sSearchNameNoExt + sSearchExt + '*';
|
|
end
|
|
else
|
|
begin
|
|
if not gQuickSearchMatchBeginning then
|
|
sSearchName := '*' + sSearchName;
|
|
sSearchName := sSearchName + '*';
|
|
end;
|
|
|
|
DebugLn('sSearchName = ', sSearchName);
|
|
|
|
I := dgPanel.Row; // start search from current cursor position
|
|
iPos := I; // save cursor position
|
|
if not (fNext or fPrevious) then fSearchDirect := True;
|
|
if fSearchDirect then
|
|
begin
|
|
if fNext then
|
|
I := I + 1; // begin search from next file
|
|
iEnd := dgPanel.RowCount;
|
|
end
|
|
else
|
|
begin
|
|
if fPrevious then
|
|
I := I - 1; // begin search from previous file
|
|
iEnd := dgPanel.FixedRows - 1;
|
|
end;
|
|
|
|
try
|
|
while I <> iEnd do
|
|
begin
|
|
Result := MatchesMask(UTF8LowerCase(FFiles[I - dgPanel.FixedRows].FSFile.Name), sSearchName);
|
|
|
|
if Result then
|
|
begin
|
|
dgPanel.Row := I;
|
|
MakeVisible(I);
|
|
Exit;
|
|
end;
|
|
|
|
if fSearchDirect then
|
|
Inc(I)
|
|
else
|
|
Dec(I);
|
|
|
|
// if not Next or Previous then search from beginning of list
|
|
// to cursor position
|
|
if (not(fNext or fPrevious)) and (I = iEnd) then
|
|
begin
|
|
I := dgPanel.FixedRows;
|
|
iEnd := iPos;
|
|
iPos := I;
|
|
end;
|
|
end; // while
|
|
except
|
|
on EConvertError do; // bypass
|
|
else
|
|
raise;
|
|
end;
|
|
|
|
fNext := False;
|
|
fPrevious := False;
|
|
end;
|
|
|
|
procedure TColumnsFileView.CloseSearchPanel;
|
|
begin
|
|
pnAltSearch.Visible:=False;
|
|
edtSearch.Text:='';
|
|
SetActive(False);
|
|
end;
|
|
|
|
procedure TColumnsFileView.FilterPanelVisible;
|
|
begin
|
|
pnlFilter.Visible := True;
|
|
edtFilter.Width := pnlFilter.Width div 2;
|
|
end;
|
|
|
|
procedure TColumnsFileView.CloseFilterPanel;
|
|
begin
|
|
edtFilter.Text := ''; // Automatically triggers edtFilterChange.
|
|
pnlFilter.Visible := False;
|
|
SetFocus;
|
|
end;
|
|
|
|
procedure TColumnsFileView.ShowSearchPanel(Char : TUTF8Char);
|
|
begin
|
|
frmMain.EnableHotkeys(False);
|
|
|
|
edtSearch.Height := pnAltSearch.Canvas.TextHeight('Wg') + 1
|
|
+ GetSystemMetrics(SM_CYEDGE) * 2;
|
|
pnAltSearch.Height := edtSearch.Height + GetSystemMetrics(SM_CYEDGE);
|
|
pnAltSearch.Width := dgPanel.Width div 2;
|
|
edtSearch.Width := pnAltSearch.Width - edtSearch.Left
|
|
- GetSystemMetrics(SM_CXEDGE);
|
|
|
|
pnAltSearch.Top := pnlFooter.Top + pnlFooter.Height - pnAltSearch.Height;
|
|
pnAltSearch.Left := dgPanel.Left;
|
|
|
|
pnAltSearch.Visible := True;
|
|
edtSearch.SetFocus;
|
|
edtSearch.Tag := 0; // save current search position
|
|
fSearchDirect := True; // set search direction
|
|
fNext := False;
|
|
fPrevious := False;
|
|
edtSearch.Text := Char;
|
|
edtSearch.SelStart := UTF8Length(edtSearch.Text) + 1;
|
|
SetActive(True);
|
|
end;
|
|
|
|
procedure TColumnsFileView.ShowFilterPanel(Char : TUTF8Char = #0);
|
|
begin
|
|
frmMain.EnableHotkeys(False);
|
|
|
|
FilterPanelVisible;
|
|
edtFilter.SetFocus;
|
|
|
|
if Char <> #0 then
|
|
begin
|
|
edtFilter.Text := Char;
|
|
edtFilter.SelStart := UTF8Length(edtFilter.Text) + 1;
|
|
edtFilter.SelLength := 0;
|
|
end
|
|
else
|
|
begin
|
|
edtFilter.SelectAll;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.UpdateInfoPanel;
|
|
var
|
|
i: Integer;
|
|
FilesInDir, FilesSelected: Integer;
|
|
SizeInDir, SizeSelected: Int64;
|
|
SizeProperty: TFileSizeProperty;
|
|
SizeSupported: Boolean;
|
|
begin
|
|
if GetCurrentWorkType = fvwtCreate then
|
|
begin
|
|
lblInfo.Caption := rsMsgLoadingFileList;
|
|
end
|
|
else if Assigned(FileSource) then
|
|
begin
|
|
FilesInDir := 0;
|
|
FilesSelected := 0;
|
|
SizeInDir := 0;
|
|
SizeSelected := 0;
|
|
|
|
SizeSupported := fpSize in FileSource.SupportedFileProperties;
|
|
|
|
for i := 0 to FFiles.Count - 1 do
|
|
begin
|
|
with FFiles[i] do
|
|
begin
|
|
if FSFile.Name = '..' then Continue;
|
|
|
|
inc(FilesInDir);
|
|
if Selected then
|
|
inc(FilesSelected);
|
|
|
|
// Count size if Size property is supported.
|
|
if SizeSupported then
|
|
begin
|
|
SizeProperty := FSFile.SizeProperty;
|
|
|
|
if Selected then
|
|
SizeSelected := SizeSelected + SizeProperty.Value;
|
|
|
|
SizeInDir := SizeInDir + SizeProperty.Value;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
lblInfo.Caption := Format(rsMsgSelected,
|
|
[cnvFormatFileSize(SizeSelected),
|
|
cnvFormatFileSize(SizeInDir),
|
|
FilesSelected,
|
|
FilesInDir]);
|
|
end
|
|
else if not (csDestroying in ComponentState) then
|
|
lblInfo.Caption := '';
|
|
end;
|
|
|
|
procedure TColumnsFileView.MarkAll;
|
|
begin
|
|
MarkAllFiles(True);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MarkPlus;
|
|
var
|
|
s: String;
|
|
begin
|
|
if IsEmpty then Exit;
|
|
s := FLastMark;
|
|
if not ShowMaskInputDlg(rsMarkPlus, rsMaskInput, glsMaskHistory, s) then Exit;
|
|
FLastMark := s;
|
|
MarkGroup(s, True);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MarkShiftPlus;
|
|
var
|
|
sGroup: String;
|
|
begin
|
|
if IsActiveItemValid then
|
|
begin
|
|
sGroup := GetActiveDisplayFile.FSFile.Extension;
|
|
if sGroup <> '' then
|
|
sGroup := '.' + sGroup;
|
|
MarkGroup('*' + sGroup, True);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MarkShiftMinus;
|
|
var
|
|
sGroup: String;
|
|
begin
|
|
if IsActiveItemValid then
|
|
begin
|
|
sGroup := GetActiveDisplayFile.FSFile.Extension;
|
|
if sGroup <> '' then
|
|
sGroup := '.' + sGroup;
|
|
MarkGroup('*' + sGroup, False);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SaveSelection;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
FSelection.Clear;
|
|
for I := 0 to FFiles.Count - 1 do
|
|
with FFiles[I] do
|
|
begin
|
|
if Selected then
|
|
FSelection.Add(FSFile.Name);
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.RestoreSelection;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to FFiles.Count - 1 do
|
|
with FFiles[I] do
|
|
Selected:= (FSelection.IndexOf(FSFile.Name) >= 0);
|
|
dgPanel.Invalidate;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MarkMinus;
|
|
var
|
|
s: String;
|
|
begin
|
|
if IsEmpty then Exit;
|
|
s := FLastMark;
|
|
if not ShowMaskInputDlg(rsMarkMinus, rsMaskInput, glsMaskHistory, s) then Exit;
|
|
FLastMark := s;
|
|
MarkGroup(s, False);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
|
|
procedure TColumnsFileView.UnMarkAll;
|
|
begin
|
|
MarkAllFiles(False);
|
|
UpdateInfoPanel;
|
|
dgPanel.Invalidate;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MarkGroup(const sMask: String; bSelect: Boolean);
|
|
var
|
|
I: Integer;
|
|
SearchTemplate: TSearchTemplate = nil;
|
|
begin
|
|
if IsMaskSearchTemplate(sMask) then
|
|
begin
|
|
SearchTemplate:= gSearchTemplateList.TemplateByName[sMask];
|
|
if Assigned(SearchTemplate) then
|
|
for I := 0 to FFiles.Count - 1 do
|
|
begin
|
|
if FFiles[I].FSFile.Name = '..' then Continue;
|
|
if SearchTemplate.CheckFile(FFiles[I].FSFile) then
|
|
FFiles[I].Selected := bSelect;
|
|
end;
|
|
end
|
|
else
|
|
for I := 0 to FFiles.Count - 1 do
|
|
begin
|
|
if FFiles[I].FSFile.Name = '..' then Continue;
|
|
if MatchesMaskList(FFiles[I].FSFile.Name, sMask) then
|
|
FFiles[I].Selected := bSelect;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtPathKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
begin
|
|
case Key of
|
|
VK_ESCAPE:
|
|
begin
|
|
Key := 0;
|
|
edtPath.Visible:=False;
|
|
SetFocus;
|
|
end;
|
|
|
|
VK_RETURN,
|
|
VK_SELECT:
|
|
begin
|
|
Key := 0; // catch the enter
|
|
CurrentPath := edtPath.Text;
|
|
edtPath.Visible := False;
|
|
SetFocus;
|
|
end;
|
|
|
|
{$IFDEF LCLGTK2}
|
|
// Workaround for GTK2 - up and down arrows moving through controls.
|
|
VK_UP,
|
|
VK_DOWN:
|
|
Key := 0;
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtRenameKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
var
|
|
NewFileName: String;
|
|
OldFileNameAbsolute: String;
|
|
lenEdtText, lenEdtTextExt, i: Integer;
|
|
seperatorSet: set of AnsiChar;
|
|
aFile: TFile = nil;
|
|
begin
|
|
case Key of
|
|
VK_ESCAPE:
|
|
begin
|
|
Key := 0;
|
|
edtRename.Visible:=False;
|
|
SetFocus;
|
|
end;
|
|
|
|
VK_RETURN,
|
|
VK_SELECT:
|
|
begin
|
|
Key := 0; // catch the enter
|
|
|
|
NewFileName := edtRename.Text;
|
|
OldFileNameAbsolute := edtRename.Hint;
|
|
|
|
aFile := CloneActiveFile;
|
|
try
|
|
try
|
|
if RenameFile(FileSource, aFile, NewFileName, True) = True then
|
|
begin
|
|
edtRename.Visible:=False;
|
|
SetActiveFile(CurrentPath + NewFileName);
|
|
SetFocus;
|
|
end
|
|
else
|
|
msgError(Format(rsMsgErrRename, [ExtractFileName(OldFileNameAbsolute), NewFileName]));
|
|
|
|
except
|
|
on e: EInvalidFileProperty do
|
|
msgError(Format(rsMsgErrRename + ':' + LineEnding + '%s (%s)', [ExtractFileName(OldFileNameAbsolute), NewFileName, rsMsgInvalidFileName, e.Message]));
|
|
end;
|
|
finally
|
|
FreeAndNil(aFile);
|
|
end;
|
|
end;
|
|
|
|
VK_F2, VK_F6:
|
|
begin
|
|
Key := 0;
|
|
lenEdtText := UTF8Length(edtRename.Text);
|
|
lenEdtTextExt := UTF8Length(ExtractFileExt(edtRename.Text));
|
|
if (edtRename.SelLength = lenEdtText) then
|
|
begin
|
|
// Now all selected, change it to name-only.
|
|
edtRename.SelStart:= 0;
|
|
edtRename.SelLength:= lenEdtText - lenEdtTextExt;
|
|
end
|
|
else if (edtRename.SelStart = 0) and (edtRename.SelLength = lenEdtText - lenEdtTextExt) then
|
|
begin
|
|
// Now name-only selected, change it to ext-only.
|
|
edtRename.SelStart:= edtRename.SelLength + 1;
|
|
edtRename.SelLength:= lenEdtText - edtRename.SelStart;
|
|
end
|
|
else begin
|
|
// Partial selection cycle.
|
|
seperatorSet:= [' ', '-', '_', '.'];
|
|
i:= edtRename.SelStart + edtRename.SelLength;
|
|
while true do
|
|
begin
|
|
if (edtRename.Text[UTF8CharToByteIndex(PChar(edtRename.Text), length(edtRename.Text), i)] in seperatorSet)
|
|
and not(edtRename.Text[UTF8CharToByteIndex(PChar(edtRename.Text), length(edtRename.Text), i+1)] in seperatorSet) then
|
|
begin
|
|
edtRename.SelStart:= i;
|
|
Break;
|
|
end;
|
|
inc(i);
|
|
if i >= lenEdtText then
|
|
begin
|
|
edtRename.SelStart:= 0;
|
|
Break;
|
|
end;
|
|
end;
|
|
i:= edtRename.SelStart + 1;
|
|
while true do
|
|
begin
|
|
if (i >= lenEdtText)
|
|
or (edtRename.Text[UTF8CharToByteIndex(PChar(edtRename.Text), length(edtRename.Text), i+1)] in seperatorSet) then
|
|
begin
|
|
edtRename.SelLength:= i - edtRename.SelStart;
|
|
Break;
|
|
end;
|
|
inc(i);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF LCLGTK2}
|
|
// Workaround for GTK2 - up and down arrows moving through controls.
|
|
VK_UP,
|
|
VK_DOWN:
|
|
Key := 0;
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtFilterChange(Sender: TObject);
|
|
begin
|
|
FileFilter := edtFilter.Text;
|
|
end;
|
|
|
|
procedure TColumnsFileView.edtFilterKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
|
var
|
|
iRow: LongInt;
|
|
begin
|
|
case Key of
|
|
VK_TAB, VK_RETURN, VK_SELECT:
|
|
begin
|
|
SetFocus;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_ESCAPE: // Close panel and remove filter with Escape.
|
|
begin
|
|
CloseFilterPanel;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_UP:
|
|
begin
|
|
iRow:= dgPanel.Row - 1;
|
|
if iRow < dgPanel.FixedRows then
|
|
dgPanel.Row:= dgPanel.RowCount - 1
|
|
else
|
|
dgPanel.Row:= iRow;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_DOWN:
|
|
begin
|
|
iRow:= dgPanel.Row + 1;
|
|
if iRow < dgPanel.RowCount then
|
|
dgPanel.Row:= iRow
|
|
else
|
|
dgPanel.Row:= dgPanel.FixedRows;
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.btnCloseFilterClick(Sender: TObject);
|
|
begin
|
|
CloseFilterPanel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MakeVisible(iRow:Integer);
|
|
begin
|
|
with dgPanel do
|
|
begin
|
|
if iRow<TopRow then
|
|
TopRow:=iRow;
|
|
if iRow>TopRow+VisibleRowCount then
|
|
TopRow:=iRow-VisibleRowCount;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelExit(Sender: TObject);
|
|
begin
|
|
SetActive(False);
|
|
end;
|
|
|
|
procedure TColumnsFileView.MakeSelectedVisible;
|
|
begin
|
|
if dgPanel.Row>=0 then
|
|
MakeVisible(dgPanel.Row);
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetLastActiveFile(RowNr: Integer);
|
|
begin
|
|
if (RowNr >= dgPanel.FixedRows) and (RowNr < dgPanel.RowCount) and
|
|
(RowNr - dgPanel.FixedRows < FFiles.Count) then
|
|
begin
|
|
LastActiveFile := FFiles[RowNr - dgPanel.FixedRows].FSFile.FullPath;
|
|
end;
|
|
end;
|
|
|
|
function TColumnsFileView.SetActiveFileNow(aFilePath: String): Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if aFilePath <> '' then // find correct cursor position in Panel (drawgrid)
|
|
begin
|
|
if FileSource.GetPathType(aFilePath) = ptAbsolute then
|
|
begin
|
|
for i := 0 to FFiles.Count - 1 do
|
|
if FFiles[i].FSFile.FullPath = aFilePath then
|
|
begin
|
|
FUpdatingGrid := True;
|
|
dgPanel.Row := i + dgPanel.FixedRows;
|
|
FUpdatingGrid := False;
|
|
SetLastActiveFile(dgPanel.Row);
|
|
Exit(True);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
for i := 0 to FFiles.Count - 1 do
|
|
if FFiles[i].FSFile.Name = aFilePath then
|
|
begin
|
|
FUpdatingGrid := True;
|
|
dgPanel.Row := i + dgPanel.FixedRows;
|
|
FUpdatingGrid := False;
|
|
SetLastActiveFile(dgPanel.Row);
|
|
Exit(True);
|
|
end;
|
|
end;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetActiveFile(aFilePath: String);
|
|
begin
|
|
if GetCurrentWorkType = fvwtCreate then
|
|
begin
|
|
// File list is currently loading - remember requested file for later.
|
|
RequestedActiveFile := aFilePath;
|
|
end
|
|
else
|
|
begin
|
|
// First try to select the file in the current file list.
|
|
// If not found save it for later selection (possibly after reload).
|
|
if SetActiveFileNow(aFilePath) then
|
|
RequestedActiveFile := ''
|
|
else
|
|
RequestedActiveFile := aFilePath;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelDblClick(Sender: TObject);
|
|
var
|
|
Point : TPoint;
|
|
begin
|
|
{$IFDEF LCLGTK2}
|
|
// Workaround for two doubleclicks being sent on GTK.
|
|
if dgPanel.TooManyDoubleClicks then Exit;
|
|
{$ENDIF}
|
|
|
|
dgPanel.StartDrag:= False; // don't start drag on double click
|
|
Point:= dgPanel.ScreenToClient(Mouse.CursorPos);
|
|
|
|
// If on a file/directory then choose it.
|
|
if (Point.Y >= dgPanel.GetHeaderHeight) and
|
|
(Point.Y < dgPanel.GridHeight) and
|
|
(not IsEmpty) then
|
|
begin
|
|
ChooseFile(GetActiveDisplayFile);
|
|
end;
|
|
|
|
{$IFDEF LCLGTK2}
|
|
dgPanel.fLastDoubleClickTime := Now;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelEnter(Sender: TObject);
|
|
begin
|
|
SetActive(True);
|
|
|
|
UpdateInfoPanel;
|
|
frmMain.EnableHotkeys(True);
|
|
|
|
if Assigned(OnActivate) then
|
|
OnActivate(Self);
|
|
end;
|
|
|
|
procedure TColumnsFileView.RedrawGrid;
|
|
begin
|
|
dgPanel.Invalidate;
|
|
end;
|
|
|
|
procedure TColumnsFileView.RedrawFile(DisplayFile: TDisplayFile);
|
|
var
|
|
VisibleFiles: TRange;
|
|
i: Integer;
|
|
begin
|
|
VisibleFiles := GetVisibleFilesIndexes;
|
|
for i := VisibleFiles.First to VisibleFiles.Last do
|
|
begin
|
|
if FFiles[i] = DisplayFile then
|
|
begin
|
|
dgPanel.InvalidateRow(i + dgPanel.FixedRows);
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.UpdateColumnsView;
|
|
var
|
|
ColumnsClass: TPanelColumnsClass;
|
|
OldFilePropertiesNeeded: TFilePropertiesTypes;
|
|
begin
|
|
if (ActiveColm <> '') or (isSlave and Assigned(ActiveColmSlave)) then
|
|
begin
|
|
// If the ActiveColm set doesn't exist this will retrieve either
|
|
// the first set or the default set.
|
|
ColumnsClass := GetColumnsClass;
|
|
// Set name in case a different set was loaded.
|
|
ActiveColm := ColumnsClass.Name;
|
|
|
|
SetColumnsWidths;
|
|
|
|
dgPanel.FocusRectVisible := ColumnsClass.GetCursorBorder;
|
|
dgPanel.FocusColor := ColumnsClass.GetCursorBorderColor;
|
|
|
|
OldFilePropertiesNeeded := FilePropertiesNeeded;
|
|
FilePropertiesNeeded := GetFilePropertiesNeeded;
|
|
if FilePropertiesNeeded >= OldFilePropertiesNeeded then
|
|
begin
|
|
EnsureDisplayProperties;
|
|
end;
|
|
end;
|
|
// else No columns set yet.
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelKeyUp(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
begin
|
|
case Key of
|
|
VK_SHIFT: begin
|
|
FLastSelectionStartRow := -1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelMouseLeave(Sender: TObject);
|
|
begin
|
|
if (gMouseSelectionEnabled) and (gMouseSelectionButton = 1) then
|
|
dgPanel.FMouseDown:= False;
|
|
end;
|
|
|
|
procedure TColumnsFileView.dgPanelKeyDown(Sender: TObject; var Key: Word;
|
|
Shift: TShiftState);
|
|
|
|
function CheckSearchOrFilter(ModifierKeys: TShiftState;
|
|
SearchOrFilterEnabled: Boolean;
|
|
SearchOrFilterMode: TShiftState;
|
|
out UTF8Char: TUTF8Char): Boolean;
|
|
begin
|
|
Result := False;
|
|
|
|
// used for quick search/filter by Ctrl+Alt+Letter and Alt+Letter
|
|
if SearchOrFilterEnabled then
|
|
begin
|
|
if ((SearchOrFilterMode <> []) and
|
|
// Check only Ctrl and Alt.
|
|
(ModifierKeys * [ssCtrl, ssAlt] = SearchOrFilterMode))
|
|
{$IFDEF MSWINDOWS}
|
|
// Entering international characters with Ctrl+Alt on Windows.
|
|
or ((SearchOrFilterMode = []) and
|
|
(ModifierKeys * [ssCtrl, ssAlt] = [ssCtrl, ssAlt]) and
|
|
(ModifierKeys - [ssCtrl, ssAlt, ssShift, ssCaps] = []))
|
|
{$ENDIF}
|
|
then
|
|
begin
|
|
UTF8Char := VirtualKeyToUTF8Char(Key, ModifierKeys - SearchOrFilterMode);
|
|
Result := UTF8Char <> '';
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ModifierKeys: TShiftState;
|
|
ScreenPoint: TPoint;
|
|
UTF8Char: TUTF8Char;
|
|
aFile: TDisplayFile;
|
|
begin
|
|
ModifierKeys := GetKeyShiftStateEx;
|
|
|
|
if CheckSearchOrFilter(ModifierKeys, gQuickSearch, gQuickSearchMode, UTF8Char) then
|
|
begin
|
|
ShowSearchPanel(UTF8Char);
|
|
Key := 0;
|
|
Exit;
|
|
end;
|
|
|
|
if CheckSearchOrFilter(ModifierKeys, gQuickFilter, gQuickFilterMode, UTF8Char) then
|
|
begin
|
|
ShowFilterPanel(UTF8Char);
|
|
Key := 0;
|
|
Exit;
|
|
end;
|
|
|
|
case Key of
|
|
VK_APPS:
|
|
begin
|
|
cm_ContextMenu('');
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_INSERT:
|
|
begin
|
|
if not IsEmpty then
|
|
begin
|
|
if IsActiveItemValid then
|
|
SelectFile(GetActiveDisplayFile);
|
|
dgPanel.InvalidateRow(dgPanel.Row);
|
|
if dgPanel.Row < dgPanel.RowCount-1 then
|
|
dgPanel.Row := dgPanel.Row+1;
|
|
MakeSelectedVisible;
|
|
end;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_MULTIPLY:
|
|
begin
|
|
InvertAll;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_ADD:
|
|
begin
|
|
if Shift = [ssCtrl] then
|
|
MarkAll
|
|
else if Shift = [] then
|
|
MarkPlus
|
|
else if Shift = [ssShift] then
|
|
MarkShiftPlus;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_SUBTRACT:
|
|
begin
|
|
if Shift = [ssCtrl] then
|
|
UnMarkAll
|
|
else if Shift = [] then
|
|
MarkMinus
|
|
else if Shift = [ssShift] then
|
|
MarkShiftMinus;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_SHIFT:
|
|
begin
|
|
FLastSelectionStartRow:= dgPanel.Row;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_HOME, VK_END, VK_PRIOR, VK_NEXT:
|
|
if (ssShift in Shift) then
|
|
begin
|
|
Application.QueueAsyncCall(@SelectRange, -1);
|
|
//Key := 0; // not needed!
|
|
end;
|
|
|
|
// cursors keys in Lynx like mode
|
|
VK_LEFT:
|
|
if (Shift = []) and gLynxLike then
|
|
begin
|
|
ChangePathToParent(True);
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_RIGHT:
|
|
if (Shift = []) and gLynxLike then
|
|
begin
|
|
if Assigned(GetActiveDisplayFile) then
|
|
ChooseFile(GetActiveDisplayFile, True);
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_UP, VK_DOWN:
|
|
begin
|
|
if ssShift in Shift then
|
|
begin
|
|
if IsActiveItemValid then
|
|
begin
|
|
SelectFile(GetActiveDisplayFile);
|
|
if (dgPanel.Row = dgPanel.RowCount-1) or (dgPanel.Row = dgPanel.FixedRows) then
|
|
dgPanel.Invalidate;
|
|
//Key := 0; // not needed!
|
|
end;
|
|
end
|
|
{$IFDEF LCLGTK2}
|
|
else
|
|
begin
|
|
if ((dgPanel.Row = dgPanel.RowCount-1) and (Key = VK_DOWN))
|
|
or ((dgPanel.Row = dgPanel.FixedRows) and (Key = VK_UP)) then
|
|
Key := 0;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
VK_SPACE:
|
|
if (Shift = []) and
|
|
((not frmMain.IsCommandLineVisible) or (frmMain.edtCommand.Text = '')) then
|
|
begin
|
|
if not IsEmpty then
|
|
begin
|
|
aFile := GetActiveDisplayFile;
|
|
if IsItemValid(aFile) then
|
|
begin
|
|
if (aFile.FSFile.IsDirectory or
|
|
aFile.FSFile.IsLinkToDirectory) and
|
|
not aFile.Selected then
|
|
begin
|
|
CalculateSpace(aFile);
|
|
end;
|
|
|
|
SelectFile(aFile);
|
|
end;
|
|
|
|
if gSpaceMovesDown then
|
|
dgPanel.Row := dgPanel.Row + 1;
|
|
|
|
dgPanel.Invalidate;
|
|
MakeSelectedVisible;
|
|
end;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_BACK:
|
|
if (Shift = []) and
|
|
((not frmMain.IsCommandLineVisible) or (frmMain.edtCommand.Text = '')) then
|
|
begin
|
|
if (frmMain.edtCommand.Tag = 0) then
|
|
begin
|
|
ChangePathToParent(True);
|
|
RedrawGrid;
|
|
end;
|
|
Key := 0;
|
|
end;
|
|
|
|
VK_RETURN, VK_SELECT:
|
|
begin
|
|
if (Shift=[]) or (Shift=[ssCaps]) then // 21.05.2009 - не учитываем CapsLock при перемещении по панелям
|
|
begin
|
|
// Only if there are items in the panel.
|
|
if not IsEmpty then
|
|
begin
|
|
ChooseFile(GetActiveDisplayFile);
|
|
Key := 0;
|
|
end;
|
|
end
|
|
// execute active file in terminal (Shift+Enter)
|
|
else if Shift=[ssShift] then
|
|
begin
|
|
if IsActiveItemValid then
|
|
begin
|
|
mbSetCurrentDir(CurrentPath);
|
|
ExecCmdFork(CurrentPath + GetActiveDisplayFile.FSFile.Name, True, gRunInTerm);
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
VK_MENU: // Alt key
|
|
if dgPanel.Dragging then
|
|
begin
|
|
// Force transform to external dragging in anticipation of user
|
|
// pressing Alt+Tab to change active application window.
|
|
|
|
// Disable flag, so that dragging isn't immediately transformed
|
|
// back to internal before the other application window is shown.
|
|
uDragDropEx.AllowTransformToInternal := False;
|
|
|
|
GetCursorPos(ScreenPoint);
|
|
dgPanel.TransformDraggingToExternal(ScreenPoint);
|
|
end;
|
|
|
|
VK_ESCAPE:
|
|
if GetCurrentWorkType <> fvwtNone then
|
|
begin
|
|
StopWorkers;
|
|
Key := 0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.lblPathClick(Sender: TObject);
|
|
begin
|
|
SetFocus;
|
|
|
|
if lblPath.SelectedDir <> '' then
|
|
begin
|
|
// User clicked on a subdirectory of the path.
|
|
CurrentPath := lblPath.SelectedDir;
|
|
end
|
|
else
|
|
Actions.cm_ViewHistory('');
|
|
end;
|
|
|
|
procedure TColumnsFileView.lblPathMouseUp(Sender: TObject; Button: TMouseButton;
|
|
Shift: TShiftState; X, Y: Integer);
|
|
begin
|
|
case Button of
|
|
mbMiddle:
|
|
begin
|
|
SetFocus;
|
|
Actions.cm_DirHotList('');
|
|
end;
|
|
|
|
mbRight:
|
|
begin
|
|
ShowPathEdit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.pnlHeaderResize(Sender: TObject);
|
|
begin
|
|
UpdateAddressLabel;
|
|
UpdatePathLabel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.ColumnsMenuClick(Sender: TObject);
|
|
var
|
|
frmColumnsSetConf: TfColumnsSetConf;
|
|
Index: Integer;
|
|
begin
|
|
Case (Sender as TMenuItem).Tag of
|
|
1000: //This
|
|
begin
|
|
frmColumnsSetConf := TfColumnsSetConf.Create(nil);
|
|
try
|
|
{EDIT Set}
|
|
frmColumnsSetConf.edtNameofColumnsSet.Text:=ColSet.GetColumnSet(ActiveColm).CurrentColumnsSetName;
|
|
Index:=ColSet.Items.IndexOf(ActiveColm);
|
|
frmColumnsSetConf.lbNrOfColumnsSet.Caption:=IntToStr(1 + Index);
|
|
frmColumnsSetConf.Tag:=Index;
|
|
frmColumnsSetConf.SetColumnsClass(GetColumnsClass);
|
|
{EDIT Set}
|
|
if frmColumnsSetConf.ShowModal = mrOK then
|
|
begin
|
|
// Force saving changes to config file.
|
|
SaveGlobs;
|
|
end;
|
|
finally
|
|
FreeAndNil(frmColumnsSetConf);
|
|
end;
|
|
|
|
frmMain.ReLoadTabs(frmMain.LeftTabs);
|
|
frmMain.ReLoadTabs(frmMain.RightTabs);
|
|
end;
|
|
1001: //All columns
|
|
begin
|
|
Actions.cm_Options('15');
|
|
frmMain.ReLoadTabs(frmMain.LeftTabs);
|
|
frmMain.ReLoadTabs(frmMain.RightTabs);
|
|
end;
|
|
|
|
else
|
|
begin
|
|
ActiveColm:=ColSet.Items[(Sender as TMenuItem).Tag];
|
|
UpdateColumnsView;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TColumnsFileView.GetGridHorzLine: Boolean;
|
|
begin
|
|
Result := goHorzLine in dgPanel.Options;
|
|
end;
|
|
|
|
function TColumnsFileView.GetGridVertLine: Boolean;
|
|
begin
|
|
Result := goVertLine in dgPanel.Options;
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetGridHorzLine(const AValue: Boolean);
|
|
begin
|
|
if AValue then
|
|
dgPanel.Options := dgPanel.Options + [goHorzLine]
|
|
else
|
|
dgPanel.Options := dgPanel.Options - [goHorzLine];
|
|
end;
|
|
|
|
procedure TColumnsFileView.SetGridVertLine(const AValue: Boolean);
|
|
begin
|
|
if AValue then
|
|
dgPanel.Options := dgPanel.Options + [goVertLine]
|
|
else
|
|
dgPanel.Options := dgPanel.Options - [goVertLine]
|
|
end;
|
|
|
|
constructor TColumnsFileView.Create(AOwner: TWinControl; AFileSource: IFileSource; APath: String);
|
|
begin
|
|
inherited Create(AOwner, AFileSource, APath);
|
|
|
|
FFiles := TDisplayFiles.Create;
|
|
FColumnsSorting := TColumnsSortings.Create;
|
|
ActiveColm := 'Default';
|
|
|
|
// Update view before making file source file list,
|
|
// so that file list isn't unnecessarily displayed twice.
|
|
UpdateView;
|
|
MakeFileSourceFileList;
|
|
end;
|
|
|
|
constructor TColumnsFileView.Create(AOwner: TWinControl; AFileView: TFileView);
|
|
begin
|
|
inherited Create(AOwner, AFileView);
|
|
UpdateView;
|
|
end;
|
|
|
|
constructor TColumnsFileView.Create(AOwner: TWinControl; AConfig: TIniFileEx; ASectionName: String; ATabIndex: Integer);
|
|
begin
|
|
inherited Create(AOwner, AConfig, ASectionName, ATabIndex);
|
|
|
|
FFiles := TDisplayFiles.Create;
|
|
FColumnsSorting := TColumnsSortings.Create;
|
|
|
|
LoadConfiguration(ASectionName, ATabIndex);
|
|
end;
|
|
|
|
constructor TColumnsFileView.Create(AOwner: TWinControl; AConfig: TXmlConfig; ANode: TXmlNode);
|
|
begin
|
|
inherited Create(AOwner, AConfig, ANode);
|
|
|
|
FFiles := TDisplayFiles.Create;
|
|
FColumnsSorting := TColumnsSortings.Create;
|
|
|
|
LoadConfiguration(AConfig, ANode);
|
|
|
|
if FileSourcesCount > 0 then
|
|
begin
|
|
// Update view before making file source file list,
|
|
// so that file list isn't unnecessarily displayed twice.
|
|
UpdateView;
|
|
MakeFileSourceFileList;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.CreateDefault(AOwner: TWinControl);
|
|
begin
|
|
DebugLn('TColumnsFileView.Create components');
|
|
|
|
dgPanel := nil;
|
|
|
|
BorderStyle := bsNone; // Before Create or the window handle may be recreated
|
|
inherited CreateDefault(AOwner);
|
|
Align := alClient;
|
|
|
|
ActiveColm := '';
|
|
ActiveColmSlave := nil;
|
|
isSlave := False;
|
|
FColumnsSorting := nil;
|
|
FLastSelectionStartRow := -1;
|
|
FLastMark := '*';
|
|
FSelection:= TStringListEx.Create;
|
|
FUpdatingGrid := False;
|
|
|
|
// -- other components
|
|
|
|
dgPanel:=TDrawGridEx.Create(Self, Self);
|
|
|
|
pnlHeader:=TPanel.Create(Self);
|
|
pnlHeader.Parent:=Self;
|
|
pnlHeader.Align:=alTop;
|
|
pnlHeader.BevelInner:=bvNone;
|
|
pnlHeader.BevelOuter:=bvNone;
|
|
pnlHeader.AutoSize := True;
|
|
|
|
lblAddress := TPathLabel.Create(pnlHeader, False);
|
|
lblAddress.Parent := pnlHeader;
|
|
lblAddress.BorderSpacing.Bottom := 1;
|
|
|
|
lblPath := TPathLabel.Create(pnlHeader, True);
|
|
lblPath.Parent := pnlHeader;
|
|
|
|
// Display path below address.
|
|
// For correct alignment, first put path at the top, then address at the top.
|
|
lblPath.Align := alTop;
|
|
lblAddress.Align := alTop;
|
|
|
|
edtPath:=TEdit.Create(lblPath);
|
|
edtPath.Parent:=pnlHeader;
|
|
edtPath.Visible:=False;
|
|
edtPath.TabStop:=False;
|
|
|
|
pnlFooter:=TPanel.Create(Self);
|
|
pnlFooter.Parent:=Self;
|
|
pnlFooter.Align:=alBottom;
|
|
pnlFooter.BevelInner:=bvNone;
|
|
pnlFooter.BevelOuter:=bvNone;
|
|
pnlFooter.AutoSize := True;
|
|
|
|
lblInfo:=TLabel.Create(pnlFooter);
|
|
lblInfo.Parent:=pnlFooter;
|
|
lblInfo.AutoSize:=False;
|
|
lblInfo.Height := lblInfo.Canvas.TextHeight('Wg');
|
|
lblInfo.Align := alClient;
|
|
|
|
edtRename:=TEdit.Create(dgPanel);
|
|
edtRename.Parent:=dgPanel;
|
|
edtRename.Visible:=False;
|
|
edtRename.TabStop:=False;
|
|
edtRename.AutoSize:=False;
|
|
|
|
// now create search panel
|
|
pnAltSearch:=TPanel.Create(Self);
|
|
pnAltSearch.Parent:=Self;
|
|
pnAltSearch.Caption:=rsQuickSearchPanel;
|
|
pnAltSearch.Alignment:=taLeftJustify;
|
|
pnAltSearch.Visible := False;
|
|
|
|
edtSearch:=TEdit.Create(pnAltSearch);
|
|
edtSearch.Parent:=pnAltSearch;
|
|
edtSearch.TabStop:=False;
|
|
edtSearch.Left:=64;
|
|
edtSearch.Top:=1;
|
|
|
|
// Create filter panel.
|
|
pnlFilter := TPanel.Create(Self);
|
|
pnlFilter.Parent := Self;
|
|
pnlFilter.Visible := False;
|
|
pnlFilter.Align := alBottom;
|
|
pnlFilter.AutoSize := True;
|
|
pnlFilter.Caption := rsQuickFilterPanel;
|
|
pnlFilter.Alignment := taLeftJustify;
|
|
pnlFilter.BevelWidth := 1;
|
|
pnlFilter.BevelInner := bvSpace;
|
|
|
|
edtFilter := TEdit.Create(pnlFilter);
|
|
edtFilter.Parent := pnlFilter;
|
|
edtFilter.TabStop := False;
|
|
edtfilter.BorderSpacing.Left := 64;
|
|
edtFilter.Align := alLeft;
|
|
|
|
btnCloseFilter := TButton.Create(pnlFilter);
|
|
btnCloseFilter.Parent := pnlFilter;
|
|
btnCloseFilter.Align := alRight;
|
|
btnCloseFilter.Caption := 'x';
|
|
btnCloseFilter.AutoSize := True;
|
|
|
|
tmContextMenu:= TTimer.Create(Self);
|
|
tmContextMenu.Enabled:= False;
|
|
tmContextMenu.Interval:= 500;
|
|
tmContextMenu.OnTimer:= @tmContextMenuTimer;
|
|
|
|
tmClearGrid := TTimer.Create(Self);
|
|
tmClearGrid.Enabled := False;
|
|
tmClearGrid.Interval := 500;
|
|
tmClearGrid.OnTimer := @tmClearGridTimer;
|
|
|
|
{$IFDEF LCLCARBON}
|
|
// Under Carbon AutoSize don't work without it
|
|
pnlHeader.ClientHeight:= 0;
|
|
pnlFooter.ClientHeight:= 0;
|
|
{$ENDIF}
|
|
|
|
// ---
|
|
dgPanel.OnUTF8KeyPress := @UTF8KeyPressEvent;
|
|
dgPanel.OnMouseLeave:= @dgPanelMouseLeave;
|
|
dgPanel.OnMouseDown := @dgPanelMouseDown;
|
|
dgPanel.OnStartDrag := @dgPanelStartDrag;
|
|
dgPanel.OnMouseMove:= @dgPanelMouseMove;
|
|
dgPanel.OnDragOver := @dgPanelDragOver;
|
|
dgPanel.OnDragDrop:= @dgPanelDragDrop;
|
|
dgPanel.OnEndDrag:= @dgPanelEndDrag;
|
|
dgPanel.OnDblClick:=@dgPanelDblClick;
|
|
dgPanel.OnEnter:=@dgPanelEnter;
|
|
dgPanel.OnExit:=@dgPanelExit;
|
|
dgPanel.OnKeyUp:=@dgPanelKeyUp;
|
|
dgPanel.OnKeyDown:=@dgPanelKeyDown;
|
|
dgPanel.OnHeaderClick:=@dgPanelHeaderClick;
|
|
dgPanel.OnMouseWheelUp := @dgPanelMouseWheelUp;
|
|
dgPanel.OnMouseWheelDown := @dgPanelMouseWheelDown;
|
|
dgPanel.OnSelection:= @dgPanelSelection;
|
|
dgPanel.OnShowHint:= @dgPanelShowHint;
|
|
dgPanel.OnTopLeftChanged:= @dgPanelTopLeftChanged;
|
|
dgpanel.OnResize:= @dgPanelResize;
|
|
|
|
edtSearch.OnChange := @edtSearchChange;
|
|
edtSearch.OnKeyDown := @edtSearchKeyDown;
|
|
edtSearch.OnExit := @edtSearchExit;
|
|
|
|
edtFilter.OnChange := @edtFilterChange;
|
|
edtFilter.OnKeyDown := @edtFilterKeyDown;
|
|
edtFilter.OnEnter := @edtFilterEnter;
|
|
edtFilter.OnExit := @edtFilterExit;
|
|
|
|
edtPath.OnKeyDown := @edtPathKeyDown;
|
|
edtPath.OnExit := @edtPathExit;
|
|
|
|
edtRename.OnKeyDown := @edtRenameKeyDown;
|
|
edtRename.OnExit := @edtRenameExit;
|
|
|
|
btnCloseFilter.OnClick := @btnCloseFilterClick;
|
|
|
|
pnlHeader.OnResize := @pnlHeaderResize;
|
|
|
|
lblPath.OnClick := @lblPathClick;
|
|
lblPath.OnMouseUp := @lblPathMouseUp;
|
|
|
|
pmColumnsMenu := TPopupMenu.Create(Self);
|
|
pmColumnsMenu.Parent := Self;
|
|
end;
|
|
|
|
destructor TColumnsFileView.Destroy;
|
|
begin
|
|
FreeThenNil(FSelection);
|
|
FreeThenNil(FColumnsSorting);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TColumnsFileView.Clone(NewParent: TWinControl): TColumnsFileView;
|
|
begin
|
|
Result := TColumnsFileView.Create(NewParent, Self);
|
|
end;
|
|
|
|
procedure TColumnsFileView.CloneTo(FileView: TFileView);
|
|
begin
|
|
if Assigned(FileView) then
|
|
begin
|
|
inherited CloneTo(FileView);
|
|
|
|
with FileView as TColumnsFileView do
|
|
begin
|
|
FLastMark := Self.FLastMark;
|
|
FLastSelectionStartRow := Self.FLastSelectionStartRow;
|
|
|
|
if Self.FileFilter <> '' then
|
|
begin
|
|
edtFilter.Text := Self.FileFilter; // will trigger assiging to FileFilter
|
|
FilterPanelVisible;
|
|
end;
|
|
|
|
FColumnsSorting := Self.FColumnsSorting.Clone;
|
|
|
|
ActiveColm := Self.ActiveColm;
|
|
ActiveColmSlave := nil; // set to nil because only used in preview?
|
|
isSlave := Self.isSlave;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.AddFileSource(aFileSource: IFileSource; aPath: String);
|
|
begin
|
|
inherited AddFileSource(aFileSource, aPath);
|
|
|
|
FUpdatingGrid := True;
|
|
dgPanel.Row := 0;
|
|
FUpdatingGrid := False;
|
|
|
|
UpdateAddressLabel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.RemoveCurrentFileSource;
|
|
var
|
|
FocusedFile: String;
|
|
begin
|
|
// Temporary. Do this by remembering the file name in a list?
|
|
FocusedFile := ExtractFileName(FileSource.CurrentAddress);
|
|
|
|
inherited;
|
|
|
|
SetActiveFile(FocusedFile);
|
|
|
|
UpdateAddressLabel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.BeforeMakeFileList;
|
|
begin
|
|
inherited;
|
|
if gListFilesInThread then
|
|
begin
|
|
// Display info that file list is being loaded.
|
|
UpdateInfoPanel;
|
|
|
|
// If we cleared grid here there would be flickering if list operation is quickly completed.
|
|
// So, only clear the grid after the file list has been loading for some time.
|
|
tmClearGrid.Enabled := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.AfterMakeFileList;
|
|
begin
|
|
inherited;
|
|
tmClearGrid.Enabled := False;
|
|
DisplayFileListHasChanged;
|
|
EnsureDisplayProperties; // After displaying.
|
|
end;
|
|
|
|
procedure TColumnsFileView.DisplayFileListHasChanged;
|
|
begin
|
|
MakeColumnsStrings;
|
|
|
|
// Update grid row count.
|
|
SetRowCount(FFiles.Count);
|
|
RedrawGrid;
|
|
|
|
if SetActiveFileNow(RequestedActiveFile) then
|
|
RequestedActiveFile := ''
|
|
else
|
|
// Requested file was not found, restore position to last active file.
|
|
SetActiveFileNow(LastActiveFile);
|
|
|
|
UpdateInfoPanel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MakeColumnsStrings;
|
|
var
|
|
i, ACol: Integer;
|
|
ColumnsClass: TPanelColumnsClass;
|
|
AFile: TDisplayFile;
|
|
begin
|
|
ColumnsClass := GetColumnsClass;
|
|
|
|
for i := 0 to FFiles.Count - 1 do
|
|
begin
|
|
AFile := FFiles[i];
|
|
AFile.DisplayStrings.Clear;
|
|
for ACol := 0 to ColumnsClass.Count - 1 do
|
|
begin
|
|
AFile.DisplayStrings.Add(ColumnsClass.GetColumnItemResultString(
|
|
ACol, AFile.FSFile, FileSource));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.MakeColumnsStrings(AFile: TDisplayFile);
|
|
var
|
|
ACol: Integer;
|
|
ColumnsClass: TPanelColumnsClass;
|
|
begin
|
|
ColumnsClass := GetColumnsClass;
|
|
|
|
AFile.DisplayStrings.Clear;
|
|
for ACol := 0 to ColumnsClass.Count - 1 do
|
|
begin
|
|
AFile.DisplayStrings.Add(ColumnsClass.GetColumnItemResultString(
|
|
ACol, AFile.FSFile, FileSource));
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.EnsureDisplayProperties;
|
|
var
|
|
VisibleFiles: TRange;
|
|
i: Integer;
|
|
AFileList: TFVWorkerFileList;
|
|
Worker: TFileViewWorker;
|
|
AFile: TDisplayFile;
|
|
begin
|
|
if (GetCurrentWorkType = fvwtCreate) or
|
|
(not Assigned(FFiles)) or
|
|
(csDestroying in ComponentState) then
|
|
Exit;
|
|
|
|
VisibleFiles := GetVisibleFilesIndexes;
|
|
|
|
if not gListFilesInThread then
|
|
begin
|
|
for i := VisibleFiles.First to VisibleFiles.Last do
|
|
begin
|
|
AFile := FFiles[i];
|
|
if AFile.FSFile.Name <> '..' then
|
|
begin
|
|
if AFile.IconID = -1 then
|
|
AFile.IconID := PixMapManager.GetIconByFile(AFile.FSFile, fspDirectAccess in FileSource.Properties, True);
|
|
FileSource.RetrieveProperties(AFile.FSFile, FilePropertiesNeeded);
|
|
MakeColumnsStrings(AFile);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
AFileList := TFVWorkerFileList.Create;
|
|
try
|
|
for i := VisibleFiles.First to VisibleFiles.Last do
|
|
begin
|
|
AFile := FFiles[i];
|
|
if (AFile.FSFile.Name <> '..') and
|
|
(FileSource.CanRetrieveProperties(AFile.FSFile, FilePropertiesNeeded) or (AFile.IconID = -1)) then
|
|
begin
|
|
AFileList.AddClone(AFile, AFile);
|
|
end;
|
|
end;
|
|
|
|
if AFileList.Count > 0 then
|
|
begin
|
|
Worker := TFilePropertiesRetriever.Create(
|
|
FileSource,
|
|
WorkersThread,
|
|
FilePropertiesNeeded,
|
|
@UpdateFile,
|
|
AFileList);
|
|
|
|
AddWorker(Worker, False);
|
|
WorkersThread.QueueFunction(@Worker.StartParam);
|
|
end;
|
|
|
|
finally
|
|
if Assigned(AFileList) then
|
|
FreeAndNil(AFileList);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.UpdateFile(const UpdatedFile: TDisplayFile;
|
|
const UserData: Pointer);
|
|
var
|
|
propType: TFilePropertyType;
|
|
aFile: TFile;
|
|
OrigDisplayFile: TDisplayFile;
|
|
begin
|
|
OrigDisplayFile := TDisplayFile(UserData);
|
|
|
|
if not IsReferenceValid(OrigDisplayFile) then
|
|
Exit; // File does not exist anymore (reference is invalid).
|
|
|
|
aFile := OrigDisplayFile.FSFile;
|
|
|
|
{$IF (fpc_version>2) or ((fpc_version=2) and (fpc_release>4))}
|
|
// This is a bit faster.
|
|
for propType in UpdatedFile.FSFile.AssignedProperties - aFile.AssignedProperties do
|
|
{$ELSE}
|
|
for propType := Low(TFilePropertyType) to High(TFilePropertyType) do
|
|
if (propType in UpdatedFile.FSFile.AssignedProperties) and
|
|
(not (propType in aFile.AssignedProperties)) then
|
|
{$ENDIF}
|
|
begin
|
|
aFile.Properties[propType] := UpdatedFile.FSFile.ReleaseProperty(propType);
|
|
end;
|
|
|
|
if UpdatedFile.IconID <> -1 then
|
|
OrigDisplayFile.IconID := UpdatedFile.IconID;
|
|
|
|
MakeColumnsStrings(OrigDisplayFile);
|
|
RedrawFile(OrigDisplayFile);
|
|
end;
|
|
|
|
procedure TColumnsFileView.CalcSpaceUpdateFile(const UpdatedFile: TDisplayFile;
|
|
const UserData: Pointer);
|
|
var
|
|
OrigDisplayFile: TDisplayFile;
|
|
begin
|
|
OrigDisplayFile := TDisplayFile(UserData);
|
|
|
|
if not IsReferenceValid(OrigDisplayFile) then
|
|
Exit; // File does not exist anymore (reference is invalid).
|
|
|
|
OrigDisplayFile.FSFile.Size := UpdatedFile.FSFile.Size;
|
|
MakeColumnsStrings(OrigDisplayFile);
|
|
RedrawFile(OrigDisplayFile);
|
|
end;
|
|
|
|
procedure TColumnsFileView.WorkerStarting(const Worker: TFileViewWorker);
|
|
begin
|
|
inherited;
|
|
dgPanel.Cursor := crHourGlass;
|
|
UpdateInfoPanel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.WorkerFinished(const Worker: TFileViewWorker);
|
|
begin
|
|
inherited;
|
|
dgPanel.Cursor := crDefault;
|
|
UpdateInfoPanel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.UpdateView;
|
|
var
|
|
bLoadingFilelist: Boolean;
|
|
begin
|
|
inherited;
|
|
|
|
bLoadingFilelist := GetCurrentWorkType = fvwtCreate;
|
|
StopWorkers;
|
|
|
|
pnlHeader.Visible := gCurDir; // Current directory
|
|
pnlFooter.Visible := gStatusBar; // Status bar
|
|
GridVertLine:= gGridVertLine;
|
|
GridHorzLine:= gGridHorzLine;
|
|
|
|
UpdateAddressLabel;
|
|
UpdatePathLabel;
|
|
dgPanel.UpdateView;
|
|
UpdateColumnsView;
|
|
|
|
if bLoadingFilelist then
|
|
MakeFileSourceFileList
|
|
else if Assigned(FFiles) then // This condition is needed when cloning.
|
|
ReDisplayFileList;
|
|
end;
|
|
|
|
function TColumnsFileView.GetActiveDisplayFile: TDisplayFile;
|
|
var
|
|
CurrentRow: Integer;
|
|
begin
|
|
if not IsEmpty then
|
|
begin
|
|
CurrentRow := dgPanel.Row;
|
|
if CurrentRow < dgPanel.FixedRows then
|
|
CurrentRow := dgPanel.FixedRows
|
|
else if CurrentRow > FFiles.Count then
|
|
CurrentRow := dgPanel.FixedRows;
|
|
|
|
Result := FFiles[CurrentRow - dgPanel.FixedRows]; // minus fixed header
|
|
end
|
|
else
|
|
Result := nil; // No files in the panel.
|
|
end;
|
|
|
|
function TColumnsFileView.GetVisibleFilesIndexes: TRange;
|
|
begin
|
|
Result := dgPanel.GetVisibleRows;
|
|
Dec(Result.First, dgPanel.FixedRows);
|
|
Dec(Result.Last, dgPanel.FixedRows);
|
|
|
|
if Result.First < 0 then
|
|
Result.First := 0;
|
|
if Result.Last >= FFiles.Count then
|
|
Result.Last := FFiles.Count - 1;
|
|
end;
|
|
|
|
function TColumnsFileView.GetColumnsClass: TPanelColumnsClass;
|
|
begin
|
|
if isSlave then
|
|
Result := ActiveColmSlave
|
|
else
|
|
Result := ColSet.GetColumnSet(ActiveColm);
|
|
end;
|
|
|
|
procedure TColumnsFileView.CalculateSpaceOfAllDirectories;
|
|
var
|
|
i: Integer;
|
|
AFileList: TFVWorkerFileList;
|
|
AFile: TDisplayFile;
|
|
begin
|
|
AFileList := TFVWorkerFileList.Create;
|
|
try
|
|
for i := 0 to FFiles.Count - 1 do
|
|
begin
|
|
AFile := FFiles[i];
|
|
if IsItemValid(AFile) and AFile.FSFile.IsDirectory then
|
|
begin
|
|
AFileList.AddClone(AFile, AFile);
|
|
end;
|
|
end;
|
|
|
|
CalculateSpace(AFileList);
|
|
|
|
finally
|
|
if Assigned(AFileList) then
|
|
FreeAndNil(AFileList);
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.CalculateSpace(AFile: TDisplayFile);
|
|
var
|
|
AFileList: TFVWorkerFileList;
|
|
begin
|
|
if GetCurrentWorkType = fvwtCreate then
|
|
Exit;
|
|
|
|
AFileList := TFVWorkerFileList.Create;
|
|
try
|
|
if IsItemValid(AFile) and AFile.FSFile.IsDirectory then
|
|
begin
|
|
AFileList.AddClone(AFile, AFile);
|
|
end;
|
|
|
|
CalculateSpace(AFileList);
|
|
|
|
finally
|
|
if Assigned(AFileList) then
|
|
FreeAndNil(AFileList);
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.CalculateSpace(var AFileList: TFVWorkerFileList);
|
|
var
|
|
Worker: TFileViewWorker;
|
|
begin
|
|
if GetCurrentWorkType = fvwtCreate then
|
|
Exit;
|
|
|
|
if AFileList.Count > 0 then
|
|
begin
|
|
Worker := TCalculateSpaceWorker.Create(
|
|
FileSource,
|
|
WorkersThread,
|
|
@CalcSpaceUpdateFile,
|
|
AFileList);
|
|
|
|
AddWorker(Worker);
|
|
WorkersThread.QueueFunction(@Worker.StartParam);
|
|
end
|
|
else
|
|
FreeAndNil(AFileList);
|
|
end;
|
|
|
|
procedure TColumnsFileView.UTF8KeyPressEvent(Sender: TObject; var UTF8Key: TUTF8Char);
|
|
|
|
|
|
function CheckSearchOrFilter(ModifierKeys: TShiftState;
|
|
SearchOrFilterEnabled: Boolean;
|
|
SearchOrFilterMode: TShiftState): Boolean;
|
|
begin
|
|
if SearchOrFilterEnabled and (SearchOrFilterMode = []) and
|
|
// Check only ssCtrl and ssAlt.
|
|
(ModifierKeys * [ssCtrl, ssAlt] = SearchOrFilterMode) then
|
|
begin
|
|
// Make upper case if either caps-lock is toggled or shift pressed.
|
|
if (ssCaps in ModifierKeys) xor (ssShift in ModifierKeys) then
|
|
UTF8Key := UTF8UpperCase(UTF8Key)
|
|
else
|
|
UTF8Key := UTF8LowerCase(UTF8Key);
|
|
|
|
Result := True;
|
|
end
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
var
|
|
ModifierKeys: TShiftState;
|
|
begin
|
|
// quick search by Letter only
|
|
|
|
// Check for certain Ascii keys.
|
|
if (Length(UTF8Key) = 1) and ((Ord(UTF8Key[1]) <= 32) or
|
|
(UTF8Key[1] in ['+','-','*','/','\'])) then Exit;
|
|
|
|
ModifierKeys := GetKeyShiftStateEx;
|
|
|
|
if CheckSearchOrFilter(ModifierKeys, gQuickSearch, gQuickSearchMode) then
|
|
begin
|
|
ShowSearchPanel(UTF8Key);
|
|
UTF8Key := '';
|
|
end
|
|
else if CheckSearchOrFilter(ModifierKeys, gQuickFilter, gQuickFilterMode) then
|
|
begin
|
|
ShowFilterPanel(UTF8Key);
|
|
UTF8Key := '';
|
|
end
|
|
end;
|
|
|
|
procedure TColumnsFileView.DoDragDropOperation(Operation: TDragDropOperation;
|
|
var DropParams: TDropParams);
|
|
var
|
|
AFile: TDisplayFile;
|
|
iCol, iRow: Integer;
|
|
ClientDropPoint: TPoint;
|
|
begin
|
|
try
|
|
with DropParams do
|
|
begin
|
|
if Files.Count > 0 then
|
|
begin
|
|
ClientDropPoint := dgPanel.ScreenToClient(ScreenDropPoint);
|
|
dgPanel.MouseToCell(ClientDropPoint.X, ClientDropPoint.Y, iCol, iRow);
|
|
|
|
// default to current active directory in the destination panel
|
|
TargetPath := Self.CurrentPath;
|
|
|
|
if (DropIntoDirectories = True) and
|
|
(iRow >= dgPanel.FixedRows) and
|
|
(dgPanel.MouseOnGrid(ClientDropPoint.X, ClientDropPoint.Y)) then
|
|
begin
|
|
AFile := FFiles[iRow - dgPanel.FixedRows];
|
|
|
|
// If dropped into a directory modify destination path accordingly.
|
|
if Assigned(AFile) and
|
|
(AFile.FSFile.IsDirectory or AFile.FSFile.IsLinkToDirectory) then
|
|
begin
|
|
if AFile.FSFile.Name = '..' then
|
|
// remove the last subdirectory in the path
|
|
TargetPath := GetParentDir(TargetPath)
|
|
else
|
|
TargetPath := TargetPath + AFile.FSFile.Name + DirectorySeparator;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Execute the operation.
|
|
frmMain.DoDragDropOperation(Operation, DropParams);
|
|
|
|
finally
|
|
if Assigned(DropParams) then
|
|
FreeAndNil(DropParams);
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_MarkInvert(param: string='');
|
|
begin
|
|
InvertAll;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_MarkMarkAll(param: string='');
|
|
begin
|
|
MarkAll;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_MarkUnmarkAll(param: string='');
|
|
begin
|
|
UnMarkAll;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_MarkPlus(param: string='');
|
|
begin
|
|
MarkPlus;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_MarkMinus(param: string='');
|
|
begin
|
|
MarkMinus;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_MarkCurrentExtension(param: string='');
|
|
begin
|
|
MarkShiftPlus;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_UnmarkCurrentExtension(param: string='');
|
|
begin
|
|
MarkShiftMinus;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_SaveSelection(param: string);
|
|
begin
|
|
SaveSelection;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_RestoreSelection(param: string);
|
|
begin
|
|
RestoreSelection;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_SaveSelectionToFile(param: string);
|
|
begin
|
|
with dmComData do
|
|
begin
|
|
SaveDialog.DefaultExt:= '.txt';
|
|
SaveDialog.Filter:= '*.txt|*.txt';
|
|
SaveDialog.FileName:= param;
|
|
if (param <> EmptyStr) or SaveDialog.Execute then
|
|
try
|
|
SaveSelection;
|
|
FSelection.SaveToFile(SaveDialog.FileName);
|
|
except
|
|
on E: Exception do
|
|
msgError(rsMsgErrSaveFile + '-' + E.Message);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_LoadSelectionFromFile(param: string);
|
|
begin
|
|
with dmComData do
|
|
begin
|
|
OpenDialog.DefaultExt:= '.txt';
|
|
OpenDialog.Filter:= '*.txt|*.txt';
|
|
OpenDialog.FileName:= param;
|
|
if ((param <> EmptyStr) and mbFileExists(param)) or OpenDialog.Execute then
|
|
try
|
|
FSelection.LoadFromFile(OpenDialog.FileName);
|
|
RestoreSelection;
|
|
except
|
|
on E: Exception do
|
|
msgError(rsMsgErrEOpen + '-' + E.Message);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_LoadSelectionFromClip(param: string);
|
|
begin
|
|
FSelection.Text:= Clipboard.AsText;
|
|
RestoreSelection;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_QuickSearch(param: string='');
|
|
begin
|
|
ShowSearchPanel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_QuickFilter(param: string='');
|
|
begin
|
|
ShowFilterPanel;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_Open(param: string='');
|
|
begin
|
|
if Assigned(GetActiveDisplayFile) then
|
|
ChooseFile(GetActiveDisplayFile);
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_CountDirContent(param: string='');
|
|
begin
|
|
CalculateSpaceOfAllDirectories;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_RenameOnly(param: string='');
|
|
var
|
|
aFile: TFile;
|
|
begin
|
|
if (fsoSetFileProperty in FileSource.GetOperationsTypes) then
|
|
begin
|
|
aFile:= CloneActiveFile;
|
|
if Assigned(aFile) then
|
|
try
|
|
if aFile.IsNameValid then
|
|
begin
|
|
ShowRenameFileEdit(CurrentPath + aFile.Name);
|
|
end;
|
|
finally
|
|
FreeAndNil(aFile);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_ContextMenu(param: string='');
|
|
var
|
|
Rect: TRect;
|
|
Point: TPoint;
|
|
begin
|
|
Rect := dgPanel.CellRect(0, dgPanel.Row);
|
|
Point.X := Rect.Left + ((Rect.Right - Rect.Left) div 2);
|
|
Point.Y := Rect.Top + ((Rect.Bottom - Rect.Top) div 2);
|
|
Point := dgPanel.ClientToScreen(Point);
|
|
Actions.DoContextMenu(Self, Point.X, Point.Y, False);
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_EditPath(param: string);
|
|
begin
|
|
ShowPathEdit;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_GoToFirstFile(param: string);
|
|
begin
|
|
dgPanel.Row:= dgPanel.FixedRows;
|
|
end;
|
|
|
|
procedure TColumnsFileView.cm_GoToLastFile(param: string);
|
|
begin
|
|
dgPanel.Row:= dgPanel.RowCount - 1;
|
|
end;
|
|
|
|
{ TDrawGridEx }
|
|
|
|
constructor TDrawGridEx.Create(AOwner: TComponent; AParent: TWinControl);
|
|
begin
|
|
// Initialize D&D before calling inherited create,
|
|
// because it will create the control and call InitializeWnd.
|
|
DragDropSource := nil;
|
|
DragDropTarget := nil;
|
|
TransformDragging := False;
|
|
FMouseDown := False;
|
|
|
|
{$IFDEF LCLGTK2}
|
|
FLastDoubleClickTime := Now;
|
|
{$ENDIF}
|
|
|
|
inherited Create(AOwner);
|
|
|
|
// Workaround for Lazarus issue 18832.
|
|
// Set Fixed... before setting ...Count.
|
|
FixedRows := 0;
|
|
FixedCols := 0;
|
|
|
|
// Override default values to start with no columns and no rows.
|
|
RowCount := 0;
|
|
ColCount := 0;
|
|
|
|
Self.Parent := AParent;
|
|
ColumnsView := AParent as TColumnsFileView;
|
|
|
|
StartDrag := False;
|
|
DropRowIndex := -1;
|
|
HintRowIndex := -1;
|
|
|
|
DoubleBuffered := True;
|
|
Align := alClient;
|
|
Options := [goFixedVertLine, goFixedHorzLine, goTabs, goRowSelect,
|
|
goColSizing, goThumbTracking, goSmoothScroll];
|
|
|
|
TitleStyle := tsStandard;
|
|
TabStop := False;
|
|
|
|
UpdateView;
|
|
end;
|
|
|
|
procedure TDrawGridEx.UpdateView;
|
|
|
|
function CalculateDefaultRowHeight: Integer;
|
|
var
|
|
OldFont, NewFont: TFont;
|
|
i: Integer;
|
|
MaxFontHeight: Integer = 0;
|
|
CurrentHeight: Integer;
|
|
ColumnsSet: TPanelColumnsClass;
|
|
begin
|
|
// Start with height of the icons.
|
|
if gShowIcons <> sim_none then
|
|
MaxFontHeight := gIconsSize;
|
|
|
|
// Get columns settings.
|
|
with (Parent as TColumnsFileView) do
|
|
begin
|
|
if not isSlave then
|
|
ColumnsSet := ColSet.GetColumnSet(ActiveColm)
|
|
else
|
|
ColumnsSet := ActiveColmSlave;
|
|
end;
|
|
|
|
// Assign temporary font.
|
|
OldFont := Canvas.Font;
|
|
NewFont := TFont.Create;
|
|
Canvas.Font := NewFont;
|
|
|
|
// Search columns settings for the biggest font (in height).
|
|
for i := 0 to ColumnsSet.Count - 1 do
|
|
begin
|
|
Canvas.Font.Name := ColumnsSet.GetColumnFontName(i);
|
|
Canvas.Font.Style := ColumnsSet.GetColumnFontStyle(i);
|
|
Canvas.Font.Size := ColumnsSet.GetColumnFontSize(i);
|
|
|
|
CurrentHeight := Canvas.GetTextHeight('Wg');
|
|
MaxFontHeight := Max(MaxFontHeight, CurrentHeight);
|
|
end;
|
|
|
|
// Restore old font.
|
|
Canvas.Font := OldFont;
|
|
FreeAndNil(NewFont);
|
|
|
|
Result := MaxFontHeight;
|
|
end;
|
|
|
|
var
|
|
TabHeaderHeight: Integer;
|
|
TempRowHeight: Integer;
|
|
begin
|
|
Flat := gInterfaceFlat;
|
|
Color := ColumnsView.DimColor(gBackColor);
|
|
AutoFillColumns:= gAutoFillColumns;
|
|
ShowHint:= (gShowToolTipMode <> []);
|
|
|
|
// Calculate row height.
|
|
TempRowHeight := CalculateDefaultRowHeight;
|
|
if TempRowHeight > 0 then
|
|
DefaultRowHeight := TempRowHeight;
|
|
|
|
// Set rows of header.
|
|
if gTabHeader then
|
|
begin
|
|
if RowCount < 1 then
|
|
RowCount := 1;
|
|
|
|
FixedRows := 1;
|
|
|
|
TabHeaderHeight := Max(gIconsSize, Canvas.TextHeight('Wg'));
|
|
if not gInterfaceFlat then
|
|
begin
|
|
TabHeaderHeight := TabHeaderHeight + 2;
|
|
end;
|
|
RowHeights[0] := TabHeaderHeight;
|
|
end
|
|
else
|
|
FixedRows := 0;
|
|
|
|
FixedCols := 0;
|
|
end;
|
|
|
|
procedure TDrawGridEx.InitializeWnd;
|
|
begin
|
|
inherited;
|
|
|
|
// Register as drag&drop source and target.
|
|
DragDropSource := uDragDropEx.CreateDragDropSource(Self);
|
|
if Assigned(DragDropSource) then
|
|
DragDropSource.RegisterEvents(nil, nil, @OnExDragEnd);
|
|
|
|
DragDropTarget := uDragDropEx.CreateDragDropTarget(Self);
|
|
if Assigned(DragDropTarget) then
|
|
DragDropTarget.RegisterEvents(@OnExDragEnter,@OnExDragOver,
|
|
@OnExDrop,@OnExDragLeave);
|
|
end;
|
|
|
|
procedure TDrawGridEx.FinalizeWnd;
|
|
begin
|
|
if Assigned(DragDropSource) then
|
|
FreeAndNil(DragDropSource);
|
|
if Assigned(DragDropTarget) then
|
|
FreeAndNil(DragDropTarget);
|
|
|
|
inherited;
|
|
end;
|
|
|
|
procedure TDrawGridEx.DrawCell(aCol, aRow: Integer; aRect: TRect;
|
|
aState: TGridDrawState);
|
|
var
|
|
//shared variables
|
|
s: string;
|
|
iTextTop: Integer;
|
|
AFile: TDisplayFile;
|
|
FileSourceDirectAccess: Boolean;
|
|
ColumnsSet: TPanelColumnsClass;
|
|
|
|
//------------------------------------------------------
|
|
//begin subprocedures
|
|
//------------------------------------------------------
|
|
|
|
procedure DrawFixed;
|
|
//------------------------------------------------------
|
|
var
|
|
SortingDirection: TSortDirection;
|
|
TitleX: Integer;
|
|
begin
|
|
// Draw background.
|
|
Canvas.Brush.Color := GetColumnColor(ACol, True);
|
|
Canvas.FillRect(aRect);
|
|
|
|
SetCanvasFont(GetColumnFont(aCol, True));
|
|
|
|
iTextTop := aRect.Top + (RowHeights[aRow] - Canvas.TextHeight('Wg')) div 2;
|
|
|
|
TitleX := 0;
|
|
s := ColumnsSet.GetColumnTitle(ACol);
|
|
|
|
SortingDirection := ColumnsView.FColumnsSorting.GetSortingDirection(ACol);
|
|
if SortingDirection <> sdNone then
|
|
begin
|
|
TitleX := TitleX + gIconsSize;
|
|
PixMapManager.DrawBitmap(
|
|
PixMapManager.GetIconBySortingDirection(SortingDirection),
|
|
Canvas,
|
|
aRect.Left, aRect.Top);
|
|
end;
|
|
|
|
TitleX := max(TitleX, 4);
|
|
|
|
if gCutTextToColWidth then
|
|
begin
|
|
if (aRect.Right - aRect.Left) < TitleX then
|
|
// Column too small to display text.
|
|
Exit
|
|
else
|
|
while Canvas.TextWidth(s) - ((aRect.Right - aRect.Left) - TitleX) > 0 do
|
|
UTF8Delete(s, UTF8Length(s), 1);
|
|
end;
|
|
|
|
Canvas.TextOut(aRect.Left + TitleX, iTextTop, s);
|
|
end; // of DrawHeader
|
|
//------------------------------------------------------
|
|
|
|
|
|
procedure DrawIconCell;
|
|
//------------------------------------------------------
|
|
var
|
|
IconID: PtrInt;
|
|
begin
|
|
if (gShowIcons <> sim_none) then
|
|
begin
|
|
IconID := AFile.IconID;
|
|
// Draw default icon if there is no icon for the file.
|
|
if IconID = -1 then
|
|
IconID := PixMapManager.GetDefaultIcon(AFile.FSFile);
|
|
|
|
PixMapManager.DrawBitmap(IconID,
|
|
AFile.FSFile,
|
|
FileSourceDirectAccess,
|
|
Canvas,
|
|
aRect.Left + 1,
|
|
// center icon vertically
|
|
aRect.Top + (RowHeights[ARow] - gIconsSize) div 2);
|
|
end;
|
|
|
|
s := AFile.DisplayStrings.Strings[ACol];
|
|
|
|
if gCutTextToColWidth then
|
|
begin
|
|
while Canvas.TextWidth(s) - (aRect.Right - aRect.Left) - 4 > 0 do
|
|
Delete(s, Length(s), 1);
|
|
end;
|
|
|
|
if (gShowIcons <> sim_none) then
|
|
Canvas.TextOut(aRect.Left + gIconsSize + 4, iTextTop, s)
|
|
else
|
|
Canvas.TextOut(aRect.Left + 2, iTextTop, s);
|
|
end; //of DrawIconCell
|
|
//------------------------------------------------------
|
|
|
|
procedure DrawOtherCell;
|
|
//------------------------------------------------------
|
|
var
|
|
tw, cw: Integer;
|
|
begin
|
|
s := AFile.DisplayStrings.Strings[ACol];
|
|
|
|
if gCutTextToColWidth then
|
|
begin
|
|
while Canvas.TextWidth(s) - (aRect.Right - aRect.Left) - 4 > 0 do
|
|
Delete(s, Length(s), 1);
|
|
end;
|
|
|
|
case ColumnsSet.GetColumnAlign(ACol) of
|
|
|
|
taRightJustify:
|
|
begin
|
|
cw := ColWidths[ACol];
|
|
tw := Canvas.TextWidth(s);
|
|
Canvas.TextOut(aRect.Left + cw - tw - 3, iTextTop, s);
|
|
end;
|
|
|
|
taLeftJustify:
|
|
begin
|
|
Canvas.TextOut(aRect.Left + 3, iTextTop, s);
|
|
end;
|
|
|
|
taCenter:
|
|
begin
|
|
cw := ColWidths[ACol];
|
|
tw := Canvas.TextWidth(s);
|
|
Canvas.TextOut(aRect.Left + ((cw - tw - 3) div 2), iTextTop, s);
|
|
end;
|
|
|
|
end; //of case
|
|
end; //of DrawOtherCell
|
|
//------------------------------------------------------
|
|
|
|
procedure PrepareColors;
|
|
//------------------------------------------------------
|
|
var
|
|
TextColor: TColor = -1;
|
|
BackgroundColor: TColor;
|
|
//---------------------
|
|
begin
|
|
Canvas.Font.Name := ColumnsSet.GetColumnFontName(ACol);
|
|
Canvas.Font.Size := ColumnsSet.GetColumnFontSize(ACol);
|
|
Canvas.Font.Style := ColumnsSet.GetColumnFontStyle(ACol);
|
|
|
|
// Set up default background color first.
|
|
if (gdSelected in aState) and ColumnsView.Active and (not gUseFrameCursor) then
|
|
BackgroundColor := ColumnsSet.GetColumnCursorColor(ACol)
|
|
else
|
|
begin
|
|
// Alternate rows background color.
|
|
if odd(ARow) then
|
|
BackgroundColor := ColumnsSet.GetColumnBackground(ACol)
|
|
else
|
|
BackgroundColor := ColumnsSet.GetColumnBackground2(ACol);
|
|
end;
|
|
|
|
// Set text color.
|
|
if ColumnsSet.GetColumnOvercolor(ACol) then
|
|
TextColor := gColorExt.GetColorBy(AFile.FSFile);
|
|
if TextColor = -1 then
|
|
TextColor := ColumnsSet.GetColumnTextColor(ACol);
|
|
|
|
if AFile.Selected then
|
|
begin
|
|
if gUseInvertedSelection then
|
|
begin
|
|
//------------------------------------------------------
|
|
if (gdSelected in aState) and ColumnsView.Active and (not gUseFrameCursor) then
|
|
begin
|
|
Canvas.Font.Color := InvertColor(ColumnsSet.GetColumnCursorText(ACol));
|
|
end
|
|
else
|
|
begin
|
|
BackgroundColor := ColumnsSet.GetColumnMarkColor(ACol);
|
|
Canvas.Font.Color := TextColor;
|
|
end;
|
|
//------------------------------------------------------
|
|
end
|
|
else
|
|
begin
|
|
Canvas.Font.Color := ColumnsSet.GetColumnMarkColor(ACol);
|
|
end;
|
|
end
|
|
else if (gdSelected in aState) and ColumnsView.Active and (not gUseFrameCursor) then
|
|
begin
|
|
Canvas.Font.Color := ColumnsSet.GetColumnCursorText(ACol);
|
|
end
|
|
else
|
|
begin
|
|
Canvas.Font.Color := TextColor;
|
|
end;
|
|
|
|
// Draw background.
|
|
Canvas.Brush.Color := ColumnsView.DimColor(BackgroundColor);
|
|
Canvas.FillRect(aRect);
|
|
end;// of PrepareColors;
|
|
|
|
procedure DrawLines;
|
|
begin
|
|
// Draw frame cursor.
|
|
if gUseFrameCursor and (gdSelected in aState) and ColumnsView.Active then
|
|
begin
|
|
Canvas.Pen.Color := ColumnsSet.GetColumnCursorColor(ACol);
|
|
Canvas.Line(aRect.Left, aRect.Top, aRect.Right, aRect.Top);
|
|
Canvas.Line(aRect.Left, aRect.Bottom - 1, aRect.Right, aRect.Bottom - 1);
|
|
end;
|
|
|
|
// Draw drop selection.
|
|
if ARow = DropRowIndex then
|
|
begin
|
|
Canvas.Pen.Color := ColumnsSet.GetColumnTextColor(ACol);
|
|
Canvas.Line(aRect.Left, aRect.Top, aRect.Right, aRect.Top);
|
|
Canvas.Line(aRect.Left, aRect.Bottom - 1, aRect.Right, aRect.Bottom - 1);
|
|
end;
|
|
end;
|
|
//------------------------------------------------------
|
|
//end of subprocedures
|
|
//------------------------------------------------------
|
|
|
|
begin
|
|
ColumnsSet := ColumnsView.GetColumnsClass;
|
|
|
|
if gdFixed in aState then
|
|
begin
|
|
DrawFixed // Draw column headers
|
|
end
|
|
else if ColumnsView.FFiles.Count > 0 then
|
|
begin
|
|
AFile := ColumnsView.FFiles[ARow - FixedRows]; // substract fixed rows (header)
|
|
FileSourceDirectAccess := fspDirectAccess in ColumnsView.FileSource.Properties;
|
|
|
|
PrepareColors;
|
|
|
|
iTextTop := aRect.Top + (RowHeights[aRow] - Canvas.TextHeight('Wg')) div 2;
|
|
|
|
if ACol = 0 then
|
|
DrawIconCell // Draw icon in the first column
|
|
else
|
|
DrawOtherCell;
|
|
end;
|
|
|
|
DrawCellGrid(aCol,aRow,aRect,aState);
|
|
DrawLines;
|
|
end;
|
|
|
|
procedure TDrawGridEx.MouseMove(Shift: TShiftState; X, Y: Integer);
|
|
var
|
|
Point: TPoint;
|
|
AFile: TDisplayFile;
|
|
ExpectedButton: TShiftStateEnum;
|
|
iCol, iRow: Integer;
|
|
aRect: TRect;
|
|
begin
|
|
inherited MouseMove(Shift, X, Y);
|
|
|
|
if FMouseDown and Self.Dragging then
|
|
begin
|
|
// If dragging has started then clear MouseDown flag.
|
|
if (Abs(DragStartPoint.X - X) > DragManager.DragThreshold) or
|
|
(Abs(DragStartPoint.Y - Y) > DragManager.DragThreshold) then
|
|
begin
|
|
FMouseDown := False;
|
|
end;
|
|
end;
|
|
|
|
// If dragging is currently in effect, the window has mouse capture and
|
|
// we can retrieve the window over which the mouse cursor currently is.
|
|
if Self.Dragging and uDragDropEx.IsExternalDraggingSupported then
|
|
begin
|
|
Point := Self.ClientToScreen(Classes.Point(X, Y));
|
|
|
|
// use specifically LCLIntf.WindowFromPoint to avoid confusion with Windows.WindowFromPoint
|
|
if LCLIntf.WindowFromPoint(Point) = 0 then
|
|
begin
|
|
// If result is 0 then the window belongs to another process
|
|
// and we transform intra-process dragging into inter-process dragging.
|
|
|
|
TransformDraggingToExternal(Point);
|
|
end;
|
|
end
|
|
|
|
else
|
|
|
|
// if we are about to start dragging
|
|
if StartDrag then
|
|
begin
|
|
StartDrag := False;
|
|
|
|
case LastMouseButton of
|
|
mbLeft : ExpectedButton := ssLeft;
|
|
mbMiddle : ExpectedButton := ssMiddle;
|
|
mbRight : ExpectedButton := ssRight;
|
|
else Exit;
|
|
end;
|
|
|
|
// Make sure the same mouse button is still pressed.
|
|
if not (ExpectedButton in Shift) then
|
|
begin
|
|
ClearMouseButtonAfterDrag;
|
|
end
|
|
else if DragRowIndex >= FixedRows then
|
|
begin
|
|
AFile := (Parent as TColumnsFileView).FFiles[DragRowIndex - FixedRows]; // substract fixed rows (header)
|
|
// Check if valid item is being dragged.
|
|
if (Parent as TColumnsFileView).IsItemValid(AFile) then
|
|
begin
|
|
BeginDrag(False);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Show file info tooltip
|
|
if ShowHint then
|
|
begin
|
|
if MouseOnGrid(X, Y) then
|
|
begin
|
|
MouseToCell(X, Y, iCol, iRow);
|
|
if (iRow <> HintRowIndex) and (iRow >= FixedRows) then
|
|
begin
|
|
HintRowIndex:= iRow;
|
|
Application.CancelHint;
|
|
Self.Hint:= EmptyStr; // don't show by default
|
|
with (Parent as TColumnsFileView) do
|
|
if InRange(HintRowIndex - FixedRows, 0, FFiles.Count - 1) then
|
|
begin
|
|
AFile := FFiles[HintRowIndex - FixedRows];
|
|
aRect:= CellRect(0, HintRowIndex);
|
|
iCol:= aRect.Right - aRect.Left - 8;
|
|
if gShowIcons <> sim_none then
|
|
Dec(iCol, gIconsSize);
|
|
if iCol < Self.Canvas.TextWidth(AFile.FSFile.Name) then // with file name
|
|
Self.Hint:= AFile.FSFile.Name
|
|
else if (stm_only_large_name in gShowToolTipMode) then // don't show
|
|
Exit
|
|
else if not AFile.FSFile.IsDirectory then // without name
|
|
Self.Hint:= #32;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
HintRowIndex:= -1;
|
|
Application.CancelHint;
|
|
Self.Hint:= EmptyStr;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TDrawGridEx.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
|
|
Y: Integer);
|
|
begin
|
|
{$IFDEF LCLGTK2}
|
|
// Workaround for two doubleclicks being sent on GTK.
|
|
// MouseUp event is sent just after doubleclick, so if we drop
|
|
// doubleclick events we have to also drop MouseUp events that follow them.
|
|
if TooManyDoubleClicks then Exit;
|
|
{$ENDIF}
|
|
|
|
StartDrag := False;
|
|
|
|
inherited MouseUp(Button, Shift, X, Y);
|
|
|
|
// Call handler only if button-up was not lifted to finish drag&drop operation.
|
|
if FMouseDown then
|
|
begin
|
|
(Parent as TColumnsFileView).dgPanelMouseUp(Self, Button, Shift, X, Y);
|
|
FMouseDown := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TDrawGridEx.MouseDown(Button: TMouseButton; Shift: TShiftState; X,Y: Integer);
|
|
begin
|
|
{$IFDEF LCLGTK2}
|
|
// Workaround for two doubleclicks being sent on GTK.
|
|
// MouseDown event is sent just before doubleclick, so if we drop
|
|
// doubleclick events we have to also drop MouseDown events that precede them.
|
|
if TooManyDoubleClicks then Exit;
|
|
{$ENDIF}
|
|
|
|
FMouseDown := True;
|
|
|
|
if MouseOnGrid(X, Y) then
|
|
inherited MouseDown(Button, Shift, X, Y)
|
|
else
|
|
begin
|
|
if Assigned(OnMouseDown) then
|
|
OnMouseDown(Self, Button, Shift, X, Y);
|
|
end;
|
|
end;
|
|
|
|
function TDrawGridEx.MouseOnGrid(X, Y: LongInt): Boolean;
|
|
var
|
|
bTemp: Boolean;
|
|
iRow, iCol: LongInt;
|
|
begin
|
|
bTemp:= AllowOutboundEvents;
|
|
AllowOutboundEvents:= False;
|
|
MouseToCell(X, Y, iCol, iRow);
|
|
AllowOutboundEvents:= bTemp;
|
|
Result:= not ((iCol < 0) and (iRow < 0));
|
|
end;
|
|
|
|
function TDrawGridEx.GetHeaderHeight: Integer;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 0 to FixedRows-1 do
|
|
Result := Result + RowHeights[i];
|
|
if Flat and (BorderStyle = bsSingle) then // TCustomGrid.GetBorderWidth
|
|
Result := Result + 1;
|
|
end;
|
|
|
|
procedure TDrawGridEx.ChangeDropRowIndex(NewIndex: Integer);
|
|
var
|
|
OldDropRowIndex: Integer;
|
|
begin
|
|
if DropRowIndex <> NewIndex then
|
|
begin
|
|
OldDropRowIndex := DropRowIndex;
|
|
|
|
// Set new index before redrawing.
|
|
DropRowIndex := NewIndex;
|
|
|
|
if OldDropRowIndex >= 0 then // invalidate old row if need
|
|
InvalidateRow(OldDropRowIndex);
|
|
if NewIndex >= 0 then
|
|
InvalidateRow(NewIndex);
|
|
end;
|
|
end;
|
|
|
|
procedure TDrawGridEx.TransformDraggingToExternal(ScreenPoint: TPoint);
|
|
var
|
|
SourcePanel: TColumnsFileView;
|
|
begin
|
|
// Set flag temporarily before stopping internal dragging,
|
|
// so that triggered events will know that dragging is transforming.
|
|
TransformDragging := True;
|
|
|
|
// Stop internal dragging
|
|
DragManager.DragStop(False);
|
|
|
|
{$IF DEFINED(LCLGTK) or DEFINED(LCLGTK2)}
|
|
// Under GTK, DragManager does not release it's mouse capture on
|
|
// DragStop(). We must release it here manually or LCL will get confused
|
|
// with who "owns" the capture after the GTK drag&drop finishes.
|
|
ReleaseMouseCapture;
|
|
{$ENDIF}
|
|
|
|
// Clear flag before starting external dragging.
|
|
TransformDragging := False;
|
|
|
|
SourcePanel := (Parent as TColumnsFileView);
|
|
|
|
// Start external dragging.
|
|
// On Windows it does not return until dragging is finished.
|
|
|
|
SourcePanel.StartDragEx(LastMouseButton, ScreenPoint);
|
|
end;
|
|
|
|
function TDrawGridEx.OnExDragEnter(var DropEffect: TDropEffect; ScreenPoint: TPoint):Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
function TDrawGridEx.OnExDragOver(var DropEffect: TDropEffect; ScreenPoint: TPoint):Boolean;
|
|
var
|
|
ClientPoint: TPoint;
|
|
Dummy, iRow: Integer;
|
|
AFile: TDisplayFile = nil;
|
|
TargetPanel: TColumnsFileView = nil;
|
|
begin
|
|
Result := False;
|
|
|
|
ClientPoint := Self.ScreenToClient(ScreenPoint);
|
|
|
|
TargetPanel := (Self.Parent as TColumnsFileView);
|
|
|
|
// Allow dropping into empty panel or on the header.
|
|
if TargetPanel.IsEmpty or (ClientPoint.Y < GetHeaderHeight) then
|
|
begin
|
|
ChangeDropRowIndex(-1);
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
|
|
MouseToCell(ClientPoint.X, ClientPoint.Y, Dummy, iRow);
|
|
|
|
if iRow >= FixedRows then
|
|
// Get the item over which there is something dragged.
|
|
AFile := TargetPanel.FFiles[iRow - FixedRows]; // substract fixed rows (header)
|
|
|
|
if Assigned(AFile) and
|
|
(AFile.FSFile.IsDirectory or AFile.FSFile.IsLinkToDirectory) and
|
|
(MouseOnGrid(ClientPoint.X, ClientPoint.Y)) then
|
|
// It is a directory or link.
|
|
begin
|
|
ChangeDropRowIndex(iRow);
|
|
Result := True;
|
|
end
|
|
else
|
|
begin
|
|
ChangeDropRowIndex(-1);
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
function TDrawGridEx.OnExDrop(const FileNamesList: TStringList; DropEffect: TDropEffect;
|
|
ScreenPoint: TPoint):Boolean;
|
|
var
|
|
Files: TFiles;
|
|
DropParams: TDropParams;
|
|
TargetFileView: TFileView;
|
|
begin
|
|
if FileNamesList.Count > 0 then
|
|
begin
|
|
Files := TFileSystemFileSource.CreateFilesFromFileList(
|
|
ExtractFilePath(FileNamesList[0]), FileNamesList);
|
|
try
|
|
TargetFileView := Self.Parent as TFileView;
|
|
|
|
DropParams := TDropParams.Create(
|
|
Files, DropEffect, ScreenPoint, True,
|
|
nil, TargetFileView, TargetFileView.CurrentPath);
|
|
|
|
frmMain.DropFiles(DropParams);
|
|
except
|
|
FreeAndNil(Files);
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
ChangeDropRowIndex(-1);
|
|
Result := True;
|
|
end;
|
|
|
|
function TDrawGridEx.OnExDragLeave: Boolean;
|
|
begin
|
|
ChangeDropRowIndex(-1);
|
|
Result := True;
|
|
end;
|
|
|
|
function TDrawGridEx.OnExDragBegin: Boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
function TDrawGridEx.OnExDragEnd: Boolean;
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
var
|
|
startPoint: TPoint;
|
|
currentPoint: TPoint;
|
|
{$ENDIF}
|
|
begin
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
// On windows dragging can be transformed back into internal.
|
|
// Check if drag was aborted due to mouse moving back into
|
|
// the application window or the user just cancelled it.
|
|
if (DragDropSource.GetLastStatus = DragDropAborted) and
|
|
TransformDragging then
|
|
begin
|
|
// Transform to internal dragging again.
|
|
|
|
// Save current mouse position.
|
|
GetCursorPos(currentPoint);
|
|
|
|
// Temporarily set cursor position to the point where the drag was started
|
|
// so that DragManager can properly read the control being dragged.
|
|
startPoint := ClientToScreen(Self.DragStartPoint);
|
|
SetCursorPos(startPoint.X,startPoint.Y);
|
|
|
|
// Begin internal dragging.
|
|
BeginDrag(True);
|
|
|
|
// Move cursor back.
|
|
SetCursorPos(currentPoint.X, currentPoint.Y);
|
|
|
|
// Clear flag.
|
|
TransformDragging := False;
|
|
|
|
Exit;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
ClearMouseButtonAfterDrag;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TDrawGridEx.ClearMouseButtonAfterDrag;
|
|
begin
|
|
// Clear some control specific flags.
|
|
ControlState := ControlState - [csClicked, csLButtonDown];
|
|
|
|
// reset TCustomGrid state
|
|
FGridState := gsNormal;
|
|
end;
|
|
|
|
{$IFDEF LCLGTK2}
|
|
function TDrawGridEx.TooManyDoubleClicks: Boolean;
|
|
begin
|
|
Result := ((Now - fLastDoubleClickTime) <= ((1/86400)*(DblClickTime/1000)));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function TDrawGridEx.GetVisibleRows: TRange;
|
|
var
|
|
w: Integer;
|
|
rc: Integer;
|
|
begin
|
|
if (TopRow<0)or(csLoading in ComponentState) then begin
|
|
Result.First := 0;
|
|
Result.Last := -1;
|
|
Exit;
|
|
end;
|
|
// visible TopLeft Cell
|
|
Result.First:=TopRow;
|
|
Result.Last:=Result.First;
|
|
rc := RowCount;
|
|
|
|
// Top Margin of next visible Row and Bottom most visible cell
|
|
if rc>FixedRows then begin
|
|
w:=RowHeights[Result.First] + GCache.FixedHeight - GCache.TLRowOff;
|
|
while (Result.Last<rc-1)and(W<GCache.ClientHeight) do begin
|
|
Inc(Result.Last);
|
|
W:=W+RowHeights[Result.Last];
|
|
end;
|
|
end else begin
|
|
Result.Last := Result.First - 1; // no visible cells here
|
|
end;
|
|
end;
|
|
|
|
// -- TColumnsSortings --------------------------------------------------------
|
|
|
|
procedure TColumnsSortings.AddSorting(iColumn : Integer; SortDirection : TSortDirection);
|
|
var
|
|
i : Integer;
|
|
pSortingColumn : PColumnsSorting;
|
|
begin
|
|
i := Count - 1;
|
|
while i >= 0 do
|
|
begin
|
|
pSortingColumn := PColumnsSorting(Self[i]);
|
|
if pSortingColumn^.Column = iColumn then
|
|
begin
|
|
pSortingColumn^.SortDirection := ReverseSortDirection(pSortingColumn^.SortDirection);
|
|
Exit;
|
|
end;
|
|
dec(i);
|
|
end;
|
|
|
|
new(pSortingColumn);
|
|
pSortingColumn^.Column := iColumn;
|
|
pSortingColumn^.SortDirection := SortDirection;
|
|
Add(pSortingColumn);
|
|
end;
|
|
|
|
Destructor TColumnsSortings.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
|
|
function TColumnsSortings.Clone: TColumnsSortings;
|
|
var
|
|
i: Integer;
|
|
pSortingColumn : PColumnsSorting;
|
|
begin
|
|
Result := TColumnsSortings.Create;
|
|
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
pSortingColumn := PColumnsSorting(Self[i]);
|
|
Result.AddSorting(pSortingColumn^.Column, pSortingColumn^.SortDirection);
|
|
end;
|
|
end;
|
|
|
|
procedure TColumnsSortings.Clear;
|
|
var
|
|
i : Integer;
|
|
pSortingColumn : PColumnsSorting;
|
|
begin
|
|
i := Count - 1;
|
|
while i >= 0 do
|
|
begin
|
|
pSortingColumn := PColumnsSorting(Self[i]);
|
|
dispose(pSortingColumn);
|
|
dec(i);
|
|
end;
|
|
|
|
Inherited Clear;
|
|
end;
|
|
|
|
function TColumnsSortings.GetSortingDirection(iColumn : Integer) : TSortDirection;
|
|
var
|
|
i : Integer;
|
|
pSortingColumn : PColumnsSorting;
|
|
begin
|
|
Result := sdNone;
|
|
|
|
i := Count - 1;
|
|
while i >= 0 do
|
|
begin
|
|
pSortingColumn := PColumnsSorting(Self[i]);
|
|
if pSortingColumn^.Column = iColumn then
|
|
begin
|
|
Result := pSortingColumn^.SortDirection;
|
|
break;
|
|
end;
|
|
dec(i);
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|