You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2026/04/25 21:38:08 UTC

(cayenne) 11/15: Modeler cleanup

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git

commit 7aafab8b0b79d74ffde26c180f78fd2a3a3246e3
Author: Andrus Adamchik <an...@objectstyle.com>
AuthorDate: Sat Apr 25 08:01:13 2026 -0400

    Modeler cleanup
---
 .../cayenne/modeler/pref/ProjectSelectionMemo.java | 691 +++++++++++++++++++++
 .../modeler/pref/ProjectStatePreferences.java      |  19 +
 .../pref/project/AttributeDisplayEventType.java    | 106 ----
 .../pref/project/DataMapDisplayEventType.java      |  68 --
 .../pref/project/DataNodeDisplayEventType.java     |  55 --
 .../modeler/pref/project/DisplayEventType.java     |  80 ---
 .../modeler/pref/project/DisplayEventTypes.java    | 198 ------
 .../pref/project/DomainDisplayEventType.java       |  49 --
 .../EmbeddableAttributeDisplayEventType.java       |  98 ---
 .../pref/project/EmbeddableDisplayEventType.java   |  62 --
 .../pref/project/EntityDisplayEventType.java       |  98 ---
 .../project/MultipleObjectsDisplayEventType.java   | 166 -----
 .../pref/project/ProcedureDisplayEventType.java    |  63 --
 .../ProcedureParameterDisplayEventType.java        |  93 ---
 .../pref/project/QueryDisplayEventType.java        |  62 --
 .../pref/project/RelationshipDisplayEventType.java | 106 ----
 .../modeler/ui/project/ProjectController.java      |  39 +-
 17 files changed, 712 insertions(+), 1341 deletions(-)

diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/ProjectSelectionMemo.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/ProjectSelectionMemo.java
new file mode 100644
index 000000000..ad52bcd50
--- /dev/null
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/ProjectSelectionMemo.java
@@ -0,0 +1,691 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.modeler.pref;
+
+import org.apache.cayenne.configuration.ConfigurationNode;
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.configuration.DataNodeDescriptor;
+import org.apache.cayenne.map.Attribute;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.Embeddable;
+import org.apache.cayenne.map.EmbeddableAttribute;
+import org.apache.cayenne.map.Entity;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.Procedure;
+import org.apache.cayenne.map.ProcedureParameter;
+import org.apache.cayenne.map.QueryDescriptor;
+import org.apache.cayenne.map.Relationship;
+import org.apache.cayenne.modeler.event.display.AttributeDisplayEvent;
+import org.apache.cayenne.modeler.event.display.DataMapDisplayEvent;
+import org.apache.cayenne.modeler.event.display.DataNodeDisplayEvent;
+import org.apache.cayenne.modeler.event.display.DomainDisplayEvent;
+import org.apache.cayenne.modeler.event.display.EmbeddableAttributeDisplayEvent;
+import org.apache.cayenne.modeler.event.display.EmbeddableDisplayEvent;
+import org.apache.cayenne.modeler.event.display.EntityDisplayEvent;
+import org.apache.cayenne.modeler.event.display.MultipleObjectsDisplayEvent;
+import org.apache.cayenne.modeler.event.display.ProcedureDisplayEvent;
+import org.apache.cayenne.modeler.event.display.ProcedureParameterDisplayEvent;
+import org.apache.cayenne.modeler.event.display.QueryDisplayEvent;
+import org.apache.cayenne.modeler.event.display.RelationshipDisplayEvent;
+import org.apache.cayenne.modeler.ui.project.ProjectController;
+import org.apache.cayenne.util.CayenneMapEntry;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Saves and restores the modeler's last project selection through {@link ProjectStatePreferences}.
+ * Replaces the parallel {@code DisplayEventType} hierarchy by deriving the selection kind directly
+ * from the controller state and dispatching to the right {@code controller.display*()} method.
+ */
+final class ProjectSelectionMemo {
+
+    enum Kind {
+        domain, dataNode, dataMap,
+        objEntity, dbEntity,
+        objAttributes, dbAttributes,
+        objRelationships, dbRelationships,
+        embeddable, embeddableAttributes,
+        procedure, procedureParameters,
+        query, multipleObjects
+    }
+
+    private ProjectSelectionMemo() {
+    }
+
+    static void save(ProjectController controller, ProjectStatePreferences prefs) {
+        Kind kind = leafKind(controller);
+        if (kind == null) {
+            return;
+        }
+
+        prefs.setEvent(kind.name());
+
+        switch (kind) {
+            case domain:
+                saveDomainPath(controller, prefs);
+                break;
+            case dataNode:
+                saveNodePath(controller, prefs);
+                break;
+            case dataMap:
+                saveDataMapPath(controller, prefs);
+                break;
+            case objEntity:
+            case dbEntity:
+                saveEntityPath(controller, prefs);
+                break;
+            case objAttributes:
+                saveEntityPath(controller, prefs);
+                prefs.setObjAttrs(joinNames(controller.getSelectedObjAttributes()));
+                break;
+            case dbAttributes:
+                saveEntityPath(controller, prefs);
+                prefs.setDbAttrs(joinNames(controller.getSelectedDbAttributes()));
+                break;
+            case objRelationships:
+                saveEntityPath(controller, prefs);
+                prefs.setObjRels(joinNames(controller.getSelectedObjRelationships()));
+                break;
+            case dbRelationships:
+                saveEntityPath(controller, prefs);
+                prefs.setDbRels(joinNames(controller.getSelectedDbRelationships()));
+                break;
+            case embeddable:
+                saveEmbeddablePath(controller, prefs);
+                break;
+            case embeddableAttributes:
+                saveEmbeddablePath(controller, prefs);
+                prefs.setEmbAttrs(joinEmbeddableAttributeNames(controller.getSelectedEmbeddableAttributes()));
+                break;
+            case procedure:
+                saveProcedurePath(controller, prefs);
+                break;
+            case procedureParameters:
+                saveProcedurePath(controller, prefs);
+                prefs.setProcedureParams(joinNames(controller.getSelectedProcedureParameters()));
+                break;
+            case query:
+                saveQueryPath(controller, prefs);
+                break;
+            case multipleObjects:
+                saveMultipleObjects(controller, prefs);
+                break;
+        }
+    }
+
+    static void restore(ProjectController controller, ProjectStatePreferences prefs) {
+        Kind kind = parseKind(prefs.getEvent());
+        if (kind == null) {
+            return;
+        }
+
+        DataChannelDescriptor domain = (DataChannelDescriptor) controller.getProject().getRootNode();
+        if (!domain.getName().equals(prefs.getDomain())) {
+            return;
+        }
+
+        switch (kind) {
+            case domain:
+                controller.displayDomain(new DomainDisplayEvent(ProjectSelectionMemo.class, domain));
+                break;
+            case dataNode:
+                restoreDataNode(controller, domain, prefs);
+                break;
+            case dataMap:
+                restoreDataMap(controller, domain, prefs);
+                break;
+            case objEntity:
+            case dbEntity:
+                restoreEntity(controller, domain, prefs);
+                break;
+            case objAttributes:
+            case dbAttributes:
+                restoreAttributes(controller, domain, prefs);
+                break;
+            case objRelationships:
+            case dbRelationships:
+                restoreRelationships(controller, domain, prefs);
+                break;
+            case embeddable:
+                restoreEmbeddable(controller, domain, prefs);
+                break;
+            case embeddableAttributes:
+                restoreEmbeddableAttributes(controller, domain, prefs);
+                break;
+            case procedure:
+                restoreProcedure(controller, domain, prefs);
+                break;
+            case procedureParameters:
+                restoreProcedureParameters(controller, domain, prefs);
+                break;
+            case query:
+                restoreQuery(controller, domain, prefs);
+                break;
+            case multipleObjects:
+                restoreMultipleObjects(controller, domain, prefs);
+                break;
+        }
+    }
+
+    private static Kind leafKind(ProjectController c) {
+        if (c.getSelectedObjAttributes() != null && c.getSelectedObjAttributes().length > 0) {
+            return Kind.objAttributes;
+        }
+        if (c.getSelectedDbAttributes() != null && c.getSelectedDbAttributes().length > 0) {
+            return Kind.dbAttributes;
+        }
+        if (c.getSelectedObjRelationships() != null && c.getSelectedObjRelationships().length > 0) {
+            return Kind.objRelationships;
+        }
+        if (c.getSelectedDbRelationships() != null && c.getSelectedDbRelationships().length > 0) {
+            return Kind.dbRelationships;
+        }
+        if (c.getSelectedEmbeddableAttributes() != null && c.getSelectedEmbeddableAttributes().length > 0) {
+            return Kind.embeddableAttributes;
+        }
+        if (c.getSelectedProcedureParameters() != null && c.getSelectedProcedureParameters().length > 0) {
+            return Kind.procedureParameters;
+        }
+        if (c.getSelectedObjEntity() != null) {
+            return Kind.objEntity;
+        }
+        if (c.getSelectedDbEntity() != null) {
+            return Kind.dbEntity;
+        }
+        if (c.getSelectedEmbeddable() != null) {
+            return Kind.embeddable;
+        }
+        if (c.getSelectedProcedure() != null) {
+            return Kind.procedure;
+        }
+        if (c.getSelectedQuery() != null) {
+            return Kind.query;
+        }
+        if (c.getSelectedDataMap() != null) {
+            return Kind.dataMap;
+        }
+        if (c.getSelectedDataNode() != null) {
+            return Kind.dataNode;
+        }
+        if (c.getSelectedPaths() != null && c.getSelectedPaths().length > 0) {
+            return Kind.multipleObjects;
+        }
+        if (c.getSelectedDataDomain() != null) {
+            return Kind.domain;
+        }
+        return null;
+    }
+
+    private static Kind parseKind(String name) {
+        if (name == null || name.isEmpty()) {
+            return null;
+        }
+        try {
+            return Kind.valueOf(name);
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+
+    // -------------------- save helpers --------------------
+
+    private static void saveDomainPath(ProjectController controller, ProjectStatePreferences prefs) {
+        prefs.setDomain(controller.getSelectedDataDomain().getName());
+    }
+
+    private static void saveNodePath(ProjectController controller, ProjectStatePreferences prefs) {
+        prefs.setDomain(controller.getSelectedDataDomain().getName());
+        prefs.setNode(controller.getSelectedDataNode().getName());
+    }
+
+    private static void saveDataMapPath(ProjectController controller, ProjectStatePreferences prefs) {
+        DataChannelDescriptor domain = controller.getSelectedDataDomain();
+        if (domain == null) {
+            return;
+        }
+        prefs.setDomain(domain.getName());
+        DataNodeDescriptor node = controller.getSelectedDataNode();
+        prefs.setNode(node != null ? node.getName() : "");
+        DataMap dataMap = controller.getSelectedDataMap();
+        if (dataMap != null) {
+            prefs.setDataMap(dataMap.getName());
+        }
+    }
+
+    private static void saveEntityPath(ProjectController controller, ProjectStatePreferences prefs) {
+        prefs.setDomain(controller.getSelectedDataDomain().getName());
+        DataNodeDescriptor node = controller.getSelectedDataNode();
+        prefs.setNode(node != null ? node.getName() : "");
+        prefs.setDataMap(controller.getSelectedDataMap().getName());
+
+        ObjEntity objEntity = controller.getSelectedObjEntity();
+        DbEntity dbEntity = controller.getSelectedDbEntity();
+        if (objEntity != null) {
+            prefs.setObjEntity(objEntity.getName());
+            prefs.setDbEntity(null);
+        } else if (dbEntity != null) {
+            prefs.setDbEntity(dbEntity.getName());
+            prefs.setObjEntity(null);
+        }
+    }
+
+    private static void saveEmbeddablePath(ProjectController controller, ProjectStatePreferences prefs) {
+        prefs.setDomain(controller.getSelectedDataDomain().getName());
+        prefs.setDataMap(controller.getSelectedDataMap().getName());
+        prefs.setEmbeddable(controller.getSelectedEmbeddable().getClassName());
+    }
+
+    private static void saveProcedurePath(ProjectController controller, ProjectStatePreferences prefs) {
+        prefs.setDomain(controller.getSelectedDataDomain().getName());
+        prefs.setDataMap(controller.getSelectedDataMap().getName());
+        prefs.setProcedure(controller.getSelectedProcedure().getName());
+    }
+
+    private static void saveQueryPath(ProjectController controller, ProjectStatePreferences prefs) {
+        prefs.setDomain(controller.getSelectedDataDomain().getName());
+        prefs.setDataMap(controller.getSelectedDataMap().getName());
+        prefs.setQuery(controller.getSelectedQuery().getName());
+    }
+
+    private static void saveMultipleObjects(ProjectController controller, ProjectStatePreferences prefs) {
+        prefs.setParentObject(nameOf(controller.getSelectedParentPath()));
+
+        ConfigurationNode[] paths = controller.getSelectedPaths();
+        if (paths == null) {
+            prefs.setMultipleObjects("");
+            return;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (ConfigurationNode object : paths) {
+            String objectName = nameOf(object);
+            if (!objectName.isEmpty()) {
+                sb.append(objectName).append(",");
+            }
+        }
+        prefs.setMultipleObjects(sb.toString());
+    }
+
+    // -------------------- restore helpers --------------------
+
+    private static void restoreDataNode(ProjectController controller, DataChannelDescriptor domain,
+                                        ProjectStatePreferences prefs) {
+        DataNodeDescriptor node = domain.getNodeDescriptor(prefs.getNode());
+        if (node == null) {
+            return;
+        }
+        controller.displayDataNode(new DataNodeDisplayEvent(ProjectSelectionMemo.class, domain, node));
+    }
+
+    private static void restoreDataMap(ProjectController controller, DataChannelDescriptor domain,
+                                       ProjectStatePreferences prefs) {
+        DataMap dataMap = domain.getDataMap(prefs.getDataMap());
+        if (dataMap == null) {
+            return;
+        }
+        DataNodeDescriptor node = domain.getNodeDescriptor(prefs.getNode());
+        controller.displayDataMap(new DataMapDisplayEvent(ProjectSelectionMemo.class, dataMap, domain, node));
+    }
+
+    private static void restoreEntity(ProjectController controller, DataChannelDescriptor domain,
+                                      ProjectStatePreferences prefs) {
+        DataMap dataMap = domain.getDataMap(prefs.getDataMap());
+        if (dataMap == null) {
+            return;
+        }
+        Entity<?, ?, ?> entity = lookupEntity(dataMap, prefs);
+        if (entity == null) {
+            return;
+        }
+        DataNodeDescriptor node = domain.getNodeDescriptor(prefs.getNode());
+        EntityDisplayEvent event = new EntityDisplayEvent(ProjectSelectionMemo.class, entity, dataMap, node, domain);
+        if (entity instanceof ObjEntity) {
+            controller.displayObjEntity(event);
+        } else if (entity instanceof DbEntity) {
+            controller.displayDbEntity(event);
+        }
+    }
+
+    private static void restoreAttributes(ProjectController controller, DataChannelDescriptor domain,
+                                          ProjectStatePreferences prefs) {
+        DataMap dataMap = domain.getDataMap(prefs.getDataMap());
+        if (dataMap == null) {
+            return;
+        }
+        Entity<?, ?, ?> entity = lookupEntity(dataMap, prefs);
+        if (entity == null) {
+            return;
+        }
+        DataNodeDescriptor node = domain.getNodeDescriptor(prefs.getNode());
+
+        EntityDisplayEvent entityEvent = new EntityDisplayEvent(ProjectSelectionMemo.class, entity, dataMap, node, domain);
+        Attribute<?, ?, ?>[] attrs = lookupEntityAttributes(entity, prefs);
+        AttributeDisplayEvent attrEvent = new AttributeDisplayEvent(ProjectSelectionMemo.class, attrs, entity, dataMap, domain);
+
+        if (entity instanceof ObjEntity) {
+            controller.displayObjEntity(entityEvent);
+            controller.displayObjAttribute(attrEvent);
+        } else if (entity instanceof DbEntity) {
+            controller.displayDbEntity(entityEvent);
+            controller.displayDbAttribute(attrEvent);
+        }
+    }
+
+    private static void restoreRelationships(ProjectController controller, DataChannelDescriptor domain,
+                                             ProjectStatePreferences prefs) {
+        DataMap dataMap = domain.getDataMap(prefs.getDataMap());
+        if (dataMap == null) {
+            return;
+        }
+        Entity<?, ?, ?> entity = lookupEntity(dataMap, prefs);
+        if (entity == null) {
+            return;
+        }
+        DataNodeDescriptor node = domain.getNodeDescriptor(prefs.getNode());
+
+        EntityDisplayEvent entityEvent = new EntityDisplayEvent(ProjectSelectionMemo.class, entity, dataMap, node, domain);
+        Relationship<?, ?, ?>[] rels = lookupEntityRelationships(entity, prefs);
+        RelationshipDisplayEvent relEvent = new RelationshipDisplayEvent(ProjectSelectionMemo.class, rels, entity, dataMap, domain);
+
+        if (entity instanceof ObjEntity) {
+            controller.displayObjEntity(entityEvent);
+            controller.displayObjRelationship(relEvent);
+        } else if (entity instanceof DbEntity) {
+            controller.displayDbEntity(entityEvent);
+            controller.displayDbRelationship(relEvent);
+        }
+    }
+
+    private static void restoreEmbeddable(ProjectController controller, DataChannelDescriptor domain,
+                                          ProjectStatePreferences prefs) {
+        DataMap dataMap = domain.getDataMap(prefs.getDataMap());
+        if (dataMap == null) {
+            return;
+        }
+        Embeddable embeddable = dataMap.getEmbeddable(prefs.getEmbeddable());
+        if (embeddable == null) {
+            return;
+        }
+        controller.displayEmbeddable(new EmbeddableDisplayEvent(ProjectSelectionMemo.class, embeddable, dataMap, domain));
+    }
+
+    private static void restoreEmbeddableAttributes(ProjectController controller, DataChannelDescriptor domain,
+                                                    ProjectStatePreferences prefs) {
+        DataMap dataMap = domain.getDataMap(prefs.getDataMap());
+        if (dataMap == null) {
+            return;
+        }
+        Embeddable embeddable = dataMap.getEmbeddable(prefs.getEmbeddable());
+        if (embeddable == null) {
+            return;
+        }
+        controller.displayEmbeddable(new EmbeddableDisplayEvent(ProjectSelectionMemo.class, embeddable, dataMap, domain));
+
+        EmbeddableAttribute[] attrs = lookupEmbeddableAttributes(embeddable, prefs);
+        controller.displayEmbeddableAttribute(new EmbeddableAttributeDisplayEvent(
+                ProjectSelectionMemo.class, embeddable, attrs, dataMap, domain));
+    }
+
+    private static void restoreProcedure(ProjectController controller, DataChannelDescriptor domain,
+                                         ProjectStatePreferences prefs) {
+        DataMap dataMap = domain.getDataMap(prefs.getDataMap());
+        if (dataMap == null) {
+            return;
+        }
+        Procedure procedure = dataMap.getProcedure(prefs.getProcedure());
+        if (procedure == null) {
+            return;
+        }
+        controller.displayProcedure(new ProcedureDisplayEvent(ProjectSelectionMemo.class, procedure, dataMap, domain));
+    }
+
+    private static void restoreProcedureParameters(ProjectController controller, DataChannelDescriptor domain,
+                                                   ProjectStatePreferences prefs) {
+        DataMap dataMap = domain.getDataMap(prefs.getDataMap());
+        if (dataMap == null) {
+            return;
+        }
+        Procedure procedure = dataMap.getProcedure(prefs.getProcedure());
+        if (procedure == null) {
+            return;
+        }
+        controller.displayProcedure(new ProcedureDisplayEvent(ProjectSelectionMemo.class, procedure, dataMap, domain));
+
+        ProcedureParameter[] params = lookupProcedureParameters(procedure, prefs);
+        controller.displayProcedureParameter(new ProcedureParameterDisplayEvent(
+                ProjectSelectionMemo.class, params, procedure, dataMap, domain));
+    }
+
+    private static void restoreQuery(ProjectController controller, DataChannelDescriptor domain,
+                                     ProjectStatePreferences prefs) {
+        DataMap dataMap = domain.getDataMap(prefs.getDataMap());
+        if (dataMap == null) {
+            return;
+        }
+        QueryDescriptor query = dataMap.getQueryDescriptor(prefs.getQuery());
+        if (query == null) {
+            return;
+        }
+        controller.displayQuery(new QueryDisplayEvent(ProjectSelectionMemo.class, query, dataMap, domain));
+    }
+
+    private static void restoreMultipleObjects(ProjectController controller, DataChannelDescriptor domain,
+                                               ProjectStatePreferences prefs) {
+        String parentName = prefs.getParentObject();
+        ConfigurationNode parent;
+        ConfigurationNode[] objects;
+
+        DataMap parentMap = domain.getDataMap(parentName);
+        DataNodeDescriptor parentNode = domain.getNodeDescriptor(parentName);
+
+        if (parentMap != null) {
+            parent = parentMap;
+            objects = lookupMultipleObjects(parentMap, prefs);
+        } else if (parentNode != null) {
+            parent = parentNode;
+            objects = lookupMultipleObjects(parentNode, prefs);
+        } else {
+            parent = domain;
+            objects = lookupMultipleObjects(domain, prefs);
+        }
+
+        controller.displayMultipleObjects(new MultipleObjectsDisplayEvent(ProjectSelectionMemo.class, objects, parent));
+    }
+
+    // -------------------- name lookups --------------------
+
+    private static Entity<?, ?, ?> lookupEntity(DataMap dataMap, ProjectStatePreferences prefs) {
+        return !prefs.getObjEntity().isEmpty()
+                ? dataMap.getObjEntity(prefs.getObjEntity())
+                : dataMap.getDbEntity(prefs.getDbEntity());
+    }
+
+    private static Attribute<?, ?, ?>[] lookupEntityAttributes(Entity<?, ?, ?> entity, ProjectStatePreferences prefs) {
+        String stored = (entity instanceof ObjEntity) ? prefs.getObjAttrs() : prefs.getDbAttrs();
+        List<Attribute<?, ?, ?>> result = new ArrayList<>();
+        for (String name : stored.split(",")) {
+            Attribute<?, ?, ?> attr = entity.getAttribute(name);
+            if (attr != null) {
+                result.add(attr);
+            }
+        }
+        return result.toArray(new Attribute[0]);
+    }
+
+    private static Relationship<?, ?, ?>[] lookupEntityRelationships(Entity<?, ?, ?> entity, ProjectStatePreferences prefs) {
+        String stored = (entity instanceof ObjEntity) ? prefs.getObjRels() : prefs.getDbRels();
+        List<Relationship<?, ?, ?>> result = new ArrayList<>();
+        for (String name : stored.split(",")) {
+            Relationship<?, ?, ?> rel = entity.getRelationship(name);
+            if (rel != null) {
+                result.add(rel);
+            }
+        }
+        return result.toArray(new Relationship[0]);
+    }
+
+    private static EmbeddableAttribute[] lookupEmbeddableAttributes(Embeddable embeddable, ProjectStatePreferences prefs) {
+        String stored = prefs.getEmbAttrs();
+        List<EmbeddableAttribute> result = new ArrayList<>();
+        if (stored.isEmpty()) {
+            return new EmbeddableAttribute[0];
+        }
+        for (String name : stored.split(",")) {
+            EmbeddableAttribute attr = embeddable.getAttribute(name);
+            if (attr != null) {
+                result.add(attr);
+            }
+        }
+        return result.toArray(new EmbeddableAttribute[0]);
+    }
+
+    private static ProcedureParameter[] lookupProcedureParameters(Procedure procedure, ProjectStatePreferences prefs) {
+        String stored = prefs.getProcedureParams();
+        List<ProcedureParameter> result = new ArrayList<>();
+        if (stored.isEmpty()) {
+            return new ProcedureParameter[0];
+        }
+        for (String name : stored.split(",")) {
+            for (ProcedureParameter p : procedure.getCallParameters()) {
+                if (p.getName().equals(name)) {
+                    result.add(p);
+                }
+            }
+        }
+        return result.toArray(new ProcedureParameter[0]);
+    }
+
+    private static ConfigurationNode[] lookupMultipleObjects(DataChannelDescriptor domain, ProjectStatePreferences prefs) {
+        String stored = prefs.getMultipleObjects();
+        if (stored.isEmpty()) {
+            return new ConfigurationNode[0];
+        }
+        List<ConfigurationNode> result = new ArrayList<>();
+        for (String name : stored.split(",")) {
+            ConfigurationNode node = findInDomain(domain, name);
+            if (node != null) {
+                result.add(node);
+            }
+        }
+        return result.toArray(new ConfigurationNode[0]);
+    }
+
+    private static ConfigurationNode[] lookupMultipleObjects(DataNodeDescriptor node, ProjectStatePreferences prefs) {
+        String stored = prefs.getMultipleObjects();
+        if (stored.isEmpty()) {
+            return new ConfigurationNode[0];
+        }
+        List<ConfigurationNode> result = new ArrayList<>();
+        for (String name : stored.split(",")) {
+            if (node.getDataMapNames().contains(name)) {
+                result.add(node.getDataChannelDescriptor().getDataMap(name));
+            }
+        }
+        return result.toArray(new ConfigurationNode[0]);
+    }
+
+    private static ConfigurationNode[] lookupMultipleObjects(DataMap dataMap, ProjectStatePreferences prefs) {
+        String stored = prefs.getMultipleObjects();
+        if (stored.isEmpty()) {
+            return new ConfigurationNode[0];
+        }
+        List<ConfigurationNode> result = new ArrayList<>();
+        for (String name : stored.split(",")) {
+            if (dataMap.getObjEntity(name) != null) {
+                result.add(dataMap.getObjEntity(name));
+            } else if (dataMap.getDbEntity(name) != null) {
+                result.add(dataMap.getDbEntity(name));
+            } else if (dataMap.getEmbeddable(name) != null) {
+                result.add(dataMap.getEmbeddable(name));
+            } else if (dataMap.getProcedure(name) != null) {
+                result.add(dataMap.getProcedure(name));
+            } else if (dataMap.getQueryDescriptor(name) != null) {
+                result.add(dataMap.getQueryDescriptor(name));
+            }
+        }
+        return result.toArray(new ConfigurationNode[0]);
+    }
+
+    private static ConfigurationNode findInDomain(DataChannelDescriptor domain, String name) {
+        if (domain.getName().equals(name)) {
+            return domain;
+        }
+        for (DataNodeDescriptor n : domain.getNodeDescriptors()) {
+            if (n.getName().equals(name)) {
+                return n;
+            }
+        }
+        for (DataMap m : domain.getDataMaps()) {
+            if (m.getName().equals(name)) {
+                return m;
+            }
+        }
+        return null;
+    }
+
+    // -------------------- name formatting --------------------
+
+    private static String joinNames(CayenneMapEntry[] entries) {
+        if (entries == null) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (CayenneMapEntry entry : entries) {
+            if (entry == null) {
+                continue;
+            }
+            sb.append(entry.getName()).append(",");
+        }
+        return sb.toString();
+    }
+
+    private static String joinEmbeddableAttributeNames(EmbeddableAttribute[] entries) {
+        if (entries == null) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (EmbeddableAttribute entry : entries) {
+            if (entry == null) {
+                continue;
+            }
+            sb.append(entry.getName()).append(",");
+        }
+        return sb.toString();
+    }
+
+    private static String nameOf(ConfigurationNode object) {
+        if (object instanceof CayenneMapEntry) {
+            return ((CayenneMapEntry) object).getName();
+        } else if (object instanceof DataChannelDescriptor) {
+            return ((DataChannelDescriptor) object).getName();
+        } else if (object instanceof DataNodeDescriptor) {
+            return ((DataNodeDescriptor) object).getName();
+        } else if (object instanceof DataMap) {
+            return ((DataMap) object).getName();
+        } else if (object instanceof Embeddable) {
+            return ((Embeddable) object).getClassName();
+        } else if (object instanceof QueryDescriptor) {
+            return ((QueryDescriptor) object).getName();
+        }
+        return "";
+    }
+}
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/ProjectStatePreferences.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/ProjectStatePreferences.java
index 6d7a6e56c..3a33a9230 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/ProjectStatePreferences.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/ProjectStatePreferences.java
@@ -19,6 +19,9 @@
 
 package org.apache.cayenne.modeler.pref;
 
+import org.apache.cayenne.modeler.ui.project.ProjectController;
+
+import java.util.prefs.BackingStoreException;
 import java.util.prefs.Preferences;
 
 public class ProjectStatePreferences extends RenamedPreferences {
@@ -63,6 +66,22 @@ public class ProjectStatePreferences extends RenamedPreferences {
         super(pref);
     }
 
+    public void saveSelection(ProjectController controller) {
+        if (getCurrentPreference() == null) {
+            return;
+        }
+        try {
+            getCurrentPreference().clear();
+        } catch (BackingStoreException e) {
+            // ignore exception
+        }
+        ProjectSelectionMemo.save(controller, this);
+    }
+
+    public void restoreSelection(ProjectController controller) {
+        ProjectSelectionMemo.restore(controller, this);
+    }
+
     public String getEvent() {
         if (event == null) {
             event = getCurrentPreference().get(EVENT_PROPERTY, "");
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/AttributeDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/AttributeDisplayEventType.java
deleted file mode 100644
index e7f09a3ff..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/AttributeDisplayEventType.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataNodeDescriptor;
-import org.apache.cayenne.map.Attribute;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.Entity;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.AttributeDisplayEvent;
-import org.apache.cayenne.modeler.event.display.EntityDisplayEvent;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class AttributeDisplayEventType extends EntityDisplayEventType {
-
-    AttributeDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataNodeDescriptor dataNode = dataChannel.getNodeDescriptor(preferences.getNode());
-        DataMap dataMap = dataChannel.getDataMap(preferences.getDataMap());
-        if (dataMap == null) {
-            return;
-        }
-
-        Entity<?,?,?> entity = getLastEntity(dataMap);
-        if (entity == null) {
-            return;
-        }
-
-        Attribute<?,?,?>[] attributes = getLastEntityAttributes(entity);
-
-        EntityDisplayEvent entityDisplayEvent = new EntityDisplayEvent(this, entity, dataMap, dataNode, dataChannel);
-        AttributeDisplayEvent attributeDisplayEvent = new AttributeDisplayEvent(this, attributes, entity, dataMap, dataChannel);
-
-        if (entity instanceof ObjEntity) {
-            controller.displayObjEntity(entityDisplayEvent);
-            controller.displayObjAttribute(attributeDisplayEvent);
-        } else if (entity instanceof DbEntity) {
-            controller.displayDbEntity(entityDisplayEvent);
-            controller.displayDbAttribute(attributeDisplayEvent);
-        }
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(AttributeDisplayEvent.class.getSimpleName());
-        preferences.setDomain(controller.getSelectedDataDomain().getName());
-        preferences.setNode(controller.getSelectedDataNode() != null ? controller.getSelectedDataNode().getName() : "");
-        preferences.setDataMap(controller.getSelectedDataMap().getName());
-
-        if (controller.getSelectedObjEntity() != null) {
-            preferences.setObjEntity(controller.getSelectedObjEntity().getName());
-            preferences.setObjAttrs(parseToString(controller.getSelectedObjAttributes()));
-            preferences.setDbEntity(null);
-        } else if (controller.getSelectedDbEntity() != null) {
-            preferences.setDbEntity(controller.getSelectedDbEntity().getName());
-            preferences.setDbAttrs(parseToString(controller.getSelectedDbAttributes()));
-            preferences.setObjEntity(null);
-        }
-    }
-
-    protected Attribute<?,?,?>[] getLastEntityAttributes(Entity<?,?,?> entity) {
-        List<Attribute<?,?,?>> attributeList = new ArrayList<>();
-
-        String attrs = (entity instanceof ObjEntity) ? preferences.getObjAttrs() : preferences.getDbAttrs();
-        for (String attrName : attrs.split(",")) {
-            Attribute<?,?,?> attr = entity.getAttribute(attrName);
-            if(attr != null) {
-                attributeList.add(attr);
-            }
-        }
-
-        return attributeList.toArray(new Attribute[0]);
-    }
-
-}
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DataMapDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DataMapDisplayEventType.java
deleted file mode 100644
index dda2c2acf..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DataMapDisplayEventType.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataNodeDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.DataMapDisplayEvent;
-
-class DataMapDisplayEventType extends DisplayEventType {
-
-    public DataMapDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataNodeDescriptor dataNode = dataChannel.getNodeDescriptor(preferences.getNode());
-        DataMap dataMap = dataChannel.getDataMap(preferences.getDataMap());
-        if (dataMap == null) {
-            return;
-        }
-
-        DataMapDisplayEvent dataMapDisplayEvent = new DataMapDisplayEvent(this, dataMap, dataChannel, dataNode);
-        controller.displayDataMap(dataMapDisplayEvent);
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(DataMapDisplayEvent.class.getSimpleName());
-
-        DataChannelDescriptor domain = controller.getSelectedDataDomain();
-        DataNodeDescriptor node = controller.getSelectedDataNode();
-        DataMap dataMap = controller.getSelectedDataMap();
-
-        if (domain != null) {
-            preferences.setDomain(domain.getName());
-            preferences.setNode(node != null ? node.getName() : "");
-
-            if (dataMap != null) {
-                preferences.setDataMap(dataMap.getName());
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DataNodeDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DataNodeDisplayEventType.java
deleted file mode 100644
index c5f51c595..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DataNodeDisplayEventType.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataNodeDescriptor;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.DataNodeDisplayEvent;
-
-class DataNodeDisplayEventType extends DisplayEventType {
-
-    public DataNodeDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataNodeDescriptor dataNode = dataChannel.getNodeDescriptor(preferences.getNode());
-        if (dataNode == null) {
-            return;
-        }
-
-        DataNodeDisplayEvent dataNodeDisplayEvent = new DataNodeDisplayEvent(this, dataChannel, dataNode);
-        controller.displayDataNode(dataNodeDisplayEvent);
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(DataNodeDisplayEvent.class.getSimpleName());
-        preferences.setDomain(controller.getSelectedDataDomain().getName());
-        preferences.setNode(controller.getSelectedDataNode().getName());
-    }
-}
\ No newline at end of file
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DisplayEventType.java
deleted file mode 100644
index d0fe9d087..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DisplayEventType.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.ConfigurationNode;
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataNodeDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.Embeddable;
-import org.apache.cayenne.map.QueryDescriptor;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.pref.ProjectStatePreferences;
-import org.apache.cayenne.util.CayenneMapEntry;
-
-public abstract class DisplayEventType {
-
-    protected ProjectController controller;
-    protected ProjectStatePreferences preferences;
-
-    public DisplayEventType(ProjectController controller) {
-        this.controller = controller;
-        this.preferences = controller.getProjectStatePreferences();
-    }
-
-    public abstract void fireLastDisplayEvent();
-
-    public abstract void saveLastDisplayEvent();
-
-    protected String getObjectName(ConfigurationNode object) {
-        if (object instanceof CayenneMapEntry) {
-            return ((CayenneMapEntry) object).getName();
-        } else if (object instanceof DataChannelDescriptor) {
-            return ((DataChannelDescriptor) object).getName();
-        } else if (object instanceof DataNodeDescriptor) {
-            return ((DataNodeDescriptor) object).getName();
-        } else if (object instanceof DataMap) {
-            return ((DataMap) object).getName();
-        } else if (object instanceof Embeddable) {
-            return ((Embeddable) object).getClassName();
-        } else if (object instanceof QueryDescriptor) {
-            return ((QueryDescriptor) object).getName();
-        } else {
-            return "";
-        }
-    }
-
-    protected String parseToString(CayenneMapEntry[] array) {
-        if (array == null) {
-            return "";
-        }
-
-        StringBuilder sb = new StringBuilder();
-        for (CayenneMapEntry entry : array) {
-            if(entry == null) {
-                continue;
-            }
-            sb.append(entry.getName()).append(",");
-        }
-
-        return sb.toString();
-    }
-
-}
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DisplayEventTypes.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DisplayEventTypes.java
deleted file mode 100644
index 6e46e3efa..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DisplayEventTypes.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.AttributeDisplayEvent;
-import org.apache.cayenne.modeler.event.display.DataMapDisplayEvent;
-import org.apache.cayenne.modeler.event.display.DataNodeDisplayEvent;
-import org.apache.cayenne.modeler.event.display.DomainDisplayEvent;
-import org.apache.cayenne.modeler.event.display.EmbeddableAttributeDisplayEvent;
-import org.apache.cayenne.modeler.event.display.EmbeddableDisplayEvent;
-import org.apache.cayenne.modeler.event.display.EntityDisplayEvent;
-import org.apache.cayenne.modeler.event.display.MultipleObjectsDisplayEvent;
-import org.apache.cayenne.modeler.event.display.ProcedureDisplayEvent;
-import org.apache.cayenne.modeler.event.display.ProcedureParameterDisplayEvent;
-import org.apache.cayenne.modeler.event.display.QueryDisplayEvent;
-import org.apache.cayenne.modeler.event.display.RelationshipDisplayEvent;
-
-public enum DisplayEventTypes {
-
-    DomainDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            return new DomainDisplayEventType(controller);
-        }
-
-        @Override
-        public String toString() {
-            return DomainDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    DataNodeDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            return new DataNodeDisplayEventType(controller);
-        }
-
-        @Override
-        public String toString() {
-            return DataNodeDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    DataMapDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            return new DataMapDisplayEventType(controller);
-        }
-
-        @Override
-        public String toString() {
-            return DataMapDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    EntityDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            if (controller.getSelectedObjAttributes().length != 0 || controller.getSelectedDbAttributes().length != 0) {
-                return new AttributeDisplayEventType(controller);
-            } else if (controller.getSelectedObjRelationships().length != 0 || controller.getSelectedDbRelationships().length != 0) {
-                return new RelationshipDisplayEventType(controller);
-            } else {
-                return new EntityDisplayEventType(controller);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return EntityDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    AttributeDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            return new AttributeDisplayEventType(controller);
-        }
-
-        @Override
-        public String toString() {
-            return AttributeDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    RelationshipDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            return new RelationshipDisplayEventType(controller);
-        }
-
-        @Override
-        public String toString() {
-            return RelationshipDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    EmbeddableDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            if (controller.getSelectedEmbeddableAttributes().length != 0) {
-                return new EmbeddableAttributeDisplayEventType(controller);
-            } else {
-                return new EmbeddableDisplayEventType(controller);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return EmbeddableDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    EmbeddableAttributeDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            return new EmbeddableAttributeDisplayEventType(controller);
-        }
-
-        @Override
-        public String toString() {
-            return EmbeddableAttributeDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    ProcedureDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            if (controller.getSelectedProcedureParameters().length != 0) {
-                return new ProcedureParameterDisplayEventType(controller);
-            } else {
-                return new ProcedureDisplayEventType(controller);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return ProcedureDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    ProcedureParameterDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            return new ProcedureParameterDisplayEventType(controller);
-        }
-
-        @Override
-        public String toString() {
-            return ProcedureParameterDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    QueryDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            return new QueryDisplayEventType(controller);
-        }
-
-        @Override
-        public String toString() {
-            return QueryDisplayEvent.class.getSimpleName();
-        }
-    },
-
-    MultipleObjectsDisplayEvent {
-        @Override
-        public DisplayEventType createDisplayEventType(ProjectController controller) {
-            return new MultipleObjectsDisplayEventType(controller);
-        }
-
-        @Override
-        public String toString() {
-            return MultipleObjectsDisplayEvent.class.getSimpleName();
-        }
-    };
-
-    public abstract DisplayEventType createDisplayEventType(ProjectController controller);
-
-}
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DomainDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DomainDisplayEventType.java
deleted file mode 100644
index 7579a0a72..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/DomainDisplayEventType.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.DomainDisplayEvent;
-
-class DomainDisplayEventType extends DisplayEventType {
-
-    public DomainDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DomainDisplayEvent domainDisplayEvent = new DomainDisplayEvent(this, dataChannel);
-        controller.displayDomain(domainDisplayEvent);
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(DomainDisplayEvent.class.getSimpleName());
-        preferences.setDomain(controller.getSelectedDataDomain().getName());
-    }
-
-}
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/EmbeddableAttributeDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/EmbeddableAttributeDisplayEventType.java
deleted file mode 100644
index 0bf2bf432..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/EmbeddableAttributeDisplayEventType.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.Embeddable;
-import org.apache.cayenne.map.EmbeddableAttribute;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.EmbeddableAttributeDisplayEvent;
-import org.apache.cayenne.modeler.event.display.EmbeddableDisplayEvent;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class EmbeddableAttributeDisplayEventType extends EmbeddableDisplayEventType {
-
-    public EmbeddableAttributeDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataMap dataMap = dataChannel.getDataMap(preferences.getDataMap());
-        if (dataMap == null) {
-            return;
-        }
-
-        Embeddable embeddable = dataMap.getEmbeddable(preferences.getEmbeddable());
-        if (embeddable == null) {
-            return;
-        }
-
-        EmbeddableDisplayEvent embeddableDisplayEvent = new EmbeddableDisplayEvent(this, embeddable, dataMap, dataChannel);
-        controller.displayEmbeddable(embeddableDisplayEvent);
-
-        EmbeddableAttribute[] embeddableAttributes = getLastEmbeddableAttributes(embeddable);
-        EmbeddableAttributeDisplayEvent attributeDisplayEvent = new EmbeddableAttributeDisplayEvent(this, embeddable, embeddableAttributes, dataMap, dataChannel);
-        controller.displayEmbeddableAttribute(attributeDisplayEvent);
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(EmbeddableAttributeDisplayEvent.class.getSimpleName());
-        preferences.setDomain(controller.getSelectedDataDomain().getName());
-        preferences.setDataMap(controller.getSelectedDataMap().getName());
-        preferences.setEmbeddable(controller.getSelectedEmbeddable().getClassName());
-
-        EmbeddableAttribute[] currentEmbAttributes = controller.getSelectedEmbeddableAttributes();
-        if (currentEmbAttributes == null) {
-            preferences.setEmbAttrs("");
-        } else {
-            StringBuilder sb = new StringBuilder();
-            for (EmbeddableAttribute embeddableAttribute : currentEmbAttributes) {
-                sb.append(embeddableAttribute.getName()).append(",");
-            }
-            preferences.setEmbAttrs(sb.toString());
-        }
-    }
-
-    protected EmbeddableAttribute[] getLastEmbeddableAttributes(Embeddable embeddable) {
-        List<EmbeddableAttribute> embeddableAttributeList = new ArrayList<EmbeddableAttribute>();
-        EmbeddableAttribute[] attributes = new EmbeddableAttribute[0];
-
-        String embAttrs = preferences.getEmbAttrs();
-        if (embAttrs.isEmpty()) {
-            return embeddableAttributeList.toArray(attributes);
-        }
-
-        for (String embAttrName : embAttrs.split(",")) {
-            embeddableAttributeList.add(embeddable.getAttribute(embAttrName));
-        }
-
-        return embeddableAttributeList.toArray(attributes);
-    }
-}
\ No newline at end of file
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/EmbeddableDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/EmbeddableDisplayEventType.java
deleted file mode 100644
index 9d22e1cbc..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/EmbeddableDisplayEventType.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.Embeddable;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.EmbeddableDisplayEvent;
-
-class EmbeddableDisplayEventType extends DisplayEventType {
-
-    public EmbeddableDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataMap dataMap = dataChannel.getDataMap(preferences.getDataMap());
-        if (dataMap == null) {
-            return;
-        }
-
-        Embeddable embeddable = dataMap.getEmbeddable(preferences.getEmbeddable());
-        if (embeddable == null) {
-            return;
-        }
-
-        EmbeddableDisplayEvent embeddableDisplayEvent = new EmbeddableDisplayEvent(this, embeddable, dataMap, dataChannel);
-        controller.displayEmbeddable(embeddableDisplayEvent);
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(EmbeddableDisplayEvent.class.getSimpleName());
-        preferences.setDomain(controller.getSelectedDataDomain().getName());
-        preferences.setDataMap(controller.getSelectedDataMap().getName());
-        preferences.setEmbeddable(controller.getSelectedEmbeddable().getClassName());
-    }
-}
\ No newline at end of file
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/EntityDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/EntityDisplayEventType.java
deleted file mode 100644
index c9d689b93..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/EntityDisplayEventType.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataNodeDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.Entity;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.EntityDisplayEvent;
-
-class EntityDisplayEventType extends DisplayEventType {
-
-    EntityDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataNodeDescriptor dataNode = dataChannel.getNodeDescriptor(preferences.getNode());
-        DataMap dataMap = dataChannel.getDataMap(preferences.getDataMap());
-        if (dataMap == null) {
-            return;
-        }
-
-        Entity<?, ?, ?> entity = getLastEntity(dataMap);
-        if (entity == null) {
-            return;
-        }
-
-        EntityDisplayEvent entityDisplayEvent = new EntityDisplayEvent(this, entity, dataMap, dataNode, dataChannel);
-        if (entity instanceof ObjEntity) {
-            controller.displayObjEntity(entityDisplayEvent);
-        } else if (entity instanceof DbEntity) {
-            controller.displayDbEntity(entityDisplayEvent);
-        }
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-
-        preferences.setEvent(EntityDisplayEvent.class.getSimpleName());
-
-        DataChannelDescriptor domain = controller.getSelectedDataDomain();
-        DataNodeDescriptor node = controller.getSelectedDataNode();
-        DataMap dataMap = controller.getSelectedDataMap();
-        DbEntity dbEntity = controller.getSelectedDbEntity();
-        ObjEntity objEntity = controller.getSelectedObjEntity();
-
-        if (domain != null) {
-            preferences.setDomain(domain.getName());
-            preferences.setNode(node != null ? node.getName() : "");
-
-            if (dataMap != null) {
-
-                preferences.setDataMap(dataMap.getName());
-
-                if (objEntity != null) {
-                    preferences.setObjEntity(objEntity.getName());
-                    preferences.setDbEntity(null);
-                } else if (dbEntity != null) {
-                    preferences.setDbEntity(dbEntity.getName());
-                    preferences.setObjEntity(null);
-                }
-            }
-        }
-    }
-
-    Entity<?, ?, ?> getLastEntity(DataMap dataMap) {
-        return !preferences.getObjEntity().isEmpty()
-                ? dataMap.getObjEntity(preferences.getObjEntity())
-                : dataMap.getDbEntity(preferences.getDbEntity());
-    }
-}
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/MultipleObjectsDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/MultipleObjectsDisplayEventType.java
deleted file mode 100644
index 02ea66058..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/MultipleObjectsDisplayEventType.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.ConfigurationNode;
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataNodeDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.MultipleObjectsDisplayEvent;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class MultipleObjectsDisplayEventType extends DisplayEventType {
-
-    public MultipleObjectsDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-
-        String parentObjectName = preferences.getParentObject();
-        ConfigurationNode parentObject;
-        ConfigurationNode[] multipleObjects;
-
-        if (dataChannel.getDataMap(parentObjectName) != null) {
-            DataMap dataMap = dataChannel.getDataMap(parentObjectName);
-            parentObject = dataMap;
-            multipleObjects = getLastMultipleObjects(dataMap);
-        } else if (dataChannel.getNodeDescriptor(parentObjectName) != null) {
-            DataNodeDescriptor dataNode = dataChannel.getNodeDescriptor(parentObjectName);
-            parentObject = dataNode;
-            multipleObjects = getLastMultipleObjects(dataNode);
-        } else {
-            parentObject = dataChannel;
-            multipleObjects = getLastMultipleObjects(dataChannel);
-        }
-
-        MultipleObjectsDisplayEvent multipleDisplayEvent = new MultipleObjectsDisplayEvent(this, multipleObjects, parentObject);
-        controller.displayMultipleObjects(multipleDisplayEvent);
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(MultipleObjectsDisplayEvent.class.getSimpleName());
-        preferences.setParentObject(getObjectName(controller.getSelectedParentPath()));
-
-        ConfigurationNode[] multipleObjects = controller.getSelectedPaths();
-        if (multipleObjects == null) {
-            preferences.setMultipleObjects("");
-        } else {
-            StringBuilder sb = new StringBuilder();
-            for (ConfigurationNode object : multipleObjects) {
-                String objectName = getObjectName(object);
-                if (!objectName.isEmpty()) {
-                    sb.append(objectName).append(",");
-                }
-            }
-            preferences.setMultipleObjects(sb.toString());
-        }
-    }
-
-    protected ConfigurationNode[] getLastMultipleObjects(DataChannelDescriptor dataChannel) {
-        List<ConfigurationNode> configurationNodeList = new ArrayList<>();
-        ConfigurationNode[] nodes = new ConfigurationNode[0];
-
-        String multipleObjects = preferences.getMultipleObjects();
-        if (multipleObjects.isEmpty()) {
-            return configurationNodeList.toArray(nodes);
-        }
-
-        for (String objectName : multipleObjects.split(",")) {
-            ConfigurationNode configNode = getConfigNode(dataChannel, objectName);
-            if (configNode != null) {
-                configurationNodeList.add(configNode);
-            }
-        }
-
-        return configurationNodeList.toArray(nodes);
-    }
-
-    protected ConfigurationNode[] getLastMultipleObjects(DataNodeDescriptor dataNode) {
-        List<ConfigurationNode> configurationNodeList = new ArrayList<>();
-        ConfigurationNode[] nodes = new ConfigurationNode[0];
-
-        String multipleObjects = preferences.getMultipleObjects();
-        if (multipleObjects.isEmpty()) {
-            return configurationNodeList.toArray(nodes);
-        }
-
-        for (String objectName : multipleObjects.split(",")) {
-            if (dataNode.getDataMapNames().contains(objectName)) {
-                configurationNodeList.add(dataNode.getDataChannelDescriptor().getDataMap(objectName));
-            }
-        }
-
-        return configurationNodeList.toArray(nodes);
-    }
-
-    protected ConfigurationNode[] getLastMultipleObjects(DataMap dataMap) {
-        List<ConfigurationNode> configurationNodeList = new ArrayList<>();
-        ConfigurationNode[] nodes = new ConfigurationNode[0];
-
-        String multipleObjects = preferences.getMultipleObjects();
-        if (multipleObjects.isEmpty()) {
-            return configurationNodeList.toArray(nodes);
-        }
-
-        for (String objectName : multipleObjects.split(",")) {
-            if (dataMap.getObjEntity(objectName) != null) {
-                configurationNodeList.add(dataMap.getObjEntity(objectName));
-            } else if (dataMap.getDbEntity(objectName) != null) {
-                configurationNodeList.add(dataMap.getDbEntity(objectName));
-            } else if (dataMap.getEmbeddable(objectName) != null) {
-                configurationNodeList.add(dataMap.getEmbeddable(objectName));
-            } else if (dataMap.getProcedure(objectName) != null) {
-                configurationNodeList.add(dataMap.getProcedure(objectName));
-            } else if (dataMap.getQueryDescriptor(objectName) != null) {
-                configurationNodeList.add(dataMap.getQueryDescriptor(objectName));
-            }
-        }
-
-        return configurationNodeList.toArray(nodes);
-    }
-
-    private ConfigurationNode getConfigNode(DataChannelDescriptor dataChannel, String objectName) {
-        if (dataChannel.getName().equals(objectName)) {
-            return dataChannel;
-        }
-
-        for (DataNodeDescriptor dataNode : dataChannel.getNodeDescriptors()) {
-            if (dataNode.getName().equals(objectName)) {
-                return dataNode;
-            }
-        }
-
-        for (DataMap dataMap : dataChannel.getDataMaps()) {
-            if (dataMap.getName().equals(objectName)) {
-                return dataMap;
-            }
-        }
-
-        return null;
-    }
-
-}
\ No newline at end of file
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/ProcedureDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/ProcedureDisplayEventType.java
deleted file mode 100644
index 97a6d3cbd..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/ProcedureDisplayEventType.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.Procedure;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.ProcedureDisplayEvent;
-
-class ProcedureDisplayEventType extends DisplayEventType {
-
-    public ProcedureDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataMap dataMap = dataChannel.getDataMap(preferences.getDataMap());
-        if (dataMap == null) {
-            return;
-        }
-
-        Procedure procedure = dataMap.getProcedure(preferences.getProcedure());
-        if (procedure == null) {
-            return;
-        }
-
-        ProcedureDisplayEvent procedureDisplayEvent = new ProcedureDisplayEvent(this, procedure, dataMap, dataChannel);
-        controller.displayProcedure(procedureDisplayEvent);
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(ProcedureDisplayEvent.class.getSimpleName());
-        preferences.setDomain(controller.getSelectedDataDomain().getName());
-        preferences.setDataMap(controller.getSelectedDataMap().getName());
-        preferences.setProcedure(controller.getSelectedProcedure().getName());
-    }
-
-}
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/ProcedureParameterDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/ProcedureParameterDisplayEventType.java
deleted file mode 100644
index 8638363d6..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/ProcedureParameterDisplayEventType.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.Procedure;
-import org.apache.cayenne.map.ProcedureParameter;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.ProcedureDisplayEvent;
-import org.apache.cayenne.modeler.event.display.ProcedureParameterDisplayEvent;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class ProcedureParameterDisplayEventType extends ProcedureDisplayEventType {
-
-    public ProcedureParameterDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataMap dataMap = dataChannel.getDataMap(preferences.getDataMap());
-        if (dataMap == null) {
-            return;
-        }
-
-        Procedure procedure = dataMap.getProcedure(preferences.getProcedure());
-        if (procedure == null) {
-            return;
-        }
-
-        ProcedureDisplayEvent procedureDisplayEvent = new ProcedureDisplayEvent(this, procedure, dataMap, dataChannel);
-        controller.displayProcedure(procedureDisplayEvent);
-
-        ProcedureParameter[] procedureParameters = getLastProcedureParameters(procedure);
-        ProcedureParameterDisplayEvent procedureParameterDisplayEvent =
-                new ProcedureParameterDisplayEvent(this, procedureParameters, procedure, dataMap, dataChannel);
-        controller.displayProcedureParameter(procedureParameterDisplayEvent);
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(ProcedureParameterDisplayEvent.class.getSimpleName());
-        preferences.setDomain(controller.getSelectedDataDomain().getName());
-        preferences.setDataMap(controller.getSelectedDataMap().getName());
-        preferences.setProcedure(controller.getSelectedProcedure().getName());
-        preferences.setProcedureParams(parseToString(controller.getSelectedProcedureParameters()));
-    }
-
-    protected ProcedureParameter[] getLastProcedureParameters(Procedure procedure) {
-        List<ProcedureParameter> procedureParameterList = new ArrayList<ProcedureParameter>();
-        ProcedureParameter[] parameters = new ProcedureParameter[0];
-
-        String procedureParams = preferences.getProcedureParams();
-        if (procedureParams.isEmpty()) {
-            return procedureParameterList.toArray(parameters);
-        }
-
-        for (String procedureParamName : procedureParams.split(",")) {
-            for (ProcedureParameter procedureParameter : procedure.getCallParameters()) {
-                if (procedureParameter.getName().equals(procedureParamName)) {
-                    procedureParameterList.add(procedureParameter);
-                }
-            }
-        }
-
-        return procedureParameterList.toArray(parameters);
-    }
-}
\ No newline at end of file
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/QueryDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/QueryDisplayEventType.java
deleted file mode 100644
index abb87f45d..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/QueryDisplayEventType.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.QueryDisplayEvent;
-import org.apache.cayenne.map.QueryDescriptor;
-
-class QueryDisplayEventType extends DisplayEventType {
-
-    public QueryDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataMap dataMap = dataChannel.getDataMap(preferences.getDataMap());
-        if (dataMap == null) {
-            return;
-        }
-
-        QueryDescriptor query = dataMap.getQueryDescriptor(preferences.getQuery());
-        if (query == null) {
-            return;
-        }
-
-        QueryDisplayEvent queryDisplayEvent = new QueryDisplayEvent(this, query, dataMap, dataChannel);
-        controller.displayQuery(queryDisplayEvent);
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(QueryDisplayEvent.class.getSimpleName());
-        preferences.setDomain(controller.getSelectedDataDomain().getName());
-        preferences.setDataMap(controller.getSelectedDataMap().getName());
-        preferences.setQuery(controller.getSelectedQuery().getName());
-    }
-}
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/RelationshipDisplayEventType.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/RelationshipDisplayEventType.java
deleted file mode 100644
index 78204f7c3..000000000
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/project/RelationshipDisplayEventType.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.modeler.pref.project;
-
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataNodeDescriptor;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.Entity;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.map.Relationship;
-import org.apache.cayenne.modeler.ui.project.ProjectController;
-import org.apache.cayenne.modeler.event.display.EntityDisplayEvent;
-import org.apache.cayenne.modeler.event.display.RelationshipDisplayEvent;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class RelationshipDisplayEventType extends EntityDisplayEventType {
-
-    RelationshipDisplayEventType(ProjectController controller) {
-        super(controller);
-    }
-
-    @Override
-    public void fireLastDisplayEvent() {
-        DataChannelDescriptor dataChannel = (DataChannelDescriptor) controller.getProject().getRootNode();
-        if (!dataChannel.getName().equals(preferences.getDomain())) {
-            return;
-        }
-
-        DataNodeDescriptor dataNode = dataChannel.getNodeDescriptor(preferences.getNode());
-        DataMap dataMap = dataChannel.getDataMap(preferences.getDataMap());
-        if (dataMap == null) {
-            return;
-        }
-
-        Entity<?,?,?> entity = getLastEntity(dataMap);
-        if (entity == null) {
-            return;
-        }
-
-        Relationship<?,?,?>[] relationships = getLastEntityRelationships(entity);
-
-        EntityDisplayEvent entityDisplayEvent = new EntityDisplayEvent(this, entity, dataMap, dataNode, dataChannel);
-        RelationshipDisplayEvent displayEvent = new RelationshipDisplayEvent(this, relationships, entity, dataMap, dataChannel);
-
-        if (entity instanceof ObjEntity) {
-            controller.displayObjEntity(entityDisplayEvent);
-            controller.displayObjRelationship(displayEvent);
-        } else if (entity instanceof DbEntity) {
-            controller.displayDbEntity(entityDisplayEvent);
-            controller.displayDbRelationship(displayEvent);
-        }
-    }
-
-    @Override
-    public void saveLastDisplayEvent() {
-        preferences.setEvent(RelationshipDisplayEvent.class.getSimpleName());
-        preferences.setDomain(controller.getSelectedDataDomain().getName());
-        preferences.setNode(controller.getSelectedDataNode() != null ? controller.getSelectedDataNode().getName() : "");
-        preferences.setDataMap(controller.getSelectedDataMap().getName());
-
-        if (controller.getSelectedObjEntity() != null) {
-            preferences.setObjEntity(controller.getSelectedObjEntity().getName());
-            preferences.setObjRels(parseToString(controller.getSelectedObjRelationships()));
-            preferences.setDbEntity(null);
-        } else if (controller.getSelectedDbEntity() != null) {
-            preferences.setDbEntity(controller.getSelectedDbEntity().getName());
-            preferences.setDbRels(parseToString(controller.getSelectedDbRelationships()));
-            preferences.setObjEntity(null);
-        }
-    }
-
-    private Relationship<?,?,?>[] getLastEntityRelationships(Entity<?,?,?> entity) {
-        List<Relationship<?,?,?>> relationshipList = new ArrayList<>();
-
-        String rels = (entity instanceof ObjEntity) ? preferences.getObjRels() : preferences.getDbRels();
-        for (String objRelName : rels.split(",")) {
-            Relationship<?,?,?> rel = entity.getRelationship(objRelName);
-            if(rel != null) {
-                relationshipList.add(rel);
-            }
-        }
-
-        return relationshipList.toArray(new Relationship[0]);
-    }
-
-}
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/ProjectController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/ProjectController.java
index 343073b01..956904c35 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/ProjectController.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/ProjectController.java
@@ -66,8 +66,6 @@ import org.apache.cayenne.modeler.pref.DataNodeDefaults;
 import org.apache.cayenne.modeler.pref.ProjectStatePreferences;
 import org.apache.cayenne.modeler.ui.ModelerController;
 import org.apache.cayenne.modeler.util.Comparators;
-import org.apache.cayenne.modeler.pref.project.DisplayEventTypes;
-import org.apache.cayenne.modeler.pref.project.MultipleObjectsDisplayEventType;
 import org.apache.cayenne.project.ConfigurationNodeParentGetter;
 import org.apache.cayenne.project.Project;
 import org.apache.cayenne.util.IDUtil;
@@ -79,12 +77,10 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.EventListener;
-import java.util.EventObject;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.prefs.BackingStoreException;
 import java.util.prefs.Preferences;
 
 /**
@@ -298,45 +294,14 @@ public class ProjectController extends ChildController<ModelerController> {
     }
 
     public void saveSelectionToPrefs() {
-
         if (project == null) {
             return;
         }
-
-        EventObject displayEvent = getLastDisplayEvent();
-        ConfigurationNode[] multiplyObjects = getSelectedPaths();
-
-        if (displayEvent == null && multiplyObjects == null) {
-            return;
-        }
-
-        ProjectStatePreferences preferences = getProjectStatePreferences();
-        if (preferences.getCurrentPreference() == null) {
-            return;
-        }
-
-        try {
-            preferences.getCurrentPreference().clear();
-        } catch (BackingStoreException e) {
-            // ignore exception
-        }
-
-        if (displayEvent != null) {
-            DisplayEventTypes.valueOf(displayEvent.getClass().getSimpleName())
-                    .createDisplayEventType(this)
-                    .saveLastDisplayEvent();
-        } else if (multiplyObjects.length != 0) {
-            new MultipleObjectsDisplayEventType(this).saveLastDisplayEvent();
-        }
+        getProjectStatePreferences().saveSelection(this);
     }
 
     public void restoreSelectionFromPrefs() {
-        ProjectStatePreferences preferences = getProjectStatePreferences();
-
-        String displayEventName = preferences.getEvent();
-        if (!displayEventName.isEmpty()) {
-            DisplayEventTypes.valueOf(displayEventName).createDisplayEventType(this).fireLastDisplayEvent();
-        }
+        getProjectStatePreferences().restoreSelection(this);
     }
 
     public boolean isDirty() {