UPD: Move some common code to separate class for views that have a main control with a filelist.

This commit is contained in:
cobines 2012-04-25 13:49:49 +00:00
commit 4fff67e5fb
6 changed files with 1501 additions and 2375 deletions

View file

@ -7,9 +7,9 @@ interface
uses
LMessages, Grids, uFileView, uFileSource, Graphics,
Classes, SysUtils, Controls, ExtCtrls, ComCtrls, contnrs, fgl,
uFile, uDisplayFile, uFormCommands, uDragDropEx, DCXmlConfig,
uFile, uDisplayFile, uFormCommands, DCXmlConfig,
DCClassesUtf8, uFileSorting, uFileViewHistory, uFileProperty, uFileViewWorker,
uFunctionThread, uFileSystemWatcher, fQuickSearch, uTypes, uOrderedFileView,
uFunctionThread, uFileSystemWatcher, uTypes, uFileViewWithMainCtrl,
uFileViewHeader;
type
@ -29,6 +29,8 @@ type
procedure Resize; override;
procedure RowHeightsChanged; override;
procedure ColWidthsChanged; override;
procedure FinalizeWnd; override;
procedure InitializeWnd; override;
function MouseOnGrid(X, Y: LongInt): Boolean;
function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
@ -43,7 +45,7 @@ type
{ TBriefFileView }
TBriefFileView = class (TOrderedFileView)
TBriefFileView = class (TFileViewWithMainCtrl)
private
TabHeader: THeaderControl;
dgPanel: TBriefDrawGrid;
@ -51,9 +53,6 @@ type
procedure MakeColumnsStrings(AFile: TDisplayFile);
procedure SetFilesDisplayItems;
procedure dgPanelEnter(Sender: TObject);
procedure dgPanelExit(Sender: TObject);
procedure dgPanelDblClick(Sender: TObject);
procedure dgPanelTopLeftChanged(Sender: TObject);
procedure TabHeaderSectionClick(HeaderControl: TCustomHeaderControl;
Section: THeaderSection);
@ -62,7 +61,10 @@ type
procedure BeforeMakeFileList; override;
procedure AfterMakeFileList; override;
procedure AfterChangePath; override;
procedure DoMainControlShowHint(FileIndex: PtrInt; X, Y: Integer); override;
function GetActiveFileIndex: PtrInt; override;
function GetFileIndexFromCursor(X, Y: Integer; out AtFileList: Boolean): PtrInt; override;
function GetFileRect(FileIndex: PtrInt): TRect; override;
function GetVisibleFilesIndexes: TRange; override;
procedure RedrawFile(FileIndex: PtrInt); override;
procedure RedrawFile(DisplayFile: TDisplayFile); override;
@ -78,39 +80,21 @@ type
procedure AddFileSource(aFileSource: IFileSource; aPath: String); override;
procedure SaveConfiguration(AConfig: TXmlConfig; ANode: TXmlNode); override;
{en
Handles drag&drop operations onto the file view.
Does any graphic work and executes operations with dropped files if allowed.
Handles freeing DropParams.
}
procedure DoDragDropOperation(Operation: TDragDropOperation;
var DropParams: TDropParams); override;
end;
implementation
uses
LCLIntf, LCLType, Forms,
LCLProc, uMasks, Clipbrd, uLng, uShowMsg, uGlobs, uPixmapManager, uDebug,
LCLProc, Clipbrd, uLng, uShowMsg, uGlobs, uPixmapManager, uDebug,
uDCUtils, uOSUtils, math, fMain, fMaskInputDlg, uSearchTemplate,
uInfoToolTip, dmCommonData,
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}
;
uFileFunctions;
{ TBriefDrawGrid }
@ -157,6 +141,12 @@ begin
end;
end;
procedure TBriefDrawGrid.InitializeWnd;
begin
inherited InitializeWnd;
BriefView.InitializeDragDropEx(Self);
end;
procedure TBriefDrawGrid.UpdateView;
function CalculateDefaultRowHeight: Integer;
@ -193,8 +183,6 @@ var
TempRowHeight: Integer;
begin
Flat := gInterfaceFlat;
Color := BriefView.DimColor(gBackColor);
ShowHint:= (gShowToolTipMode <> []);
// Calculate row height.
TempRowHeight := CalculateDefaultRowHeight;
@ -290,7 +278,6 @@ begin
end;
end;
end;
BriefView.DoHandleKeyDown(Key, Shift);
inherited KeyDown(Key, Shift);
end;
@ -480,6 +467,12 @@ begin
DrawLines;
end;
procedure TBriefDrawGrid.FinalizeWnd;
begin
BriefView.FinalizeDragDropEx(Self);
inherited FinalizeWnd;
end;
{ TBriefFileView }
function TBriefFileView.GetVisibleFilesIndexes: TRange; {Done}
@ -517,40 +510,6 @@ begin
dgPanel.InvalidateCell(ACol, ARow);
end;
procedure TBriefFileView.dgPanelEnter(Sender: TObject);
begin
SetActive(True);
end;
procedure TBriefFileView.dgPanelExit(Sender: TObject);
begin
SetActive(False);
end;
procedure TBriefFileView.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 >= 0) and
(Point.Y < dgPanel.GridHeight) then
begin
ChooseFile(GetActiveDisplayFile);
end;
{$IFDEF LCLGTK2}
// dgPanel.fLastDoubleClickTime := Now;
{$ENDIF}
end;
procedure TBriefFileView.dgPanelTopLeftChanged(Sender: TObject);
begin
EnsureDisplayProperties;
@ -566,10 +525,7 @@ procedure TBriefFileView.CreateDefault(AOwner: TWinControl);
begin
inherited CreateDefault(AOwner);
dgPanel:= TBriefDrawGrid.Create(Self, Self);
HotMan.Register(dgPanel, 'Files Panel');
Align:= alClient;
MainControl := dgPanel;
TabHeader:= TBriefHeaderControl.Create(Self);
TabHeader.Parent:= Self;
@ -582,9 +538,6 @@ begin
TabHeader.OnSectionClick:= @TabHeaderSectionClick;
dgPanel.OnTopLeftChanged:= @dgPanelTopLeftChanged;
dgPanel.OnDblClick:=@dgPanelDblClick;
dgPanel.OnEnter:=@dgPanelEnter;
dgPanel.OnExit:=@dgPanelExit;
end;
procedure TBriefFileView.BeforeMakeFileList;
@ -614,6 +567,30 @@ begin
Result := dgPanel.CellToIndex(dgPanel.Col, dgPanel.Row);
end;
function TBriefFileView.GetFileIndexFromCursor(X, Y: Integer; out AtFileList: Boolean): PtrInt;
var
bTemp: Boolean;
iRow, iCol: LongInt;
begin
with dgPanel do
begin
bTemp:= AllowOutboundEvents;
AllowOutboundEvents:= False;
MouseToCell(X, Y, iCol, iRow);
AllowOutboundEvents:= bTemp;
Result:= CellToIndex(iCol, iRow);
AtFileList := True; // Always at file list because header in dgPanel not used
end;
end;
function TBriefFileView.GetFileRect(FileIndex: PtrInt): TRect;
var
ACol, ARow: Integer;
begin
dgPanel.IndexToCell(FileIndex, ACol, ARow);
Result := dgPanel.CellRect(ACol, ARow);
end;
procedure TBriefFileView.Resize;
var
I: Integer;
@ -637,9 +614,6 @@ end;
destructor TBriefFileView.Destroy;
begin
if Assigned(HotMan) then
HotMan.UnRegister(dgPanel);
inherited Destroy;
end;
@ -677,17 +651,31 @@ begin
dgPanel.UpdateView;
end;
procedure TBriefFileView.DoDragDropOperation(Operation: TDragDropOperation;
var DropParams: TDropParams);
begin
end;
procedure TBriefFileView.DoFileUpdated(AFile: TDisplayFile; UpdatedProperties: TFilePropertiesTypes);
begin
MakeColumnsStrings(AFile);
inherited DoFileUpdated(AFile, UpdatedProperties);
end;
procedure TBriefFileView.DoMainControlShowHint(FileIndex: PtrInt; X, Y: Integer);
var
aRect: TRect;
ACol, ARow, iCol: Integer;
AFile: TDisplayFile;
begin
AFile := FFiles[FileIndex];
dgPanel.IndexToCell(FileIndex, ACol, ARow);
aRect:= dgPanel.CellRect(ACol, ARow);
iCol:= aRect.Right - aRect.Left - 8;
if gShowIcons <> sim_none then
Dec(iCol, gIconsSize);
if iCol < dgPanel.Canvas.TextWidth(AFile.FSFile.Name) then // with file name
dgPanel.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
dgPanel.Hint:= #32;
end;
end.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -564,7 +564,9 @@ begin
FReloadTimer.Enabled := False;
FReloadTimer.OnTimer := @ReloadTimerEvent;
BorderStyle := bsNone; // Before Create or the window handle may be recreated
inherited Create(AOwner);
Align := alClient;
Parent := AOwner;
if Parent is TFileViewPage then

View file

@ -0,0 +1,985 @@
{
Double Commander
-------------------------------------------------------------------------
Base class for file views which have a main control with a list of files.
Copyright (C) 2012 Przemyslaw Nagay (cobines@gmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
}
unit uFileViewWithMainCtrl;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Controls, ExtCtrls, LCLType,
uFileViewWorker,
uOrderedFileView,
uFileView,
uDragDropEx;
type
{ TFileViewWithMainCtrl }
TFileViewWithMainCtrl = class(TOrderedFileView)
private
FMainControl: TWinControl;
{$IFDEF LCLGTK2}
FLastDoubleClickTime : TDateTime;
function TooManyDoubleClicks: Boolean;
{$ENDIF}
{ 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;
procedure SetMainControl(AValue: TWinControl);
procedure tmContextMenuTimer(Sender: TObject);
// If internal dragging is currently in effect, this function
// stops internal dragging and starts external.
procedure TransformDraggingToExternal(ScreenPoint: TPoint);
protected
// Used to register as a drag and drop source and target.
FDragDropSource: uDragDropEx.TDragDropSource;
FDragDropTarget: uDragDropEx.TDragDropTarget;
FHintFileIndex: PtrInt;
FMainControlLastMouseButton: TMouseButton; // Mouse button that initiated dragging
{en
Used to check if button-up was received after button-down
or after dropping something after dragging with right mouse button.
}
FMainControlMouseDown: Boolean;
FMouseSelectionStartIndex: Integer;
FDragStartPoint: TPoint;
FDragFileIndex: PtrInt;
FDropFileIndex: PtrInt;
FStartDrag: Boolean;
tmContextMenu: TTimer;
// Simulates releasing mouse button that started a dragging operation,
// but was released in another window or another application.
procedure ClearAfterDragDrop; virtual;
procedure CreateDefault(AOwner: TWinControl); override;
procedure DoMainControlShowHint(FileIndex: PtrInt; X, Y: Integer); virtual; abstract;
procedure DoUpdateView; override;
procedure FinalizeDragDropEx(AControl: TWinControl);
{en
Retrieves file index under mouse cursor.
@param(X, Y
Should be client coordinates of MainControl.)
@param(AtFileList
Whether X, Y point to the filelist, not at specific file but at empty space.
If AtFileList is @false then X, Y point somewhere outside the file list.)
}
function GetFileIndexFromCursor(X, Y: Integer; out AtFileList: Boolean): PtrInt; virtual; abstract;
procedure InitializeDragDropEx(AControl: TWinControl);
procedure MainControlDblClick(Sender: TObject);
procedure MainControlDragDrop(Sender, Source: TObject; X, Y: Integer);
procedure MainControlDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
procedure MainControlEndDrag(Sender, Target: TObject; X, Y: Integer);
procedure MainControlEnter(Sender: TObject);
procedure MainControlExit(Sender: TObject);
procedure MainControlKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure MainControlKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure MainControlMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure MainControlMouseLeave(Sender: TObject);
procedure MainControlMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure MainControlMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure MainControlShowHint(Sender: TObject; HintInfo: PHintInfo);
procedure MainControlUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
{en
Changes drawing colors depending on if this panel is active.
}
procedure SetActive(bActive: Boolean); override;
{en
Updates the drop row index, which is used to draw a rectangle
on directories during drag&drop operations.
}
procedure SetDropFileIndex(NewFileIndex: PtrInt);
procedure WorkerStarting(const Worker: TFileViewWorker); override;
procedure WorkerFinished(const Worker: TFileViewWorker); override;
property MainControl: TWinControl read FMainControl write SetMainControl;
public
destructor Destroy; override;
procedure DoDragDropOperation(Operation: TDragDropOperation;
var DropParams: TDropParams); override;
function Focused: Boolean; override;
procedure SetFocus; override;
published
procedure cm_ContextMenu(const Params: array of string);
end;
implementation
uses
{$IF DEFINED(LCLGTK2)}
Gtk2Proc, // for ReleaseMouseCapture
GTK2Globals, // for DblClickTime
{$ENDIF}
{$IFDEF MSWINDOWS}
LCLIntf,
{$ENDIF}
Forms,
fMain,
uGlobs, uInfoToolTip, uDisplayFile, uFile, uFileSystemFileSource;
type
TControlHandlersHack = class(TWinControl)
end;
{ TFileViewWithMainCtrl }
procedure TFileViewWithMainCtrl.ClearAfterDragDrop;
begin
// Clear some control specific flags.
MainControl.ControlState := MainControl.ControlState - [csClicked, csLButtonDown];
end;
procedure TFileViewWithMainCtrl.cm_ContextMenu(const Params: array of string);
var
Rect: TRect;
Point: TPoint;
begin
Rect := GetFileRect(GetActiveFileIndex);
Point.X := Rect.Left + ((Rect.Right - Rect.Left) div 2);
Point.Y := Rect.Top + ((Rect.Bottom - Rect.Top) div 2);
Point := MainControl.ClientToScreen(Point);
frmMain.Commands.DoContextMenu(Self, Point.X, Point.Y, False);
end;
procedure TFileViewWithMainCtrl.CreateDefault(AOwner: TWinControl);
begin
FDropFileIndex := -1;
FHintFileIndex := -1;
{$IFDEF LCLGTK2}
FLastDoubleClickTime := Now;
{$ENDIF}
FStartDrag := False;
inherited CreateDefault(AOwner);
tmContextMenu := TTimer.Create(Self);
tmContextMenu.Enabled := False;
tmContextMenu.Interval := 500;
tmContextMenu.OnTimer := @tmContextMenuTimer;
end;
destructor TFileViewWithMainCtrl.Destroy;
begin
if Assigned(HotMan) then
HotMan.UnRegister(MainControl);
inherited Destroy;
end;
procedure TFileViewWithMainCtrl.DoDragDropOperation(Operation: TDragDropOperation; var DropParams: TDropParams);
var
AFile: TDisplayFile;
ClientDropPoint: TPoint;
FileIndex: PtrInt;
AtFileList: Boolean;
begin
try
with DropParams do
begin
if Files.Count > 0 then
begin
ClientDropPoint := MainControl.ScreenToClient(ScreenDropPoint);
FileIndex := GetFileIndexFromCursor(ClientDropPoint.X, ClientDropPoint.Y, AtFileList);
// default to current active directory in the destination panel
TargetPath := Self.CurrentPath;
if (DropIntoDirectories = True) and IsFileIndexInRange(FileIndex) then
begin
AFile := FFiles[FileIndex];
// 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 := TargetPanel.FileSource.GetParentDir(TargetPath)
else
TargetPath := TargetPath + AFile.FSFile.Name + DirectorySeparator;
end;
end;
end;
end;
// Execute the operation.
frmMain.DoDragDropOperation(Operation, DropParams);
finally
FreeAndNil(DropParams);
end;
end;
procedure TFileViewWithMainCtrl.DoUpdateView;
begin
inherited DoUpdateView;
MainControl.Color := DimColor(gBackColor);
MainControl.ShowHint := (gShowToolTipMode <> []);
end;
procedure TFileViewWithMainCtrl.FinalizeDragDropEx(AControl: TWinControl);
begin
FreeAndNil(FDragDropSource);
FreeAndNil(FDragDropTarget);
end;
function TFileViewWithMainCtrl.Focused: Boolean;
begin
Result := Assigned(MainControl) and MainControl.Focused;
end;
procedure TFileViewWithMainCtrl.InitializeDragDropEx(AControl: TWinControl);
begin
// Register as drag&drop source and target.
FDragDropSource := uDragDropEx.CreateDragDropSource(AControl);
if Assigned(FDragDropSource) then
FDragDropSource.RegisterEvents(nil, nil, @OnExDragEnd);
FDragDropTarget := uDragDropEx.CreateDragDropTarget(AControl);
if Assigned(FDragDropTarget) then
FDragDropTarget.RegisterEvents(@OnExDragEnter, @OnExDragOver,
@OnExDrop, @OnExDragLeave);
end;
procedure TFileViewWithMainCtrl.MainControlDblClick(Sender: TObject);
var
Point : TPoint;
FileIndex : PtrInt;
AtFileList: Boolean;
begin
{$IFDEF LCLGTK2}
// Workaround for two doubleclicks being sent on GTK.
if TooManyDoubleClicks then Exit;
{$ENDIF}
FStartDrag := False; // don't start drag on double click
Point := MainControl.ScreenToClient(Mouse.CursorPos);
// If on a file/directory then choose it.
FileIndex := GetFileIndexFromCursor(Point.x, Point.y, AtFileList);
if IsFileIndexInRange(FileIndex) then
ChooseFile(FFiles[FileIndex]);
{$IFDEF LCLGTK2}
FLastDoubleClickTime := Now;
{$ENDIF}
end;
procedure TFileViewWithMainCtrl.MainControlDragDrop(Sender, Source: TObject; X, Y: Integer);
var
SourcePanel: TFileViewWithMainCtrl;
SourceFiles: TFiles;
DropParams: TDropParams;
begin
if not (Source is TWinControl) or
not (TWinControl(Source).Parent is TFileViewWithMainCtrl) then
Exit;
SourcePanel := ((Source as TWinControl).Parent) as TFileViewWithMainCtrl;
// Get file names from source panel.
SourceFiles := SourcePanel.CloneSelectedFiles;
try
// Drop onto target panel.
DropParams := TDropParams.Create(
SourceFiles, // Will be freed automatically.
GetDropEffectByKeyAndMouse(GetKeyShiftState,
SourcePanel.FMainControlLastMouseButton),
MainControl.ClientToScreen(Classes.Point(X, Y)),
True,
SourcePanel,
Self, Self.CurrentPath);
frmMain.DropFiles(DropParams);
SetDropFileIndex(-1);
except
FreeAndNil(SourceFiles);
raise;
end;
end;
procedure TFileViewWithMainCtrl.MainControlDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
var
AFile: TDisplayFile;
SourcePanel: TFileView;
TargetPanel: TFileView;
SourceDir, TargetDir: String;
FileIndex: PtrInt;
AtFileList: Boolean;
begin
Accept := False;
if not (Source is TWinControl) or
not (TWinControl(Source).Parent is TFileView) then
Exit;
SourcePanel := ((Source as TWinControl).Parent) as TFileView;
TargetPanel := Self;
FileIndex := GetFileIndexFromCursor(X, Y, AtFileList);
// 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 FileIndex = InvalidFileIndex then
begin
SetDropFileIndex(-1);
Accept := True;
Exit;
end;
SourceDir := SourcePanel.CurrentPath;
TargetDir := TargetPanel.CurrentPath;
AFile := FFiles[FileIndex];
if (AFile.FSFile.IsDirectory or AFile.FSFile.IsLinkToDirectory) then
begin
if State = dsDragLeave then
// Mouse is leaving the control or drop will occur immediately.
// Don't draw DropRow rectangle.
SetDropFileIndex(-1)
else
SetDropFileIndex(FileIndex);
if Sender = Source then
begin
if not ((FileIndex = FDragFileIndex) or (AFile.Selected = True)) then
Accept := True;
end
else
begin
if Assigned(SourcePanel) and Assigned(TargetPanel) then
begin
if AFile.FSFile.Name = '..' then
TargetDir := TargetPanel.FileSource.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
SetDropFileIndex(-1);
if Assigned(SourcePanel) then
begin
if SourcePanel.CurrentPath <> TargetPanel.CurrentPath then
Accept := True;
end
else
Accept := True;
end
else
begin
SetDropFileIndex(-1);
end;
end;
procedure TFileViewWithMainCtrl.MainControlEndDrag(Sender, Target: TObject; X, Y: Integer);
procedure ClearDropNode(aFileView: TFileView);
begin
if aFileView is TFileViewWithMainCtrl then
TFileViewWithMainCtrl(aFileView).SetDropFileIndex(-1);
end;
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.
ClearDropNode(frmMain.FrameLeft);
ClearDropNode(frmMain.FrameRight);
if uDragDropEx.TransformDragging = False then
ClearAfterDragDrop;
end;
procedure TFileViewWithMainCtrl.MainControlEnter(Sender: TObject);
begin
SetActive(True);
if Assigned(OnActivate) then
OnActivate(Self);
end;
procedure TFileViewWithMainCtrl.MainControlExit(Sender: TObject);
begin
SetActive(False);
end;
procedure TFileViewWithMainCtrl.MainControlKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
ScreenPoint: TPoint;
begin
case Key of
VK_APPS:
begin
cm_ContextMenu([]);
Key := 0;
end;
VK_MENU: // Alt key
if MainControl.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);
TransformDraggingToExternal(ScreenPoint);
end;
end;
DoHandleKeyDown(Key, Shift);
end;
procedure TFileViewWithMainCtrl.MainControlKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
case Key of
VK_SHIFT:
FLastSelectionStartIndex := -1;
end;
end;
procedure TFileViewWithMainCtrl.MainControlMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
FileIndex: PtrInt;
AtFileList: Boolean;
AFile, APreviousFile: TDisplayFile;
begin
FileIndex := GetFileIndexFromCursor(X, Y, AtFileList);
if not AtFileList then
Exit;
SetFocus;
// history navigation for mice with extra buttons
case Button of
mbExtra1:
begin
GoToPrevHistory;
Exit;
end;
mbExtra2:
begin
GoToNextHistory;
Exit;
end;
end;
if IsFileIndexInRange(FileIndex) then
begin
AFile := FFiles[FileIndex];
FMainControlLastMouseButton := Button;
case Button of
mbRight:
begin
SetActiveFile(FileIndex);
if gMouseSelectionEnabled and (gMouseSelectionButton = 1) then
begin
FMouseSelectionStartIndex := FileIndex;
tmContextMenu.Enabled:= True; // start context menu timer
FLastSelectionState:= not AFile.Selected;
MarkFile(AFile, FLastSelectionState, False);
DoSelectionChanged(FileIndex);
Exit;
end;
end;
mbLeft:
begin
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
APreviousFile := GetActiveDisplayFile;
if Assigned(APreviousFile) then
MarkFile(APreviousFile, True, False);
end;
InvertFileSelection(AFile, False);
DoSelectionChanged(FileIndex);
end
else if ssShift in Shift then
begin
SelectRange(FileIndex);
end
else if (gMouseSelectionButton = 0) then
begin
if not AFile.Selected then
MarkFiles(False);
end;
end;//of mouse selection handler
end;
else
SetActiveFile(FileIndex);
Exit;
end;
{ Dragging }
if not MainControl.Dragging then // we could be in dragging mode already (started by a different button)
begin
// indicate that drag start at next mouse move event
FStartDrag := True;
FDragStartPoint.X := X;
FDragStartPoint.Y := Y;
FDragFileIndex := FileIndex;
uDragDropEx.TransformDragging := False;
uDragDropEx.AllowTransformToInternal := True;
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;
end;
procedure TFileViewWithMainCtrl.MainControlMouseLeave(Sender: TObject);
begin
if (gMouseSelectionEnabled) and (gMouseSelectionButton = 1) then
FMainControlMouseDown:= False;
end;
procedure TFileViewWithMainCtrl.MainControlMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
Point: TPoint;
AFile: TDisplayFile;
ExpectedButton: TShiftStateEnum;
FileIndex: PtrInt;
AtFileList: Boolean;
i, SelStartIndex, SelEndIndex: Integer;
begin
if FMainControlMouseDown and MainControl.Dragging then
begin
// If dragging has started then clear MouseDown flag.
if (Abs(FDragStartPoint.X - X) > DragManager.DragThreshold) or
(Abs(FDragStartPoint.Y - Y) > DragManager.DragThreshold) then
begin
FMainControlMouseDown := 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 MainControl.Dragging and uDragDropEx.IsExternalDraggingSupported then
begin
Point := MainControl.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 FStartDrag then
begin
FStartDrag := False;
case FMainControlLastMouseButton 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
ClearAfterDragDrop;
end
else if IsFileIndexInRange(FDragFileIndex) then
begin
AFile := FFiles[FDragFileIndex];
// Check if valid item is being dragged.
if IsItemValid(AFile) then
begin
MainControl.BeginDrag(False);
end;
end;
end;
// Show file info tooltip.
if ShowHint and
not MainControl.Dragging and ([ssLeft, ssMiddle, ssRight] * Shift = []) then
begin
FileIndex := GetFileIndexFromCursor(X, Y, AtFileList);
if FileIndex <> FHintFileIndex then
begin
FHintFileIndex := FileIndex;
Application.CancelHint;
MainControl.Hint:= EmptyStr; // don't show by default
if IsFileIndexInRange(FileIndex) then
DoMainControlShowHint(FHintFileIndex, X, Y);
end;
end;
// Selection with right mouse button, if enabled.
if FMainControlMouseDown and (FMainControlLastMouseButton = mbRight) and
gMouseSelectionEnabled and (gMouseSelectionButton = 1) then
begin
FileIndex := GetFileIndexFromCursor(X, Y, AtFileList);
if IsFileIndexInRange(FileIndex) and
(GetActiveFileIndex <> FileIndex) then
begin
tmContextMenu.Enabled:= False; // stop context menu timer
if FMouseSelectionStartIndex < FileIndex then
begin
SelStartIndex := FMouseSelectionStartIndex;
SelEndIndex := FileIndex;
end
else
begin
SelStartIndex := FileIndex;
SelEndIndex := FMouseSelectionStartIndex;
end;
SetActiveFile(FileIndex);
BeginUpdate;
try
for i := SelStartIndex to SelEndIndex do
MarkFile(FFiles[i], FLastSelectionState);
finally
EndUpdate;
end;
end;
end;
end;
procedure TFileViewWithMainCtrl.MainControlMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
FStartDrag := False;
// Handle only if button-up was not lifted to finish drag&drop operation.
if not FMainControlMouseDown then
Exit;
FMainControlMouseDown := False;
end;
procedure TFileViewWithMainCtrl.MainControlShowHint(Sender: TObject; HintInfo: PHintInfo);
var
AFile: TDisplayFile;
sHint: UTF8String;
begin
if (HintInfo^.HintStr = EmptyStr) or not IsFileIndexInRange(FHintFileIndex) then
Exit; // don't show
AFile := FFiles[FHintFileIndex];
if not AFile.FSFile.IsDirectory then
begin
sHint:= GetFileInfoToolTip(FileSource, AFile.FSFile);
with HintInfo^ do
begin
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;
end;
procedure TFileViewWithMainCtrl.MainControlUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
begin
// check if ShiftState is equal to quick search / filter modes
if quickSearch.CheckSearchOrFilter(UTF8Key) then
Exit;
end;
function TFileViewWithMainCtrl.OnExDragBegin: Boolean;
begin
Result := True;
end;
function TFileViewWithMainCtrl.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 TransformDragging and (FDragDropSource.GetLastStatus = DragDropAborted) 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 := MainControl.ClientToScreen(FDragStartPoint);
SetCursorPos(startPoint.X, startPoint.Y);
// Begin internal dragging.
MainControl.BeginDrag(True);
// Move cursor back.
SetCursorPos(currentPoint.X, currentPoint.Y);
// Clear flag.
TransformDragging := False;
Exit(True);
end;
{$ENDIF}
ClearAfterDragDrop;
Result := True;
end;
function TFileViewWithMainCtrl.OnExDragEnter(var DropEffect: TDropEffect; ScreenPoint: TPoint): Boolean;
begin
Result := True;
end;
function TFileViewWithMainCtrl.OnExDragLeave: Boolean;
begin
SetDropFileIndex(-1);
Result := True;
end;
function TFileViewWithMainCtrl.OnExDragOver(var DropEffect: TDropEffect; ScreenPoint: TPoint): Boolean;
var
ClientPoint: TPoint;
AFile: TDisplayFile;
FileIndex: PtrInt;
AtFileList: Boolean;
begin
// Dropping into empty panel allowed.
Result := True;
ClientPoint := MainControl.ScreenToClient(ScreenPoint);
FileIndex := GetFileIndexFromCursor(ClientPoint.x, ClientPoint.y, AtFileList);
if IsFileIndexInRange(FileIndex) then
begin
// Get the file over which there is something dragged.
AFile := FFiles[FileIndex];
// If it is a directory or link mark possibility of drop.
if (AFile.FSFile.IsDirectory or AFile.FSFile.IsLinkToDirectory) then
SetDropFileIndex(FileIndex)
else
SetDropFileIndex(-1);
end
else
SetDropFileIndex(-1);
end;
function TFileViewWithMainCtrl.OnExDrop(const FileNamesList: TStringList; DropEffect: TDropEffect; ScreenPoint: TPoint): Boolean;
var
AFiles: TFiles;
DropParams: TDropParams;
begin
if FileNamesList.Count > 0 then
begin
AFiles := TFileSystemFileSource.CreateFilesFromFileList(
ExtractFilePath(FileNamesList[0]), FileNamesList);
try
DropParams := TDropParams.Create(
AFiles, DropEffect, ScreenPoint, True,
nil, Self, Self.CurrentPath);
frmMain.DropFiles(DropParams);
except
FreeAndNil(AFiles);
raise;
end;
end;
SetDropFileIndex(-1);
Result := True;
end;
procedure TFileViewWithMainCtrl.SetActive(bActive: Boolean);
begin
inherited SetActive(bActive);
MainControl.Color := DimColor(gBackColor);
end;
procedure TFileViewWithMainCtrl.SetDropFileIndex(NewFileIndex: PtrInt);
var
OldDropIndex: PtrInt;
begin
if FDropFileIndex <> NewFileIndex then
begin
OldDropIndex := FDropFileIndex;
// Set new index before redrawing.
FDropFileIndex := NewFileIndex;
if IsFileIndexInRange(OldDropIndex) then
RedrawFile(OldDropIndex);
if IsFileIndexInRange(NewFileIndex) then
RedrawFile(NewFileIndex);
end;
end;
procedure TFileViewWithMainCtrl.SetFocus;
begin
// CanFocus checks parent controls, but not parent form.
if GetParentForm(Self).CanFocus and MainControl.CanFocus then
MainControl.SetFocus;
end;
procedure TFileViewWithMainCtrl.SetMainControl(AValue: TWinControl);
begin
if FMainControl = AValue then Exit;
FMainControl := AValue;
FMainControl.OnEnter := @MainControlEnter;
FMainControl.OnExit := @MainControlExit;
FMainControl.OnKeyDown := @MainControlKeyDown;
FMainControl.OnKeyUp := @MainControlKeyUp;
FMainControl.OnShowHint := @MainControlShowHint;
FMainControl.OnUTF8KeyPress := @MainControlUTF8KeyPress;
TControlHandlersHack(FMainControl).OnDblClick := @MainControlDblClick;
TControlHandlersHack(FMainControl).OnDragDrop := @MainControlDragDrop;
TControlHandlersHack(FMainControl).OnDragOver := @MainControlDragOver;
TControlHandlersHack(FMainControl).OnEndDrag := @MainControlEndDrag;
TControlHandlersHack(FMainControl).OnMouseDown := @MainControlMouseDown;
TControlHandlersHack(FMainControl).OnMouseLeave := @MainControlMouseLeave;
TControlHandlersHack(FMainControl).OnMouseMove := @MainControlMouseMove;
TControlHandlersHack(FMainControl).OnMouseUp := @MainControlMouseUp;
HotMan.Register(MainControl, 'Files Panel');
end;
procedure TFileViewWithMainCtrl.tmContextMenuTimer(Sender: TObject);
var
AFile: TDisplayFile;
ClientPoint, MousePoint: TPoint;
Background: Boolean;
FileIndex: PtrInt;
AtFileList: Boolean;
begin
FMainControlMouseDown:= False;
tmContextMenu.Enabled:= False; // stop context menu timer
MousePoint := Mouse.CursorPos;
ClientPoint := MainControl.ScreenToClient(MousePoint);
FileIndex := GetFileIndexFromCursor(ClientPoint.x, ClientPoint.y, AtFileList);
Background := not IsFileIndexInRange(FileIndex);
if not Background then
begin
AFile := FFiles[FileIndex];
MarkFile(AFile, not FLastSelectionState, False);
DoSelectionChanged(FileIndex);
end;
frmMain.Commands.DoContextMenu(Self, MousePoint.x, MousePoint.y, Background);
end;
procedure TFileViewWithMainCtrl.TransformDraggingToExternal(ScreenPoint: TPoint);
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;
// Start external dragging.
// On Windows it does not return until dragging is finished.
if IsFileIndexInRange(FDragFileIndex) then
begin
BeginDragExternal(FFiles[FDragFileIndex],
FDragDropSource,
FMainControlLastMouseButton,
ScreenPoint);
end;
end;
{$IFDEF LCLGTK2}
function TFileViewWithMainCtrl.TooManyDoubleClicks: Boolean;
begin
Result := ((Now - FLastDoubleClickTime) <= ((1/86400)*(DblClickTime/1000)));
end;
{$ENDIF}
procedure TFileViewWithMainCtrl.WorkerFinished(const Worker: TFileViewWorker);
begin
inherited WorkerFinished(Worker);
MainControl.Cursor := crDefault;
UpdateInfoPanel; // Update status line only
end;
procedure TFileViewWithMainCtrl.WorkerStarting(const Worker: TFileViewWorker);
begin
inherited WorkerStarting(Worker);
MainControl.Cursor := crHourGlass;
UpdateInfoPanel; // Update status line only
end;
end.

View file

@ -49,23 +49,26 @@ type
procedure quickSearchChangeFilter(Sender: TObject; AFilterText: UTF8String; const AFilterOptions: TQuickSearchOptions);
procedure quickSearchExecute(Sender: TObject);
procedure quickSearchHide(Sender: TObject);
function IsFileIndexInRange(FileIndex: PtrInt): Boolean;
protected
lblFilter: TLabel;
quickSearch: TfrmQuickSearch;
FLastActiveFileIndex: Integer;
FLastSelectionStartIndex: Integer;
FLastSelectionState: Boolean;
FUpdatingActiveFile: Boolean;
procedure AfterChangePath; override;
procedure CreateDefault(AOwner: TWinControl); override;
procedure DoFileIndexChanged(NewFileIndex: PtrInt);
procedure DoHandleKeyDown(var Key: Word; Shift: TShiftState); override;
procedure DoSelectionChanged; override;
procedure DoSelectionChanged(FileIndex: PtrInt);
procedure EnsureDisplayProperties;
function GetActiveDisplayFile: TDisplayFile; override;
function GetActiveFileIndex: PtrInt; virtual; abstract;
function GetFileRect(FileIndex: PtrInt): TRect; virtual; abstract;
function GetVisibleFilesIndexes: TRange; virtual; abstract;
function IsFileIndexInRange(FileIndex: PtrInt): Boolean;
procedure RedrawFile(FileIndex: PtrInt); overload; virtual; abstract;
procedure RedrawFiles; virtual; abstract;
{en
@ -180,6 +183,17 @@ begin
pmOperationsCancel.Parent := Self;
end;
procedure TOrderedFileView.DoFileIndexChanged(NewFileIndex: PtrInt);
begin
if (FLastActiveFileIndex <> NewFileIndex) and (not FUpdatingActiveFile) then
begin
SetLastActiveFile(NewFileIndex);
if Assigned(OnChangeActiveFile) then
OnChangeActiveFile(Self, FFiles[NewFileIndex].FSFile);
end;
end;
procedure TOrderedFileView.DoHandleKeyDown(var Key: Word; Shift: TShiftState);
var
mi: TMenuItem;