Issue #2137: enable automatic tab restore

This commit is contained in:
Ansgar Becker
2025-04-16 15:37:14 +02:00
parent b9d40bd214
commit 48e3dc0b81
4 changed files with 91 additions and 62 deletions

View File

@ -15,7 +15,6 @@ type
// Sync with main branch and Delphi structures
TSynMemo = TSynEdit;
TVirtualStringTree = TLazVirtualStringTree;
TExtFileSaveDialog = class(TSaveDialog);
TImageIndex = Integer;
PInt = ^Integer;
TProgressBarState = (pbsNormal, pbsError, pbsPaused);
@ -441,7 +440,7 @@ type
//function GetCurrentPackageFullName(out Len: Cardinal; Name: PWideChar): Integer; stdcall; external kernel32 delayed;
function GetThemeColor(Color: TColor): TColor;
function ThemeIsDark(ThemeName: String=''): Boolean;
//function ProcessExists(pid: Cardinal; ExeNamePattern: String): Boolean;
function ProcessExists(pid: Cardinal; ExeNamePattern: String): Boolean;
//procedure ToggleCheckBoxWithoutClick(chk: TCheckBox; State: Boolean);
function SynCompletionProposalPrettyText(ImageIndex: Integer; LeftText, CenterText, RightText: String; LeftColor: TColor=-1; CenterColor: TColor=-1; RightColor: TColor=-1): String;
function PopupComponent(Sender: TObject): TComponent;
@ -663,15 +662,9 @@ end;
function DeleteFileWithUndo(sFileName: string): Boolean;
//var
// fos: TSHFileOpStruct;
begin
{FillChar(fos, SizeOf(fos), 0);
fos.wFunc := FO_DELETE;
fos.pFrom := PChar(sFileName + #0);
fos.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_SILENT;
Result := (0 = ShFileOperation(fos));}
Beep;
// Todo: move to trash, cross-platform
Result := DeleteFile(sFileName);
end;
@ -2767,6 +2760,7 @@ var
hFile: DWORD;
begin
// Check if file is writable
// replaced through LazFileUtils.FileIsWritable
if not FileExists(FilePath) then begin
// Return true if file does not exist
Result := True;
@ -2792,14 +2786,16 @@ begin
end;
{function ProcessExists(pid: Cardinal; ExeNamePattern: String): Boolean;
var
function ProcessExists(pid: Cardinal; ExeNamePattern: String): Boolean;
{var
Proc: TProcessEntry32;
SnapShot: THandle;
ContinueLoop: Boolean;
ContinueLoop: Boolean;}
begin
// Check if a given process id exists
SnapShot := CreateToolhelp32Snapshot(TH32CS_SnapProcess, 0);
// Todo: convert code for cross-platforming
Result := False;
{SnapShot := CreateToolhelp32Snapshot(TH32CS_SnapProcess, 0);
Proc.dwSize := Sizeof(Proc);
Result := False;
ContinueLoop := Process32First(SnapShot, Proc);
@ -2809,8 +2805,8 @@ begin
Break;
ContinueLoop := Process32Next(SnapShot, Proc);
end;
CloseHandle(Snapshot);
end;}
CloseHandle(Snapshot);}
end;
{procedure ToggleCheckBoxWithoutClick(chk: TCheckBox; State: Boolean);

View File

@ -59,22 +59,23 @@ type
property EncodingIndex: Cardinal read FEncodingIndex write FEncodingIndex;
end;
{TExtFileSaveDialog = class(TFileSaveDialog)
TExtFileSaveDialog = class(TSaveDialog)
private
FFilters: TStringList;
FLineBreaks: TStringList;
FLineBreakIndex: TLineBreaks;
const idLineBreakCombo = 1;
procedure FileOkClickNoOp(Sender: TObject; var CanClose: Boolean);
protected
procedure DoOnExecute; override;
function DoOnFileOkClick: Boolean; override;
//procedure DoOnExecute; override;
//function DoOnFileOkClick: Boolean; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AddFileType(FileMask, DisplayName: String);
property LineBreaks: TStringList read FLineBreaks;
property LineBreakIndex: TLineBreaks read FLineBreakIndex write FLineBreakIndex;
end;}
end;
{TExtSynHotKey = class(TSynHotKey)
private
@ -502,14 +503,10 @@ end;
procedure TExtFileOpenDialog.AddFileType(FileMask, DisplayName: String);
var
//FileType: TFileTypeItem;
i: Integer;
NewFilter: String;
begin
// Shorthand for callers
{FileType := FileTypes.Add;
FileType.DisplayName := DisplayName;
FileType.FileMask := FileMask;}
FFilters.Values[DisplayName] := FileMask;
NewFilter := '';
for i:=FFilters.Count-1 downto 0 do begin
@ -567,7 +564,7 @@ end;}
{ TExtFileSaveDialog }
{constructor TExtFileSaveDialog.Create(AOwner: TComponent);
constructor TExtFileSaveDialog.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FLineBreaks := TStringList.Create;
@ -575,6 +572,7 @@ begin
FLineBreaks.Add(_('UNIX linebreaks'));
FLineBreaks.Add(_('Mac OS linebreaks'));
FLineBreakIndex := lbsWindows;
FFilters := TStringList.Create;
end;
@ -587,16 +585,22 @@ end;
procedure TExtFileSaveDialog.AddFileType(FileMask, DisplayName: String);
var
FileType: TFileTypeItem;
i: Integer;
NewFilter: String;
begin
// Shorthand for callers
FileType := FileTypes.Add;
FileType.DisplayName := DisplayName;
FileType.FileMask := FileMask;
FFilters.Values[DisplayName] := FileMask;
NewFilter := '';
for i:=FFilters.Count-1 downto 0 do begin
NewFilter := NewFilter + Format('%s (%s)|%s', [FFilters.Names[i], FFilters.ValueFromIndex[i], FFilters.ValueFromIndex[i]]);
if i > 0 then
NewFilter := NewFilter + '|';
end;
Filter := NewFilter;
end;
procedure TExtFileSaveDialog.DoOnExecute;
{procedure TExtFileSaveDialog.DoOnExecute;
var
iCustomize: IFileDialogCustomize;
i, ComboIndex: Integer;
@ -622,7 +626,7 @@ begin
iCustomize.EndVisualGroup;
end;
end;
end;
end;}
procedure TExtFileSaveDialog.FileOkClickNoOp(Sender: TObject; var CanClose: Boolean);
@ -631,7 +635,7 @@ begin
end;
function TExtFileSaveDialog.DoOnFileOkClick: Boolean;
{function TExtFileSaveDialog.DoOnFileOkClick: Boolean;
var
iCustomize: IFileDialogCustomize;
ComboIndex: Cardinal;

View File

@ -1089,12 +1089,12 @@ object MainForm: TMainForm
Height = 315
Top = 0
Width = 780
ActivePage = tabData
ActivePage = tabQuery
Align = alClient
HotTrack = True
Images = ImageListIcons8
PopupMenu = popupMainTabs
TabIndex = 3
TabIndex = 4
TabOrder = 1
OnChange = PageControlMainChange
OnChanging = PageControlMainChanging
@ -3019,7 +3019,7 @@ object MainForm: TMainForm
OnDragOver = SynMemoQueryDragOver
OnKeyPress = SynMemoQueryKeyPress
OnMouseWheel = AnySynMemoMouseWheel
Gutter.Width = 30
Gutter.Width = 68
Gutter.MouseActions = <>
RightGutter.Width = 0
RightGutter.MouseActions = <>
@ -3473,6 +3473,10 @@ object MainForm: TMainForm
OnSpecialLineColors = SynMemoQuerySpecialLineColors
OnStatusChange = SynMemoQueryStatusChange
inline SynLeftGutterPartList1: TSynGutterPartList
object SynGutterMarks1: TSynGutterMarks
Width = 30
MouseActions = <>
end
object SynGutterLineNumber1: TSynGutterLineNumber
Width = 17
MouseActions = <>
@ -3483,6 +3487,18 @@ object MainForm: TMainForm
ZeroStart = False
LeadingZeros = False
end
object SynGutterChanges1: TSynGutterChanges
Width = 5
MouseActions = <>
ModifiedColor = 59900
SavedColor = clGreen
end
object SynGutterSeparator1: TSynGutterSeparator
Width = 3
MouseActions = <>
MarkupInfo.Background = clWhite
MarkupInfo.Foreground = clGray
end
object SynGutterCodeFolding1: TSynGutterCodeFolding
Width = 13
MouseActions = <>

View File

@ -14,7 +14,7 @@ uses
Generics.Defaults, opensslsockets, StdActns, Clipbrd, Types, LCLType, EditBtn,
FileUtil, LMessages, jsonconf, dbconnection, dbstructures, dbstructures.mysql,
generic_types, apphelpers, extra_controls, createdatabase,
SynEditMarkupSpecialLine, searchreplace, ImgList;
SynEditMarkupSpecialLine, searchreplace, ImgList, IniFiles, LazFileUtils;
type
@ -1298,9 +1298,9 @@ type
//procedure SetLogToFile(Value: Boolean);
procedure StoreLastSessions;
function HandleUnixTimestampColumn(Sender: TBaseVirtualTree; Column: TColumnIndex): Boolean;
//function InitTabsIniFile: TIniFile;
//procedure StoreTabs;
//function RestoreTabs: Boolean;
function InitTabsIniFile: TIniFile;
procedure StoreTabs;
function RestoreTabs: Boolean;
procedure SetHintFontByControl(Control: TWinControl=nil);
public
QueryTabs: TQueryTabList;
@ -2331,10 +2331,10 @@ begin
end;
end;
{// Restore backup'ed query tabs
// Restore backup'ed query tabs
if AppSettings.RestoreTabsInitValue then begin
TimerStoreTabs.Enabled := RestoreTabs;
end;}
end;
// Load SQL file(s) by command line
if not RunQueryFiles(FileNames, nil, false) then begin
@ -2353,10 +2353,10 @@ begin
end;
{function TMainForm.InitTabsIniFile: TIniFile;
function TMainForm.InitTabsIniFile: TIniFile;
var
WaitingSince: UInt64;
Attempts: Integer;
//WaitingSince: UInt64;
//Attempts: Integer;
TabsIniFilename: String;
begin
// Try to open tabs.ini for writing or reading
@ -2365,7 +2365,7 @@ begin
TabsIniFilename := ExtractFilePath(Application.ExeName) + 'tabs.ini'
else
TabsIniFilename := AppSettings.DirnameUserAppData + 'tabs.ini';
WaitingSince := GetTickCount64;
{WaitingSince := GetTickCount64;
Attempts := 0;
while not FileIsWritable(TabsIniFilename) do begin
if GetTickCount64 - WaitingSince > 3000 then
@ -2375,16 +2375,16 @@ begin
end;
if Attempts > 0 then begin
LogSQL(Format('Had to wait %d ms before opening %s', [GetTickCount64 - WaitingSince, TabsIniFilename]), lcDebug);
end;
end;}
// Catch errors when file cannot be created
if not FileExists(TabsIniFilename) then begin
SaveUnicodeFile(TabsIniFilename, '', UTF8NoBOMEncoding);
end;
Result := TIniFile.Create(TabsIniFilename);
end;}
end;
{procedure TMainForm.StoreTabs;
procedure TMainForm.StoreTabs;
var
Tab: TQueryTab;
Section, TabCaption: String;
@ -2413,8 +2413,8 @@ begin
TabsIni.WriteString(Section, TQueryTab.IdentFilename, Tab.MemoFilename);
if TabsIni.ReadString(Section, TQueryTab.IdentCaption, '') <> TabCaption then
TabsIni.WriteString(Section, TQueryTab.IdentCaption, TabCaption);
if TabsIni.ReadInteger(Section, TQueryTab.IdentPid, 0) <> Integer(GetCurrentProcessId) then
TabsIni.WriteInteger(Section, TQueryTab.IdentPid, Integer(GetCurrentProcessId));
if TabsIni.ReadInteger(Section, TQueryTab.IdentPid, 0) <> Integer(GetProcessId) then
TabsIni.WriteInteger(Section, TQueryTab.IdentPid, Integer(GetProcessId));
if TabsIni.ReadInteger(Section, TQueryTab.IdentEditorHeight, 0) <> Tab.pnlMemo.Height then
TabsIni.WriteInteger(Section, TQueryTab.IdentEditorHeight, Tab.pnlMemo.Height);
if TabsIni.ReadInteger(Section, TQueryTab.IdentHelpersWidth, 0) <> Tab.pnlHelpers.Width then
@ -2444,7 +2444,7 @@ begin
end;
// Delete tab section if tab was closed and section belongs to this app instance
pid := Cardinal(TabsIni.ReadInteger(Section, TQueryTab.IdentPid, 0));
if (not SectionTabExists) and (pid = GetCurrentProcessId) then begin
if (not SectionTabExists) and (pid = GetProcessId) then begin
TabsIni.EraseSection(Section);
end;
end;
@ -2457,14 +2457,14 @@ begin
ErrorDialog(_('Storing tab setup failed'),
'Tabs won''t be stored in this session.' + CRLF + CRLF +
E.Message + CRLF + CRLF +
SysErrorMessage(GetLastError)
SysErrorMessage(GetLastOSError)
);
end;
end;
end;}
end;
{function TMainForm.RestoreTabs: Boolean;
function TMainForm.RestoreTabs: Boolean;
var
Tab: TQueryTab;
Sections, SlowTabs: TStringList;
@ -2513,7 +2513,7 @@ begin
Encoding := GetEncodingByName(TabsIni.ReadString(Section, TQueryTab.IdentFileEncoding, 'UTF-8'));
// Don't restore this tab if it belongs to a different running Heidi process
if (pid > 0) and (pid <> GetCurrentProcessId) and ProcessExists(pid, APPNAME) then begin
if (pid > 0) and (pid <> GetProcessId) and ProcessExists(pid, APPNAME) then begin
LogSQL(IfThen(BackupFilename.IsEmpty, Filename, BackupFilename)+' loaded in process #'+pid.ToString);
Continue;
end;
@ -2590,11 +2590,11 @@ begin
ErrorDialog(_('Restoring tab setup failed'),
'Tabs won''t be stored in this session.' + CRLF + CRLF +
E.Message + CRLF + CRLF +
SysErrorMessage(GetLastError)
SysErrorMessage(GetLastOSError)
);
end;
end;
end;}
end;
procedure TMainForm.SetHintFontByControl(Control: TWinControl=nil);
@ -2614,7 +2614,7 @@ end;
procedure TMainForm.TimerStoreTabsTimer(Sender: TObject);
begin
// Backup unsaved content every 10 seconds
//StoreTabs;
StoreTabs;
end;
@ -2627,6 +2627,7 @@ begin
Dialog.Free;
end;
procedure TMainForm.actDisconnectExecute(Sender: TObject);
var
Connection: TDBConnection;
@ -12328,7 +12329,19 @@ begin
QueryTab.Memo.RightEdge := SynMemoQuery.RightEdge;
QueryTab.Memo.WantTabs := SynMemoQuery.WantTabs;
QueryTab.Memo.Highlighter := SynMemoQuery.Highlighter;
QueryTab.Memo.Gutter.Assign(SynMemoQuery.Gutter);
QueryTab.Memo.Gutter.Width := SynMemoQuery.Gutter.Width;
// Todo: adding and gutter part results in EAccessviolation's when closing a query tab.
// Fix that and use the same gutter as in mother query tab
// In the meantime we let SynEdit use TSynGutter.CreateDefaultGutterParts
//for GutterPartIndex:=0 to SynMemoQuery.Gutter.Parts.Count-1 do begin
// QueryTab.Memo.Gutter.Parts.Add(SynMemoQuery.Gutter.Parts[GutterPartIndex].ClassType.Create(QueryTab.Memo.Gutter.Parts));
// QueryTab.Memo.Gutter.Parts[GutterPartIndex].Width := SynMemoQuery.Gutter.Parts[GutterPartIndex].Width;
//end;
//QueryTab.Memo.Gutter.Parts.Clear;
//QueryTab.Memo.Gutter.Parts.Add(TSynGutterLineNumber.Create(QueryTab.Memo.Gutter.Parts));
//QueryTab.Memo.Gutter.Parts[0].Width := SynMemoQuery.Gutter.Parts[0].Width;
//QueryTab.Memo.Gutter.Parts.Add(TSynGutterCodeFolding.Create(QueryTab.Memo.Gutter.Parts));
//QueryTab.Memo.Gutter.Parts[1].Width := SynMemoQuery.Gutter.Parts[1].Width;
QueryTab.Memo.Font.Assign(SynMemoQuery.Font);
//QueryTab.Memo.ActiveLineColor := SynMemoQuery.ActiveLineColor;
QueryTab.Memo.OnStatusChange := SynMemoQuery.OnStatusChange;
@ -13123,7 +13136,7 @@ begin
end;
end;
// Leave space for close button on closable query tabs
Text := Text + ' ';
//Text := Text + ' ';
end;
PageControlMain.Pages[PageIndex].Caption := Text;
FixQueryTabCloseButtons;
@ -13186,8 +13199,8 @@ begin
else begin
Dialog := TExtFileSaveDialog.Create(Self);
Dialog.Options := Dialog.Options + [ofOverwritePrompt];
//Dialog.AddFileType('*.sql', _('SQL files'));
//Dialog.AddFileType('*.*', _('All files'));
Dialog.AddFileType('*.sql', _('SQL files'));
Dialog.AddFileType('*.*', _('All files'));
Dialog.DefaultExt := 'sql';
//Dialog.LineBreakIndex := Tab.MemoLineBreaks;
if Dialog.Execute then begin