Skip to content

Commit c123ccd

Browse files
committed
fix: (backport of c976d17) 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 aa0f350 commit c123ccd

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
@@ -10398,8 +10398,66 @@ procedure TMainForm.DBtreePaintText(Sender: TBaseVirtualTree; const
1039810398
procedure TMainForm.RefreshTree(FocusNewObject: TDBObject=nil);
1039910399
var
1040010400
DBNode: PVirtualNode;
10401-
OnlyDBNode, Expanded: Boolean;
10401+
OnlyDBNode, Expanded, ObjectFocused, CreatingObject: Boolean;
1040210402
SessNode: PVirtualNode;
10403+
ExpandedNodes: TStringList;
10404+
RefreshedObj: TDBObject;
10405+
10406+
function GroupKey(DBObj: PDBObject): String;
10407+
begin
10408+
Result := DBObj.Database + #9 + IntToStr(Integer(DBObj.GroupType));
10409+
end;
10410+
10411+
procedure StoreExpandedState(DBNode: PVirtualNode);
10412+
var
10413+
GroupNode: PVirtualNode;
10414+
DBObj: PDBObject;
10415+
begin
10416+
// Remember expanded state of a database node and its group nodes, as ResetNode collapses them
10417+
if (not (vsInitialized in DBNode.States)) or (not DBtree.Expanded[DBNode]) then
10418+
Exit;
10419+
DBObj := DBtree.GetNodeData(DBNode);
10420+
ExpandedNodes.Add(DBObj.Database);
10421+
GroupNode := DBtree.GetFirstChildNoInit(DBNode);
10422+
while Assigned(GroupNode) do begin
10423+
if (vsInitialized in GroupNode.States) and DBtree.Expanded[GroupNode] then begin
10424+
DBObj := DBtree.GetNodeData(GroupNode);
10425+
if DBObj.NodeType = lntGroup then
10426+
ExpandedNodes.Add(GroupKey(DBObj));
10427+
end;
10428+
GroupNode := DBtree.GetNextSiblingNoInit(GroupNode);
10429+
end;
10430+
end;
10431+
10432+
procedure RestoreExpandedState(DBNode: PVirtualNode);
10433+
var
10434+
GroupNode: PVirtualNode;
10435+
DBObj: PDBObject;
10436+
begin
10437+
// Node was probably just reset, so its data needs to be initialized again
10438+
DBtree.ValidateNode(DBNode, False);
10439+
DBObj := DBtree.GetNodeData(DBNode);
10440+
if ExpandedNodes.IndexOf(DBObj.Database) = -1 then
10441+
Exit;
10442+
try
10443+
DBtree.Expanded[DBNode] := True;
10444+
if not actGroupObjects.Checked then
10445+
Exit;
10446+
GroupNode := DBtree.GetFirstChild(DBNode);
10447+
while Assigned(GroupNode) do begin
10448+
DBObj := DBtree.GetNodeData(GroupNode);
10449+
if (DBObj.NodeType = lntGroup) and (ExpandedNodes.IndexOf(GroupKey(DBObj)) > -1) then
10450+
DBtree.Expanded[GroupNode] := True;
10451+
GroupNode := DBtree.GetNextSibling(GroupNode);
10452+
end;
10453+
except
10454+
// Expanding reads the objects of that database. Leave the node collapsed if that fails,
10455+
// so the rest of the refresh and the reselection of the focused node still happens.
10456+
on E:EDbError do
10457+
LogSQL('RefreshTree: '+E.Message, lcError);
10458+
end;
10459+
end;
10460+
1040310461
begin
1040410462
// This refreshes exactly one session node and all its db and table nodes.
1040510463
// Also, tries to focus the previous focused object, if present.
@@ -10417,27 +10475,60 @@ procedure TMainForm.RefreshTree(FocusNewObject: TDBObject=nil);
1041710475
// ReInit tree population
1041810476
FTreeRefreshInProgress := True;
1041910477
SelectNode(DBtree, nil);
10478+
ExpandedNodes := TStringList.Create;
10479+
ExpandedNodes.CaseSensitive := True;
1042010480
try
1042110481
if not OnlyDBNode then begin
10482+
SessNode := GetRootNode(DBtree, FocusNewObject.Connection);
10483+
if Assigned(SessNode) then begin
10484+
DBNode := DBtree.GetFirstChildNoInit(SessNode);
10485+
while Assigned(DBNode) do begin
10486+
StoreExpandedState(DBNode);
10487+
DBNode := DBtree.GetNextSiblingNoInit(DBNode);
10488+
end;
10489+
end;
1042210490
FocusNewObject.Connection.ClearAllDbObjects;
1042310491
FocusNewObject.Connection.RefreshAllDatabases;
10424-
SessNode := GetRootNode(DBtree, FocusNewObject.Connection);
1042510492
if Assigned(SessNode) then begin
1042610493
Expanded := DBtree.Expanded[SessNode];
1042710494
DBtree.ResetNode(SessNode);
1042810495
DBtree.Expanded[SessNode] := Expanded;
10496+
if Expanded then begin
10497+
DBNode := DBtree.GetFirstChild(SessNode);
10498+
while Assigned(DBNode) do begin
10499+
RestoreExpandedState(DBNode);
10500+
DBNode := DBtree.GetNextSibling(DBNode);
10501+
end;
10502+
end;
1042910503
end;
1043010504
end else begin
10431-
FocusNewObject.Connection.ClearDbObjects(FocusNewObject.Database);
1043210505
DBNode := FindDbNode(DBtree, FocusNewObject.Connection, FocusNewObject.Database);
1043310506
if Assigned(DBNode) then
10507+
StoreExpandedState(DBNode);
10508+
FocusNewObject.Connection.ClearDbObjects(FocusNewObject.Database);
10509+
if Assigned(DBNode) then begin
1043410510
DBtree.ResetNode(DBNode);
10511+
RestoreExpandedState(DBNode);
10512+
end;
1043510513
end;
1043610514

1043710515
// Reselect active or new database if present. Could have been deleted or renamed.
1043810516
try
10439-
if FocusNewObject.NodeType in [lntTable..lntEvent] then
10517+
if FocusNewObject.NodeType in [lntTable..lntEvent] then begin
10518+
// A just created object has no schema yet, while the server put it into the default one,
10519+
// e.g. "dbo" or "public". Take over the schema, so the node gets found in the tree.
10520+
if FocusNewObject.Schema.IsEmpty and (not FocusNewObject.Name.IsEmpty) then begin
10521+
for RefreshedObj in FocusNewObject.Connection.GetDBObjects(FocusNewObject.Database) do begin
10522+
if (RefreshedObj.NodeType = FocusNewObject.NodeType)
10523+
and (not RefreshedObj.Schema.IsEmpty)
10524+
and FocusNewObject.Connection.IdentifierEquals(RefreshedObj.Name, FocusNewObject.Name) then begin
10525+
FocusNewObject.Schema := RefreshedObj.Schema;
10526+
Break;
10527+
end;
10528+
end;
10529+
end;
1044010530
ActiveDBObj := FocusNewObject;
10531+
end;
1044110532
if not Assigned(DBtree.FocusedNode) then
1044210533
SetActiveDatabase(FocusNewObject.Database, FocusNewObject.Connection);
1044310534
if not Assigned(DBtree.FocusedNode) then
@@ -10449,6 +10540,22 @@ procedure TMainForm.RefreshTree(FocusNewObject: TDBObject=nil);
1044910540

1045010541
finally
1045110542
FTreeRefreshInProgress := False;
10543+
ExpandedNodes.Free;
10544+
// Tabs were left untouched during refresh. Hide editor and data tab if the previously focused
10545+
// object is gone, e.g. after it was dropped. Keep the editor of a new, yet unsaved object.
10546+
ObjectFocused := (FActiveDbObj <> nil) and (FActiveDbObj.NodeType in [lntTable..lntEvent, lntColumn]);
10547+
CreatingObject := Assigned(ActiveObjectEditor) and ActiveObjectEditor.DBObject.Name.IsEmpty;
10548+
if (not ObjectFocused) and (not CreatingObject) then begin
10549+
if (PageControlMain.ActivePage = tabEditor) or (PageControlMain.ActivePage = tabData) then begin
10550+
if (FActiveDbObj <> nil) and (FActiveDbObj.NodeType <> lntNone) then begin
10551+
tabDatabase.TabVisible := True;
10552+
SetMainTab(tabDatabase);
10553+
end else
10554+
SetMainTab(tabHost);
10555+
end;
10556+
tabEditor.TabVisible := False;
10557+
tabData.TabVisible := False;
10558+
end;
1045210559
// Tree node filtering needs a hit in special cases, e.g. after a db was dropped
1045310560
if editDatabaseFilter.Text <> '' then
1045410561
editDatabaseFilter.OnChange(editDatabaseFilter);

0 commit comments

Comments
 (0)