UPD: Further work on file source operations.

This commit is contained in:
cobines 2009-07-19 12:16:46 +00:00
commit bc1df274e3
19 changed files with 844 additions and 495 deletions

View file

@ -59,7 +59,7 @@
<PackageName Value="viewerpackage"/>
</Item5>
</RequiredPackages>
<Units Count="72">
<Units Count="71">
<Unit0>
<Filename Value="doublecmd.lpr"/>
<IsPartOfProject Value="True"/>
@ -459,58 +459,53 @@
<UnitName Value="uMethodsList"/>
</Unit61>
<Unit62>
<Filename Value="newdesign\ufilesourcecopyinoperation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileSourceCopyInOperation"/>
</Unit62>
<Unit63>
<Filename Value="newdesign\ufilesourcecopyoutoperation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileSourceCopyOutOperation"/>
</Unit63>
<Unit64>
<Filename Value="newdesign\ufilesourcedeleteoperation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileSourceDeleteOperation"/>
</Unit64>
<Unit65>
</Unit62>
<Unit63>
<Filename Value="newdesign\ufilesystemcopyoperation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileSystemCopyOperation"/>
</Unit65>
<Unit66>
</Unit63>
<Unit64>
<Filename Value="ufileviewnotebook.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileViewNotebook"/>
</Unit66>
<Unit67>
</Unit64>
<Unit65>
<Filename Value="newdesign\uvirtualfilesource.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uVirtualFileSource"/>
</Unit67>
<Unit68>
</Unit65>
<Unit66>
<Filename Value="newdesign\urealfilesource.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uRealFileSource"/>
</Unit68>
<Unit69>
</Unit66>
<Unit67>
<Filename Value="newdesign\uoperationsmanager.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uOperationsManager"/>
</Unit69>
<Unit70>
</Unit67>
<Unit68>
<Filename Value="newdesign\uoperationthread.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uOperationThread"/>
</Unit70>
<Unit71>
</Unit68>
<Unit69>
<Filename Value="newdesign\fviewoperations.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="frmViewOperations"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="fViewOperations"/>
</Unit71>
</Unit69>
<Unit70>
<Filename Value="newdesign\ufilesourcecopyoperation.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uFileSourceCopyOperation"/>
</Unit70>
</Units>
</ProjectOptions>
<CompilerOptions>

View file

@ -21,10 +21,13 @@ uses
LResources,
SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, Buttons, ExtCtrls,
uOperationsManager;
uOperationsManager, uFileSourceOperation;
type
TFileOpDlgLook = set of (fodl_from_lbl, fodl_to_lbl, fodl_first_pb, fodl_second_pb);
{ TfrmFileOp }
TfrmFileOp = class(TForm)
@ -45,10 +48,16 @@ type
private
{ Private declarations }
FOperationHandle: TOperationHandle;
FUpdateTimer: TTimer;
FUpdateTimer: TTimer; //<en Timer for updating statistics.
procedure OnUpdateTimer(Sender: TObject);
procedure InitializeControls(FileOpDlgLook: TFileOpDlgLook);
procedure InitializeCopyOperation(Operation: TFileSourceOperation);
procedure UpdateCopyOperation(Operation: TFileSourceOperation);
public
iProgress1Max: Integer;
iProgress1Pos: Integer;
@ -67,7 +76,9 @@ type
implementation
uses
fMain, dmCommonData, uFileOpThread, uFileSourceOperation, LCLProc;
fMain, dmCommonData, uFileOpThread, LCLProc, uLng,
uFileSourceOperationTypes,
uFileSourceCopyOperation;
procedure TfrmFileOp.btnCancelClick(Sender: TObject);
begin
@ -105,8 +116,11 @@ begin
end;
procedure TfrmFileOp.FormCreate(Sender: TObject);
var
Operation: TFileSourceOperation;
begin
Thread:= nil;
{
pbFirst.Position:= 0;
pbSecond.Position:= 0;
pbFirst.Max:= 1;
@ -115,17 +129,36 @@ begin
iProgress2Max:= 0;
iProgress1Pos:= 0;
iProgress2Pos:= 0;
}
lblFileNameFrom.Caption := '';
lblFileNameTo.Caption := '';
lblEstimated.Caption := '';
pbFirst.DoubleBuffered:= True;
pbSecond.DoubleBuffered:= True;
Self.DoubleBuffered:= True;
Operation := OperationsManager.GetOperationByHandle(FOperationHandle);
if Assigned(Operation) then
begin
case Operation.ID of
fsoCopyIn, fsoCopyOut:
InitializeCopyOperation(Operation);
else
begin
Caption := 'Unknown operation';
InitializeControls([fodl_first_pb]);
end;
end;
end;
FUpdateTimer := TTimer.Create(Self);
FUpdateTimer.Interval := 100;
FUpdateTimer.OnTimer := @OnUpdateTimer;
FUpdateTimer.Enabled := True;
pbFirst.Max := 100;
end;
procedure TfrmFileOp.FormShow(Sender: TObject);
@ -142,16 +175,37 @@ constructor TfrmFileOp.Create(OperationHandle: TOperationHandle);
begin
FOperationHandle := OperationHandle;
inherited Create(Application);
AutoSize := True;
end;
procedure TfrmFileOp.OnUpdateTimer(Sender: TObject);
var
Operation: TFileSourceOperation;
begin
Operation := OperationsManager.OperationByHandle[FOperationHandle];
Operation := OperationsManager.GetOperationByHandle(FOperationHandle);
if Assigned(Operation) then
begin
pbFirst.Position := Operation.Progress;
case Operation.ID of
fsoCopyOut:
UpdateCopyOperation(Operation);
else
// Operation not currently supported for display.
// Only show general progress.
pbFirst.Position := Operation.Progress;
end;
Caption := IntToStr(Operation.Progress) + '% ' + Hint;
{
// Estimate remaining time somehow (by progress or individual statistics).
if sEstimated <> lblEstimated.Caption then
begin
lblEstimated.Caption:= sEstimated;
lblEstimated.Invalidate;
end;
}
end
else
begin
@ -162,6 +216,50 @@ begin
end;
end;
procedure TfrmFileOp.InitializeControls(FileOpDlgLook: TFileOpDlgLook);
begin
lblFrom.Visible := fodl_from_lbl in FileOpDlgLook;
lblFileNameFrom.Visible := fodl_from_lbl in FileOpDlgLook;
lblTo.Visible := fodl_to_lbl in FileOpDlgLook;
lblFileNameTo.Visible := fodl_to_lbl in FileOpDlgLook;
pbFirst.Visible := fodl_first_pb in FileOpDlgLook;
pbSecond.Visible := fodl_second_pb in FileOpDlgLook;
end;
procedure TfrmFileOp.InitializeCopyOperation(Operation: TFileSourceOperation);
var
CopyOperation: TFileSourceCopyOperation;
begin
//CopyOperation := Operation as TFileSourceCopyOperation;
Caption := rsDlgCp;
InitializeControls([fodl_from_lbl, fodl_to_lbl, fodl_first_pb, fodl_second_pb]);
end;
procedure TfrmFileOp.UpdateCopyOperation(Operation: TFileSourceOperation);
var
CopyOperation: TFileSourceCopyOperation;
CopyStatistics: TFileSourceCopyOperationStatistics;
begin
CopyOperation := Operation as TFileSourceCopyOperation;
CopyStatistics := CopyOperation.RetrieveStatistics;
with CopyStatistics do
begin
lblFileNameFrom.Caption := CurrentFileFrom;
lblFileNameTo.Caption := CurrentFileTo;
if CurrentFileTotalBytes <> 0 then
pbFirst.Position := (CurrentFileDoneBytes * 100) div CurrentFileTotalBytes
else
pbFirst.Position := pbFirst.Max;
if TotalBytes <> 0 then
pbSecond.Position := (DoneBytes * 100) div TotalBytes
else
pbSecond.Position := pbSecond.Max;
end;
end;
procedure TfrmFileOp.ToggleProgressBarStyle;
begin
if (pbFirst.Style = pbstMarquee) and (pbSecond.Style = pbstMarquee) then

View file

@ -11,6 +11,7 @@ object frmMain: TfrmMain
Menu = mnuMain
OnActivate = FormActivate
OnClose = frmMainClose
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
OnDestroy = FormDestroy
OnDropFiles = FormDropFiles

View file

@ -349,6 +349,7 @@ type
procedure dskToolButtonClick(Sender: TObject; NumberOfButton: Integer);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
procedure FormUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
procedure FormWindowStateChange(Sender: TObject);
@ -761,6 +762,17 @@ begin
DestroyDrivesList(DrivesList);
end;
procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: boolean);
begin
if OperationsManager.OperationsCount > 0 then
begin
msgWarning('There are operations running!');
CanClose := False;
end
else
CanClose := True;
end;
procedure TfrmMain.FormDropFiles(Sender: TObject; const FileNames: array of String);
// This should not be needed for now as drag&drop should work through uDragDropEx.
{var

View file

@ -1,11 +1,12 @@
object frmViewOperations: TfrmViewOperations
Left = 295
Height = 246
Top = 181
Width = 634
Left = 192
Height = 259
Top = 192
Width = 644
Caption = 'frmViewOperations'
ClientHeight = 246
ClientWidth = 634
ClientHeight = 259
ClientWidth = 644
FormStyle = fsStayOnTop
OnCreate = FormCreate
LCLVersion = '0.9.27'
object lblOperationsCount: TLabel
@ -17,20 +18,21 @@ object frmViewOperations: TfrmViewOperations
ParentColor = False
end
object lblCount: TLabel
Left = 144
Left = 224
Height = 18
Top = 16
Width = 47
Caption = 'lblCount'
ParentColor = False
end
object lblProgress: TLabel
Left = 224
Height = 18
Top = 16
Width = 61
Caption = 'lblProgress'
ParentColor = False
object sboxOperations: TScrollBox
Left = 22
Height = 161
Top = 47
Width = 594
TabOrder = 0
OnDblClick = sboxOperationsDblClick
OnPaint = sboxOperationsPaint
end
object UpdateTimer: TTimer
Interval = 100

View file

@ -14,12 +14,14 @@ type
{ TfrmViewOperations }
TfrmViewOperations = class(TForm)
lblProgress: TLabel;
lblCount: TLabel;
lblOperationsCount: TLabel;
sboxOperations: TScrollBox;
UpdateTimer: TTimer;
procedure FormCreate(Sender: TObject);
procedure OnUpdateTimer(Sender: TObject);
procedure sboxOperationsDblClick(Sender: TObject);
procedure sboxOperationsPaint(Sender: TObject);
private
procedure UpdateView(Operation: TFileSourceOperation);
@ -34,19 +36,30 @@ var
implementation
uses
uOperationsManager, lclproc;
uOperationsManager,
uFileSourceOperationTypes,
uFileSourceCopyOperation,
uLng, LCLProc, fFileOpDlg;
const
aRowHeight = 50;
{ TfrmViewOperations }
procedure TfrmViewOperations.FormCreate(Sender: TObject);
begin
lblCount.Caption := '0';
lblProgress.Caption := '0 %';
sboxOperations.AutoScroll := True;
sboxOperations.VertScrollBar.Visible := True;
OperationsManager.OnOperationAdded := @UpdateView;
OperationsManager.OnOperationRemoved := @UpdateView;
OperationsManager.OnOperationStarted := @UpdateView;
OperationsManager.OnOperationFinished := @UpdateView;
lblCount.Caption := IntToStr(OperationsManager.OperationsCount);
sboxOperations.Invalidate; // force redraw
end;
procedure TfrmViewOperations.OnUpdateTimer(Sender: TObject);
@ -58,10 +71,73 @@ begin
begin
// Timer is called from main thread, so it is safe
// to use reference to Operation from OperationsManager.
Operation := OperationsManager.OperationByIndex[i];
Operation := OperationsManager.GetOperationByIndex(i);
if Assigned(Operation) then
begin
lblProgress.Caption := IntToStr(Operation.Progress) + ' %';
sboxOperations.Invalidate; // force redraw
end;
end;
end;
procedure TfrmViewOperations.sboxOperationsDblClick(Sender: TObject);
var
OperationNumber: Integer;
CursorPos: TPoint;
Operation: TFileSourceOperation;
CopyDialog: TfrmFileOp;
begin
CursorPos := Mouse.CursorPos;
CursorPos := sboxOperations.ScreenToClient(CursorPos);
OperationNumber := CursorPos.Y div aRowHeight;
Operation := OperationsManager.GetOperationByIndex(OperationNumber);
if Assigned(Operation) then
begin
// Example for Copy operation for now.
if Operation is TFileSourceCopyOutOperation then
begin
CopyDialog := TfrmFileOp.Create(OperationsManager.GetHandleById(OperationNumber));
CopyDialog.Show;
end;
end;
end;
procedure TfrmViewOperations.sboxOperationsPaint(Sender: TObject);
var
Operation: TFileSourceOperation;
i: Integer;
OutString: String;
begin
for i := 0 to OperationsManager.OperationsCount - 1 do
begin
// Timer is called from main thread, so it is safe
// to use reference to Operation from OperationsManager.
Operation := OperationsManager.GetOperationByIndex(i);
if Assigned(Operation) then
begin
case Operation.ID of
fsoCopyIn, fsoCopyOut:
OutString := rsDlgCp;
else
OutString := 'Unknown operation';
end;
OutString := IntToStr(OperationsManager.GetHandleById(i)) + ': '
+ OutString + ' - '
+ IntToStr(Operation.Progress) + ' %';
sboxOperations.Canvas.Brush.Color := Canvas.Brush.Color;
sboxOperations.Canvas.Rectangle(0, 0 + (aRowHeight * i), sboxOperations.Width, aRowHeight + (aRowHeight * i));
sboxOperations.Canvas.TextOut(5, 5 + (aRowHeight * i), OutString);
sboxOperations.Caption := OutString;
sboxOperations.Canvas.Brush.Color := clHighlight;
sboxOperations.Canvas.FillRect(
5,
5 + (aRowHeight * i) + sboxOperations.Canvas.TextHeight('Pg'),
5 + (sboxOperations.Width - 10) * Operation.Progress div 100,
aRowHeight * (i + 1) - 5);
end;
end;
end;
@ -69,7 +145,7 @@ end;
procedure TfrmViewOperations.UpdateView(Operation: TFileSourceOperation);
begin
lblCount.Caption := IntToStr(OperationsManager.OperationsCount);
lblProgress.Caption := IntToStr(Operation.Progress) + ' %';
sboxOperations.Invalidate; // force redraw
end;
initialization

View file

@ -1,47 +0,0 @@
unit uFileSourceCopyInOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceOperation;
type
{en
Operation that copies files from another file source into a file source of specific type
(to file system for TFileSystemCopyInOperation,
to network for TNetworkCopyInOperation, etc.).
Source file source must be a file system file source.
(Or is it enough if it's a file source with directly accessible files ? (DirectAccess flag))
Target file source should match the class type.
Example meaning of this operation:
- archive: pack
- network: upload
}
TFileSourceCopyInOperation = class(TFileSourceOperation)
public
constructor Create; override;
destructor Destroy; override;
end;
implementation
constructor TFileSourceCopyInOperation.Create;
begin
inherited;
end;
destructor TFileSourceCopyInOperation.Destroy;
begin
inherited;
end;
end.

View file

@ -0,0 +1,151 @@
unit uFileSourceCopyOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, syncobjs,
uFileSourceOperation,
uFileSourceOperationTypes;
type
// Statistics are the same for CopyIn and CopyOut operations.
TFileSourceCopyOperationStatistics = record
CurrentFileFrom: String;
CurrentFileTo: String;
CurrentFileTotalBytes: Int64;
CurrentFileDoneBytes: Int64;
TotalFiles: Int64;
DoneFiles: Int64;
TotalBytes: Int64;
DoneBytes: Int64;
end;
{en
Base class for CopyIn and CopyOut operations.
}
TFileSourceCopyOperation = class(TFileSourceOperation)
private
FStatistics: TFileSourceCopyOperationStatistics;
FStatisticsLock: TCriticalSection; //<en For synchronizing statistics.
protected
procedure UpdateStatistics(NewStatistics: TFileSourceCopyOperationStatistics);
public
constructor Create; override;
destructor Destroy; override;
function RetrieveStatistics: TFileSourceCopyOperationStatistics;
end;
{en
Operation that copies files from another file source into a file source of specific type
(to file system for TFileSystemCopyInOperation,
to network for TNetworkCopyInOperation, etc.).
Source file source must be a file system file source.
(Or is it enough if it's a file source with directly accessible files ? (DirectAccess flag))
Target file source should match the class type.
Example meaning of this operation:
- archive: pack
- network: upload
}
TFileSourceCopyInOperation = class(TFileSourceCopyOperation)
protected
function GetID: TFileSourceOperationType; override;
end;
{en
Operation that copies files into another file source from a file source of specific type
(from file system for TFileSystemCopyOutOperation,
from network for TNetworkCopyOutOperation, etc.).
Source file source should match the class type.
Target file source must be a file system file source.
(Or is it enough if it's a file source with directly accessible files ? (DirectAccess flag))
Example meaning of this operation:
- archive: unpack
- network: download
}
TFileSourceCopyOutOperation = class(TFileSourceCopyOperation)
protected
function GetID: TFileSourceOperationType; override;
end;
implementation
// -- TFileSourceCopyOperation ------------------------------------------------
constructor TFileSourceCopyOperation.Create;
begin
with FStatistics do
begin
CurrentFileFrom := '';
CurrentFileTo := '';
TotalFiles := 0;
DoneFiles := 0;
TotalBytes := 0;
DoneBytes := 0;
CurrentFileTotalBytes := 0;
CurrentFileDoneBytes := 0;
end;
FStatisticsLock := TCriticalSection.Create;
inherited Create;
end;
destructor TFileSourceCopyOperation.Destroy;
begin
inherited Destroy;
FreeAndNil(FStatisticsLock);
end;
procedure TFileSourceCopyOperation.UpdateStatistics(NewStatistics: TFileSourceCopyOperationStatistics);
begin
FStatisticsLock.Acquire;
try
Self.FStatistics := NewStatistics;
finally
FStatisticsLock.Release;
end;
end;
function TFileSourceCopyOperation.RetrieveStatistics: TFileSourceCopyOperationStatistics;
begin
FStatisticsLock.Acquire;
try
Result := Self.FStatistics;
finally
FStatisticsLock.Release;
end;
end;
// -- TFileSourceCopyInOperation ----------------------------------------------
function TFileSourceCopyInOperation.GetID: TFileSourceOperationType;
begin
Result := fsoCopyIn;
end;
// -- TFileSourceCopyOutOperation ---------------------------------------------
function TFileSourceCopyOutOperation.GetID: TFileSourceOperationType;
begin
Result := fsoCopyOut;
end;
end.

View file

@ -1,47 +0,0 @@
unit uFileSourceCopyOutOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceOperation;
type
{en
Operation that copies files into another file source from a file source of specific type
(from file system for TFileSystemCopyOutOperation,
from network for TNetworkCopyOutOperation, etc.).
Source file source should match the class type.
Target file source must be a file system file source.
(Or is it enough if it's a file source with directly accessible files ? (DirectAccess flag))
Example meaning of this operation:
- archive: unpack
- network: download
}
TFileSourceCopyOutOperation = class(TFileSourceOperation)
public
constructor Create; override;
destructor Destroy; override;
end;
implementation
constructor TFileSourceCopyOutOperation.Create;
begin
inherited;
end;
destructor TFileSourceCopyOutOperation.Destroy;
begin
inherited;
end;
end.

View file

@ -1,38 +1,47 @@
unit uFileSourceDeleteOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceOperation;
type
{en
Operation that deletes files from an arbitrary file source.
File source should match the class type.
}
TFileSourceDeleteOperation = class(TFileSourceOperation)
public
constructor Create; override;
destructor Destroy; override;
end;
implementation
constructor TFileSourceDeleteOperation.Create;
begin
inherited;
end;
destructor TFileSourceDeleteOperation.Destroy;
begin
inherited;
end;
end.
unit uFileSourceDeleteOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceOperation,
uFileSourceOperationTypes;
type
{en
Operation that deletes files from an arbitrary file source.
File source should match the class type.
}
TFileSourceDeleteOperation = class(TFileSourceOperation)
protected
function GetID: TFileSourceOperationType; override;
public
constructor Create; override;
destructor Destroy; override;
end;
implementation
constructor TFileSourceDeleteOperation.Create;
begin
inherited;
end;
destructor TFileSourceDeleteOperation.Destroy;
begin
inherited;
end;
function TFileSourceDeleteOperation.GetID: TFileSourceOperationType;
begin
Result := fsoDelete;
end;
end.

View file

@ -1,61 +1,69 @@
unit uFileSourceListOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceOperation,
uFile;
type
TFileSourceListOperation = class(TFileSourceOperation)
protected
FFiles: TFiles;
function GetFiles: TFiles;
public
constructor Create; override;
destructor Destroy; override;
// Retrieves files and revokes ownership of TFiles list.
// The result of this function should be freed by the caller.
function ReleaseFiles: TFiles;
property Files: TFiles read GetFiles;
end;
implementation
constructor TFileSourceListOperation.Create;
begin
FFiles := TFiles.Create;
inherited Create;
end;
destructor TFileSourceListOperation.Destroy;
begin
inherited Destroy;
if Assigned(FFiles) then
FreeAndNil(FFiles);
end;
function TFileSourceListOperation.GetFiles: TFiles;
begin
Result := FFiles;
end;
function TFileSourceListOperation.ReleaseFiles: TFiles;
begin
Result := FFiles;
FFiles := nil; // revoke ownership
end;
end.
unit uFileSourceListOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceOperation,
uFileSourceOperationTypes,
uFile;
type
TFileSourceListOperation = class(TFileSourceOperation)
protected
FFiles: TFiles;
function GetFiles: TFiles;
function GetID: TFileSourceOperationType; override;
public
constructor Create; override;
destructor Destroy; override;
// Retrieves files and revokes ownership of TFiles list.
// The result of this function should be freed by the caller.
function ReleaseFiles: TFiles;
property Files: TFiles read GetFiles;
end;
implementation
constructor TFileSourceListOperation.Create;
begin
FFiles := TFiles.Create;
inherited Create;
end;
destructor TFileSourceListOperation.Destroy;
begin
inherited Destroy;
if Assigned(FFiles) then
FreeAndNil(FFiles);
end;
function TFileSourceListOperation.GetID: TFileSourceOperationType;
begin
Result := fsoList;
end;
function TFileSourceListOperation.GetFiles: TFiles;
begin
Result := FFiles;
end;
function TFileSourceListOperation.ReleaseFiles: TFiles;
begin
Result := FFiles;
FFiles := nil; // revoke ownership
end;
end.

View file

@ -1,100 +1,140 @@
unit uFileSourceOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
const
guidIFileSourceOperationUI = '{3BC28C8B-8A3E-4F71-8828-52483B8C057A}';
type
TFileSourceOperationState =
(fsosNotStarted, //<en before operation has started
fsosStarting, //<en responded to Start command
fsosRunning,
fsosPausing, //<en responded to Pause command
fsosPaused,
fsosStopping, //<en responded to Stop command
fsosStopped); //<en finished due to Stop command or on its own
TFileSourceOperationStopReason =
(fssrFinished, //<en normal finish
fssrAborted); //<en aborted due to Stop command (by user)
TFileSourceOperation = class
protected
FState: TFileSourceOperationState; // both must be synchronized
FStopReason: TFileSourceOperationStopReason;
{en
Should return a general progress (0-100) of the operation.
Specific statistics are returned by the individual operations.
It is only read from the main thread and written to by operation
executing thread, thus probably doesn't need synchronization.
}
function GetProgress: Integer; virtual abstract;
public
constructor Create; virtual;
{en
Tries to execute operation.
First immediately sets state to fsosStarting and after it is started to fsosRunning.
}
procedure Execute; virtual abstract;
{en
Tries to put operation into a paused state.
First immediately sets state to fsosPausing and after it is paused to fsosPaused.
}
procedure Pause; virtual abstract;
{en
Tries to stop operation.
First immediately sets state to fsosStopping and after it is stopped to fsosStopped.
}
procedure Stop; virtual abstract;
property Progress: Integer read GetProgress;
end;
{en
Interface used by each operation to communicate
with the main application and the user.
The operations are able to use different interfaces for communicating
(message boxes, logging in edit controls or file with no questions,
through console, ...).
When operation has no communication interface it runs silent by
any options it has set (for example: overwrite all or skip all).
}
IFileSourceOperationUI = interface [guidIFileSourceOperationUI]
{en If the operation should terminate.}
function Terminate: Boolean;
// While copying file exists. Ask what to do:
//function FileExists: what to do;
// FileReadOnly
// AskOverwrite
// etc.
end;
implementation
constructor TFileSourceOperation.Create;
begin
inherited Create;
FState := fsosNotStarted;
FStopReason := fssrFinished;
end;
end.
unit uFileSourceOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceOperationTypes;
const
guidIFileSourceOperationUI = '{3BC28C8B-8A3E-4F71-8828-52483B8C057A}';
type
TFileSourceOperationState =
(fsosNotStarted, //<en before operation has started
fsosStarting, //<en responded to Start command
fsosRunning,
fsosPausing, //<en responded to Pause command
fsosPaused,
fsosWaitingForFeedback, //<en waiting for a response from a user through the assigned UI
fsosStopping, //<en responded to Stop command
fsosStopped); //<en finished due to Stop command or on its own
TFileSourceOperationStopReason =
(fssrFinished, //<en normal finish
fssrAborted); //<en aborted due to Stop command (by user)
{en
Base class for each file source operation.
}
TFileSourceOperation = class
private
FProgress: Integer;
{en
Should return a general progress (0-100) of the operation.
Specific statistics are returned by the individual operations.
It is only read from the main thread and written to by operation
executing thread, thus probably doesn't need synchronization.
}
function GetProgress: Integer;
protected
FState: TFileSourceOperationState; // both must be synchronized
FStopReason: TFileSourceOperationStopReason;
procedure UpdateProgress(NewProgress: Integer);
{en
This should be set to the correct file operation type in each concrete descendant.
We rely on this when making a decision based on operation type. This way
it's easier to maintain different sorts of things we can do with operations
and statistics, without having to include knowledge of those things
in the operations classes hierarchy.
}
function GetID: TFileSourceOperationType; virtual abstract;
public
constructor Create; virtual;
destructor Destroy; override;
{en
Tries to execute operation.
First immediately sets state to fsosStarting and after it is started to fsosRunning.
}
procedure Execute; virtual abstract;
{en
Tries to put operation into a paused state.
First immediately sets state to fsosPausing and after it is paused to fsosPaused.
}
procedure Pause; virtual abstract;
{en
Tries to stop operation.
First immediately sets state to fsosStopping and after it is stopped to fsosStopped.
}
procedure Stop; virtual abstract;
property Progress: Integer read GetProgress;
property ID: TFileSourceOperationType read GetID;
end;
{en
Interface used by each operation to communicate
with the main application and the user.
The operations are able to use different interfaces for communicating
(message boxes, logging in edit controls or file with no questions,
through console, ...).
When operation has no communication interface it runs silent by
any options it has set (for example: overwrite all or skip all).
}
IFileSourceOperationUI = interface [guidIFileSourceOperationUI]
{en If the operation should terminate.}
function Terminate: Boolean;
// While copying file exists. Ask what to do:
//function FileExists: what to do;
// FileReadOnly
// AskOverwrite
// etc.
end;
implementation
constructor TFileSourceOperation.Create;
begin
FState := fsosNotStarted;
FStopReason := fssrFinished;
FProgress := 0;
inherited Create;
end;
destructor TFileSourceOperation.Destroy;
begin
inherited Destroy;
end;
function TFileSourceOperation.GetProgress: Integer;
begin
// Doesn't need synchronization.
Result := FProgress;
end;
procedure TFileSourceOperation.UpdateProgress(NewProgress: Integer);
begin
// Doesn't need synchronization.
FProgress := NewProgress;
end;
end.

View file

@ -1,119 +1,139 @@
unit uFileSystemCopyOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceCopyInOperation,
uFileSourceCopyOutOperation,
uFileSystemFileSource,
uFileSource,
uFile;
type
{
Both operations are the same, just source and target reversed.
Implement them in terms of the same functions,
or have one use the other.
}
TFileSystemCopyInOperation = class(TFileSourceCopyInOperation)
private
FSourceFileSource: TFileSystemFileSource;
FTargetFileSource: TFileSystemFileSource;
FSourceFiles: TFiles;
FTargetFiles: TFiles;
public
constructor Create(SourceFileSource: TFileSystemFileSource;
TargetFileSource: TFileSystemFileSource;
SourceFiles: TFiles;
TargetFiles: TFiles); reintroduce;
procedure Execute; override;
end;
TFileSystemCopyOutOperation = class(TFileSourceCopyOutOperation)
private
FSourceFileSource: TFileSystemFileSource;
FTargetFileSource: TFileSystemFileSource;
FSourceFiles: TFiles;
FTargetFiles: TFiles;
FProgress: Integer;
protected
function GetProgress: Integer; override;
public
constructor Create(SourceFileSource: TFileSystemFileSource;
TargetFileSource: TFileSystemFileSource;
SourceFiles: TFiles;
TargetFiles: TFiles); reintroduce;
procedure Execute; override;
end;
implementation
// -- TFileSystemCopyInOperation ----------------------------------------------
constructor TFileSystemCopyInOperation.Create(SourceFileSource: TFileSystemFileSource;
TargetFileSource: TFileSystemFileSource;
SourceFiles: TFiles;
TargetFiles: TFiles);
begin
inherited Create;
FSourceFileSource := SourceFileSource;
FTargetFileSource := TargetFileSource;
FSourceFiles := SourceFiles;
FTargetFiles := TargetFiles;
end;
procedure TFileSystemCopyInOperation.Execute;
begin
end;
// -- TFileSystemCopyOutOperation ---------------------------------------------
constructor TFileSystemCopyOutOperation.Create(SourceFileSource: TFileSystemFileSource;
TargetFileSource: TFileSystemFileSource;
SourceFiles: TFiles;
TargetFiles: TFiles);
begin
inherited Create;
FSourceFileSource := SourceFileSource;
FTargetFileSource := TargetFileSource;
FSourceFiles := SourceFiles;
FTargetFiles := TargetFiles;
FProgress := 0;
end;
function TFileSystemCopyOutOperation.GetProgress: Integer;
begin
Result := FProgress;
end;
procedure TFileSystemCopyOutOperation.Execute;
var
i: Integer;
begin
// Some dummy long operation for now.
for i := 1 to 300 do
begin
Sleep(50);
FProgress := (i * 100) div 300;
end;
end;
end.
unit uFileSystemCopyOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceCopyOperation,
uFileSystemFileSource,
uFileSource,
uFile;
type
{
Both operations are the same, just source and target reversed.
Implement them in terms of the same functions,
or have one use the other.
}
TFileSystemCopyInOperation = class(TFileSourceCopyInOperation)
private
FSourceFileSource: TFileSystemFileSource;
FTargetFileSource: TFileSystemFileSource;
FSourceFiles: TFiles;
FTargetFiles: TFiles;
public
constructor Create(SourceFileSource: TFileSystemFileSource;
TargetFileSource: TFileSystemFileSource;
SourceFiles: TFiles;
TargetFiles: TFiles); reintroduce;
procedure Execute; override;
end;
TFileSystemCopyOutOperation = class(TFileSourceCopyOutOperation)
private
FSourceFileSource: TFileSystemFileSource;
FTargetFileSource: TFileSystemFileSource;
FSourceFiles: TFiles;
FTargetFiles: TFiles;
public
constructor Create(SourceFileSource: TFileSystemFileSource;
TargetFileSource: TFileSystemFileSource;
SourceFiles: TFiles;
TargetFiles: TFiles); reintroduce;
procedure Execute; override;
end;
implementation
// -- TFileSystemCopyInOperation ----------------------------------------------
constructor TFileSystemCopyInOperation.Create(SourceFileSource: TFileSystemFileSource;
TargetFileSource: TFileSystemFileSource;
SourceFiles: TFiles;
TargetFiles: TFiles);
begin
inherited Create;
FSourceFileSource := SourceFileSource;
FTargetFileSource := TargetFileSource;
FSourceFiles := SourceFiles;
FTargetFiles := TargetFiles;
end;
procedure TFileSystemCopyInOperation.Execute;
begin
end;
// -- TFileSystemCopyOutOperation ---------------------------------------------
constructor TFileSystemCopyOutOperation.Create(SourceFileSource: TFileSystemFileSource;
TargetFileSource: TFileSystemFileSource;
SourceFiles: TFiles;
TargetFiles: TFiles);
begin
inherited Create;
FSourceFileSource := SourceFileSource;
FTargetFileSource := TargetFileSource;
FSourceFiles := SourceFiles;
FTargetFiles := TargetFiles;
end;
procedure TFileSystemCopyOutOperation.Execute;
var
i: Integer;
Statistics: TFileSourceCopyOperationStatistics;
begin
// Get initialized statistics; then we change only what is needed.
Statistics := RetrieveStatistics;
with Statistics do
begin
TotalBytes := 300 * 50;
TotalFiles := 300;
end;
// Some dummy long operation for now.
for i := 1 to 300 do
begin
with Statistics do
begin
CurrentFileFrom := 'sourceFile_' + inttostr(i) +'.pas';
CurrentFileTo := 'targetFile_'+ inttostr(i)+ '.pas';
end;
UpdateStatistics(Statistics);
// Main work single step.
Sleep(50);
// Update overall progress.
// (should this be under the same lock as statistics?)
UpdateProgress((i * 100) div 300);
// Update specific statistics.
with Statistics do
begin
DoneFiles := DoneFiles + 1;
DoneBytes := DoneBytes + 50;
end;
end;
// Final statistics.
UpdateStatistics(Statistics);
UpdateProgress(100);
end;
end.

View file

@ -6,7 +6,7 @@ interface
uses
Classes, SysUtils, ExtCtrls, LCLIntf, syncobjs,
uOperationThread, uFileSourceOperation;
uOperationThread, uFileSourceOperation, lclproc;
type
@ -18,14 +18,25 @@ const
type
{en
Possible options when adding a new operation.
}
TOperationStartingState =
(ossDontStart, //<en Don't start automatically.
ossAutoStart, //<en Start automatically.
ossQueue); //<en Don't start automatically,
//<en unless there are no other operations working.
POperationsManagerEntry = ^TOperationsManagerEntry;
TOperationsManagerEntry = record
Thread : TOperationThread;
Operation: TFileSourceOperation;
Handle : TOperationHandle;
Thread : TOperationThread;
Operation : TFileSourceOperation;
Handle : TOperationHandle;
StartingState: TOperationStartingState;
end;
TOperationManagerEvent = procedure(Operation: TFileSourceOperation) of object;
{en
Manages file source operations.
Executes them, stores threads, allows querying active operations
@ -35,7 +46,7 @@ type
private
FOperations: TFPList; //<en List of TOperationsManagerEntry
FLock: TCriticalSection;
FNextUnusedHandle: TOperationHandle;
FLastUsedHandle: TOperationHandle;
// Events follow.
// (do this with multiple listeners, so many viewers can look through active operations).
@ -53,8 +64,6 @@ type
procedure ThreadTerminatedEvent(Sender: TObject);
function GetOperationsCount: Integer;
function GetOperationByIndex(Index: Integer): TFileSourceOperation;
function GetOperationByHandle(Handle: TOperationHandle): TFileSourceOperation;
function GetNextUnusedHandle: TOperationHandle;
@ -63,10 +72,18 @@ type
destructor Destroy; override;
function AddOperation(Operation: TFileSourceOperation;
StartImmediately: Boolean): TOperationHandle;
StartingState: TOperationStartingState): TOperationHandle;
{en
Operations retrieved this way can be safely used from the main GUI thread.
But they should not be stored for longer use, because they
may be destroyed by the Operations Manager when they finish.
}
function GetOperationByIndex(Index: Integer): TFileSourceOperation;
function GetOperationByHandle(Handle: TOperationHandle): TFileSourceOperation;
function GetHandleById(Index: Integer): TOperationHandle;
property OperationByIndex[Index: Integer]: TFileSourceOperation read GetOperationByIndex;
property OperationByHandle[Handle: TOperationHandle]: TFileSourceOperation read GetOperationByHandle;
property OperationsCount: Integer read GetOperationsCount;
// Events.
@ -85,7 +102,7 @@ constructor TOperationsManager.Create;
begin
FOperations := TFPList.Create;
FLock := TCriticalSection.Create;
FNextUnusedHandle := 1; // Start from 1.
FLastUsedHandle := 0;
FOnOperationAdded := nil;
FOnOperationRemoved := nil;
@ -106,7 +123,7 @@ begin
end;
function TOperationsManager.AddOperation(Operation: TFileSourceOperation;
StartImmediately: Boolean): TOperationHandle;
StartingState: TOperationStartingState): TOperationHandle;
var
Thread: TOperationThread;
Entry: POperationsManagerEntry;
@ -128,6 +145,7 @@ begin
Entry^.Operation := Operation;
Entry^.Thread := Thread;
Entry^.Handle := GetNextUnusedHandle;
Entry^.StartingState := StartingState;
FOperations.Add(Entry);
@ -141,7 +159,7 @@ begin
if Assigned(FOnOperationAdded) then
FOnOperationAdded(Operation);
if StartImmediately then
if StartingState = ossAutoStart then
begin
Thread.Resume;
@ -171,7 +189,7 @@ begin
if (Index >= 0) and (Index < FOperations.Count) then
begin
Entry := POperationsManagerEntry(FOperations.Items[Index]);
if Assigned(Entry) and Assigned(Entry^.Operation) then
if Assigned(Entry^.Operation) then
Result := Entry^.Operation;
end
else
@ -200,33 +218,45 @@ begin
end;
end;
function TOperationsManager.GetHandleById(Index: Integer): TOperationHandle;
var
Entry: POperationsManagerEntry = nil;
begin
if (Index >= 0) and (Index < FOperations.Count) then
begin
Entry := POperationsManagerEntry(FOperations.Items[Index]);
Result := Entry^.Handle;
end
else
Result := InvalidOperationHandle;
end;
function TOperationsManager.GetNextUnusedHandle: TOperationHandle;
begin
// Handles are consecutively incremented.
// Even if they overflow there is little probability that
// there will be that many operations.
Result := InterLockedIncrement(FNextUnusedHandle);
Result := InterLockedIncrement(FLastUsedHandle);
if Result = InvalidOperationHandle then
Result := InterLockedIncrement(FNextUnusedHandle);
Result := InterLockedIncrement(FLastUsedHandle);
end;
procedure TOperationsManager.ThreadTerminatedEvent(Sender: TObject);
var
Thread: TOperationThread;
Entry: POperationsManagerEntry = nil;
i: Integer;
Index: Integer = -1;
begin
// This function is executed from the main thread (through Synchronize).
// This function is executed from the GUI thread (through Synchronize).
Thread := Sender as TOperationThread;
// Search the terminated in the operations list.
for i := 0 to FOperations.Count - 1 do
// Search the terminated thread in the operations list.
for Index := 0 to FOperations.Count - 1 do
begin
Entry := POperationsManagerEntry(FOperations.Items[i]);
Entry := POperationsManagerEntry(FOperations.Items[Index]);
if Entry^.Thread = Thread then
begin
FOperations.Delete(i);
break;
end;
end;
@ -236,11 +266,13 @@ begin
if Assigned(FOnOperationFinished) then
FOnOperationFinished(Entry^.Operation);
FreeAndNil(Entry^.Thread);
FOperations.Delete(Index);
if Assigned(FOnOperationRemoved) then
FOnOperationRemoved(Entry^.Operation);
Entry^.Thread := nil; // Thread frees himself automatically on terminate.
// Here the operation should not be used anymore
// (by the thread and by any operations viewer).
FreeAndNil(Entry^.Operation);

View file

@ -6,7 +6,7 @@ interface
uses
Classes, SysUtils,
uFileSourceOperation;
uFileSourceOperation, LCLProc;
type
@ -36,7 +36,7 @@ implementation
constructor TOperationThread.Create(CreateSuspended: Boolean; Operation: TFileSourceOperation);
begin
FreeOnTerminate := False;
FreeOnTerminate := True;
FOperation := Operation;
//FOperation.UI.Terminate := @CheckTerminated;

View file

@ -1289,7 +1289,7 @@ begin
// Dummy operation for testing.
debugln('Starting copy operation');
Operation := TFileSystemCopyOutOperation.Create(nil,nil,nil,nil);
OperationHandle := OperationsManager.AddOperation(Operation, True);
OperationHandle := OperationsManager.AddOperation(Operation, ossAutoStart);
// The OperationHandle should now be passed to the dialog displaying progress.

View file

@ -18,7 +18,8 @@ unit uDeleteThread;
interface
uses
uFileOpThread, uFileList, uTypes, SysUtils, LCLProc;
uFileOpThread, uFileList, uTypes, SysUtils, LCLProc,
fFileOpDlg;
type

View file

@ -23,8 +23,6 @@ uses
Classes, uFileList, fFileOpDlg, uTypes, uDescr, fMsg, uShowMsg {$IFNDEF NOFAKETHREAD}, uFakeThread{$ENDIF};
type
TFileOpDlgLook = set of (fodl_from_lbl, fodl_to_lbl, fodl_first_pb, fodl_second_pb);
{ TFileOpThread }
{$IFDEF NOFAKETHREAD}
TFileOpThread = class(TThread)

View file

@ -46,7 +46,7 @@ unit uWipeThread;
interface
uses
uFileOpThread, uFileList, uTypes, SysUtils, LCLProc;
uFileOpThread, uFileList, uTypes, SysUtils, LCLProc, fFileOpDlg;
type