From 4a8c53c4e034e38866fa731ab3225a5d83448f04 Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Fri, 28 Nov 2025 10:17:23 +0100 Subject: [PATCH] feat: convert all save/open dialogs to the new custom one * TOpenDialog => TExtFileOpenDialog * TSaveDialog => TExtFileSaveDialog * show encodings and linebreaks only when the caller sets data for these * show selected path from tree in label at top * support DefaultExt string * support OnTypeChange event * disallow changing tree folder for ofNoChangeDir * catch EInvalidPath in SetInitialDir * show hidden folders in tree * enable sorting in file listing Refs #2268 --- source/bineditor.pas | 441 +++++++++++++++++++-------------------- source/connections.pas | 23 +- source/exportgrid.pas | 20 +- source/extfiledialog.lfm | 31 ++- source/extfiledialog.pas | 107 +++++++--- source/insertfiles.lfm | 11 +- source/insertfiles.pas | 14 +- source/main.pas | 27 +-- source/tabletools.pas | 21 +- 9 files changed, 379 insertions(+), 316 deletions(-) diff --git a/source/bineditor.pas b/source/bineditor.pas index c7b0349d..6393f0ea 100644 --- a/source/bineditor.pas +++ b/source/bineditor.pas @@ -1,221 +1,220 @@ -unit bineditor; - -{$mode delphi}{$H+} - -interface - -uses - Classes, Graphics, Forms, Controls, StdCtrls, laz.VirtualTrees, - ComCtrls, Dialogs, SysUtils, extra_controls, LCLType; - -{$I const.inc} - -type - TfrmBinEditor = class(TExtForm) - memoText: TMemo; - tlbStandard: TToolBar; - btnWrap: TToolButton; - btnLoadBinary: TToolButton; - btnApply: TToolButton; - btnCancel: TToolButton; - lblTextLength: TLabel; - procedure btnApplyClick(Sender: TObject); - procedure btnCancelClick(Sender: TObject); - procedure btnLoadBinaryClick(Sender: TObject); - procedure btnWrapClick(Sender: TObject); - procedure FormDestroy(Sender: TObject); - procedure FormShow(Sender: TObject); - procedure memoTextChange(Sender: TObject); - procedure memoTextKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); - procedure FormCreate(Sender: TObject); - procedure FormClose(Sender: TObject; var Action: TCloseAction); - private - { Private declarations } - FModified: Boolean; - FStopping: Boolean; - procedure SetModified(NewVal: Boolean); - public - function GetText: String; - procedure SetText(text: String); - procedure SetTitleText(Title: String); - procedure SetMaxLength(len: integer); - procedure SetFont(font: TFont); - property Modified: Boolean read FModified write SetModified; - end; - - -implementation - -uses apphelpers, main; - - -{$R *.lfm} - - -function TfrmBinEditor.GetText: String; -var - Ansi: AnsiString; -begin - // Convert hex to binary string before returning - Ansi := ''; - SetLength(Ansi, memoText.GetTextLen div 2); - HexToBin(PChar(memoText.Text), PAnsiChar(Ansi), Length(Ansi)); - Result := String(Ansi); -end; - -procedure TfrmBinEditor.SetText(text: String); -begin - // Skip '0x'. - memoText.Text := Copy(text, 3); -end; - - -procedure TfrmBinEditor.SetTitleText(Title: String); -begin - // Add column name to window title bar - if Title <> '' then - Caption := Title + ' - ' + Caption; -end; - - -procedure TfrmBinEditor.SetMaxLength(len: integer); -begin - // Input: Length in bytes. - memoText.MaxLength := len * 2; -end; - -procedure TfrmBinEditor.SetFont(font: TFont); -begin - memoText.Font.Name := font.Name; - memoText.Font.Size := font.Size; -end; - -procedure TfrmBinEditor.FormCreate(Sender: TObject); -begin - HasSizeGrip := True; - Width := AppSettings.ReadInt(asMemoEditorWidth); - Height := AppSettings.ReadInt(asMemoEditorHeight); -end; - - -procedure TfrmBinEditor.FormDestroy(Sender: TObject); -begin - AppSettings.WriteInt(asMemoEditorWidth, Width); - AppSettings.WriteInt(asMemoEditorHeight, Height); - AppSettings.WriteBool(asMemoEditorWrap, btnWrap.Down); -end; - - -procedure TfrmBinEditor.FormShow(Sender: TObject); -begin - // Restore form dimensions - if AppSettings.ReadBool(asMemoEditorWrap) then - btnWrap.Click; - // Fix label position: - lblTextLength.Top := tlbStandard.Top + (tlbStandard.Height-lblTextLength.Height) div 2; - memoText.SelectAll; - memoText.SetFocus; - memoTextChange(Sender); - Modified := False; -end; - - -procedure TfrmBinEditor.memoTextKeyDown(Sender: TObject; var Key: Word; Shift: - TShiftState); -begin - case Key of - // Cancel by Escape - VK_ESCAPE: btnCancelClick(Sender); - // Apply changes and end editing by Ctrl + Enter - VK_RETURN: if ssCtrl in Shift then btnApplyClick(Sender); - Ord('a'), Ord('A'): if (ssCtrl in Shift) and (not (ssAlt in Shift)) then Mainform.actSelectAllExecute(Sender); - end; -end; - -procedure TfrmBinEditor.btnWrapClick(Sender: TObject); -var - WasModified: Boolean; -begin - Screen.Cursor := crHourglass; - // Changing the scrollbars invoke the OnChange event. We avoid thinking the text was really modified. - WasModified := Modified; - if memoText.ScrollBars = ssBoth then - memoText.ScrollBars := ssVertical - else - memoText.ScrollBars := ssBoth; - TToolbutton(Sender).Down := memoText.ScrollBars = ssVertical; - Modified := WasModified; - Screen.Cursor := crDefault; -end; - - -procedure TfrmBinEditor.btnLoadBinaryClick(Sender: TObject); -var - d: TOpenDialog; -begin - d := TOpenDialog.Create(Self); - d.Filter := _('All files')+' (*.*)|*.*'; - d.FilterIndex := 0; - if d.Execute then try - Screen.Cursor := crHourglass; - memoText.Text := BinToWideHex(ReadBinaryFile(d.FileName, memoText.MaxLength)); - finally - Screen.Cursor := crDefault; - end; - d.Free; -end; - - -procedure TfrmBinEditor.btnCancelClick(Sender: TObject); -begin - if FStopping then - Exit; - FStopping := True; - TCustomVirtualStringTree(Owner).CancelEditNode; -end; - - -procedure TfrmBinEditor.FormClose(Sender: TObject; var Action: TCloseAction); -var - DoPost: Boolean; -begin - if FStopping then - Exit; - FStopping := True; - if Modified then - DoPost := MessageDialog(_('Apply modifications?'), mtConfirmation, [mbYes, mbNo]) = mrYes - else - DoPost := False; - if DoPost then - TCustomVirtualStringTree(Owner).EndEditNode - else - TCustomVirtualStringTree(Owner).CancelEditNode; -end; - - -procedure TfrmBinEditor.btnApplyClick(Sender: TObject); -begin - FStopping := True; - TCustomVirtualStringTree(Owner).EndEditNode; -end; - - -procedure TfrmBinEditor.memoTextChange(Sender: TObject); -begin - lblTextLength.Caption := FormatNumber(Length(memoText.Text) / 2) + ' bytes.'; - if memoText.MaxLength > 0 then - lblTextLength.Caption := lblTextLength.Caption + ' (Max: '+FormatNumber(memoText.MaxLength / 2)+')'; - Modified := True; -end; - - -procedure TfrmBinEditor.SetModified(NewVal: Boolean); -begin - if FModified <> NewVal then begin - FModified := NewVal; - btnApply.Enabled := FModified; - end; -end; - - -end. +unit bineditor; + +{$mode delphi}{$H+} + +interface + +uses + Classes, Graphics, Forms, Controls, StdCtrls, laz.VirtualTrees, + ComCtrls, Dialogs, SysUtils, extra_controls, LCLType, extfiledialog; + +{$I const.inc} + +type + TfrmBinEditor = class(TExtForm) + memoText: TMemo; + tlbStandard: TToolBar; + btnWrap: TToolButton; + btnLoadBinary: TToolButton; + btnApply: TToolButton; + btnCancel: TToolButton; + lblTextLength: TLabel; + procedure btnApplyClick(Sender: TObject); + procedure btnCancelClick(Sender: TObject); + procedure btnLoadBinaryClick(Sender: TObject); + procedure btnWrapClick(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure FormShow(Sender: TObject); + procedure memoTextChange(Sender: TObject); + procedure memoTextKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure FormCreate(Sender: TObject); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + private + { Private declarations } + FModified: Boolean; + FStopping: Boolean; + procedure SetModified(NewVal: Boolean); + public + function GetText: String; + procedure SetText(text: String); + procedure SetTitleText(Title: String); + procedure SetMaxLength(len: integer); + procedure SetFont(font: TFont); + property Modified: Boolean read FModified write SetModified; + end; + + +implementation + +uses apphelpers, main; + + +{$R *.lfm} + + +function TfrmBinEditor.GetText: String; +var + Ansi: AnsiString; +begin + // Convert hex to binary string before returning + Ansi := ''; + SetLength(Ansi, memoText.GetTextLen div 2); + HexToBin(PChar(memoText.Text), PAnsiChar(Ansi), Length(Ansi)); + Result := String(Ansi); +end; + +procedure TfrmBinEditor.SetText(text: String); +begin + // Skip '0x'. + memoText.Text := Copy(text, 3); +end; + + +procedure TfrmBinEditor.SetTitleText(Title: String); +begin + // Add column name to window title bar + if Title <> '' then + Caption := Title + ' - ' + Caption; +end; + + +procedure TfrmBinEditor.SetMaxLength(len: integer); +begin + // Input: Length in bytes. + memoText.MaxLength := len * 2; +end; + +procedure TfrmBinEditor.SetFont(font: TFont); +begin + memoText.Font.Name := font.Name; + memoText.Font.Size := font.Size; +end; + +procedure TfrmBinEditor.FormCreate(Sender: TObject); +begin + HasSizeGrip := True; + Width := AppSettings.ReadInt(asMemoEditorWidth); + Height := AppSettings.ReadInt(asMemoEditorHeight); +end; + + +procedure TfrmBinEditor.FormDestroy(Sender: TObject); +begin + AppSettings.WriteInt(asMemoEditorWidth, Width); + AppSettings.WriteInt(asMemoEditorHeight, Height); + AppSettings.WriteBool(asMemoEditorWrap, btnWrap.Down); +end; + + +procedure TfrmBinEditor.FormShow(Sender: TObject); +begin + // Restore form dimensions + if AppSettings.ReadBool(asMemoEditorWrap) then + btnWrap.Click; + // Fix label position: + lblTextLength.Top := tlbStandard.Top + (tlbStandard.Height-lblTextLength.Height) div 2; + memoText.SelectAll; + memoText.SetFocus; + memoTextChange(Sender); + Modified := False; +end; + + +procedure TfrmBinEditor.memoTextKeyDown(Sender: TObject; var Key: Word; Shift: + TShiftState); +begin + case Key of + // Cancel by Escape + VK_ESCAPE: btnCancelClick(Sender); + // Apply changes and end editing by Ctrl + Enter + VK_RETURN: if ssCtrl in Shift then btnApplyClick(Sender); + Ord('a'), Ord('A'): if (ssCtrl in Shift) and (not (ssAlt in Shift)) then Mainform.actSelectAllExecute(Sender); + end; +end; + +procedure TfrmBinEditor.btnWrapClick(Sender: TObject); +var + WasModified: Boolean; +begin + Screen.Cursor := crHourglass; + // Changing the scrollbars invoke the OnChange event. We avoid thinking the text was really modified. + WasModified := Modified; + if memoText.ScrollBars = ssBoth then + memoText.ScrollBars := ssVertical + else + memoText.ScrollBars := ssBoth; + TToolbutton(Sender).Down := memoText.ScrollBars = ssVertical; + Modified := WasModified; + Screen.Cursor := crDefault; +end; + + +procedure TfrmBinEditor.btnLoadBinaryClick(Sender: TObject); +var + d: TExtFileOpenDialog; +begin + d := TExtFileOpenDialog.Create(Self); + d.AddFileType('*.*', _('All files')); + if d.Execute then try + Screen.Cursor := crHourglass; + memoText.Text := BinToWideHex(ReadBinaryFile(d.FileName, memoText.MaxLength)); + finally + Screen.Cursor := crDefault; + end; + d.Free; +end; + + +procedure TfrmBinEditor.btnCancelClick(Sender: TObject); +begin + if FStopping then + Exit; + FStopping := True; + TCustomVirtualStringTree(Owner).CancelEditNode; +end; + + +procedure TfrmBinEditor.FormClose(Sender: TObject; var Action: TCloseAction); +var + DoPost: Boolean; +begin + if FStopping then + Exit; + FStopping := True; + if Modified then + DoPost := MessageDialog(_('Apply modifications?'), mtConfirmation, [mbYes, mbNo]) = mrYes + else + DoPost := False; + if DoPost then + TCustomVirtualStringTree(Owner).EndEditNode + else + TCustomVirtualStringTree(Owner).CancelEditNode; +end; + + +procedure TfrmBinEditor.btnApplyClick(Sender: TObject); +begin + FStopping := True; + TCustomVirtualStringTree(Owner).EndEditNode; +end; + + +procedure TfrmBinEditor.memoTextChange(Sender: TObject); +begin + lblTextLength.Caption := FormatNumber(Length(memoText.Text) / 2) + ' bytes.'; + if memoText.MaxLength > 0 then + lblTextLength.Caption := lblTextLength.Caption + ' (Max: '+FormatNumber(memoText.MaxLength / 2)+')'; + Modified := True; +end; + + +procedure TfrmBinEditor.SetModified(NewVal: Boolean); +begin + if FModified <> NewVal then begin + FModified := NewVal; + btnApply.Enabled := FModified; + end; +end; + + +end. diff --git a/source/connections.pas b/source/connections.pas index 1a131dc4..2eeee7c2 100644 --- a/source/connections.pas +++ b/source/connections.pas @@ -13,7 +13,7 @@ uses SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls, laz.VirtualTrees, Menus, Graphics, extra_controls, lazaruscompat, dbconnection, RegExpr, Types, FileUtil, - Math, ActnList, ComboEx, EditBtn, Buttons, ColorBox; + Math, ActnList, ComboEx, EditBtn, Buttons, ColorBox, extfiledialog; type @@ -1720,7 +1720,7 @@ end; procedure Tconnform.PickFile(Sender: TObject); var - Selector: TOpenDialog; + Selector: TExtFileOpenDialog; Edit: TEditButton; i: Integer; Control: TControl; @@ -1728,18 +1728,21 @@ var begin // Select startup SQL file, SSL file or whatever button clicked Edit := Sender as TEditButton; - Selector := TOpenDialog.Create(Self); + Selector := TExtFileOpenDialog.Create(Self); if Edit = editHost then begin - Selector.Filter := 'SQLite databases ('+FILEFILTER_SQLITEDB+')|'+FILEFILTER_SQLITEDB+'|'+_('All files')+' (*.*)|*.*'; + Selector.AddFileType(FILEFILTER_SQLITEDB, 'SQLite databases'); Selector.Options := Selector.Options - [ofFileMustExist]; Selector.Options := Selector.Options + [ofAllowMultiSelect]; Selector.DefaultExt := FILEEXT_SQLITEDB; end else if (Edit = editStartupScript) or (Edit = editLogFilePath) then - Selector.Filter := _('SQL files')+' (*.sql)|*.sql|'+_('All files')+' (*.*)|*.*' - else if Edit = editSSHPrivateKey then - Selector.Filter := _('All files')+' (*.*)|*.*' - else - Selector.Filter := _('Privacy Enhanced Mail certificates')+' (*.pem)|*.pem|'+_('Certificates')+' (*.crt)|*.crt|'+_('All files')+' (*.*)|*.*'; + Selector.AddFileType('*.sql', _('SQL files')) + else if Edit = editSSHPrivateKey then begin + end + else begin + Selector.AddFileType('*.pem', _('Privacy Enhanced Mail certificates')); + Selector.AddFileType('*.crt', _('Certificates')); + end; + Selector.AddFileType('*.*', _('All files')); // Find relevant label and set open dialog's title for i:=0 to Edit.Parent.ControlCount - 1 do begin Control := Edit.Parent.Controls[i]; @@ -1750,8 +1753,6 @@ begin end; // Set initial directory to the one from the edit's file Selector.InitialDir := ExtractFilePath(Edit.Text); - //if Selector.InitialDir.IsEmpty then - // Selector.InitialDir := TPath.GetPathRoot(Application.ExeName); if Selector.Execute then begin FileNames := TStringList.Create; FileNames.Assign(Selector.Files); diff --git a/source/exportgrid.pas b/source/exportgrid.pas index 3eb655a2..dbd155c7 100644 --- a/source/exportgrid.pas +++ b/source/exportgrid.pas @@ -7,7 +7,8 @@ interface uses SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Menus, ComCtrls, laz.VirtualTrees, SynExportHTML, - extra_controls, dbstructures, lazaruscompat, RegExpr, StrUtils, comboex, EditBtn, Buttons, LCLIntf; + extra_controls, dbstructures, lazaruscompat, RegExpr, StrUtils, comboex, EditBtn, Buttons, LCLIntf, + extfiledialog; type TGridExportFormat = ( @@ -390,21 +391,20 @@ end; procedure TfrmExportGrid.editFilenameRightButtonClick(Sender: TObject); var - Dialog: TSaveDialog; + Dialog: TExtFileSaveDialog; ef: TGridExportFormat; Filename: String; begin // Select file target - Dialog := TSaveDialog.Create(Self); + Dialog := TExtFileSaveDialog.Create(Self); Filename := GetOutputFilename(editFilename.Text, MainForm.ActiveDbObj); Dialog.InitialDir := ExtractFilePath(Filename); Dialog.FileName := GetFileNameWithoutExtension(Filename); - Dialog.Filter := ''; for ef:=Low(TGridExportFormat) to High(TGridExportFormat) do - Dialog.Filter := Dialog.Filter + FormatToDescription[ef] + ' (*.'+FormatToFileExtension[ef]+')|*.'+FormatToFileExtension[ef]+'|'; - Dialog.Filter := Dialog.Filter + _('All files')+' (*.*)|*.*'; + Dialog.AddFileType('*.'+FormatToFileExtension[ef], FormatToDescription[ef]); + Dialog.AddFileType('*.*', _('All files')); Dialog.OnTypeChange := SaveDialogTypeChange; - Dialog.FilterIndex := comboFormat.ItemIndex+1; + Dialog.FilterIndex := comboFormat.ItemIndex; Dialog.OnTypeChange(Dialog); if Dialog.Execute then begin editFilename.Text := Dialog.FileName; @@ -558,13 +558,13 @@ end; procedure TfrmExportGrid.SaveDialogTypeChange(Sender: TObject); var - Dialog: TSaveDialog; + Dialog: TExtFileSaveDialog; ef: TGridExportFormat; begin // Set default file-extension of saved file and options on the dialog to show - Dialog := Sender as TSaveDialog; + Dialog := Sender as TExtFileSaveDialog; for ef:=Low(TGridExportFormat) to High(TGridExportFormat) do begin - if Dialog.FilterIndex = Integer(ef)+1 then + if Dialog.FilterIndex = Integer(ef) then Dialog.DefaultExt := FormatToFileExtension[ef]; end; end; diff --git a/source/extfiledialog.lfm b/source/extfiledialog.lfm index 6a159443..4edd6699 100644 --- a/source/extfiledialog.lfm +++ b/source/extfiledialog.lfm @@ -165,38 +165,53 @@ object frmExtFileDialog: TfrmExtFileDialog end end object ShellTreeView: TShellTreeView + AnchorSideTop.Control = lblPath + AnchorSideTop.Side = asrBottom Left = 10 - Height = 446 - Top = 10 + Height = 416 + Top = 40 Width = 261 Align = alLeft BorderSpacing.Left = 10 - BorderSpacing.Top = 10 HideSelection = False ReadOnly = False TabOrder = 0 Options = [tvoAutoItemHeight, tvoKeepCollapsedNodes, tvoShowButtons, tvoShowLines, tvoShowRoot, tvoToolTips, tvoThemedDraw] + ObjectTypes = [otFolders, otHidden] ShellListView = ShellListView + OnChange = ShellTreeViewChange + OnChanging = ShellTreeViewChanging end object splitterMain: TSplitter Left = 271 - Height = 456 - Top = 0 + Height = 416 + Top = 40 Width = 6 end object ShellListView: TShellListView + AnchorSideTop.Control = lblPath + AnchorSideTop.Side = asrBottom Left = 277 - Height = 446 - Top = 10 + Height = 416 + Top = 40 Width = 585 Align = alClient - BorderSpacing.Top = 10 BorderSpacing.Right = 10 Color = clDefault + SortType = stText TabOrder = 1 ShellTreeView = ShellTreeView OnClick = ShellListViewClick OnDblClick = ShellListViewDblClick OnSelectItem = ShellListViewSelectItem end + object lblPath: TLabel + Left = 10 + Height = 20 + Top = 10 + Width = 852 + Align = alTop + BorderSpacing.Around = 10 + Caption = 'lblPath' + end end diff --git a/source/extfiledialog.pas b/source/extfiledialog.pas index b066c4da..ada153c4 100644 --- a/source/extfiledialog.pas +++ b/source/extfiledialog.pas @@ -19,6 +19,7 @@ type comboEncoding: TComboBox; comboFileType: TComboBox; editFilename: TEdit; + lblPath: TLabel; lblLinebreaks: TLabel; lblEncoding: TLabel; lblFilename: TLabel; @@ -37,21 +38,28 @@ type procedure ShellListViewDblClick(Sender: TObject); procedure ShellListViewSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean); + procedure ShellTreeViewChange(Sender: TObject; Node: TTreeNode); + procedure ShellTreeViewChanging(Sender: TObject; Node: TTreeNode; + var AllowChange: Boolean); private FInitialDir: String; FFilterNames: TStringList; FFilterMasks: TStringList; + FFilterIndex: Integer; FDefaultExt: String; FEncodings: TStringList; FEncodingIndex: Integer; FLineBreakIndex: TLineBreaks; FOptions: TOpenOptions; FFiles: TStringList; + FOnTypeChange: TNotifyEvent; procedure SetTitle(AValue: String); function GetFileName: String; procedure SetFileName(const AValue: String); procedure SetInitialDir(const AValue: String); + procedure SetFilterIndex(AValue: Integer); public + property OnTypeChange: TNotifyEvent read FOnTypeChange write FOnTypeChange; property Title: String write SetTitle; function Execute: Boolean; procedure AddFileType(FileMask, DisplayName: String); @@ -61,18 +69,19 @@ type property DefaultExt: String read FDefaultExt write FDefaultExt; property Options: TOpenOptions read FOptions write FOptions; property Files: TStringList read FFiles; + property FilterIndex: Integer read FFilterIndex write SetFilterIndex; end; // File-open-dialog with encoding selector TExtFileOpenDialog = class(TfrmExtFileDialog) - procedure FormCreate(Sender: TObject); overload; + procedure FormShow(Sender: TObject); overload; public property Encodings: TStringList read FEncodings write FEncodings; property EncodingIndex: Integer read FEncodingIndex write FEncodingIndex; end; TExtFileSaveDialog = class(TfrmExtFileDialog) - procedure FormCreate(Sender: TObject); overload; + procedure FormShow(Sender: TObject); overload; public property LineBreakIndex: TLineBreaks read FLineBreakIndex write FLineBreakIndex; end; @@ -100,20 +109,15 @@ begin raise Exception.CreateFmt('Constructor of base class %s called. Use one of its descendants instead.', [ClassName]); FFilterNames := TStringList.Create; FFilterMasks := TStringList.Create; + FFilterIndex := 0; + FDefaultExt := ''; FEncodings := TStringList.Create; - {$IFDEF LINUX} - FLineBreakIndex := lbsUnix; - {$ENDIF} - {$IFDEF WINDOWS} - FLineBreakIndex := lbsWindows; - {$ENDIF} - {$IFDEF DARWIN} - FLineBreakIndex := lbsMac; - {$ENDIF} + FLineBreakIndex := lbsNone; FFiles := TStringList.Create; comboFileType.Items.Clear; editFilename.Text := ''; comboLineBreaks.Items.Clear; + FOnTypeChange := nil; end; procedure TfrmExtFileDialog.FormDestroy(Sender: TObject); @@ -130,17 +134,15 @@ var LineBreakIndexInt: Integer; begin ShellListView.MultiSelect := ofAllowMultiSelect in FOptions; - ShellTreeView.Enabled := not (ofNoChangeDir in FOptions); - // Todo: support ofFileMustExist and convert usages of TOpenDialog and TSaveDialog - if FInitialDir.IsEmpty then begin - if not PreviousDir.IsEmpty then - SetInitialDir(PreviousDir) - else - SetInitialDir(GetUserDir); - end; + // Todo: support ofFileMustExist + if not FInitialDir.IsEmpty then + SetInitialDir(FInitialDir) + else if not PreviousDir.IsEmpty then + SetInitialDir(PreviousDir) + else + SetInitialDir(GetUserDir); - comboFileType.ItemIndex := 0; - comboFileType.OnChange(Sender); + SetFilterIndex(FFilterIndex); comboEncoding.Items.AddStrings(FEncodings, True); if (FEncodingIndex >=0) and (FEncodingIndex < comboEncoding.Items.Count) then @@ -158,11 +160,14 @@ procedure TfrmExtFileDialog.comboFileTypeChange(Sender: TObject); var FileMask: String; begin + FFilterIndex := comboFileType.ItemIndex; if (comboFileType.ItemIndex >= 0) and (FFilterMasks.Count > comboFileType.ItemIndex) then FileMask := FFilterMasks[comboFileType.ItemIndex] else FileMask := '*.*'; ShellListView.Mask := FileMask; + if Assigned(FOnTypeChange) then + FOnTypeChange(Self); end; procedure TfrmExtFileDialog.comboLineBreaksChange(Sender: TObject); @@ -226,6 +231,18 @@ begin end; end; +procedure TfrmExtFileDialog.ShellTreeViewChange(Sender: TObject; Node: TTreeNode + ); +begin + lblPath.Caption := ShellTreeView.Path; +end; + +procedure TfrmExtFileDialog.ShellTreeViewChanging(Sender: TObject; + Node: TTreeNode; var AllowChange: Boolean); +begin + AllowChange := not (ofNoChangeDir in FOptions); +end; + procedure TfrmExtFileDialog.SetTitle(AValue: String); begin Caption := AValue; @@ -233,10 +250,13 @@ end; function TfrmExtFileDialog.GetFileName: String; begin - if ShellListView.Selected <> nil then + if (editFilename.Text <> '') and (ShellTreeView.Selected <> nil) then begin + Result := ShellTreeView.Path + editFilename.Text; + if IsEmpty(ExtractFileExt(Result)) and (not FDefaultExt.IsEmpty) then + Result := Result + '.' + FDefaultExt; + end + else if ShellListView.Selected <> nil then Result := ShellListView.GetPathFromItem(ShellListView.Selected) - else if (editFilename.Text <> '') and (ShellTreeView.Selected <> nil) then - Result := ShellTreeView.Path + editFilename.Text else Result := ''; end; @@ -246,35 +266,58 @@ var fn: String; begin fn := ExpandFileName(AValue); - ShellTreeView.Path := ExtractFilePath(fn); + SetInitialDir(ExtractFilePath(fn)); editFilename.Text := ExtractFileName(fn); ShellListView.Selected := ShellListView.FindCaption(0, fn, false, true, true); end; procedure TfrmExtFileDialog.SetInitialDir(const AValue: String); begin - ShellTreeView.Path := AValue; + FInitialDir := AValue; + try + ShellTreeView.Path := FInitialDir; + except + on E:EInvalidPath do begin + ErrorDialog(E.Message); + // In case, re-enable changing directory in tree + Exclude(FOptions, ofNoChangeDir); + end; + end; end; +procedure TfrmExtFileDialog.SetFilterIndex(AValue: Integer); +begin + if (AValue >= 0) and (AValue < comboFileType.Items.Count) then begin + comboFileType.ItemIndex := AValue; + comboFileTypeChange(Self); + end; +end; { TExtFileOpenDialog } -procedure TExtFileOpenDialog.FormCreate(Sender: TObject); +procedure TExtFileOpenDialog.FormShow(Sender: TObject); +var + EncodingVisible: Boolean; begin inherited; - lblEncoding.Visible := True; - comboEncoding.Visible := True; + EncodingVisible := comboEncoding.Items.Count > 0; + lblEncoding.Visible := EncodingVisible; + comboEncoding.Visible := EncodingVisible; end; + { TExtFileSaveDialog } -procedure TExtFileSaveDialog.FormCreate(Sender: TObject); +procedure TExtFileSaveDialog.FormShow(Sender: TObject); +var + LinebreaksVisible: Boolean; begin inherited; - lblLinebreaks.Visible := True; - comboLineBreaks.Visible := True; + LinebreaksVisible := FLineBreakIndex in [lbsWindows, lbsUnix, lbsMac]; + lblLinebreaks.Visible := LinebreaksVisible; + comboLineBreaks.Visible := LinebreaksVisible; end; end. diff --git a/source/insertfiles.lfm b/source/insertfiles.lfm index 5a43e8e6..4735f802 100644 --- a/source/insertfiles.lfm +++ b/source/insertfiles.lfm @@ -12,7 +12,6 @@ object frmInsertFiles: TfrmInsertFiles Constraints.MinWidth = 250 DesignTimePPI = 120 Position = poOwnerFormCenter - LCLVersion = '4.2.0.0' OnCreate = FormCreate OnDestroy = FormDestroy OnShow = FormShow @@ -153,7 +152,7 @@ object frmInsertFiles: TfrmInsertFiles Options = [coDraggable, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible, coAllowFocus] Position = 2 Text = 'Value' - Width = 386 + Width = 382 end> Header.Height = 32 Header.Images = MainForm.ImageListMain @@ -238,7 +237,7 @@ object frmInsertFiles: TfrmInsertFiles item Position = 0 Text = 'Filename' - Width = 423 + Width = 419 end item CheckBox = True @@ -318,10 +317,4 @@ object frmInsertFiles: TfrmInsertFiles end end end - object OpenDialog: TOpenDialog - Filter = 'All files (*.*)|*.*|Common images (*.jpg, *.gif, *.bmp, *.png)|*.jpg;*.gif;*.bmp;*.png' - Options = [ofHideReadOnly, ofAllowMultiSelect, ofFileMustExist, ofEnableSizing] - Left = 10 - Top = 570 - end end diff --git a/source/insertfiles.pas b/source/insertfiles.pas index 9fd3d388..bec930f9 100644 --- a/source/insertfiles.pas +++ b/source/insertfiles.pas @@ -7,7 +7,7 @@ interface uses SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, Math, Graphics, ComCtrls, LCLType, extra_controls, lazaruscompat, - dbconnection, dbstructures, laz.VirtualTrees, RegExpr, apphelpers; + dbconnection, dbstructures, laz.VirtualTrees, RegExpr, apphelpers, extfiledialog; type TColInfo = class @@ -30,7 +30,6 @@ type TfrmInsertFiles = class(TExtForm) btnInsert: TButton; btnCancel: TButton; - OpenDialog: TOpenDialog; grpSelectObject: TGroupBox; lblTable: TLabel; comboDBs: TComboBox; @@ -93,6 +92,7 @@ type private FConnection: TDBConnection; FMaxFileSize: Int64; + FOpenDialog: TExtFileOpenDialog; public { Public declarations } //procedure AcceptFiles( var msg : TMessage ); message WM_DROPFILES; @@ -124,6 +124,10 @@ begin FixVT(ListColumns); Width := AppSettings.ReadInt(asFileImportWindowWidth); Height := AppSettings.ReadInt(asFileImportWindowHeight); + FOpenDialog := TExtFileOpenDialog.Create(Self); + FOpenDialog.Options := FOpenDialog.Options + [ofAllowMultiSelect]; + FOpenDialog.AddFileType('*.*', _('All files')); + FOpenDialog.AddFileType('*.jpg;*.gif;*.bmp;*.png', _('Common images')); end; @@ -397,12 +401,12 @@ var i: Integer; begin // Add file(s) to list - if OpenDialog.Execute then begin + if FOpenDialog.Execute then begin Screen.Cursor := crHourglass; ListFiles.BeginUpdate; try - for i:=0 to OpenDialog.Files.Count-1 do - AddFile(OpenDialog.Files[i]); + for i:=0 to FOpenDialog.Files.Count-1 do + AddFile(FOpenDialog.Files[i]); finally ListFiles.EndUpdate; end; diff --git a/source/main.pas b/source/main.pas index 9dac70a8..2ff9120c 100644 --- a/source/main.pas +++ b/source/main.pas @@ -3130,13 +3130,14 @@ end; procedure TMainForm.actExportSettingsExecute(Sender: TObject); var - Dialog: TSaveDialog; + Dialog: TExtFileSaveDialog; begin // Export settings to .txt file - Dialog := TSaveDialog.Create(Self); + Dialog := TExtFileSaveDialog.Create(Self); Dialog.Title := f_('Export %s settings to file ...', [APPNAME]); Dialog.DefaultExt := 'txt'; - Dialog.Filter := _('Text files')+' (*.txt)|*.txt|'+_('All files')+' (*.*)|*.*'; + Dialog.AddFileType('*.txt', _('Text files')); + Dialog.AddFileType('*.*', _('All files')); Dialog.Options := Dialog.Options + [ofOverwritePrompt]; if Dialog.Execute then try AppSettings.ExportSettings(Dialog.FileName); @@ -3151,12 +3152,13 @@ end; procedure TMainForm.actImportSettingsExecute(Sender: TObject); var - Dialog: TOpenDialog; + Dialog: TExtFileOpenDialog; begin // Import settings from .txt or .reg file - Dialog := TOpenDialog.Create(Self); + Dialog := TExtFileOpenDialog.Create(Self); Dialog.Title := f_('Import %s settings from file ...', [APPNAME]); - Dialog.Filter := _('Text files')+' (*.txt)|*.txt|'+_('All files')+' (*.*)|*.*'; + Dialog.AddFileType('*.txt', _('Text files')); + Dialog.AddFileType('*.*', _('All files')); ImportSettingsDone := False; if Dialog.Execute then try if LowerCase(ExtractFileExt(Dialog.FileName)) = 'reg' then @@ -3767,13 +3769,13 @@ var Content: AnsiString; FileStream: TFileStream; StrLen: Integer; - Dialog: TSaveDialog; + Dialog: TExtFileSaveDialog; begin // Save BLOB to local file Grid := ActiveGrid; Results := GridResult(Grid); - Dialog := TSaveDialog.Create(Self); - Dialog.Filter := _('All files')+' (*.*)|*.*'; + Dialog := TExtFileSaveDialog.Create(Self); + Dialog.AddFileType('*.*', _('All files')); Dialog.FileName := Results.ColumnOrgNames[Grid.FocusedColumn-1]; if not (Results.DataType(Grid.FocusedColumn-1).Category in [dtcBinary, dtcSpatial]) then Dialog.FileName := Dialog.FileName + '.txt'; @@ -5612,16 +5614,17 @@ end; procedure TMainForm.actAttachDatabaseExecute(Sender: TObject); var - Selector: TOpenDialog; + Selector: TExtFileOpenDialog; OldFiles, NewFiles: TStringList; i: Integer; Conn: TDBConnection; DbAlias: String; begin // Attach new or existing SQLite database file - Selector := TOpenDialog.Create(Self); + Selector := TExtFileOpenDialog.Create(Self); //Selector.InitialDir := ?; - Selector.Filter := 'SQLite databases ('+FILEFILTER_SQLITEDB+')|'+FILEFILTER_SQLITEDB+'|'+_('All files')+' (*.*)|*.*'; + Selector.AddFileType(FILEFILTER_SQLITEDB, 'SQLite databases'); + Selector.AddFileType('*.*', _('All files')); Selector.Options := Selector.Options - [ofFileMustExist]; Selector.Options := Selector.Options + [ofAllowMultiSelect]; Selector.DefaultExt := FILEEXT_SQLITEDB; diff --git a/source/tabletools.pas b/source/tabletools.pas index e1884cc7..a61d7e74 100644 --- a/source/tabletools.pas +++ b/source/tabletools.pas @@ -13,7 +13,7 @@ uses SysUtils, Classes, Controls, Forms, StdCtrls, ComCtrls, Buttons, Dialogs, laz.VirtualTrees, ExtCtrls, Graphics, RegExpr, Math, Generics.Collections, extra_controls, dbconnection, apphelpers, lazaruscompat, Menus, DateUtils, Zipper, StrUtils, - SynEdit, ClipBrd, generic_types, fpjson, Variants, EditBtn, LazFileUtils, Types, LCLType; + SynEdit, ClipBrd, generic_types, fpjson, Variants, EditBtn, LazFileUtils, Types, LCLType, extfiledialog; type TToolMode = (tmMaintenance, tmFind, tmSQLExport, tmBulkTableEdit, tmGenerateData); @@ -1675,17 +1675,22 @@ end; procedure TfrmTableTools.btnExportOutputTargetSelectClick(Sender: TObject); var - SaveDialog: TSaveDialog; + SaveDialog: TExtFileSaveDialog; Browse: TSelectDirectoryDialog; begin if (comboExportOutputType.Text = OUTPUT_FILE) or (comboExportOutputType.Text = OUTPUT_FILE_COMPRESSED) then begin // Select filename - SaveDialog := TSaveDialog.Create(Self); - SaveDialog.DefaultExt := 'sql'; - if comboExportOutputType.Text = OUTPUT_FILE then - SaveDialog.Filter := _('SQL files')+' (*.sql)|*.sql|'+_('All files')+' (*.*)|*.*' - else - SaveDialog.Filter := _('ZIP files')+' (*.zip)|*.zip|'+_('All files')+' (*.*)|*.*'; + SaveDialog := TExtFileSaveDialog.Create(Self); + SaveDialog.FileName := comboExportOutputTarget.Text; + if comboExportOutputType.Text = OUTPUT_FILE then begin + SaveDialog.AddFileType('*.sql', _('SQL files')); + SaveDialog.DefaultExt := 'sql'; + end + else begin + SaveDialog.AddFileType('*.zip', _('ZIP files')); + SaveDialog.DefaultExt := 'zip'; + end; + SaveDialog.AddFileType('*.*', _('All files')); // Don't prompt here if file exists, but later when exporting starts. See issue #835 // SaveDialog.Options := SaveDialog.Options + [ofOverwritePrompt]; if SaveDialog.Execute then