Skip to content

Commit

Permalink
Refactor TreeStateHelper
Browse files Browse the repository at this point in the history
- Refactored and optimised
- Set drilldown to home when closing the tree
  • Loading branch information
Phillipus committed Jan 25, 2025
1 parent 2a6d09d commit 99535c1
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ public void doCreatePartControl(Composite parent) {
// Drill down
fDrillDownAdapter = new DrillDownAdapter(fTreeViewer);

// Set drill down to home when disposing parent (not when the tree is disposed as the content provider is null at that point)
parent.addDisposeListener(e -> {
setDrillDownHome();
});

// Listen to Double-click and press Return Action
fTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
@Override
Expand Down Expand Up @@ -209,22 +214,14 @@ public void init(IViewSite site, IMemento memento) throws PartInitException {

@Override
public void saveState(IMemento memento) {
// saveState() is called periodically by Eclipse so only do this when closing the workbench
// saveState() is called periodically by Eclipse when auto-saving the workbench so only reset the drill-down when closing the Workbench.
// This period is set in org.eclipse.ui.internal.IPreferenceConstants#WORKBENCH_SAVE_INTERVAL and the default is 5 minutes.
if(PlatformUI.getWorkbench().isClosing()) {
// Reset drill-down
if(fDrillDownAdapter.canGoHome()) {
try {
getViewer().getControl().setRedraw(false);
fDrillDownAdapter.goHome();
}
finally {
getViewer().getControl().setRedraw(true);
}
}

// Save expanded tree state
TreeStateHelper.INSTANCE.saveStateOnApplicationClose(fTreeViewer, memento);
setDrillDownHome();
}

// Save expanded tree state
TreeStateHelper.INSTANCE.saveStateToMemento(fTreeViewer, memento);
}

/**
Expand Down Expand Up @@ -672,13 +669,26 @@ private boolean hasExpandableNodes(Object object) {
}

/**
* Reset drilldown if it is showing a deleted object
* Check the tree's input is not a deleted object. If it is, set drilldown to home
*/
private void checkDrillDown() {
if(fTreeViewer.getInput() instanceof IArchimateModelObject && ((IArchimateModelObject)fTreeViewer.getInput()).getArchimateModel() == null) {
if(fDrillDownAdapter.canGoHome()) {
private void checkDrillDownHasValidInput() {
if(fTreeViewer.getInput() instanceof IArchimateModelObject modelObject && modelObject.getArchimateModel() == null) {
setDrillDownHome();
}
}

/**
* Set the drill down to home
*/
private void setDrillDownHome() {
if(fDrillDownAdapter.canGoHome()) { // Important check!
try {
getViewer().getControl().setRedraw(false);
fDrillDownAdapter.goHome();
}
finally {
getViewer().getControl().setRedraw(true);
}
}
}

Expand Down Expand Up @@ -785,9 +795,7 @@ public void propertyChange(PropertyChangeEvent evt) {
// New Model created or opened
if(propertyName == IEditorModelManager.PROPERTY_MODEL_CREATED || propertyName == IEditorModelManager.PROPERTY_MODEL_OPENED) {
// Go Home
if(fDrillDownAdapter.canGoHome()) {
fDrillDownAdapter.goHome();
}
setDrillDownHome();

getViewer().refreshTreePreservingExpandedNodes();

Expand All @@ -810,7 +818,7 @@ else if(propertyName == IEditorModelManager.PROPERTY_MODEL_REMOVED) {
TreeModelCutAndPaste.INSTANCE.clear();

// Check Drilldown state
checkDrillDown();
checkDrillDownHasValidInput();

// Search Filter soft reset
if(fSearchWidget != null && !fSearchWidget.isDisposed()) {
Expand Down Expand Up @@ -894,7 +902,7 @@ protected void eCoreChanged(Notification msg) {
}
else {
super.eCoreChanged(msg);
checkDrillDown();
checkDrillDownHasValidInput();
}
}

Expand Down Expand Up @@ -949,7 +957,7 @@ private void refreshFromNotifications(List<Notification> notifications) {
getViewer().getControl().setRedraw(true);
}

checkDrillDown();
checkDrillDownHasValidInput();
}

// =================================================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@

import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.ui.IMemento;

import com.archimatetool.editor.model.IEditorModelManager;
import com.archimatetool.model.IArchimateModel;
import com.archimatetool.model.IArchimateModelObject;
import com.archimatetool.model.IIdentifier;
import com.archimatetool.model.util.ArchimateModelUtils;
import com.archimatetool.model.util.Logger;



Expand All @@ -29,137 +29,127 @@
*
* @author Phillip Beauvoir
*/
@SuppressWarnings("nls")
public class TreeStateHelper {

public static TreeStateHelper INSTANCE = new TreeStateHelper();

private static final String ELEMENT_SEP_CHAR = " "; //$NON-NLS-1$
private static final String ELEMENT_SEP_CHAR = " ";

private static final String MEMENTO_EXPANDED = "expanded"; //$NON-NLS-1$
private static final String MEMENTO_MODEL = "model"; //$NON-NLS-1$
private static final String MEMENTO_FILE = "file"; //$NON-NLS-1$
private static final String MEMENTO_ELEMENTS = "elements"; //$NON-NLS-1$
private static final String MEMENTO_EXPANDED = "expanded";
private static final String MEMENTO_MODEL = "model";
private static final String MEMENTO_FILE = "file";
private static final String MEMENTO_ELEMENTS = "elements";

// Expanded tree elements or element ids for the session
private List<Object> fExpandedElements = new ArrayList<Object>();
// Expanded tree elements
private List<Object> expandedElements;

/**
* Flag to show we have restored from Memento first time open
*/
private boolean fRestoredFromMemento = false;
// State has been restored from memento the first time the Tree is created
private boolean restoredFromMemento;

private class FileMap {
File file;
String[] elements;
}
private TreeStateHelper() {}

/**
* Set the Memento on Application Open
* @param memento
* Set the Memento when the tree is first created.
* We store expanded elements now, as the tree has not yet been created.
* We will use the expanded elements later in restoreExpandedTreeElements.
* This is called from {@link TreeModelView#init(org.eclipse.ui.IViewSite, IMemento)}, but we only want to do this once.
*/
void setMemento(IMemento memento) {
// This is also called when the TreeView is opened, but we only want to do this once
if(memento == null || fRestoredFromMemento) {
if(restoredFromMemento || memento == null) {
return;
}

// Store expanded elements as ids now, as the tree has not been created yet
for(IMemento expandedMem : memento.getChildren(MEMENTO_EXPANDED)) {
for(IMemento elementMem : expandedMem.getChildren(MEMENTO_MODEL)) {
String file = elementMem.getString(MEMENTO_FILE);
String elements = elementMem.getString(MEMENTO_ELEMENTS);
if(file != null && elements != null) {
FileMap fm = new FileMap();
fm.file = new File(file);
fm.elements = elements.split(ELEMENT_SEP_CHAR);
fExpandedElements.add(fm);
restoredFromMemento = true;

IMemento expandedMem = memento.getChild(MEMENTO_EXPANDED);
if(expandedMem != null) {
expandedElements = new ArrayList<>();

try {
for(IMemento elementMem : expandedMem.getChildren(MEMENTO_MODEL)) {
String filePath = elementMem.getString(MEMENTO_FILE);
String elements = elementMem.getString(MEMENTO_ELEMENTS);
if(filePath != null && elements != null) {
File file = new File(filePath);
for(IArchimateModel model : IEditorModelManager.INSTANCE.getModels()) {
// Get model from file
if(file.equals(model.getFile())) {
// Get object ID map
Map<String, EObject> objectMap = ArchimateModelUtils.getObjectIDMap(model);
// Get objects from IDs
for(String id : elements.split(ELEMENT_SEP_CHAR)) {
EObject object = objectMap.get(id);
if(object != null) {
expandedElements.add(object);
}
}
break;
}
}
}
}
}
// We don't want to fail at this point
catch(Exception ex) {
Logger.logError("Error restoring tree state", ex);
}
}

fRestoredFromMemento = true;
}

/**
* Restore expanded elements on TreeView creation
* Restore expanded elements on TreeView part creation.
* This is called from {@link TreeModelView#doCreatePartControl(org.eclipse.swt.widgets.Composite)
*/
void restoreExpandedTreeElements(TreeViewer viewer) {
// Store expanded tree elements if View is closed
viewer.getTree().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
fExpandedElements.clear();
for(Object element : viewer.getVisibleExpandedElements()) {
fExpandedElements.add(element);
}
// Store expanded tree elements if the TreeViewer is closed so they can be restored when it's re-opened
// We could restore from the memento but this is more efficient and the drill-down is reset when closing the Tree.
viewer.getTree().addDisposeListener(e -> {
expandedElements = new ArrayList<>();
for(Object element : viewer.getVisibleExpandedElements()) {
expandedElements.add(element);
}
});

for(Object o : fExpandedElements) {
// Actual object
if(o instanceof IArchimateModelObject) {
viewer.expandToLevel(o, 1);
}

// String ids
if(o instanceof FileMap) {
try {
File file = ((FileMap)o).file;
String[] elements = ((FileMap)o).elements;
for(IArchimateModel model : IEditorModelManager.INSTANCE.getModels()) {
if(file.equals(model.getFile())) {
for(String id : elements) {
EObject element = ArchimateModelUtils.getObjectByID(model, id);
if(element != null) {
viewer.expandToLevel(element, 1);
}
}
break; // found model
}
}
}
catch(Exception ex) {
// We don't want to fail just for some stupid string operation
ex.printStackTrace();
}

if(expandedElements != null) {
for(Object element : expandedElements) {
viewer.expandToLevel(element, 1);
}
expandedElements = null;
}

// Allow the elements to be garbage collected
fExpandedElements.clear();
}

/**
* Save expanded state of tree elements on Application close
* @param memento
* Save expanded state of tree elements to a memento.
* This is called from {@link TreeModelView#saveState(IMemento)}
*/
void saveStateOnApplicationClose(TreeViewer viewer, IMemento memento) {
Hashtable<File, String> map = new Hashtable<File, String>();
void saveStateToMemento(TreeViewer viewer, IMemento memento) {
Map<File, String> map = new HashMap<>();

IMemento expandedMem = memento.createChild(MEMENTO_EXPANDED);

for(Object element : viewer.getVisibleExpandedElements()) {
if(element instanceof IIdentifier && element instanceof IArchimateModelObject) {
// Only store if saved in a file
File file = ((IArchimateModelObject)element).getArchimateModel().getFile();
for(Object object : viewer.getVisibleExpandedElements()) {
if(object instanceof IArchimateModelObject modelObject) {
// Only store if model has been saved to file
File file = modelObject.getArchimateModel().getFile();
if(file != null) {
String id = ((IIdentifier)element).getId();
// Create a string of character separated IDs
String id = modelObject.getId();
String string = map.get(file);
if(string == null) {
string = id;
}
else {
string += ELEMENT_SEP_CHAR + id;
}
string = string == null ? id : string + ELEMENT_SEP_CHAR + id;
map.put(file, string);
}
}
}

for(File file : map.keySet()) {
if(map.isEmpty()) {
return;
}

IMemento expandedMem = memento.createChild(MEMENTO_EXPANDED);

for(Entry<File, String> entry : map.entrySet()) {
IMemento elementMem = expandedMem.createChild(MEMENTO_MODEL);
elementMem.putString(MEMENTO_FILE, file.getAbsolutePath());
elementMem.putString(MEMENTO_ELEMENTS, map.get(file));
elementMem.putString(MEMENTO_FILE, entry.getKey().getAbsolutePath());
elementMem.putString(MEMENTO_ELEMENTS, entry.getValue());
}
}
}

0 comments on commit 99535c1

Please sign in to comment.