mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-15 03:59:10 +02:00
win32: removed limit on number of selected files (issue #7220)
git-svn-id: trunk@9750 -
This commit is contained in:
parent
268a4c8701
commit
02b059ec49
@ -189,11 +189,74 @@ begin
|
|||||||
Result := 0;
|
Result := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TWin32WSSaveDialog }
|
{Common code for OpenDialog and SaveDialog}
|
||||||
|
|
||||||
|
{The API of the multiselect open file dialog is a bit problematic.
|
||||||
|
Before calling the OpenFile function you must create a buffer (lpStrFile) to
|
||||||
|
hold the selected files.
|
||||||
|
|
||||||
|
With a multiselect dialog there is no way to create a buffer with correct size:
|
||||||
|
* either it is too small (for example 1 KB), if a lot a files are selected
|
||||||
|
* or it wastes a lot of memory (for example 1 MB), and even than you have no
|
||||||
|
guarantee, that is big enough.
|
||||||
|
|
||||||
|
The OpenFile API call returns false, if an error has occurred or the user has
|
||||||
|
pressed cancel. If there was an error CommDlgExtendedError returns
|
||||||
|
FNERR_BUFFERTOOSMALL. But enlarging the buffer at that time is not usefull
|
||||||
|
anymore, unless you show the dialog again with a bigger buffer (Sorry, the
|
||||||
|
buffer was too small, please select the files again). This is not acceptable.
|
||||||
|
|
||||||
|
It is possible to hook the filedialog, so you get messages, when the selection
|
||||||
|
changes. A naive aproach would be to see, if the buffer would be big enough for
|
||||||
|
the selected files and create or enlarge the buffer (as described in KB131462).
|
||||||
|
Unfortunately, this only works with win9x and the unicode versions of later
|
||||||
|
windows versions.
|
||||||
|
|
||||||
|
Therefore in the hook function, if the size of the initial buffer is not large
|
||||||
|
enough, the selected files are copied into another buffer. When dialog is
|
||||||
|
closed with a FNERR_BUFFERTOOSMALL error, this buffer is used to get the
|
||||||
|
selected files. If this error did not occur, the normal way of retrieving the
|
||||||
|
files is used.
|
||||||
|
}
|
||||||
|
|
||||||
type
|
type
|
||||||
TWinFileDialogFunc = function(OpenFile: LPOPENFILENAME): WINBOOL; stdcall;
|
TWinFileDialogFunc = function(OpenFile: LPOPENFILENAME): WINBOOL; stdcall;
|
||||||
|
|
||||||
|
function OpenFileDialogCallBack(hwnd : Handle; uMsg : UINT; wParam: WPARAM;
|
||||||
|
lParam: LPARAM) : UINT; stdcall;
|
||||||
|
var
|
||||||
|
OpenFileNotify: LPOFNOTIFY;
|
||||||
|
OpenFileName: POPENFILENAME;
|
||||||
|
NeededSize: DWORD;
|
||||||
|
FileNames: pstring;
|
||||||
|
begin
|
||||||
|
if uMsg = WM_NOTIFY then begin
|
||||||
|
OpenFileNotify := LPOFNOTIFY(lParam);
|
||||||
|
if OpenFileNotify^.hdr.code=CDN_SELCHANGE then begin
|
||||||
|
OpenFileName := OpenFileNotify^.lpOFN;
|
||||||
|
// NeededSize is the size that the lpStrFile buffer must have.
|
||||||
|
// the lpstrFile buffer contains the directory and a list of files
|
||||||
|
// for example 'c:\winnt'#0'file1.txt'#0'file2.txt'#0#0
|
||||||
|
// GetFolderPath is upper limit for the path, GetSpec for the files.
|
||||||
|
// This is not exact because the GetSpec returns the size for
|
||||||
|
// '"file1.txt" "file2.txt"'
|
||||||
|
NeededSize := CommDlg_OpenSave_GetFolderPath(GetParent(hwnd), nil, 0) +
|
||||||
|
CommDlg_OpenSave_GetSpec(GetParent(hwnd), nil, 0);
|
||||||
|
// test if we need to use our own storage
|
||||||
|
if OpenFileName^.nMaxFile<NeededSize then begin
|
||||||
|
if OpenFileName^.lCustData=0 then
|
||||||
|
OpenFileName^.lCustData := DWord(new(PString));
|
||||||
|
FileNames := PString(OpenFileName^.lCustData);
|
||||||
|
if length(FileNames^)<NeededSize then
|
||||||
|
SetLength(FileNames^, NeededSize*2);
|
||||||
|
CommDlg_OpenSave_GetSpec(GetParent(hwnd),
|
||||||
|
PChar(FileNames^), Length(FileNames^));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result:= 0;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure ShowFileDialog(AOpenDialog: TOpenDialog; AWinFunc: TWinFileDialogFunc);
|
procedure ShowFileDialog(AOpenDialog: TOpenDialog; AWinFunc: TWinFileDialogFunc);
|
||||||
var
|
var
|
||||||
OpenFile: OPENFILENAME;
|
OpenFile: OPENFILENAME;
|
||||||
@ -202,7 +265,8 @@ var
|
|||||||
function GetFlagsFromOptions(Options: TOpenOptions): DWord;
|
function GetFlagsFromOptions(Options: TOpenOptions): DWord;
|
||||||
begin
|
begin
|
||||||
Result := 0;
|
Result := 0;
|
||||||
if ofAllowMultiSelect in Options then Result := Result or OFN_ALLOWMULTISELECT;
|
if ofAllowMultiSelect in Options then
|
||||||
|
Result := Result or OFN_ALLOWMULTISELECT or OFN_ENABLEHOOK;
|
||||||
if ofCreatePrompt in Options then Result := Result or OFN_CREATEPROMPT;
|
if ofCreatePrompt in Options then Result := Result or OFN_CREATEPROMPT;
|
||||||
if not (ofOldStyleDialog in Options) then Result := Result or OFN_EXPLORER;
|
if not (ofOldStyleDialog in Options) then Result := Result or OFN_EXPLORER;
|
||||||
if ofExtensionDifferent in Options then Result := Result or OFN_EXTENSIONDIFFERENT;
|
if ofExtensionDifferent in Options then Result := Result or OFN_EXTENSIONDIFFERENT;
|
||||||
@ -225,8 +289,8 @@ var
|
|||||||
procedure ReplacePipe(var AFilter:string);
|
procedure ReplacePipe(var AFilter:string);
|
||||||
var i:integer;
|
var i:integer;
|
||||||
begin
|
begin
|
||||||
for i:=1 to length(AFilter) do
|
for i := 1 to length(AFilter) do
|
||||||
if AFilter[i]='|' then AFilter[i]:=#0;
|
if AFilter[i] = '|' then AFilter[i]:=#0;
|
||||||
AFilter:=AFilter + #0#0;
|
AFilter:=AFilter + #0#0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -251,6 +315,26 @@ var
|
|||||||
AFiles.Add(StrPas(pName));
|
AFiles.Add(StrPas(pName));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure SetFilesPropertyCustomFiles(AFiles:TStrings);
|
||||||
|
var
|
||||||
|
i, Start: integer;
|
||||||
|
FileNames: String;
|
||||||
|
begin
|
||||||
|
FileNames := PString(OpenFile.lCustData)^;
|
||||||
|
if (FileNames[1] = '"') then begin
|
||||||
|
Start := 1; // first quote is on pos 1
|
||||||
|
while FileNames[Start] <> #0 do begin
|
||||||
|
i := Start + 1;
|
||||||
|
while FileNames[i] <> '"' do
|
||||||
|
inc(i);
|
||||||
|
AFiles.Add(ExpandFileName(Copy(FileNames,Start+1,I - Start - 1)));
|
||||||
|
start := i+1;
|
||||||
|
while (FileNames[Start] <> #0) and (FileNames[start] <> '"') do
|
||||||
|
inc(Start);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure SetFilesPropertyForOldStyle(AFiles:TStrings);
|
procedure SetFilesPropertyForOldStyle(AFiles:TStrings);
|
||||||
var
|
var
|
||||||
SelectedStr: string;
|
SelectedStr: string;
|
||||||
@ -275,7 +359,8 @@ var
|
|||||||
|
|
||||||
var
|
var
|
||||||
FFilter: string;
|
FFilter: string;
|
||||||
FileNameBuffer: array[0..32000] of char;
|
FileNameBuffer: array[0..1000] of char;
|
||||||
|
BufferTooSmall: boolean;
|
||||||
begin
|
begin
|
||||||
FillChar(FileNameBuffer[0], sizeof(FileNameBuffer), 0);
|
FillChar(FileNameBuffer[0], sizeof(FileNameBuffer), 0);
|
||||||
StrLCopy(@FileNameBuffer[0],PChar(AOpenDialog.Filename),sizeof(FileNameBuffer)-1);
|
StrLCopy(@FileNameBuffer[0],PChar(AOpenDialog.Filename),sizeof(FileNameBuffer)-1);
|
||||||
@ -293,35 +378,44 @@ begin
|
|||||||
hWndOwner := GetOwnerHandle(AOpenDialog);
|
hWndOwner := GetOwnerHandle(AOpenDialog);
|
||||||
hInstance := System.hInstance;
|
hInstance := System.hInstance;
|
||||||
lpStrFilter := StrAlloc(Length(FFilter)+1);
|
lpStrFilter := StrAlloc(Length(FFilter)+1);
|
||||||
|
StrPCopy(lpStrFilter, FFilter);
|
||||||
nFilterIndex := AOpenDialog.FilterIndex;
|
nFilterIndex := AOpenDialog.FilterIndex;
|
||||||
Move(PChar(FFilter)^, lpStrFilter^, Length(FFilter)+1);
|
|
||||||
lpStrFile := FileNameBuffer;
|
lpStrFile := FileNameBuffer;
|
||||||
lpStrTitle := PChar(AOpenDialog.Title);
|
lpStrTitle := PChar(AOpenDialog.Title);
|
||||||
lpStrInitialDir := PChar(AOpenDialog.InitialDir);
|
lpStrInitialDir := PChar(AOpenDialog.InitialDir);
|
||||||
nMaxFile := sizeof(FileNameBuffer);
|
nMaxFile := sizeof(FileNameBuffer);
|
||||||
|
lpfnHook := @OpenFileDialogCallBack;
|
||||||
Flags := GetFlagsFromOptions(AOpenDialog.Options);
|
Flags := GetFlagsFromOptions(AOpenDialog.Options);
|
||||||
end;
|
end;
|
||||||
UserResult := AWinFunc(@OpenFile);
|
UserResult := AWinFunc(@OpenFile);
|
||||||
|
BufferTooSmall := not UserResult and (CommDlgExtendedError=FNERR_BUFFERTOOSMALL);
|
||||||
|
if BufferTooSmall then
|
||||||
|
UserResult := true;
|
||||||
SetDialogResult(AOpenDialog, UserResult);
|
SetDialogResult(AOpenDialog, UserResult);
|
||||||
with AOpenDialog do
|
with AOpenDialog do
|
||||||
begin
|
begin
|
||||||
Files.Clear;
|
Files.Clear;
|
||||||
if UserResult then
|
if UserResult then
|
||||||
begin
|
begin
|
||||||
AOpenDialog.FilterIndex := OpenFile.nFilterIndex;
|
AOpenDialog.FilterIndex := OpenFile.nFilterIndex;
|
||||||
if not (ofOldStyleDialog in Options) then // Win32 returns diferent types of strings
|
if (ofOldStyleDialog in Options) then
|
||||||
SetFilesProperty(Files)
|
SetFilesPropertyForOldStyle(Files)
|
||||||
|
else if BufferTooSmall then
|
||||||
|
SetFilesPropertyCustomFiles(Files)
|
||||||
else
|
else
|
||||||
SetFilesPropertyForOldStyle(Files);
|
SetFilesProperty(Files);
|
||||||
FileName := Files[0];
|
FileName := Files[0];
|
||||||
end else
|
end else
|
||||||
FileName := '';
|
FileName := '';
|
||||||
|
|
||||||
FreeMem(OpenFile.lpStrFile);
|
if OpenFile.lCustData<>0 then
|
||||||
|
Dispose(PString(OpenFile.lCustData));
|
||||||
StrDispose(OpenFile.lpStrFilter);
|
StrDispose(OpenFile.lpStrFilter);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TWin32WSSaveDialog }
|
||||||
|
|
||||||
class function TWin32WSSaveDialog.CreateHandle(const ACommonDialog: TCommonDialog): integer;
|
class function TWin32WSSaveDialog.CreateHandle(const ACommonDialog: TCommonDialog): integer;
|
||||||
begin
|
begin
|
||||||
ShowFileDialog(TOpenDialog(ACommonDialog), @GetSaveFileName);
|
ShowFileDialog(TOpenDialog(ACommonDialog), @GetSaveFileName);
|
||||||
|
Loading…
Reference in New Issue
Block a user