Skip to content

Commit c976d17

Browse files
fix: keep database nodes expanded on tree refresh, and hide table tabs after dropping the object
Remember expanded database and group nodes before resetting them in RefreshTree, and expand them again afterwards. Hide editor and data tab after the refresh if no table-like object is focused any more. Does not touch the FTreeRefreshInProgress timing, which broke saving tables in #2436. Take over the schema of a just created object, so it gets reselected in the tree on MS SQL and PostgreSQL. Closes #16 Closes #2431
1 parent 905eb8f commit c976d17

1 file changed

Lines changed: 111 additions & 4 deletions

File tree

‎source/main.pas‎

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10337,8 +10337,66 @@ procedure TMainForm.DBtreePaintText(Sender: TBaseVirtualTree; const
1033710337
procedure TMainForm.RefreshTree(FocusNewObject: TDBObject=nil);
1033810338
var
1033910339
DBNode: PVirtualNode;
10340-
OnlyDBNode, Expanded: Boolean;
10340+
OnlyDBNode, Expanded, ObjectFocused, CreatingObject: Boolean;
1034110341
SessNode: PVirtualNode;
10342+
ExpandedNodes: TStringList;
10343+
RefreshedObj: TDBObject;
10344+
10345+
function GroupKey(DBObj: PDBObject): String;
10346+
begin
10347+
Result := DBObj.Database + #9 + IntToStr(Integer(DBObj.GroupType));
10348+
end;
10349+
10350+
procedure StoreExpandedState(DBNode: PVirtualNode);
10351+
var
10352+
GroupNode: PVirtualNode;
10353+
DBObj: PDBObject;
10354+
begin
10355+
// Remember expanded state of a database node and its group nodes, as ResetNode collapses them
10356+
if (not (vsInitialized in DBNode.States)) or (not DBtree.Expanded[DBNode]) then
10357+
Exit;
10358+
DBObj := DBtree.GetNodeData(DBNode);
10359+
ExpandedNodes.Add(DBObj.Database);
10360+
GroupNode := DBtree.GetFirstChildNoInit(DBNode);
10361+
while Assigned(GroupNode) do begin
10362+
if (vsInitialized in GroupNode.States) and DBtree.Expanded[GroupNode] then begin
10363+
DBObj := DBtree.GetNodeData(GroupNode);
10364+
if DBObj.NodeType = lntGroup then
10365+
ExpandedNodes.Add(GroupKey(DBObj));
10366+
end;
10367+
GroupNode := DBtree.GetNextSiblingNoInit(GroupNode);
10368+
end;
10369+
end;
10370+
10371+
procedure RestoreExpandedState(DBNode: PVirtualNode);
10372+
var
10373+
GroupNode: PVirtualNode;
10374+
DBObj: PDBObject;
10375+
begin
10376+
// Node was probably just reset, so its data needs to be initialized again
10377+
DBtree.ValidateNode(DBNode, False);
10378+
DBObj := DBtree.GetNodeData(DBNode);
10379+
if ExpandedNodes.IndexOf(DBObj.Database) = -1 then
10380+
Exit;
10381+
try
10382+
DBtree.Expanded[DBNode] := True;
10383+
if not actGroupObjects.Checked then
10384+
Exit;
10385+
GroupNode := DBtree.GetFirstChild(DBNode);
10386+
while Assigned(GroupNode) do begin
10387+
DBObj := DBtree.GetNodeData(GroupNode);
10388+
if (DBObj.NodeType = lntGroup) and (ExpandedNodes.IndexOf(GroupKey(DBObj)) > -1) then
10389+
DBtree.Expanded[GroupNode] := True;
10390+
GroupNode := DBtree.GetNextSibling(GroupNode);
10391+
end;
10392+
except
10393+
// Expanding reads the objects of that database. Leave the node collapsed if that fails,
10394+
// so the rest of the refresh and the reselection of the focused node still happens.
10395+
on E:EDbError do
10396+
LogSQL('RefreshTree: '+E.Message, lcError);
10397+
end;
10398+
end;
10399+
1034210400
begin
1034310401
// This refreshes exactly one session node and all its db and table nodes.
1034410402
// Also, tries to focus the previous focused object, if present.
@@ -10356,27 +10414,60 @@ procedure TMainForm.RefreshTree(FocusNewObject: TDBObject=nil);
1035610414
// ReInit tree population
1035710415
FTreeRefreshInProgress := True;
1035810416
SelectNode(DBtree, nil);
10417+
ExpandedNodes := TStringList.Create;
10418+
ExpandedNodes.CaseSensitive := True;
1035910419
try
1036010420
if not OnlyDBNode then begin
10421+
SessNode := GetRootNode(DBtree, FocusNewObject.Connection);
10422+
if Assigned(SessNode) then begin
10423+
DBNode := DBtree.GetFirstChildNoInit(SessNode);
10424+
while Assigned(DBNode) do begin
10425+
StoreExpandedState(DBNode);
10426+
DBNode := DBtree.GetNextSiblingNoInit(DBNode);
10427+
end;
10428+
end;
1036110429
FocusNewObject.Connection.ClearAllDbObjects;
1036210430
FocusNewObject.Connection.RefreshAllDatabases;
10363-
SessNode := GetRootNode(DBtree, FocusNewObject.Connection);
1036410431
if Assigned(SessNode) then begin
1036510432
Expanded := DBtree.Expanded[SessNode];
1036610433
DBtree.ResetNode(SessNode);
1036710434
DBtree.Expanded[SessNode] := Expanded;
10435+
if Expanded then begin
10436+
DBNode := DBtree.GetFirstChild(SessNode);
10437+
while Assigned(DBNode) do begin
10438+
RestoreExpandedState(DBNode);
10439+
DBNode := DBtree.GetNextSibling(DBNode);
10440+
end;
10441+
end;
1036810442
end;
1036910443
end else begin
10370-
FocusNewObject.Connection.ClearDbObjects(FocusNewObject.Database);
1037110444
DBNode := FindDbNode(DBtree, FocusNewObject.Connection, FocusNewObject.Database);
1037210445
if Assigned(DBNode) then
10446+
StoreExpandedState(DBNode);
10447+
FocusNewObject.Connection.ClearDbObjects(FocusNewObject.Database);
10448+
if Assigned(DBNode) then begin
1037310449
DBtree.ResetNode(DBNode);
10450+
RestoreExpandedState(DBNode);
10451+
end;
1037410452
end;
1037510453

1037610454
// Reselect active or new database if present. Could have been deleted or renamed.
1037710455
try
10378-
if FocusNewObject.NodeType in [lntTable..lntEvent] then
10456+
if FocusNewObject.NodeType in [lntTable..lntEvent] then begin
10457+
// A just created object has no schema yet, while the server put it into the default one,
10458+
// e.g. "dbo" or "public". Take over the schema, so the node gets found in the tree.
10459+
if FocusNewObject.Schema.IsEmpty and (not FocusNewObject.Name.IsEmpty) then begin
10460+
for RefreshedObj in FocusNewObject.Connection.GetDBObjects(FocusNewObject.Database) do begin
10461+
if (RefreshedObj.NodeType = FocusNewObject.NodeType)
10462+
and (not RefreshedObj.Schema.IsEmpty)
10463+
and FocusNewObject.Connection.IdentifierEquals(RefreshedObj.Name, FocusNewObject.Name) then begin
10464+
FocusNewObject.Schema := RefreshedObj.Schema;
10465+
Break;
10466+
end;
10467+
end;
10468+
end;
1037910469
ActiveDBObj := FocusNewObject;
10470+
end;
1038010471
if not Assigned(DBtree.FocusedNode) then
1038110472
SetActiveDatabase(FocusNewObject.Database, FocusNewObject.Connection);
1038210473
if not Assigned(DBtree.FocusedNode) then
@@ -10388,6 +10479,22 @@ procedure TMainForm.RefreshTree(FocusNewObject: TDBObject=nil);
1038810479

1038910480
finally
1039010481
FTreeRefreshInProgress := False;
10482+
ExpandedNodes.Free;
10483+
// Tabs were left untouched during refresh. Hide editor and data tab if the previously focused
10484+
// object is gone, e.g. after it was dropped. Keep the editor of a new, yet unsaved object.
10485+
ObjectFocused := (FActiveDbObj <> nil) and (FActiveDbObj.NodeType in [lntTable..lntEvent, lntColumn]);
10486+
CreatingObject := Assigned(ActiveObjectEditor) and ActiveObjectEditor.DBObject.Name.IsEmpty;
10487+
if (not ObjectFocused) and (not CreatingObject) then begin
10488+
if (PageControlMain.ActivePage = tabEditor) or (PageControlMain.ActivePage = tabData) then begin
10489+
if (FActiveDbObj <> nil) and (FActiveDbObj.NodeType <> lntNone) then begin
10490+
tabDatabase.TabVisible := True;
10491+
SetMainTab(tabDatabase);
10492+
end else
10493+
SetMainTab(tabHost);
10494+
end;
10495+
tabEditor.TabVisible := False;
10496+
tabData.TabVisible := False;
10497+
end;
1039110498
// Tree node filtering needs a hit in special cases, e.g. after a db was dropped
1039210499
if editDatabaseFilter.Text <> '' then
1039310500
editDatabaseFilter.OnChange(editDatabaseFilter);

0 commit comments

Comments
 (0)