mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
Enhance tab restore feature:
* restore scroll position (EditorTopLine) * restore active query tab (TabFocused) * code cleanup: move identifier literals for tabs.ini to class constants
This commit is contained in:
@ -2982,6 +2982,11 @@ object MainForm: TMainForm
|
|||||||
ShortCut = 16468
|
ShortCut = 16468
|
||||||
OnExecute = actNewQueryTabExecute
|
OnExecute = actNewQueryTabExecute
|
||||||
end
|
end
|
||||||
|
object actNewQueryTabNofocus: TAction
|
||||||
|
Category = 'File'
|
||||||
|
Caption = 'New query tab in background'
|
||||||
|
OnExecute = actNewQueryTabExecute
|
||||||
|
end
|
||||||
object actCloseQueryTab: TAction
|
object actCloseQueryTab: TAction
|
||||||
Category = 'File'
|
Category = 'File'
|
||||||
Caption = 'Close query tab'
|
Caption = 'Close query tab'
|
||||||
|
141
source/main.pas
141
source/main.pas
@ -57,6 +57,16 @@ type
|
|||||||
end;
|
end;
|
||||||
TResultTabs = TObjectList<TResultTab>;
|
TResultTabs = TObjectList<TResultTab>;
|
||||||
TQueryTab = class(TComponent)
|
TQueryTab = class(TComponent)
|
||||||
|
const
|
||||||
|
IdentBackupFilename = 'BackupFilename';
|
||||||
|
IdentFilename = 'Filename';
|
||||||
|
IdentCaption = 'Caption';
|
||||||
|
IdentPid = 'pid';
|
||||||
|
IdentEditorHeight = 'EditorHeight';
|
||||||
|
IdentHelpersWidth = 'HelpersWidth';
|
||||||
|
IdentBindParams = 'BindParams';
|
||||||
|
IdentEditorTopLine = 'EditorTopLine';
|
||||||
|
IdentTabFocused = 'TabFocused';
|
||||||
private
|
private
|
||||||
FMemo: TSynMemo;
|
FMemo: TSynMemo;
|
||||||
FMemoFilename: String;
|
FMemoFilename: String;
|
||||||
@ -729,6 +739,7 @@ type
|
|||||||
actRenameQueryTab: TAction;
|
actRenameQueryTab: TAction;
|
||||||
menuRenameQueryTab: TMenuItem;
|
menuRenameQueryTab: TMenuItem;
|
||||||
Renametab1: TMenuItem;
|
Renametab1: TMenuItem;
|
||||||
|
actNewQueryTabNofocus: TAction;
|
||||||
procedure actCreateDBObjectExecute(Sender: TObject);
|
procedure actCreateDBObjectExecute(Sender: TObject);
|
||||||
procedure menuConnectionsPopup(Sender: TObject);
|
procedure menuConnectionsPopup(Sender: TObject);
|
||||||
procedure actExitApplicationExecute(Sender: TObject);
|
procedure actExitApplicationExecute(Sender: TObject);
|
||||||
@ -930,7 +941,7 @@ type
|
|||||||
function GetMainTabAt(X, Y: Integer): Integer;
|
function GetMainTabAt(X, Y: Integer): Integer;
|
||||||
procedure FixQueryTabCloseButtons;
|
procedure FixQueryTabCloseButtons;
|
||||||
function ActiveQueryTab: TQueryTab;
|
function ActiveQueryTab: TQueryTab;
|
||||||
function GetOrCreateEmptyQueryTab: TQueryTab;
|
function GetOrCreateEmptyQueryTab(DoFocus: Boolean): TQueryTab;
|
||||||
function GetQueryTabByNumber(Number: Integer): TQueryTab;
|
function GetQueryTabByNumber(Number: Integer): TQueryTab;
|
||||||
function GetQueryTabByHelpers(FindTree: TBaseVirtualTree): TQueryTab;
|
function GetQueryTabByHelpers(FindTree: TBaseVirtualTree): TQueryTab;
|
||||||
function ActiveQueryMemo: TSynMemo;
|
function ActiveQueryMemo: TSynMemo;
|
||||||
@ -2270,7 +2281,7 @@ begin
|
|||||||
// Load SQL file(s) by command line
|
// Load SQL file(s) by command line
|
||||||
if not RunQueryFiles(FileNames, nil, false) then begin
|
if not RunQueryFiles(FileNames, nil, false) then begin
|
||||||
for i:=0 to FileNames.Count-1 do begin
|
for i:=0 to FileNames.Count-1 do begin
|
||||||
Tab := GetOrCreateEmptyQueryTab;
|
Tab := GetOrCreateEmptyQueryTab(False);
|
||||||
Tab.LoadContents(FileNames[i], True, nil);
|
Tab.LoadContents(FileNames[i], True, nil);
|
||||||
if i = FileNames.Count-1 then
|
if i = FileNames.Count-1 then
|
||||||
SetMainTab(Tab.TabSheet);
|
SetMainTab(Tab.TabSheet);
|
||||||
@ -2328,27 +2339,29 @@ begin
|
|||||||
Tab.BackupUnsavedContent;
|
Tab.BackupUnsavedContent;
|
||||||
Section := Tab.Uid;
|
Section := Tab.Uid;
|
||||||
|
|
||||||
if Tab.Memo.GetTextLen > 0 then begin
|
// Avoid writing the tabs.ini file if nothing was effectively changed
|
||||||
// Avoid writing the tabs.ini file if nothing was effectively changed
|
TabCaption := Tab.TabSheet.Caption;
|
||||||
TabCaption := Tab.TabSheet.Caption;
|
TabCaption := TabCaption.Trim([' ','*']);
|
||||||
TabCaption := TabCaption.Trim([' ','*']);
|
if ExecRegExpr('^'+QuoteRegExprMetaChars(_('Query')+' #')+'\d+$', TabCaption) then
|
||||||
if ExecRegExpr('^'+QuoteRegExprMetaChars(_('Query')+' #')+'\d+$', TabCaption) then
|
TabCaption := '';
|
||||||
TabCaption := '';
|
if TabsIni.ReadString(Section, TQueryTab.IdentBackupFilename, '') <> Tab.MemoBackupFilename then
|
||||||
if TabsIni.ReadString(Section, 'BackupFilename', '') <> Tab.MemoBackupFilename then
|
TabsIni.WriteString(Section, TQueryTab.IdentBackupFilename, Tab.MemoBackupFilename);
|
||||||
TabsIni.WriteString(Section, 'BackupFilename', Tab.MemoBackupFilename);
|
if TabsIni.ReadString(Section, TQueryTab.IdentFilename, '') <> Tab.MemoFilename then
|
||||||
if TabsIni.ReadString(Section, 'Filename', '') <> Tab.MemoFilename then
|
TabsIni.WriteString(Section, TQueryTab.IdentFilename, Tab.MemoFilename);
|
||||||
TabsIni.WriteString(Section, 'Filename', Tab.MemoFilename);
|
if TabsIni.ReadString(Section, TQueryTab.IdentCaption, '') <> TabCaption then
|
||||||
if TabsIni.ReadString(Section, 'Caption', '') <> TabCaption then
|
TabsIni.WriteString(Section, TQueryTab.IdentCaption, TabCaption);
|
||||||
TabsIni.WriteString(Section, 'Caption', TabCaption);
|
if TabsIni.ReadInteger(Section, TQueryTab.IdentPid, 0) <> Integer(GetCurrentProcessId) then
|
||||||
if TabsIni.ReadInteger(Section, 'pid', 0) <> Integer(GetCurrentProcessId) then
|
TabsIni.WriteInteger(Section, TQueryTab.IdentPid, Integer(GetCurrentProcessId));
|
||||||
TabsIni.WriteInteger(Section, 'pid', Integer(GetCurrentProcessId));
|
if TabsIni.ReadInteger(Section, TQueryTab.IdentEditorHeight, 0) <> Tab.pnlMemo.Height then
|
||||||
if TabsIni.ReadInteger(Section, 'EditorHeight', 0) <> Tab.pnlMemo.Height then
|
TabsIni.WriteInteger(Section, TQueryTab.IdentEditorHeight, Tab.pnlMemo.Height);
|
||||||
TabsIni.WriteInteger(Section, 'EditorHeight', Tab.pnlMemo.Height);
|
if TabsIni.ReadInteger(Section, TQueryTab.IdentHelpersWidth, 0) <> Tab.pnlHelpers.Width then
|
||||||
if TabsIni.ReadInteger(Section, 'HelpersWidth', 0) <> Tab.pnlHelpers.Width then
|
TabsIni.WriteInteger(Section, TQueryTab.IdentHelpersWidth, Tab.pnlHelpers.Width);
|
||||||
TabsIni.WriteInteger(Section, 'HelpersWidth', Tab.pnlHelpers.Width);
|
if TabsIni.ReadString(Section, TQueryTab.IdentBindParams, '') <> Tab.ListBindParams.AsText then
|
||||||
if TabsIni.ReadString(Section, 'BindParams', '') <> Tab.ListBindParams.AsText then
|
TabsIni.WriteString(Section, TQueryTab.IdentBindParams, Tab.ListBindParams.AsText);
|
||||||
TabsIni.WriteString(Section, 'BindParams', Tab.ListBindParams.AsText);
|
if TabsIni.ReadInteger(Section, TQueryTab.IdentEditorTopLine, 1) <> Tab.Memo.TopLine then
|
||||||
end;
|
TabsIni.WriteInteger(Section, TQueryTab.IdentEditorTopLine, Tab.Memo.TopLine);
|
||||||
|
if TabsIni.ReadBool(Section, TQueryTab.IdentTabFocused, False) <> (Tab.TabSheet = Tab.TabSheet.PageControl.ActivePage) then
|
||||||
|
TabsIni.WriteBool(Section, TQueryTab.IdentTabFocused, (Tab.TabSheet = Tab.TabSheet.PageControl.ActivePage));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Tabs with deleted backup files don't get restored anyway. But a section from a closed user loaded tab
|
// Tabs with deleted backup files don't get restored anyway. But a section from a closed user loaded tab
|
||||||
@ -2365,7 +2378,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
// Delete tab section if tab was closed and section belongs to this app instance
|
// Delete tab section if tab was closed and section belongs to this app instance
|
||||||
pid := Cardinal(TabsIni.ReadInteger(Section, 'pid', 0));
|
pid := Cardinal(TabsIni.ReadInteger(Section, TQueryTab.IdentPid, 0));
|
||||||
if (not SectionTabExists) and (pid = GetCurrentProcessId) then begin
|
if (not SectionTabExists) and (pid = GetCurrentProcessId) then begin
|
||||||
TabsIni.EraseSection(Section);
|
TabsIni.EraseSection(Section);
|
||||||
end;
|
end;
|
||||||
@ -2393,8 +2406,9 @@ var
|
|||||||
Section, Filename, BackupFilename, TabCaption: String;
|
Section, Filename, BackupFilename, TabCaption: String;
|
||||||
TabsIni: TIniFile;
|
TabsIni: TIniFile;
|
||||||
pid: Cardinal;
|
pid: Cardinal;
|
||||||
EditorHeight, HelpersWidth: Integer;
|
EditorHeight, HelpersWidth, EditorTopLine: Integer;
|
||||||
BindParams: String;
|
BindParams: String;
|
||||||
|
TabFocused: Boolean;
|
||||||
begin
|
begin
|
||||||
// Restore query tab setup from tabs.ini
|
// Restore query tab setup from tabs.ini
|
||||||
Result := True;
|
Result := True;
|
||||||
@ -2408,13 +2422,15 @@ begin
|
|||||||
|
|
||||||
for Section in Sections do begin
|
for Section in Sections do begin
|
||||||
|
|
||||||
Filename := TabsIni.ReadString(Section, 'Filename', '');
|
Filename := TabsIni.ReadString(Section, TQueryTab.IdentFilename, '');
|
||||||
BackupFilename := TabsIni.ReadString(Section, 'BackupFilename', '');
|
BackupFilename := TabsIni.ReadString(Section, TQueryTab.IdentBackupFilename, '');
|
||||||
TabCaption := TabsIni.ReadString(Section, 'Caption', '');
|
TabCaption := TabsIni.ReadString(Section, TQueryTab.IdentCaption, '');
|
||||||
pid := Cardinal(TabsIni.ReadInteger(Section, 'pid', 0));
|
pid := Cardinal(TabsIni.ReadInteger(Section, TQueryTab.IdentPid, 0));
|
||||||
EditorHeight := TabsIni.ReadInteger(Section, 'EditorHeight', 0);
|
EditorHeight := TabsIni.ReadInteger(Section, TQueryTab.IdentEditorHeight, 0);
|
||||||
HelpersWidth := TabsIni.ReadInteger(Section, 'HelpersWidth', 0);
|
HelpersWidth := TabsIni.ReadInteger(Section, TQueryTab.IdentHelpersWidth, 0);
|
||||||
BindParams := TabsIni.ReadString(Section, 'BindParams', '');
|
BindParams := TabsIni.ReadString(Section, TQueryTab.IdentBindParams, '');
|
||||||
|
EditorTopLine := TabsIni.ReadInteger(Section, TQueryTab.IdentEditorTopLine, 1);
|
||||||
|
TabFocused := TabsIni.ReadBool(Section, TQueryTab.IdentTabFocused, False);
|
||||||
|
|
||||||
// Don't restore this tab if it belongs to a different running Heidi process
|
// 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 <> GetCurrentProcessId) and ProcessExists(pid, APPNAME) then begin
|
||||||
@ -2426,7 +2442,7 @@ begin
|
|||||||
// Both of them may not exist.
|
// Both of them may not exist.
|
||||||
if not BackupFilename.IsEmpty then begin
|
if not BackupFilename.IsEmpty then begin
|
||||||
if FileExists(BackupFilename) then begin
|
if FileExists(BackupFilename) then begin
|
||||||
Tab := GetOrCreateEmptyQueryTab;
|
Tab := GetOrCreateEmptyQueryTab(False);
|
||||||
Tab.Uid := Section;
|
Tab.Uid := Section;
|
||||||
Tab.LoadContents(BackupFilename, True, UTF8NoBOMEncoding);
|
Tab.LoadContents(BackupFilename, True, UTF8NoBOMEncoding);
|
||||||
Tab.MemoFilename := Filename;
|
Tab.MemoFilename := Filename;
|
||||||
@ -2439,13 +2455,16 @@ begin
|
|||||||
Tab.pnlHelpers.Width := HelpersWidth;
|
Tab.pnlHelpers.Width := HelpersWidth;
|
||||||
Tab.ListBindParams.AsText := BindParams;
|
Tab.ListBindParams.AsText := BindParams;
|
||||||
Tab.BindParamsActivated := Tab.ListBindParams.Count > 0;
|
Tab.BindParamsActivated := Tab.ListBindParams.Count > 0;
|
||||||
|
Tab.Memo.TopLine := EditorTopLine;
|
||||||
|
if TabFocused then
|
||||||
|
SetMainTab(Tab.TabSheet);
|
||||||
end else begin
|
end else begin
|
||||||
// Remove tab section if backup file is gone or inaccessible for some reason
|
// Remove tab section if backup file is gone or inaccessible for some reason
|
||||||
TabsIni.EraseSection(Section);
|
TabsIni.EraseSection(Section);
|
||||||
end;
|
end;
|
||||||
end else if not Filename.IsEmpty then begin
|
end else if not Filename.IsEmpty then begin
|
||||||
if FileExists(Filename) then begin
|
if FileExists(Filename) then begin
|
||||||
Tab := GetOrCreateEmptyQueryTab;
|
Tab := GetOrCreateEmptyQueryTab(False);
|
||||||
Tab.Uid := Section;
|
Tab.Uid := Section;
|
||||||
Tab.LoadContents(Filename, True, nil);
|
Tab.LoadContents(Filename, True, nil);
|
||||||
Tab.MemoFilename := Filename;
|
Tab.MemoFilename := Filename;
|
||||||
@ -2457,6 +2476,9 @@ begin
|
|||||||
Tab.pnlHelpers.Width := HelpersWidth;
|
Tab.pnlHelpers.Width := HelpersWidth;
|
||||||
Tab.ListBindParams.AsText := BindParams;
|
Tab.ListBindParams.AsText := BindParams;
|
||||||
Tab.BindParamsActivated := Tab.ListBindParams.Count > 0;
|
Tab.BindParamsActivated := Tab.ListBindParams.Count > 0;
|
||||||
|
Tab.Memo.TopLine := EditorTopLine;
|
||||||
|
if TabFocused then
|
||||||
|
SetMainTab(Tab.TabSheet);
|
||||||
end else begin
|
end else begin
|
||||||
// Remove tab section if user stored file was deleted by user
|
// Remove tab section if user stored file was deleted by user
|
||||||
TabsIni.EraseSection(Section);
|
TabsIni.EraseSection(Section);
|
||||||
@ -3842,7 +3864,7 @@ begin
|
|||||||
Encoding := GetEncodingByName(Dialog.Encodings[Dialog.EncodingIndex]);
|
Encoding := GetEncodingByName(Dialog.Encodings[Dialog.EncodingIndex]);
|
||||||
if not RunQueryFiles(Dialog.Files, Encoding, Sender=actRunSQL) then begin
|
if not RunQueryFiles(Dialog.Files, Encoding, Sender=actRunSQL) then begin
|
||||||
for i:=0 to Dialog.Files.Count-1 do begin
|
for i:=0 to Dialog.Files.Count-1 do begin
|
||||||
Tab := GetOrCreateEmptyQueryTab;
|
Tab := GetOrCreateEmptyQueryTab(False);
|
||||||
Tab.LoadContents(Dialog.Files[i], True, Encoding);
|
Tab.LoadContents(Dialog.Files[i], True, Encoding);
|
||||||
if i = Dialog.Files.Count-1 then
|
if i = Dialog.Files.Count-1 then
|
||||||
SetMainTab(Tab.TabSheet);
|
SetMainTab(Tab.TabSheet);
|
||||||
@ -4975,9 +4997,8 @@ begin
|
|||||||
FileList := TStringList.Create;
|
FileList := TStringList.Create;
|
||||||
FileList.Add(Filename);
|
FileList.Add(Filename);
|
||||||
if not RunQueryFiles(FileList, nil, false) then begin
|
if not RunQueryFiles(FileList, nil, false) then begin
|
||||||
Tab := GetOrCreateEmptyQueryTab;
|
Tab := GetOrCreateEmptyQueryTab(True);
|
||||||
Tab.LoadContents(Filename, True, nil);
|
Tab.LoadContents(Filename, True, nil);
|
||||||
SetMainTab(Tab.TabSheet);
|
|
||||||
end;
|
end;
|
||||||
FileList.Free;
|
FileList.Free;
|
||||||
end;
|
end;
|
||||||
@ -7026,7 +7047,7 @@ begin
|
|||||||
// query-memo - load their contents into seperate tabs
|
// query-memo - load their contents into seperate tabs
|
||||||
if not RunQueryFiles(AFiles, nil, False) then begin
|
if not RunQueryFiles(AFiles, nil, False) then begin
|
||||||
for i:=0 to AFiles.Count-1 do begin
|
for i:=0 to AFiles.Count-1 do begin
|
||||||
Tab := GetOrCreateEmptyQueryTab;
|
Tab := GetOrCreateEmptyQueryTab(True);
|
||||||
Tab.LoadContents(AFiles[i], False, nil);
|
Tab.LoadContents(AFiles[i], False, nil);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -11454,7 +11475,9 @@ begin
|
|||||||
SetupSynEditors;
|
SetupSynEditors;
|
||||||
|
|
||||||
// Show new tab
|
// Show new tab
|
||||||
SetMainTab(QueryTab.TabSheet);
|
if Sender <> actNewQueryTabNofocus then begin
|
||||||
|
SetMainTab(QueryTab.TabSheet);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -11856,7 +11879,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
function TMainForm.GetOrCreateEmptyQueryTab: TQueryTab;
|
function TMainForm.GetOrCreateEmptyQueryTab(DoFocus: Boolean): TQueryTab;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
@ -11865,19 +11888,22 @@ begin
|
|||||||
// or c) create a new one
|
// or c) create a new one
|
||||||
// Result should never be nil, unlike in ActiveQueryTab
|
// Result should never be nil, unlike in ActiveQueryTab
|
||||||
Result := nil;
|
Result := nil;
|
||||||
|
// Search empty tab
|
||||||
|
for i:=0 to QueryTabs.Count-1 do begin
|
||||||
|
if (QueryTabs[i].MemoFilename='') and (QueryTabs[i].Memo.GetTextLen=0) then begin
|
||||||
|
Result := QueryTabs[i];
|
||||||
|
if DoFocus then
|
||||||
|
SetMainTab(Result.TabSheet);
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
// Create new tab
|
||||||
if Result = nil then begin
|
if Result = nil then begin
|
||||||
// Search empty tab
|
if DoFocus then
|
||||||
for i:=0 to QueryTabs.Count-1 do begin
|
actNewQueryTabExecute(actNewQueryTab)
|
||||||
if (QueryTabs[i].MemoFilename='') and (QueryTabs[i].Memo.GetTextLen=0) then begin
|
else
|
||||||
Result := QueryTabs[i];
|
actNewQueryTabExecute(actNewQueryTabNofocus);
|
||||||
break;
|
Result := QueryTabs[QueryTabs.Count-1];
|
||||||
end;
|
|
||||||
end;
|
|
||||||
// Create new tab
|
|
||||||
if Result = nil then begin
|
|
||||||
actNewQueryTabExecute(Self);
|
|
||||||
Result := QueryTabs[QueryTabs.Count-1];
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -12086,8 +12112,13 @@ var
|
|||||||
MsgButtons: TMsgDlgButtons;
|
MsgButtons: TMsgDlgButtons;
|
||||||
begin
|
begin
|
||||||
Tab := QueryTabs[PageIndex-tabQuery.PageIndex];
|
Tab := QueryTabs[PageIndex-tabQuery.PageIndex];
|
||||||
// Unhide tabsheet so the user sees the memo content
|
|
||||||
Tab.TabSheet.PageControl.ActivePage := Tab.TabSheet;
|
// Unhide tabsheet so the user sees the memo content.
|
||||||
|
// If the dialog is suppressed anyway, the user does not need to see the text, and we avoid
|
||||||
|
// storing this as the focused tab
|
||||||
|
if AppSettings.ReadBool(asPromptSaveFileOnTabClose) then begin
|
||||||
|
Tab.TabSheet.PageControl.ActivePage := Tab.TabSheet;
|
||||||
|
end;
|
||||||
|
|
||||||
// Prompt for saving unsaved contents
|
// Prompt for saving unsaved contents
|
||||||
if Tab.MemoFilename <> '' then
|
if Tab.MemoFilename <> '' then
|
||||||
@ -12565,7 +12596,7 @@ begin
|
|||||||
ParseCommandLine(ParamBlobToStr(Msg.CopyDataStruct.lpData), ConnectionParams, FileNames);
|
ParseCommandLine(ParamBlobToStr(Msg.CopyDataStruct.lpData), ConnectionParams, FileNames);
|
||||||
if not RunQueryFiles(FileNames, nil, False) then begin
|
if not RunQueryFiles(FileNames, nil, False) then begin
|
||||||
for i:=0 to FileNames.Count-1 do begin
|
for i:=0 to FileNames.Count-1 do begin
|
||||||
Tab := GetOrCreateEmptyQueryTab;
|
Tab := GetOrCreateEmptyQueryTab(True);
|
||||||
Tab.LoadContents(FileNames[i], True, nil);
|
Tab.LoadContents(FileNames[i], True, nil);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
Reference in New Issue
Block a user