Issue #1525: remove project manager feature with all its unrelated changes

This commit is contained in:
Ansgar Becker
2025-08-19 06:27:10 +02:00
parent 80cd08a6f7
commit 1bc89b12dd
10 changed files with 38 additions and 2479 deletions

View File

@@ -6768,95 +6768,3 @@ msgstr "Select top %s rows"
msgid "Selects the first %s rows in a new query tab"
msgstr "Selects the first %s rows in a new query tab"
#. Project Manager
msgid "Project Manager"
msgstr "Project Manager"
msgid "Project &Manager"
msgstr "Project &Manager"
msgid "Toggle Project Manager tab in query helpers (F11)"
msgstr "Toggle Project Manager tab in query helpers (F11)"
#. Project Manager - Button hints
msgid "Add Project Folder"
msgstr "Add Project Folder"
msgid "Remove Project Folder"
msgstr "Remove Project Folder"
msgid "Rename Project"
msgstr "Rename Project"
msgid "Refresh Projects"
msgstr "Refresh Projects"
#. Project Manager - Context menu
msgid "Open File"
msgstr "Open File"
msgid "Open with System Editor"
msgstr "Open with System Editor"
msgid "Open Folder"
msgstr "Open Folder"
msgid "Copy Path"
msgstr "Copy Path"
msgid "Add Project Folder..."
msgstr "Add Project Folder..."
msgid "Remove Project"
msgstr "Remove Project"
#. Project Manager - Dialogs
msgid "Select Project Folder"
msgstr "Select Project Folder"
msgid "Root"
msgstr "Root"
msgid "Project Name"
msgstr "Project Name"
msgid "Enter a name for this project:"
msgstr "Enter a name for this project:"
#. Project Manager - Error messages
msgid "Error in CreateWnd: "
msgstr "Error in CreateWnd: "
msgid "Project name cannot be empty!"
msgstr "Project name cannot be empty!"
msgid "A project with this name already exists!"
msgstr "A project with this name already exists!"
msgid "This folder is already added as a project!"
msgstr "This folder is already added as a project!"
msgid "Please select a project to remove."
msgstr "Please select a project to remove."
msgid "Project not found in list."
msgstr "Project not found in list."
msgid "Please select a project to rename."
msgstr "Please select a project to rename."
msgid "Database files should be opened via the connection manager."
msgstr "Database files should be opened via the connection manager."
msgid "Use File > Connect to Database to open this SQLite database."
msgstr "Use File > Connect to Database to open this SQLite database."
msgid "Failed to copy path to clipboard: "
msgstr "Failed to copy path to clipboard: "
msgid "Error saving projects: "
msgstr "Error saving projects: "
msgid "Error opening file: "
msgstr "Error opening file: "

View File

@@ -58,8 +58,7 @@ uses
customize_highlighter in '..\..\source\customize_highlighter.pas' {frmCustomizeHighlighter},
Xml.VerySimple in '..\..\source\Xml.VerySimple.pas',
Sequal.Suggest in '..\..\source\Sequal.Suggest.pas' {SequalSuggestForm},
reformatter in '..\..\source\reformatter.pas' {frmReformatter},
projectmanager in '..\..\source\projectmanager.pas';
reformatter in '..\..\source\reformatter.pas' {frmReformatter};
{.$R *.RES}
{$R ..\..\res\icon.RES}

View File

@@ -265,7 +265,6 @@
<Form>frmReformatter</Form>
<FormType>dfm</FormType>
</DCCReference>
<DCCReference Include="..\..\source\projectmanager.pas"/>
<None Include="..\..\source\const.inc"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>

View File

@@ -58,8 +58,7 @@ uses
customize_highlighter in '..\..\source\customize_highlighter.pas' {frmCustomizeHighlighter},
Xml.VerySimple in '..\..\source\Xml.VerySimple.pas',
Sequal.Suggest in '..\..\source\Sequal.Suggest.pas' {SequalSuggestForm},
reformatter in '..\..\source\reformatter.pas' {frmReformatter},
projectmanager in '..\..\source\projectmanager.pas';
reformatter in '..\..\source\reformatter.pas' {frmReformatter};
{.$R *.RES}
{$R ..\..\res\icon.RES}

View File

@@ -267,7 +267,6 @@
<Form>frmReformatter</Form>
<FormType>dfm</FormType>
</DCCReference>
<DCCReference Include="..\..\source\projectmanager.pas"/>
<None Include="..\..\source\const.inc"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>

View File

@@ -182,7 +182,7 @@ type
asMaxColWidth, asDatagridMaximumRows, asDatagridRowsPerStep, asGridRowLineCount, asColumnHeaderClick, asReuseEditorConfiguration,
asLogToFile, asMainWinMaximized, asMainWinLeft, asMainWinTop, asMainWinWidth,
asMainWinHeight, asMainWinOnMonitor, asCoolBandIndex, asCoolBandBreak, asCoolBandWidth, asToolbarShowCaptions, asQuerymemoheight, asDbtreewidth,
asDataPreviewHeight, asDataPreviewEnabled, asLogHeight, asQueryhelperswidth, asProjectManagerWidth, asProjectManagerVisible, asProjectManagerTabActive, asStopOnErrorsInBatchMode,
asDataPreviewHeight, asDataPreviewEnabled, asLogHeight, asQueryhelperswidth, asStopOnErrorsInBatchMode,
asWrapLongLines, asCodeFolding, asDisplayBLOBsAsText, asSingleQueries, asMemoEditorWidth, asMemoEditorHeight, asMemoEditorMaximized,
asMemoEditorWrap, asMemoEditorHighlighter, asMemoEditorAlwaysFormatCode, asDelimiter, asSQLHelpWindowLeft, asSQLHelpWindowTop, asSQLHelpWindowWidth,
asSQLHelpWindowHeight, asSQLHelpPnlLeftWidth, asSQLHelpPnlRightTopHeight, asHost,
@@ -236,7 +236,6 @@ type
asSequalSuggestWindowWidth, asSequalSuggestWindowHeight, asSequalSuggestPrompt, asSequalSuggestRecentPrompts,
asReformatter, asReformatterNoDialog, asAlwaysGenerateFilter,
asGenerateDataNumRows, asGenerateDataNullAmount, asWebOnceAction,
asProjectManagerHeight,
asUnused);
TAppSetting = record
Name: String;
@@ -528,10 +527,10 @@ begin
Exit;
if FromLeft then begin
SetLength(Result, MaxLen);
Result[MaxLen] := '.';
Result[MaxLen] := '<EFBFBD>';
end else begin
Result := Copy(Result, Length(Result)-MaxLen, Length(Result));
Result := '' + Result;
Result := '<EFBFBD>' + Result;
end;
end;
@@ -854,7 +853,7 @@ end;
function RoundCommercial(e: Extended): Int64;
begin
// "Kaufmännisch runden"
// "Kaufm<EFBFBD>nnisch runden"
// In contrast to Delphi's Round() which rounds *.5 to the next even number
Result := Trunc(e);
if Frac(e) >= 0.5 then
@@ -3793,9 +3792,6 @@ begin
InitSetting(asDataPreviewEnabled, 'DataPreviewEnabled', 0, False);
InitSetting(asLogHeight, 'sqloutheight', 80);
InitSetting(asQueryhelperswidth, 'queryhelperswidth', 200);
InitSetting(asProjectManagerWidth, 'projectmanagerwidth', 260);
InitSetting(asProjectManagerVisible, 'projectmanagervisible', 0, True);
InitSetting(asProjectManagerTabActive, 'projectmanagertabactive', 0, False);
InitSetting(asStopOnErrorsInBatchMode, 'StopOnErrorsInBatchMode', 0, True);
InitSetting(asWrapLongLines, 'WrapLongLines', 0, False);
InitSetting(asCodeFolding, 'CodeFolding', 0, True);
@@ -4056,7 +4052,6 @@ begin
InitSetting(asCreateDbCollation, 'CreateDbCollation', 0, False, '');
InitSetting(asRealTrailingZeros, 'RealTrailingZeros', 1);
InitSetting(asWebOnceAction, 'WebOnceAction', 0, False, DateToStr(DateTimeNever));
InitSetting(asProjectManagerHeight, 'ProjectManagerHeight', 200);
// Initialization values
FRestoreTabsInitValue := ReadBool(asRestoreTabs);

View File

@@ -155,7 +155,6 @@ object MainForm: TMainForm
DragMode = dmAutomatic
DragType = dtVCL
Header.AutoSizeIndex = 0
Header.Height = 18
Header.Options = [hoAutoResize, hoColumnResize, hoDrag]
HintMode = hmTooltip
HotCursor = crHandPoint
@@ -191,7 +190,7 @@ object MainForm: TMainForm
item
Position = 0
Text = 'Name'
Width = 169
Width = 165
end
item
Alignment = taRightJustify
@@ -432,7 +431,6 @@ object MainForm: TMainForm
Height = 195
Align = alClient
Header.AutoSizeIndex = 0
Header.Height = 18
Header.Options = [hoColumnResize, hoDblClickResize, hoDrag, hoHotTrack, hoShowSortGlyphs, hoVisible, hoDisableAnimatedResize, hoAutoResizeInclCaption]
Header.PopupMenu = popupListHeader
Header.SortColumn = 0
@@ -516,7 +514,7 @@ object MainForm: TMainForm
Align = alClient
DragOperations = []
Header.AutoSizeIndex = 2
Header.Height = 18
Header.Height = 20
Header.Options = [hoAutoResize, hoColumnResize, hoDblClickResize, hoDrag, hoHotTrack, hoShowSortGlyphs, hoVisible, hoDisableAnimatedResize, hoAutoResizeInclCaption]
Header.PopupMenu = popupListHeader
Header.SortColumn = 0
@@ -559,7 +557,7 @@ object MainForm: TMainForm
item
Position = 2
Text = 'Global'
Width = 316
Width = 312
end>
end
end
@@ -574,7 +572,7 @@ object MainForm: TMainForm
Align = alClient
DragOperations = []
Header.AutoSizeIndex = 1
Header.Height = 18
Header.Height = 20
Header.Options = [hoAutoResize, hoColumnResize, hoDblClickResize, hoDrag, hoHotTrack, hoShowSortGlyphs, hoVisible, hoDisableAnimatedResize, hoAutoResizeInclCaption]
Header.PopupMenu = popupListHeader
Header.SortColumn = 0
@@ -611,7 +609,7 @@ object MainForm: TMainForm
Alignment = taRightJustify
Position = 1
Text = 'Value'
Width = 316
Width = 312
end
item
Alignment = taRightJustify
@@ -646,7 +644,7 @@ object MainForm: TMainForm
Height = 122
Align = alClient
Header.AutoSizeIndex = 7
Header.Height = 18
Header.Height = 20
Header.Options = [hoAutoResize, hoColumnResize, hoDblClickResize, hoDrag, hoHotTrack, hoShowSortGlyphs, hoVisible, hoDisableAnimatedResize, hoAutoResizeInclCaption]
Header.PopupMenu = popupListHeader
Header.SortColumn = 0
@@ -713,7 +711,7 @@ object MainForm: TMainForm
item
Position = 7
Text = 'Info'
Width = 186
Width = 182
end>
end
object pnlProcessViewBox: TPanel
@@ -795,7 +793,7 @@ object MainForm: TMainForm
Height = 195
Align = alClient
Header.AutoSizeIndex = 4
Header.Height = 18
Header.Height = 20
Header.Options = [hoAutoResize, hoColumnResize, hoDblClickResize, hoDrag, hoHotTrack, hoShowSortGlyphs, hoVisible, hoDisableAnimatedResize, hoAutoResizeInclCaption]
Header.PopupMenu = popupListHeader
Header.SortColumn = 1
@@ -850,7 +848,7 @@ object MainForm: TMainForm
item
Position = 4
Text = 'Percentage'
Width = 256
Width = 252
end>
end
end
@@ -867,7 +865,7 @@ object MainForm: TMainForm
Align = alClient
EditDelay = 500
Header.AutoSizeIndex = -1
Header.Height = 18
Header.Height = 20
Header.Options = [hoColumnResize, hoDblClickResize, hoDrag, hoHotTrack, hoShowSortGlyphs, hoVisible, hoDisableAnimatedResize, hoAutoResizeInclCaption]
Header.PopupMenu = popupListHeader
Header.SortColumn = 0
@@ -1029,8 +1027,6 @@ object MainForm: TMainForm
Caption = 'No data available for this item.'
Layout = tlCenter
WordWrap = True
ExplicitWidth = 165
ExplicitHeight = 14
end
object pnlDataTop: TPanel
Left = 0
@@ -1242,7 +1238,7 @@ object MainForm: TMainForm
AutoScrollDelay = 50
EditDelay = 0
Header.AutoSizeIndex = -1
Header.Height = 14
Header.Height = 20
Header.Images = VirtualImageListMain
Header.MainColumn = -1
Header.Options = [hoColumnResize, hoDblClickResize, hoDrag, hoHotTrack, hoOwnerDraw, hoShowHint, hoShowImages, hoVisible, hoDisableAnimatedResize, hoAutoResizeInclCaption]
@@ -1404,7 +1400,6 @@ object MainForm: TMainForm
DragMode = dmAutomatic
DragType = dtVCL
Header.AutoSizeIndex = 0
Header.Height = 18
Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs]
Images = VirtualImageListMain
IncrementalSearch = isVisibleOnly
@@ -1437,7 +1432,7 @@ object MainForm: TMainForm
item
Position = 0
Text = 'Main column'
Width = 85
Width = 64
end
item
Position = 1
@@ -1471,7 +1466,7 @@ object MainForm: TMainForm
AutoScrollDelay = 50
EditDelay = 0
Header.AutoSizeIndex = -1
Header.Height = 14
Header.Height = 20
Header.Images = VirtualImageListMain
Header.MainColumn = -1
Header.Options = [hoColumnResize, hoDblClickResize, hoDrag, hoHotTrack, hoOwnerDraw, hoShowHint, hoShowImages, hoDisableAnimatedResize, hoAutoResizeInclCaption]

View File

@@ -16,7 +16,7 @@ uses
Winapi.CommCtrl, System.Contnrs, System.Generics.Collections, System.Generics.Defaults, SynEditExport, SynExportHTML, SynExportRTF, System.Math, Vcl.ExtDlgs, System.Win.Registry, Vcl.AppEvnts,
routine_editor, trigger_editor, event_editor, preferences, EditVar, apphelpers, createdatabase, table_editor,
TableTools, View, Usermanager, SelectDBObject, connections, sqlhelp, dbconnection,
insertfiles, searchreplace, loaddata, copytable, csv_detector, Cromis.DirectoryWatch, SyncDB, gnugettext, projectmanager,
insertfiles, searchreplace, loaddata, copytable, csv_detector, Cromis.DirectoryWatch, SyncDB, gnugettext,
VirtualTrees, VirtualTrees.HeaderPopup, VirtualTrees.Utils, VirtualTrees.Types,
JumpList, System.Actions, System.UITypes, Vcl.Imaging.pngimage,
System.ImageList, Vcl.Styles.Utils.Forms,
@@ -102,10 +102,6 @@ type
pnlMemo: TPanel;
Memo: TSynMemo;
pnlHelpers: TPanel;
pcHelpers: TPageControl; // PageControl for Helpers and Project Manager
tabHelpers: TTabSheet; // Tab for the original Helpers
tabProjectManager: TTabSheet; // Tab for the Project Manager
projectManagerPanel: TProjectManagerPanel; // Project Manager Instance pro Tab
filterHelpers: TButtonedEdit;
treeHelpers: TVirtualStringTree;
MemoFileRenamed: Boolean;
@@ -507,7 +503,6 @@ type
pnlRight: TPanel;
btnCloseFilterPanel: TSpeedButton;
actFilterPanel: TAction;
actProjectManager: TAction;
actFindInVT1: TMenuItem;
TimerFilterVT: TTimer;
actFindTextOnServer: TAction;
@@ -800,7 +795,6 @@ type
actGenerateData: TAction;
Generatedata1: TMenuItem;
Generatedata2: TMenuItem;
menuProjectManager: TMenuItem;
actCopyGridNodes: TAction;
actCopyGridNodes1: TMenuItem;
actQueryTable: TAction;
@@ -1003,9 +997,6 @@ type
procedure actCloseQueryTabExecute(Sender: TObject);
procedure menuCloseQueryTabClick(Sender: TObject);
procedure CloseQueryTab(PageIndex: Integer);
function GetQueryTabBackupFilename(Tab: TQueryTab): String;
procedure SaveQueryTabBackupToIni(Tab: TQueryTab; const BackupFilename: String);
function ShouldCreateQueryTabBackup(Tab: TQueryTab): Boolean;
procedure CloseButtonOnMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure CloseButtonOnMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
function GetMainTabAt(X, Y: Integer): Integer;
@@ -1016,7 +1007,6 @@ type
procedure popupMainTabsPopup(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure actFilterPanelExecute(Sender: TObject);
procedure actProjectManagerExecute(Sender: TObject);
procedure TimerFilterVTTimer(Sender: TObject);
procedure PageControlMainContextPopup(Sender: TObject; MousePos: TPoint; var Handled: Boolean);
procedure menuQueryHelpersGenerateStatementClick(Sender: TObject);
@@ -1212,14 +1202,7 @@ type
var HintText: string);
procedure actCopyGridNodesExecute(Sender: TObject);
procedure actQueryTableExecute(Sender: TObject);
procedure ToggleProjectManager; // Toggle Project Manager Panel visibility
procedure ApplyProjectManagerStateToActiveTab; // Apply global Project Manager state to active tab
procedure HelpersPageControlChange(Sender: TObject); // Handle manual tab changes in helpers PageControl
procedure SynchronizeAllQueryTabsToGlobalState(ExcludeTab: TQueryTab = nil); // Sync all tabs to global state
private
// Project Manager global state
FProjectManagerTabActive: Boolean; // Global state: should Project Manager tab be active?
// Executable file details
FAppVerMajor: Integer;
FAppVerMinor: Integer;
@@ -1328,7 +1311,6 @@ type
procedure StoreTabs;
function RestoreTabs: Boolean;
procedure SetHintFontByControl(Control: TWinControl=nil);
private
public
QueryTabs: TQueryTabList;
ActiveObjectEditor: TDBObjectEditor;
@@ -1870,7 +1852,6 @@ begin
AppSettings.WriteBool(asDataPreviewEnabled, actDataPreview.Checked);
AppSettings.WriteInt(asLogHeight, SynMemoSQLLog.Height);
AppSettings.WriteBool(asFilterPanel, actFilterPanel.Checked);
AppSettings.WriteBool(asProjectManagerTabActive, FProjectManagerTabActive);
AppSettings.WriteBool(asWrapLongLines, actQueryWordWrap.Checked);
AppSettings.WriteBool(asCodeFolding, actCodeFolding.Checked);
AppSettings.WriteBool(asSingleQueries, actSingleQueries.Checked);
@@ -2027,54 +2008,6 @@ begin
QueryTab.Uid := TQueryTab.GenerateUid;
QueryTab.pnlMemo := pnlQueryMemo;
QueryTab.pnlHelpers := pnlQueryHelpers;
// Convert the first tab to use the new PageControl system
// Create PageControl for the existing pnlQueryHelpers
QueryTab.pcHelpers := TPageControl.Create(QueryTab.pnlHelpers);
QueryTab.pcHelpers.Name := 'pcHelpers0';
QueryTab.pcHelpers.Parent := QueryTab.pnlHelpers;
QueryTab.pcHelpers.Align := alClient;
QueryTab.pcHelpers.TabPosition := tpTop;
QueryTab.pcHelpers.Style := tsButtons;
QueryTab.pcHelpers.OnChange := HelpersPageControlChange; // Event handler for tab changes
// Create "Helpers" tab and move existing components
QueryTab.tabHelpers := TTabSheet.Create(QueryTab.pcHelpers);
QueryTab.tabHelpers.Name := 'tabHelpers0';
QueryTab.tabHelpers.Caption := 'Helpers';
QueryTab.tabHelpers.PageControl := QueryTab.pcHelpers;
// Move existing components to the Helpers tab
filterQueryHelpers.Parent := QueryTab.tabHelpers;
treeQueryHelpers.Parent := QueryTab.tabHelpers;
// Create "Project Manager" tab
QueryTab.tabProjectManager := TTabSheet.Create(QueryTab.pcHelpers);
QueryTab.tabProjectManager.Name := 'tabProjectManager0';
QueryTab.tabProjectManager.Caption := 'Project Manager';
QueryTab.tabProjectManager.PageControl := QueryTab.pcHelpers;
// Create Project Manager in the Project Manager tab
try
QueryTab.projectManagerPanel := TProjectManagerPanel.Create(QueryTab.tabProjectManager);
QueryTab.projectManagerPanel.Parent := QueryTab.tabProjectManager;
QueryTab.projectManagerPanel.Align := alClient;
LogSQL('Project Manager panel created successfully in main query tab', lcDebug);
except
on E: Exception do
begin
LogSQL('Error creating Project Manager in main query tab: ' + E.Message, lcError);
QueryTab.projectManagerPanel := nil;
end;
end;
// Set initial active tab based on global Project Manager state (loaded from settings)
FProjectManagerTabActive := AppSettings.ReadBool(asProjectManagerTabActive, '', False);
if FProjectManagerTabActive then
QueryTab.pcHelpers.ActivePage := QueryTab.tabProjectManager
else
QueryTab.pcHelpers.ActivePage := QueryTab.tabHelpers;
QueryTab.filterHelpers := filterQueryHelpers;
QueryTab.treeHelpers := treeQueryHelpers;
QueryTab.Memo := SynMemoQuery;
@@ -2255,33 +2188,6 @@ begin
LogSQL(f_('Theme: "%s"', [TStyleManager.ActiveStyle.Name]), lcDebug);
LogSQL(f_('Pixels per inch on current monitor: %d', [Monitor.PixelsPerInch]), lcDebug);
LogSQL(f_('Timezone offset: %d', [FTimeZoneOffset]), lcDebug);
// Create and configure Project Manager action for tab switching
try
if not Assigned(actProjectManager) then
begin
actProjectManager := TAction.Create(Self);
actProjectManager.ActionList := ActionList1;
actProjectManager.Category := 'View';
actProjectManager.Name := 'actProjectManager';
end;
if Assigned(actProjectManager) then
begin
actProjectManager.Caption := _('Project &Manager');
actProjectManager.Hint := _('Toggle Project Manager tab in query helpers (F11)');
actProjectManager.ShortCut := VK_F11;
actProjectManager.OnExecute := actProjectManagerExecute;
actProjectManager.Checked := FProjectManagerTabActive; // Reflect global state
LogSQL('Project Manager action configured for tab switching', lcDebug);
end;
except
on E: Exception do
begin
LogSQL('Error creating Project Manager action: ' + E.Message, lcError);
end;
end;
end;
@@ -6455,11 +6361,6 @@ begin
end;
end;
// Apply global Project Manager state to newly active query tab (always, not just on manual clicks)
if IsQueryTab(tab.PageIndex, True) then begin
ApplyProjectManagerStateToActiveTab;
end;
// Filter panel has one text per tab, which we need to update
UpdateFilterPanel(Sender);
@@ -11969,29 +11870,6 @@ begin
end;
procedure TMainForm.ToggleProjectManager;
begin
// Toggle global Project Manager state
FProjectManagerTabActive := not FProjectManagerTabActive;
{$IFDEF DEBUG}
LogSQL(Format('Toggled Project Manager state to: %s', [BoolToStr(FProjectManagerTabActive, True)]), lcDebug);
{$ENDIF}
// Apply the state to all existing query tabs
SynchronizeAllQueryTabsToGlobalState;
// Update action checked state
if Assigned(actProjectManager) then
actProjectManager.Checked := FProjectManagerTabActive;
// Show status message
if FProjectManagerTabActive then
ShowStatusMsg('Project Manager activated for all query tabs')
else
ShowStatusMsg('Helpers activated for all query tabs');
end;
procedure TMainForm.actCopyGridNodesExecute(Sender: TObject);
var
SenderControl: TComponent;
@@ -12575,32 +12453,10 @@ begin
else
QueryTab.pnlHelpers.Width := AppSettings.GetDefaultInt(asQueryhelperswidth);
// Create PageControl for Helpers and Project Manager tabs
QueryTab.pcHelpers := TPageControl.Create(QueryTab.pnlHelpers);
QueryTab.pcHelpers.Name := 'pcHelpers' + i.ToString;
QueryTab.pcHelpers.Parent := QueryTab.pnlHelpers;
QueryTab.pcHelpers.Align := alClient;
QueryTab.pcHelpers.TabPosition := tpTop;
QueryTab.pcHelpers.Style := tsButtons;
QueryTab.pcHelpers.OnChange := HelpersPageControlChange; // Event handler for tab changes
// Create "Helpers" tab
QueryTab.tabHelpers := TTabSheet.Create(QueryTab.pcHelpers);
QueryTab.tabHelpers.Name := 'tabHelpers' + i.ToString;
QueryTab.tabHelpers.Caption := 'Helpers';
QueryTab.tabHelpers.PageControl := QueryTab.pcHelpers;
// Create "Project Manager" tab
QueryTab.tabProjectManager := TTabSheet.Create(QueryTab.pcHelpers);
QueryTab.tabProjectManager.Name := 'tabProjectManager' + i.ToString;
QueryTab.tabProjectManager.Caption := 'Project Manager';
QueryTab.tabProjectManager.PageControl := QueryTab.pcHelpers;
// Move filter and tree to Helpers tab
QueryTab.filterHelpers := TButtonedEdit.Create(QueryTab.tabHelpers);
QueryTab.filterHelpers := TButtonedEdit.Create(QueryTab.pnlHelpers);
QueryTab.filterHelpers.Name := filterQueryHelpers.Name + i.ToString;
QueryTab.filterHelpers.Text := '';
QueryTab.filterHelpers.Parent := QueryTab.tabHelpers;
QueryTab.filterHelpers.Parent := QueryTab.pnlHelpers;
QueryTab.filterHelpers.Align := filterQueryHelpers.Align;
QueryTab.filterHelpers.TextHint := filterQueryHelpers.TextHint;
QueryTab.filterHelpers.Images := filterQueryHelpers.Images;
@@ -12611,9 +12467,9 @@ begin
QueryTab.filterHelpers.OnChange := filterQueryHelpers.OnChange;
QueryTab.filterHelpers.OnRightButtonClick := filterQueryHelpers.OnRightButtonClick;
QueryTab.treeHelpers := TVirtualStringTree.Create(QueryTab.tabHelpers);
QueryTab.treeHelpers := TVirtualStringTree.Create(QueryTab.pnlHelpers);
QueryTab.treeHelpers.Name := treeQueryHelpers.Name + i.ToString;
QueryTab.treeHelpers.Parent := QueryTab.tabHelpers;
QueryTab.treeHelpers.Parent := QueryTab.pnlHelpers;
QueryTab.treeHelpers.Align := treeQueryHelpers.Align;
QueryTab.treeHelpers.Left := treeQueryHelpers.Left;
QueryTab.treeHelpers.Constraints.MinWidth := treeQueryHelpers.Constraints.MinWidth;
@@ -12649,28 +12505,6 @@ begin
QueryTab.treeHelpers.TextMargin := treeQueryHelpers.TextMargin;
FixVT(QueryTab.treeHelpers);
// Create Project Manager in Project Manager tab
try
QueryTab.projectManagerPanel := TProjectManagerPanel.Create(QueryTab.tabProjectManager);
QueryTab.projectManagerPanel.Parent := QueryTab.tabProjectManager;
QueryTab.projectManagerPanel.Align := alClient;
// Initialize Project Manager if needed
LogSQL('Project Manager panel created successfully in query tab ' + i.ToString, lcDebug);
except
on E: Exception do
begin
LogSQL('Error creating Project Manager in query tab: ' + E.Message, lcError);
QueryTab.projectManagerPanel := nil;
end;
end;
// Set active tab based on global Project Manager state
if FProjectManagerTabActive then
QueryTab.pcHelpers.ActivePage := QueryTab.tabProjectManager
else
QueryTab.pcHelpers.ActivePage := QueryTab.tabHelpers;
QueryTab.spltQuery := TSplitter.Create(QueryTab.TabSheet);
QueryTab.spltQuery.Parent := QueryTab.TabSheet;
QueryTab.spltQuery.Top := spltQuery.Top; // Important to get it below the editor, not above. See #439
@@ -12868,8 +12702,6 @@ procedure TMainForm.CloseQueryTab(PageIndex: Integer);
var
NewPageIndex: Integer;
Grid: TVirtualStringTree;
QueryTab: TQueryTab;
BackupFilename: String;
begin
// Special case: the very first tab gets cleared but not closed
if PageIndex = tabQuery.PageIndex then begin
@@ -12878,30 +12710,6 @@ begin
end;
if not IsQueryTab(PageIndex, False) then
Exit;
// Get the query tab before we destroy it
QueryTab := QueryTabs.TabByControl(PageControlMain.Pages[PageIndex]);
// Create automatic backup if needed
if Assigned(QueryTab) and ShouldCreateQueryTabBackup(QueryTab) then
begin
try
BackupFilename := GetQueryTabBackupFilename(QueryTab);
if BackupFilename <> '' then
begin
QueryTab.Memo.Lines.SaveToFile(BackupFilename, TEncoding.UTF8);
SaveQueryTabBackupToIni(QueryTab, BackupFilename);
LogSQL(f_('Query tab content backed up to: %s', [BackupFilename]), lcInfo);
end;
except
on E: Exception do
begin
// Don't prevent tab closing due to backup errors, but log them
LogSQL(f_('Error creating query tab backup: %s', [E.Message]), lcError);
end;
end;
end;
// Cancel cell editor if active, preventing crash. See issue #2040
Grid := ActiveGrid;
if Assigned(Grid) and Grid.IsEditing then
@@ -12917,14 +12725,11 @@ begin
Dec(NewPageIndex);
// Avoid excessive flicker:
LockWindowUpdate(PageControlMain.Handle);
try
PageControlMain.Pages[PageIndex].Free;
QueryTabs.Delete(PageIndex-tabQuery.PageIndex);
PageControlMain.ActivePageIndex := NewPageIndex;
FixQueryTabCloseButtons;
finally
LockWindowUpdate(0);
end;
PageControlMain.Pages[PageIndex].Free;
QueryTabs.Delete(PageIndex-tabQuery.PageIndex);
PageControlMain.ActivePageIndex := NewPageIndex;
FixQueryTabCloseButtons;
LockWindowUpdate(0);
PageControlMain.OnChange(PageControlMain);
end;
@@ -13157,23 +12962,10 @@ var
begin
// Asynchronous timer for mousedown event on query tab close button
TimerCloseTabByButton.Enabled := False;
try
for i:=0 to QueryTabs.Count-1 do begin
if QueryTabs[i].CloseButton = FLastMouseDownCloseButton then begin
CloseQueryTab(QueryTabs[i].TabSheet.PageIndex);
break;
end;
end;
except
on E: Exception do
begin
// Stop timer on errors to prevent cascading issues
TimerCloseTabByButton.Enabled := False;
LogSQL(f_('Error in TimerCloseTabByButtonTimer: %s', [E.Message]), lcError);
// Reset the close button reference to prevent further issues
FLastMouseDownCloseButton := nil;
for i:=0 to QueryTabs.Count-1 do begin
if QueryTabs[i].CloseButton = FLastMouseDownCloseButton then begin
CloseQueryTab(QueryTabs[i].TabSheet.PageIndex);
break;
end;
end;
end;
@@ -13528,13 +13320,6 @@ begin
end;
procedure TMainForm.actProjectManagerExecute(Sender: TObject);
begin
// Toggle Project Manager tab visibility in current query tab
ToggleProjectManager;
end;
procedure TMainForm.UpdateFilterPanel(Sender: TObject);
var
tab: TTabSheet;
@@ -15292,68 +15077,11 @@ end;
destructor TQueryTab.Destroy;
begin
try
// Clean up Project Manager if it exists
if Assigned(projectManagerPanel) then
begin
try
projectManagerPanel.PrepareForShutdown;
except
// Ignore exceptions during shutdown preparation
end;
FreeAndNil(projectManagerPanel);
end;
// Clean up tab components safely
if Assigned(ResultTabs) then
begin
ResultTabs.Clear;
FreeAndNil(ResultTabs);
end;
// Free other components with error handling
try
if Assigned(DirectoryWatch) then
FreeAndNil(DirectoryWatch);
except
// Ignore DirectoryWatch cleanup errors
end;
if Assigned(ListBindParams) then
FreeAndNil(ListBindParams);
if Assigned(TimerLastChange) then
begin
TimerLastChange.Enabled := False;
FreeAndNil(TimerLastChange);
end;
if Assigned(TimerStatusUpdate) then
begin
TimerStatusUpdate.Enabled := False;
FreeAndNil(TimerStatusUpdate);
end;
except
on E: Exception do
begin
// Log but don't re-raise exceptions during destruction
{$IFDEF DEBUG}
OutputDebugString(PChar('Error in TQueryTab.Destroy: ' + E.Message));
{$ENDIF}
end;
end;
try
inherited Destroy;
except
on E: Exception do
begin
{$IFDEF DEBUG}
OutputDebugString(PChar('Error in TQueryTab.inherited Destroy: ' + E.Message));
{$ENDIF}
end;
end;
ResultTabs.Clear;
DirectoryWatch.Free;
ListBindParams.Free;
TimerLastChange.Free;
TimerStatusUpdate.Free;
end;
@@ -16018,201 +15746,5 @@ begin
Result := CompareText(Left.Name, Right.Name);
end;
{ TMainForm - Query Tab Backup Methods }
function TMainForm.GetQueryTabBackupFilename(Tab: TQueryTab): String;
var
BackupDir, Timestamp, SafeCaption: String;
begin
Result := '';
if not Assigned(Tab) then
Exit;
try
// Create backup directory if it doesn't exist
if AppSettings.PortableMode then
BackupDir := ExtractFilePath(Application.ExeName) + 'Backups\'
else
BackupDir := AppSettings.DirnameUserAppData + 'Backups\';
if not DirectoryExists(BackupDir) then
ForceDirectories(BackupDir);
// Create safe filename from tab caption
SafeCaption := Tab.TabSheet.Caption;
SafeCaption := StringReplace(SafeCaption, ' ', '_', [rfReplaceAll]);
SafeCaption := StringReplace(SafeCaption, '*', '', [rfReplaceAll]);
// Remove non-alphanumeric characters
SafeCaption := ReplaceRegExpr('[^\w]', SafeCaption, '_', True);
// Generate unique filename with timestamp
Timestamp := FormatDateTime('yyyymmdd_hhnnss', Now);
Result := BackupDir + 'query_' + SafeCaption + '_' + Timestamp + '_' + IntToStr(GetTickCount64) + '.sql';
except
on E: Exception do
begin
LogSQL(f_('Error generating backup filename: %s', [E.Message]), lcError);
Result := '';
end;
end;
end;
procedure TMainForm.SaveQueryTabBackupToIni(Tab: TQueryTab; const BackupFilename: String);
var
TabsIni: TIniFile;
TabSection: String;
begin
if not Assigned(Tab) or (BackupFilename = '') then
Exit;
try
TabsIni := InitTabsIniFile;
try
TabSection := 'Tab' + IntToStr(Tab.Number);
TabsIni.WriteString(TabSection, 'BackupFile', BackupFilename);
TabsIni.WriteString(TabSection, 'BackupTimestamp', DateTimeToStr(Now));
TabsIni.WriteString(TabSection, 'BackupReason', 'AutoBackupOnClose');
finally
TabsIni.Free;
end;
except
on E: Exception do
begin
LogSQL(f_('Error saving backup info to tabs.ini: %s', [E.Message]), lcError);
end;
end;
end;
function TMainForm.ShouldCreateQueryTabBackup(Tab: TQueryTab): Boolean;
var
QueryText: String;
begin
Result := False;
if not Assigned(Tab) or not Assigned(Tab.Memo) then
Exit;
// Only backup if content has been modified and is not empty
if not Tab.Memo.Modified then
Exit;
QueryText := Trim(Tab.Memo.Text);
if Length(QueryText) = 0 then
Exit;
// Don't backup very short queries (probably just typos)
if Length(QueryText) < 10 then
Exit;
// Don't backup if it's just whitespace or common single commands
if (Pos(#13#10, QueryText) = 0) and
(LowerCase(QueryText) = 'select') or
(LowerCase(QueryText) = 'show') or
(LowerCase(QueryText) = 'desc') or
(LowerCase(QueryText) = 'describe') then
Exit;
Result := True;
end;
procedure TMainForm.ApplyProjectManagerStateToActiveTab;
var
CurrentTab: TQueryTab;
begin
// Apply global Project Manager state to the currently active query tab
if QueryTabs.HasActiveTab then
begin
CurrentTab := QueryTabs.ActiveTab;
if Assigned(CurrentTab.pcHelpers) then
begin
// Only change if different from current state to avoid unnecessary updates
if FProjectManagerTabActive and (CurrentTab.pcHelpers.ActivePage <> CurrentTab.tabProjectManager) then
begin
CurrentTab.pcHelpers.ActivePage := CurrentTab.tabProjectManager;
{$IFDEF DEBUG}
LogSQL('Applied Project Manager tab state to query tab', lcDebug);
{$ENDIF}
end
else if not FProjectManagerTabActive and (CurrentTab.pcHelpers.ActivePage <> CurrentTab.tabHelpers) then
begin
CurrentTab.pcHelpers.ActivePage := CurrentTab.tabHelpers;
{$IFDEF DEBUG}
LogSQL('Applied Helpers tab state to query tab', lcDebug);
{$ENDIF}
end;
end;
end;
end;
procedure TMainForm.HelpersPageControlChange(Sender: TObject);
var
PageControl: TPageControl;
CurrentTab: TQueryTab;
begin
// Handle manual tab changes in helpers PageControl
PageControl := Sender as TPageControl;
// Find which query tab this PageControl belongs to
CurrentTab := nil;
if QueryTabs.HasActiveTab then
CurrentTab := QueryTabs.ActiveTab;
// Only update global state if this is the active query tab
if Assigned(CurrentTab) and (CurrentTab.pcHelpers = PageControl) then
begin
// Update global state based on which tab is now active
if PageControl.ActivePage = CurrentTab.tabProjectManager then
begin
if not FProjectManagerTabActive then
begin
FProjectManagerTabActive := True;
// Apply to all other query tabs
SynchronizeAllQueryTabsToGlobalState(CurrentTab);
// Update action state
if Assigned(actProjectManager) then
actProjectManager.Checked := True;
{$IFDEF DEBUG}
LogSQL('Project Manager manually activated - synchronized to all tabs', lcDebug);
{$ENDIF}
end;
end
else if PageControl.ActivePage = CurrentTab.tabHelpers then
begin
if FProjectManagerTabActive then
begin
FProjectManagerTabActive := False;
// Apply to all other query tabs
SynchronizeAllQueryTabsToGlobalState(CurrentTab);
// Update action state
if Assigned(actProjectManager) then
actProjectManager.Checked := False;
{$IFDEF DEBUG}
LogSQL('Helpers manually activated - synchronized to all tabs', lcDebug);
{$ENDIF}
end;
end;
end;
end;
procedure TMainForm.SynchronizeAllQueryTabsToGlobalState(ExcludeTab: TQueryTab);
var
i: Integer;
Tab: TQueryTab;
begin
// Apply global state to all query tabs except the one that triggered the change
for i := 0 to QueryTabs.Count - 1 do
begin
Tab := QueryTabs[i];
if (Tab <> ExcludeTab) and Assigned(Tab.pcHelpers) then
begin
if FProjectManagerTabActive then
Tab.pcHelpers.ActivePage := Tab.tabProjectManager
else
Tab.pcHelpers.ActivePage := Tab.tabHelpers;
end;
end;
end;
end.

View File

@@ -1,152 +0,0 @@
object ProjectManagerPanel: TProjectManagerPanel
Left = 0
Top = 0
Width = 250
Height = 400
Caption = 'Project Manager'
TabOrder = 0
object pnlHeader: TPanel
Left = 0
Top = 0
Width = 250
Height = 30
Align = alTop
BevelOuter = bvNone
TabOrder = 0
object lblProjectTitle: TLabel
Left = 8
Top = 8
Width = 43
Height = 13
Caption = 'Projects'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = [fsBold]
ParentFont = False
end
object ToolBarProjects: TToolBar
Left = 72
Top = 0
Width = 178
Height = 30
Align = alRight
AutoSize = True
ButtonHeight = 21
ButtonWidth = 22
Caption = 'ToolBarProjects'
Images = MainForm.VirtualImageListMain
ShowHint = True
TabOrder = 0
object btnAddProject: TToolButton
Left = 0
Top = 0
Hint = 'Add project folder'
ImageIndex = 45
ImageName = 'icons8-add'
OnClick = menuAddProjectClick
end
object btnRemoveProject: TToolButton
Left = 22
Top = 0
Hint = 'Remove project folder'
ImageIndex = 46
ImageName = 'icons8-delete-button'
OnClick = menuRemoveProjectClick
end
object btnSeparator1: TToolButton
Left = 44
Top = 0
Width = 8
Style = tbsSeparator
end
object btnRefreshProject: TToolButton
Left = 52
Top = 0
Hint = 'Refresh projects'
ImageIndex = 16
ImageName = 'icons8-refresh'
OnClick = menuRefreshProjectClick
end
end
end
object pnlTree: TPanel
Left = 0
Top = 30
Width = 250
Height = 370
Align = alClient
BevelOuter = bvNone
TabOrder = 1
object TreeProjects: TVirtualStringTree
Left = 0
Top = 0
Width = 250
Height = 370
Align = alClient
Header.AutoSizeIndex = 0
Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible]
Images = MainForm.VirtualImageListMain
PopupMenu = PopupProjects
TabOrder = 0
TreeOptions.MiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, toEditOnClick]
TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toThemeAware, toUseBlendedImages, toShowRoot]
TreeOptions.SelectionOptions = [toFullRowSelect]
OnBeforeExpansion = TreeProjectsBeforeExpansion
OnCollapsed = TreeProjectsCollapsed
OnDblClick = TreeProjectsDblClick
OnExpanded = TreeProjectsExpanded
OnGetImageIndex = TreeProjectsGetImageIndex
OnGetNodeDataSize = TreeProjectsGetNodeDataSize
OnGetText = TreeProjectsGetText
OnInitChildren = TreeProjectsInitChildren
OnInitNode = TreeProjectsInitNode
Columns = <
item
Position = 0
Text = 'Name'
Width = 246
end>
end
end
object PopupProjects: TPopupMenu
OnPopup = PopupProjectsPopup
Left = 104
Top = 168
object menuAddProject: TMenuItem
Caption = 'Add Project Folder...'
ImageIndex = 45
ImageName = 'icons8-add'
OnClick = menuAddProjectClick
end
object menuRemoveProject: TMenuItem
Caption = 'Remove Project'
ImageIndex = 46
ImageName = 'icons8-delete-button'
OnClick = menuRemoveProjectClick
end
object menuSeparator1: TMenuItem
Caption = '-'
end
object menuOpenFile: TMenuItem
Caption = 'Open File'
Default = True
ImageIndex = 8
ImageName = 'icons8-opened-folder'
OnClick = menuOpenFileClick
end
object menuOpenInExplorer: TMenuItem
Caption = 'Open in Explorer'
ImageIndex = 71
ImageName = 'icons8-folder'
OnClick = menuOpenInExplorerClick
end
object menuRefreshProject: TMenuItem
Caption = 'Refresh'
ImageIndex = 16
ImageName = 'icons8-refresh'
OnClick = menuRefreshProjectClick
end
end
end

View File

File diff suppressed because it is too large Load Diff