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


The following commit(s) were added to refs/heads/master by this push:
     new 272cd0c24 CAY-3004 Modeler: Rework DbEntity PK generation UI to match 
the XML model
272cd0c24 is described below

commit 272cd0c247745aa80033f0c84fb247274acee079
Author: Andrus Adamchik <[email protected]>
AuthorDate: Sun Aug 30 09:52:22 2026 -0400

    CAY-3004 Modeler: Rework DbEntity PK generation UI to match the XML model
    
    (also uinifying enabled/disabled checkbox rendering across all tables)
---
 RELEASE-NOTES.txt                                  |   6 +-
 .../table/BooleanTableCellRenderer.java}           |  33 ++--
 .../editor/dbentity/main/DbEntityMainView.java     | 177 +++++++--------------
 .../main/PKCustomSequenceGeneratorPanel.java       |  17 +-
 .../editor/dbentity/main/PKDBGeneratorPanel.java   | 155 ------------------
 .../editor/dbentity/main/PKGeneratorPanel.java     |  84 ----------
 .../dbentity/properties/DbAttributePanel.java      |   3 +
 .../dbentity/properties/DbAttributeTableModel.java |  82 +++++++++-
 .../dbentity/properties/DbRelationshipPanel.java   |  26 +--
 .../objentity/properties/ObjAttributePanel.java    |   3 +
 .../objentity/properties/ObjRelationshipPanel.java |   3 +
 11 files changed, 173 insertions(+), 416 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 6e1947196..ec13dfd55 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -14,8 +14,12 @@ Date:
 Changes/New Features:
 
 CAY-2998 Modeler: clickabe "Target" column to navigate relationships
-CAY-2999 Modeler: A restricted method in java.lang.System has been called
 CAY-3000 Modeler: unifying the main views of all model objects
+CAY-3004 Modeler: Rework DbEntity PK generation UI to match the XML model
+
+Bug Fixes:
+
+CAY-2999 Modeler: A restricted method in java.lang.System has been called
 CAY-3001 Modeler: Auto-increment attribute is not picked up
 CAY-3002 Modeler: NPE after clicking path chooser
 CAY-3003 Modeler: NPE editing unmapped ObjAttribute
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKDefaultGeneratorPanel.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/toolkit/table/BooleanTableCellRenderer.java
similarity index 50%
rename from 
cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKDefaultGeneratorPanel.java
rename to 
cayenne-modeler/src/main/java/org/apache/cayenne/modeler/toolkit/table/BooleanTableCellRenderer.java
index cb93f0664..09395b507 100644
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKDefaultGeneratorPanel.java
+++ 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/toolkit/table/BooleanTableCellRenderer.java
@@ -17,27 +17,30 @@
  *  under the License.
  ****************************************************************/
 
-package org.apache.cayenne.modeler.ui.project.editor.dbentity.main;
+package org.apache.cayenne.modeler.toolkit.table;
 
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.modeler.project.ProjectSession;
+import javax.swing.JTable;
+import javax.swing.table.TableCellRenderer;
+import java.awt.Component;
 
-import java.awt.Dimension;
+/**
+ * Renders Boolean columns with the look and feel's own checkbox renderer, 
greying out the cells that
+ * the table model reports as non-editable. The stock renderer paints those as 
if they were editable.
+ */
+public class BooleanTableCellRenderer implements TableCellRenderer {
 
-public class PKDefaultGeneratorPanel extends PKGeneratorPanel {
-    
-    public PKDefaultGeneratorPanel(ProjectSession session) {
-        super(session);
+    private final TableCellRenderer lafRenderer;
 
-        // nothing to show for the default strategy, so the panel must not 
take up any space either
-        setPreferredSize(new Dimension(0, 0));
+    public BooleanTableCellRenderer(TableCellRenderer lafRenderer) {
+        this.lafRenderer = lafRenderer;
     }
 
-    public void setDbEntity(DbEntity entity) {
-        // noop
-    }
+    @Override
+    public Component getTableCellRendererComponent(JTable table, Object value, 
boolean isSelected,
+                                                   boolean hasFocus, int row, 
int col) {
 
-    protected void onInitInternal(DbEntity entity) {
-        resetStrategy(entity, true, true);
+        Component c = lafRenderer.getTableCellRendererComponent(table, value, 
isSelected, hasFocus, row, col);
+        c.setEnabled(table.isCellEditable(row, col));
+        return c;
     }
 }
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/DbEntityMainView.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/DbEntityMainView.java
index 2f7d7d0b7..41bf842ee 100644
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/DbEntityMainView.java
+++ 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/DbEntityMainView.java
@@ -22,17 +22,16 @@ package 
org.apache.cayenne.modeler.ui.project.editor.dbentity.main;
 import com.jgoodies.forms.builder.DefaultFormBuilder;
 import com.jgoodies.forms.layout.FormLayout;
 import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbKeyGenerator;
 import org.apache.cayenne.modeler.event.display.DbEntityDisplayEvent;
 import org.apache.cayenne.modeler.event.display.DbEntityDisplayListener;
-import org.apache.cayenne.modeler.event.model.DbAttributeEvent;
-import org.apache.cayenne.modeler.event.model.DbAttributeListener;
 import org.apache.cayenne.modeler.event.model.DbEntityEvent;
 import org.apache.cayenne.modeler.project.ProjectSession;
 import org.apache.cayenne.modeler.service.action.GlobalActions;
 import org.apache.cayenne.modeler.toolkit.ProjectPanel;
 import org.apache.cayenne.modeler.toolkit.text.CMUndoableTextField;
+import org.apache.cayenne.modeler.undo.ChangePKGeneratorUndoableEdit;
 import org.apache.cayenne.modeler.ui.action.CreateAttributeAction;
 import org.apache.cayenne.modeler.ui.action.CreateObjEntityFromDbAction;
 import org.apache.cayenne.modeler.ui.action.CreateRelationshipAction;
@@ -50,13 +49,7 @@ import java.util.EventObject;
 import java.util.Objects;
 
 public class DbEntityMainView extends ProjectPanel
-        implements ExistingSelectionProcessor, DbEntityDisplayListener, 
DbAttributeListener {
-
-    static final String PK_DEFAULT_GENERATOR = "Cayenne-Generated (Default)";
-    static final String PK_DB_GENERATOR = "Database-Generated";
-    static final String PK_CUSTOM_SEQUENCE_GENERATOR = "Custom Sequence";
-
-    static final String[] PK_GENERATOR_TYPES = { PK_DEFAULT_GENERATOR, 
PK_DB_GENERATOR, PK_CUSTOM_SEQUENCE_GENERATOR };
+        implements ExistingSelectionProcessor, DbEntityDisplayListener {
 
     private final CMUndoableTextField name;
     private final CMUndoableTextField catalog;
@@ -67,9 +60,8 @@ public class DbEntityMainView extends ProjectPanel
     private final JLabel catalogLabel;
     private final JLabel schemaLabel;
 
-    private final JComboBox<String> pkGeneratorType;
-    private final JPanel pkGeneratorDetail;
-    private final CardLayout pkGeneratorDetailLayout;
+    private final JCheckBox customPKSequence;
+    private final PKCustomSequenceGeneratorPanel customPKSequencePanel;
 
     private final JToolBar toolBar;
 
@@ -83,22 +75,8 @@ public class DbEntityMainView extends ProjectPanel
         schema = new CMUndoableTextField(app.getUndoManager());
         qualifier = new CMUndoableTextField(app.getUndoManager());
         comment = new CMUndoableTextField(app.getUndoManager());
-        pkGeneratorType = new JComboBox<>();
-        pkGeneratorDetailLayout = new CardLayout();
-
-        // unlike a plain CardLayout panel, this one takes the height of the 
visible card only, so that
-        // the comment row below it follows the PK fields instead of leaving a 
gap under the short cards
-        pkGeneratorDetail = new JPanel(pkGeneratorDetailLayout) {
-            @Override
-            public Dimension getPreferredSize() {
-                for (Component card : getComponents()) {
-                    if (card.isVisible()) {
-                        return card.getPreferredSize();
-                    }
-                }
-                return super.getPreferredSize();
-            }
-        };
+        customPKSequence = new JCheckBox();
+        customPKSequencePanel = new PKCustomSequenceGeneratorPanel(session);
         initLayout();
         initBindings();
     }
@@ -114,12 +92,6 @@ public class DbEntityMainView extends ProjectPanel
         
toolBar.add(globalActions.getAction(DbEntitySyncAction.class).buildButton(2));
         
toolBar.add(globalActions.getAction(DbEntityCounterpartAction.class).buildButton(3));
 
-        pkGeneratorType.setEditable(false);
-        pkGeneratorType.setModel(new 
DefaultComboBoxModel<>(PK_GENERATOR_TYPES));
-        pkGeneratorDetail.add(new PKDefaultGeneratorPanel(session), 
PK_DEFAULT_GENERATOR);
-        pkGeneratorDetail.add(new PKDBGeneratorPanel(session), 
PK_DB_GENERATOR);
-        pkGeneratorDetail.add(new PKCustomSequenceGeneratorPanel(session), 
PK_CUSTOM_SEQUENCE_GENERATOR);
-
         FormLayout layout = new FormLayout(EditorForm.LABEL_COLUMN + ", 
$lcgap, fill:200dlu", "");
         DefaultFormBuilder builder = new DefaultFormBuilder(layout);
         builder.setBorder(EditorForm.formBorder());
@@ -127,16 +99,18 @@ public class DbEntityMainView extends ProjectPanel
         builder.append(catalogLabel, catalog);
         builder.append(schemaLabel, schema);
         builder.append("Qualifier:", qualifier);
-        builder.append("PK Strategy:", pkGeneratorType);
+        builder.append("Custom PK Sequence:", customPKSequence);
 
-        // the comment goes last, below the PK generator panel, which varies 
with the selected strategy
+        // the comment goes last, below the sequence panel, which is only 
shown when the box is checked
         DefaultFormBuilder commentBuilder = new DefaultFormBuilder(
                 new FormLayout(EditorForm.LABEL_COLUMN + ", $lcgap, 
fill:200dlu", ""));
         commentBuilder.setBorder(EditorForm.lastSectionBorder());
         commentBuilder.append("Comment:", comment);
 
+        customPKSequencePanel.setVisible(false);
+
         JPanel pkAndComment = new JPanel(new BorderLayout());
-        pkAndComment.add(pkGeneratorDetail, BorderLayout.NORTH);
+        pkAndComment.add(customPKSequencePanel, BorderLayout.NORTH);
         pkAndComment.add(commentBuilder.getPanel(), BorderLayout.CENTER);
 
         JPanel mainPanel = new JPanel(new BorderLayout());
@@ -148,24 +122,6 @@ public class DbEntityMainView extends ProjectPanel
         add(mainPanel, BorderLayout.CENTER);
     }
 
-    /**
-     * Returns the PK generator card currently in front of the CardLayout, or 
null if none is showing.
-     */
-    private PKGeneratorPanel visiblePKGeneratorPanel() {
-        for (Component card : pkGeneratorDetail.getComponents()) {
-            if (card.isVisible()) {
-                return (PKGeneratorPanel) card;
-            }
-        }
-        return null;
-    }
-
-    private void showPKGeneratorDetail(String type) {
-        pkGeneratorDetailLayout.show(pkGeneratorDetail, type);
-        // cards differ in height, so the panels below have to be laid out 
again
-        pkGeneratorDetail.revalidate();
-    }
-
     private void initBindings() {
         name.addCommitListener(this::setEntityName);
         catalog.addCommitListener(this::setCatalog);
@@ -173,14 +129,49 @@ public class DbEntityMainView extends ProjectPanel
         qualifier.addCommitListener(this::setQualifier);
         comment.addCommitListener(this::setComment);
         session.addDbEntityDisplayListener(this);
-        session.addDbAttributeListener(this);
-        pkGeneratorType.addItemListener(e -> {
-            showPKGeneratorDetail((String) pkGeneratorType.getSelectedItem());
-            PKGeneratorPanel panel = visiblePKGeneratorPanel();
-            if (panel != null) {
-                panel.onInit(session.getSelectedDbEntity());
+        customPKSequence.addActionListener(e -> 
setCustomPKSequence(customPKSequence.isSelected()));
+    }
+
+    /**
+     * Attaches or drops the entity's own PK sequence generator. This is 
independent of the
+     * "Auto-Increment" attribute flag, matching the map XML, where 
"db-key-generator" and
+     * "db-attribute/@isGenerated" are unrelated.
+     */
+    private void setCustomPKSequence(boolean custom) {
+
+        DbEntity entity = session.getSelectedDbEntity();
+
+        if (entity == null) {
+            return;
+        }
+
+        ChangePKGeneratorUndoableEdit edit = new 
ChangePKGeneratorUndoableEdit(session, entity);
+        edit.captureOldState();
+
+        if (custom) {
+            if (entity.getPrimaryKeyGenerator() == null) {
+                DbKeyGenerator generator = new DbKeyGenerator();
+                generator.setGeneratorType(DbKeyGenerator.ORACLE_TYPE);
+                entity.setPrimaryKeyGenerator(generator);
             }
-        });
+        } else {
+            entity.setPrimaryKeyGenerator(null);
+        }
+
+        showCustomPKSequencePanel(entity, custom);
+        session.fireDbEntityEvent(DbEntityEvent.ofChange(this, entity));
+
+        edit.captureNewState();
+        if (edit.hasRealChange()) {
+            app.getUndoManager().addEdit(edit);
+        }
+    }
+
+    private void showCustomPKSequencePanel(DbEntity entity, boolean visible) {
+        customPKSequencePanel.setDbEntity(entity);
+        customPKSequencePanel.setVisible(visible);
+        // the panel appearing or collapsing shifts everything below it
+        customPKSequencePanel.getParent().revalidate();
     }
 
     public void processExistingSelection(EventObject e) {
@@ -198,40 +189,22 @@ public class DbEntityMainView extends ProjectPanel
             return;
         }
 
-        // if entity hasn't changed, still notify PK Generator panels, as 
entity PK may have changed...
-        for (int i = 0; i < pkGeneratorDetail.getComponentCount(); i++) {
-            ((PKGeneratorPanel) 
pkGeneratorDetail.getComponent(i)).setDbEntity(entity);
-        }
-
         name.setText(entity.getName());
         catalog.setText(entity.getCatalog());
         schema.setText(entity.getSchema());
         qualifier.setText(ExpressionConvertor.asString(entity.getQualifier()));
         comment.setText(getComment(entity));
 
-        String type = PK_DEFAULT_GENERATOR;
-
-        if (entity.getPrimaryKeyGenerator() != null) {
-            type = PK_CUSTOM_SEQUENCE_GENERATOR;
-        } else {
-            for (DbAttribute a : entity.getPrimaryKeys()) {
-                if (a.isGenerated()) {
-                    type = PK_DB_GENERATOR;
-                    break;
-                }
-            }
-        }
-
         catalogLabel.setEnabled(true);
         catalog.setEnabled(true);
 
         schemaLabel.setEnabled(true);
         schema.setEnabled(true);
-        pkGeneratorDetail.setVisible(true);
-        pkGeneratorType.setVisible(true);
 
-        pkGeneratorType.setSelectedItem(type);
-        showPKGeneratorDetail(type);
+        // setSelected() does not fire an ActionEvent, so this doesn't loop 
back into the model
+        boolean custom = entity.getPrimaryKeyGenerator() != null;
+        customPKSequence.setSelected(custom);
+        showCustomPKSequencePanel(entity, custom);
 
         if(entity.getDataMap().getMappedEntities(entity).isEmpty()) {
             toolBar.getComponentAtIndex(4).setEnabled(false);
@@ -242,40 +215,6 @@ public class DbEntityMainView extends ProjectPanel
         }
     }
 
-    @Override
-    public void dbAttributeAdded(DbAttributeEvent e) {
-        refreshPKGenerator(e);
-    }
-
-    @Override
-    public void dbAttributeChanged(DbAttributeEvent e) {
-        refreshPKGenerator(e);
-    }
-
-    @Override
-    public void dbAttributeRemoved(DbAttributeEvent e) {
-        refreshPKGenerator(e);
-    }
-
-    /**
-     * The PK generator panels are driven by the entity attributes, so they 
must be resynced whenever
-     * attributes change, and not just on entity selection. Without this the 
"Auto Increment" column
-     * list goes stale as soon as PKs are added or removed from the Properties 
tab.
-     */
-    private void refreshPKGenerator(DbAttributeEvent e) {
-        DbEntity entity = session.getSelectedDbEntity();
-
-        // attributes of some other entity, or a non-DbEntity node is selected
-        if (entity == null || e.getEntity() != entity) {
-            return;
-        }
-
-        PKGeneratorPanel panel = visiblePKGeneratorPanel();
-        if (panel != null) {
-            panel.onInit(entity);
-        }
-    }
-
     void setEntityName(String newName) {
         if (newName != null && newName.trim().isEmpty()) {
             newName = null;
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKCustomSequenceGeneratorPanel.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKCustomSequenceGeneratorPanel.java
index c4dbb5130..78a00fd1e 100644
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKCustomSequenceGeneratorPanel.java
+++ 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKCustomSequenceGeneratorPanel.java
@@ -24,6 +24,7 @@ import com.jgoodies.forms.layout.FormLayout;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbKeyGenerator;
 import org.apache.cayenne.modeler.event.model.DbEntityEvent;
+import org.apache.cayenne.modeler.toolkit.ProjectPanel;
 import org.apache.cayenne.modeler.toolkit.text.CMUndoableTextField;
 import org.apache.cayenne.modeler.project.ProjectSession;
 import java.util.Objects;
@@ -33,7 +34,7 @@ import org.apache.cayenne.validation.ValidationException;
 import javax.swing.*;
 import java.awt.*;
 
-public class PKCustomSequenceGeneratorPanel extends PKGeneratorPanel {
+public class PKCustomSequenceGeneratorPanel extends ProjectPanel {
 
     protected CMUndoableTextField customPKName;
     protected CMUndoableTextField customPKSize;
@@ -71,18 +72,6 @@ public class PKCustomSequenceGeneratorPanel extends 
PKGeneratorPanel {
         add(builder.getPanel(), BorderLayout.CENTER);
     }
 
-    protected void onInitInternal(DbEntity entity) {
-        resetStrategy(entity, false, true);
-
-        if (entity.getPrimaryKeyGenerator() == null) {
-            DbKeyGenerator generator = new DbKeyGenerator();
-            generator.setGeneratorType(DbKeyGenerator.ORACLE_TYPE);
-            entity.setPrimaryKeyGenerator(generator);
-        } else {
-            setDbEntity(entity);
-        }
-    }
-
     public void setDbEntity(DbEntity entity) {
         DbKeyGenerator generator = entity.getPrimaryKeyGenerator();
 
@@ -133,7 +122,7 @@ public class PKCustomSequenceGeneratorPanel extends 
PKGeneratorPanel {
         }
 
         DbKeyGenerator generator = 
session.getSelectedDbEntity().getPrimaryKeyGenerator();
-        if (!Objects.equals(text, generator.getName())) {
+        if (!Objects.equals(text, generator.getGeneratorName())) {
             generator.setGeneratorName(text);
             session.fireDbEntityEvent(DbEntityEvent.ofChange(this, 
generator.getDbEntity()));
         }
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKDBGeneratorPanel.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKDBGeneratorPanel.java
deleted file mode 100644
index 15ecefae8..000000000
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKDBGeneratorPanel.java
+++ /dev/null
@@ -1,155 +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.ui.project.editor.dbentity.main;
-
-import com.jgoodies.forms.builder.DefaultFormBuilder;
-import com.jgoodies.forms.layout.FormLayout;
-import org.apache.cayenne.dba.TypesMapping;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.modeler.event.model.DbEntityEvent;
-import org.apache.cayenne.modeler.project.ProjectSession;
-import org.apache.cayenne.modeler.ui.project.editor.EditorForm;
-
-import javax.swing.*;
-import javax.swing.plaf.basic.BasicComboBoxRenderer;
-import java.awt.*;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.util.Collection;
-
-public class PKDBGeneratorPanel extends PKGeneratorPanel {
-
-    private final JComboBox<Object> attributes;
-
-    public PKDBGeneratorPanel(ProjectSession session) {
-        super(session);
-        this.attributes = new JComboBox<>();
-        initLayout();
-    }
-
-    private void initLayout() {
-        attributes.setEditable(false);
-        attributes.setRenderer(new AttributeRenderer());
-
-        DefaultFormBuilder builder = new DefaultFormBuilder(new FormLayout(
-                EditorForm.LABEL_COLUMN + ", $lcgap, fill:200dlu",
-                ""));
-        builder.setBorder(EditorForm.sectionBorder());
-        builder.append("Auto Increment:", attributes);
-
-        setLayout(new BorderLayout());
-        add(builder.getPanel(), BorderLayout.CENTER);
-    }
-
-    public void setDbEntity(DbEntity entity) {
-        // refresh only if this entity
-        if (isVisible()) {
-            updateView(entity);
-        }
-    }
-
-    protected void onInitInternal(DbEntity entity) {
-        resetStrategy(entity, true, false);
-
-        Collection pkAttributes = entity.getPrimaryKeys();
-
-        // by default check the only numeric PK
-        if (pkAttributes.size() == 1) {
-            DbAttribute pk = (DbAttribute) pkAttributes.iterator().next();
-            if (TypesMapping.isNumeric(pk.getType()) && !pk.isGenerated()) {
-                pk.setGenerated(true);
-                session.fireDbEntityEvent(DbEntityEvent.ofChange(this, 
entity));
-            }
-        }
-
-        updateView(entity);
-    }
-
-    void updateView(final DbEntity entity) {
-        for (ItemListener listener : attributes.getItemListeners()) {
-            attributes.removeItemListener(listener);
-        }
-
-        Collection<DbAttribute> pkAttributes = entity.getPrimaryKeys();
-        if (pkAttributes.isEmpty()) {
-            attributes.removeAllItems();
-            attributes.addItem("<Entity has no PK columns>");
-            attributes.setSelectedIndex(0);
-            attributes.setEnabled(false);
-        }
-        else {
-
-            attributes.setEnabled(true);
-            MutableComboBoxModel<Object> model = new 
DefaultComboBoxModel<>(pkAttributes.toArray());
-            String noSelection = "<Select Generated Column>";
-            model.insertElementAt(noSelection, 0);
-            model.setSelectedItem(noSelection);
-            attributes.setModel(model);
-
-            for (DbAttribute a : pkAttributes) {
-                if (a.isGenerated()) {
-                    model.setSelectedItem(a);
-                    break;
-                }
-            }
-
-            // listen for selection changes of the new entity
-            attributes.addItemListener(e -> {
-                Object item = e.getItem();
-                if (item instanceof DbAttribute a) {
-
-                    boolean generated = e.getStateChange() == 
ItemEvent.SELECTED;
-
-                    if (a.isGenerated() != generated) {
-                        a.setGenerated(generated);
-                        session.fireDbEntityEvent(DbEntityEvent.ofChange(this, 
entity));
-                    }
-                }
-            });
-        }
-
-        // revalidate as children layout has changed...
-        revalidate();
-    }
-
-    static class AttributeRenderer extends BasicComboBoxRenderer {
-
-        public Component getListCellRendererComponent(
-                JList list,
-                Object value,
-                int index,
-                boolean isSelected,
-                boolean cellHasFocus) {
-
-            if (value instanceof DbAttribute a) {
-                String type = TypesMapping.getSqlNameByType(a.getType());
-                value = a.getName() + " (" + (type != null ? type : "?") + ")";
-            }
-
-            return super.getListCellRendererComponent(
-                    list,
-                    value,
-                    index,
-                    isSelected,
-                    cellHasFocus);
-        }
-    }
-}
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKGeneratorPanel.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKGeneratorPanel.java
deleted file mode 100644
index 4c86d1511..000000000
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/main/PKGeneratorPanel.java
+++ /dev/null
@@ -1,84 +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.ui.project.editor.dbentity.main;
-
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.modeler.event.model.DbEntityEvent;
-import org.apache.cayenne.modeler.project.ProjectSession;
-import org.apache.cayenne.modeler.toolkit.ProjectPanel;
-import org.apache.cayenne.modeler.undo.ChangePKGeneratorUndoableEdit;
-
-public abstract class PKGeneratorPanel extends ProjectPanel {
-
-    public PKGeneratorPanel(ProjectSession session) {
-        super(session);
-    }
-
-    /**
-     * Called by parent when DbEntity changes, regardless of whether this 
panel is visible
-     * or not. Another case when this method is invoked is when entity tab 
changes and
-     * this panel may need a refresh.
-     */
-    public abstract void setDbEntity(DbEntity entity);
-
-    /**
-     * Called by parent when the panel becomes visible.
-     */
-    public void onInit(DbEntity entity) {
-        ChangePKGeneratorUndoableEdit edit = new 
ChangePKGeneratorUndoableEdit(session, entity);
-        edit.captureOldState();
-
-        onInitInternal(entity);
-
-        edit.captureNewState();
-        if (edit.hasRealChange()) {
-            app.getUndoManager().addEdit(edit);
-        }
-    }
-
-    protected abstract void onInitInternal(DbEntity entity);
-
-    protected void resetStrategy(
-            DbEntity entity,
-            boolean resetCustomSequence,
-            boolean resetDBGenerated) {
-
-        boolean hasChanges = false;
-
-        if (resetCustomSequence && entity.getPrimaryKeyGenerator() != null) {
-            entity.setPrimaryKeyGenerator(null);
-            hasChanges = true;
-        }
-
-        if (resetDBGenerated) {
-            for (DbAttribute a : entity.getPrimaryKeys()) {
-                if (a.isGenerated()) {
-                    a.setGenerated(false);
-                    hasChanges = true;
-                }
-            }
-        }
-
-        if (hasChanges) {
-            session.fireDbEntityEvent(DbEntityEvent.ofChange(this, entity));
-        }
-    }
-}
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbAttributePanel.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbAttributePanel.java
index be320c60a..83da5c138 100644
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbAttributePanel.java
+++ 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbAttributePanel.java
@@ -33,6 +33,7 @@ import org.apache.cayenne.modeler.toolkit.ProjectPanel;
 import org.apache.cayenne.modeler.toolkit.combobox.AutoCompletion;
 import org.apache.cayenne.modeler.toolkit.combobox.CMComboBox;
 import org.apache.cayenne.modeler.toolkit.table.BoardTableCellRenderer;
+import org.apache.cayenne.modeler.toolkit.table.BooleanTableCellRenderer;
 import 
org.apache.cayenne.modeler.toolkit.combobox.CMAutoCompleteComboBoxCellEditor;
 import org.apache.cayenne.modeler.toolkit.table.CMTable;
 import org.apache.cayenne.modeler.toolkit.table.CMTablePanel;
@@ -74,6 +75,8 @@ public class DbAttributePanel extends ProjectPanel implements 
DbEntityDisplayLis
         table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
         table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
         table.setDefaultRenderer(String.class, new BoardTableCellRenderer());
+        table.setDefaultRenderer(Boolean.class,
+                new 
BooleanTableCellRenderer(table.getDefaultRenderer(Boolean.class)));
 
         JPopupMenu popup = new JPopupMenu();
         
popup.add(globalActions.getAction(RemoveAttributeRelationshipAction.class).buildMenu());
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbAttributeTableModel.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbAttributeTableModel.java
index c726389fa..72b9721a2 100644
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbAttributeTableModel.java
+++ 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbAttributeTableModel.java
@@ -27,6 +27,7 @@ import 
org.apache.cayenne.modeler.event.model.DbAttributeEvent;
 import org.apache.cayenne.modeler.project.DbAttributeOps;
 import org.apache.cayenne.modeler.toolkit.table.CMTableModel;
 import org.apache.cayenne.modeler.project.ProjectSession;
+import org.apache.cayenne.modeler.undo.ChangePKGeneratorUndoableEdit;
 import org.apache.cayenne.project.extension.info.ObjectInfo;
 import java.util.Objects;
 
@@ -34,6 +35,7 @@ import javax.swing.*;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 
 /**
  * Model for DbEntity attributes. Allows adding/removing attributes, modifying 
types and names.
@@ -43,10 +45,11 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
     static final int DB_ATTRIBUTE_NAME = 0;
     static final int DB_ATTRIBUTE_TYPE = 1;
     static final int DB_ATTRIBUTE_PRIMARY_KEY = 2;
-    static final int DB_ATTRIBUTE_MANDATORY = 3;
-    static final int DB_ATTRIBUTE_MAX = 4;
-    static final int DB_ATTRIBUTE_SCALE = 5;
-    static final int DB_ATTRIBUTE_COMMENT = 6;
+    static final int DB_ATTRIBUTE_AUTO_INCREMENT = 3;
+    static final int DB_ATTRIBUTE_MANDATORY = 4;
+    static final int DB_ATTRIBUTE_MAX = 5;
+    static final int DB_ATTRIBUTE_SCALE = 6;
+    static final int DB_ATTRIBUTE_COMMENT = 7;
 
     private final DbEntity entity;
 
@@ -62,7 +65,7 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
 
     @Override
     public int getColumnCount() {
-        return 7;
+        return 8;
     }
 
     public DbAttribute getAttribute(int row) {
@@ -79,6 +82,8 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
                 return "Type";
             case DB_ATTRIBUTE_PRIMARY_KEY:
                 return "PK";
+            case DB_ATTRIBUTE_AUTO_INCREMENT:
+                return "Auto-Increment";
             case DB_ATTRIBUTE_SCALE:
                 return "Scale";
             case DB_ATTRIBUTE_MANDATORY:
@@ -96,6 +101,7 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
     public Class<?> getColumnClass(int col) {
         switch (col) {
             case DB_ATTRIBUTE_PRIMARY_KEY:
+            case DB_ATTRIBUTE_AUTO_INCREMENT:
             case DB_ATTRIBUTE_MANDATORY:
                 return Boolean.class;
             default:
@@ -117,6 +123,8 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
                 return getAttributeType(attr);
             case DB_ATTRIBUTE_PRIMARY_KEY:
                 return isPrimaryKey(attr);
+            case DB_ATTRIBUTE_AUTO_INCREMENT:
+                return isGenerated(attr);
             case DB_ATTRIBUTE_SCALE:
                 return getScale(attr);
             case DB_ATTRIBUTE_MANDATORY:
@@ -130,6 +138,32 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
         }
     }
 
+    @Override
+    public void setValueAt(Object newVal, int row, int col) {
+
+        // Auto-Increment is an entity-wide setting: turning it on for one 
attribute turns it off for
+        // another one. The single-cell edit registered by the superclass 
can't restore that, so use the
+        // edit that captures the entity PK generation state as a whole.
+        if (col == DB_ATTRIBUTE_AUTO_INCREMENT) {
+            if (Objects.deepEquals(newVal, getValueAt(row, col))) {
+                return;
+            }
+
+            ChangePKGeneratorUndoableEdit edit = new 
ChangePKGeneratorUndoableEdit(session, entity);
+            edit.captureOldState();
+
+            setUpdatedValueAt(newVal, row, col);
+
+            edit.captureNewState();
+            if (edit.hasRealChange()) {
+                session.app().getUndoManager().addEdit(edit);
+            }
+            return;
+        }
+
+        super.setValueAt(newVal, row, col);
+    }
+
     public void setUpdatedValueAt(Object newVal, int row, int col) {
         DbAttribute attr = getAttribute(row);
         if (attr == null) {
@@ -151,6 +185,9 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
                     return;
                 }
                 break;
+            case DB_ATTRIBUTE_AUTO_INCREMENT:
+                setGenerated((Boolean) newVal, attr);
+                break;
             case DB_ATTRIBUTE_SCALE:
                 setScale((String) newVal, attr);
                 break;
@@ -202,6 +239,10 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
         return (attr.isPrimaryKey()) ? Boolean.TRUE : Boolean.FALSE;
     }
 
+    public Boolean isGenerated(DbAttribute attr) {
+        return (attr.isGenerated()) ? Boolean.TRUE : Boolean.FALSE;
+    }
+
     public Boolean isMandatory(DbAttribute attr) {
         return (attr.isMandatory()) ? Boolean.TRUE : Boolean.FALSE;
     }
@@ -287,10 +328,35 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
         if (flag) {
             attr.setMandatory(true);
             fireTableCellUpdated(row, DB_ATTRIBUTE_MANDATORY);
+        } else {
+            fireTableCellUpdated(row, DB_ATTRIBUTE_AUTO_INCREMENT);
         }
         return true;
     }
 
+    /**
+     * An entity can have at most one generated attribute, so setting this 
flag clears it on all the
+     * other attributes.
+     */
+    public void setGenerated(Boolean newVal, DbAttribute attr) {
+
+        if (newVal) {
+            // copy, as setGenerated() mutates the entity's own collection
+            List<DbAttribute> generated = new 
ArrayList<>(entity.getGeneratedAttributes());
+            for (DbAttribute other : generated) {
+                if (other != attr) {
+                    other.setGenerated(false);
+                    int otherRow = objectList.indexOf(other);
+                    if (otherRow >= 0) {
+                        fireTableCellUpdated(otherRow, 
DB_ATTRIBUTE_AUTO_INCREMENT);
+                    }
+                }
+            }
+        }
+
+        attr.setGenerated(newVal);
+    }
+
     public void setMandatory(Boolean newVal, DbAttribute attr) {
         attr.setMandatory(newVal);
     }
@@ -305,6 +371,9 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
             return false;
         } else if (col == DB_ATTRIBUTE_MANDATORY) {
             return !attrib.isPrimaryKey();
+        } else if (col == DB_ATTRIBUTE_AUTO_INCREMENT) {
+            // DbAttribute.encodeAsXML() only stores "isGenerated" for PKs, so 
don't let it be set elsewhere
+            return attrib.isPrimaryKey();
         }
         return true;
     }
@@ -343,6 +412,9 @@ public class DbAttributeTableModel extends 
CMTableModel<DbAttribute> {
             case DB_ATTRIBUTE_PRIMARY_KEY:
                 sortByElementProperty("primaryKey", isAscent);
                 break;
+            case DB_ATTRIBUTE_AUTO_INCREMENT:
+                sortByElementProperty("generated", isAscent);
+                break;
             case DB_ATTRIBUTE_SCALE:
                 sortByElementProperty("scale", isAscent);
                 break;
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbRelationshipPanel.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbRelationshipPanel.java
index d486ec1d7..9b4ae3140 100644
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbRelationshipPanel.java
+++ 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/dbentity/properties/DbRelationshipPanel.java
@@ -41,6 +41,7 @@ import 
org.apache.cayenne.modeler.event.display.DbRelationshipDisplayEvent;
 import org.apache.cayenne.modeler.event.display.TablePopupHandler;
 import org.apache.cayenne.modeler.pref.adapters.CMTablePrefs;
 import org.apache.cayenne.modeler.toolkit.table.BoardTableCellRenderer;
+import org.apache.cayenne.modeler.toolkit.table.BooleanTableCellRenderer;
 import org.apache.cayenne.modeler.toolkit.table.CMTable;
 import org.apache.cayenne.modeler.toolkit.table.CMTablePanel;
 import org.apache.cayenne.modeler.toolkit.Renderers;
@@ -52,7 +53,6 @@ import javax.swing.*;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.TableModelEvent;
 import javax.swing.event.TableModelListener;
-import javax.swing.table.TableCellRenderer;
 import javax.swing.table.TableColumn;
 import java.awt.*;
 import java.awt.event.ActionEvent;
@@ -90,6 +90,8 @@ public class DbRelationshipPanel extends ProjectPanel 
implements DbEntityDisplay
         table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
         table.setDefaultRenderer(DbEntity.class, 
Renderers.entityTableRendererWithIcons(session));
         table.setDefaultRenderer(String.class, new BoardTableCellRenderer());
+        table.setDefaultRenderer(Boolean.class,
+                new 
BooleanTableCellRenderer(table.getDefaultRenderer(Boolean.class)));
 
         JPopupMenu popup = new JPopupMenu();
         popup.add(editMenu);
@@ -241,9 +243,6 @@ public class DbRelationshipPanel extends ProjectPanel 
implements DbEntityDisplay
         TableColumn targetColumn = 
table.getColumnModel().getColumn(DbRelationshipTableModel.TARGET);
         targetColumn.setCellEditor(new 
CMAutoCompleteComboBoxCellEditor(targetCombo));
 
-        TableColumn toDepPkColumn = 
table.getColumnModel().getColumn(DbRelationshipTableModel.TO_DEPENDENT_KEY);
-        toDepPkColumn.setCellRenderer(new CheckBoxCellRenderer());
-
         new 
CMTablePrefs(app.getPrefsManager().uiNode("dbEntity/relationshipTable"))
                 .bind(table, null, DbRelationshipTableModel.NAME);
     }
@@ -371,23 +370,4 @@ public class DbRelationshipPanel extends ProjectPanel 
implements DbEntityDisplay
 
         parentPanel.updateActions(rels);
     }
-
-    private static class CheckBoxCellRenderer implements TableCellRenderer {
-
-        private final JCheckBox renderer;
-
-        public CheckBoxCellRenderer() {
-            renderer = new JCheckBox();
-            renderer.setHorizontalAlignment(SwingConstants.CENTER);
-        }
-
-        @Override
-        public Component getTableCellRendererComponent(JTable table, Object 
value, boolean isSelected, boolean hasFocus, int row, int column) {
-            Color color = isSelected ? table.getSelectionBackground() : 
table.getBackground();
-            renderer.setBackground(color);
-            renderer.setEnabled(table.isCellEditable(row, column));
-            renderer.setSelected(value != null && (Boolean) value);
-            return renderer;
-        }
-    }
 }
\ No newline at end of file
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/objentity/properties/ObjAttributePanel.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/objentity/properties/ObjAttributePanel.java
index 1ef32f16f..8b243a4b1 100644
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/objentity/properties/ObjAttributePanel.java
+++ 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/objentity/properties/ObjAttributePanel.java
@@ -33,6 +33,7 @@ import org.apache.cayenne.modeler.event.model.ObjEntityEvent;
 import org.apache.cayenne.modeler.event.model.ObjEntityListener;
 import org.apache.cayenne.modeler.event.model.ProjectBeforeSaveEvent;
 import org.apache.cayenne.modeler.event.model.ProjectBeforeSaveListener;
+import org.apache.cayenne.modeler.toolkit.table.BooleanTableCellRenderer;
 import org.apache.cayenne.modeler.toolkit.ProjectPanel;
 import org.apache.cayenne.modeler.pref.adapters.CMTablePrefs;
 import org.apache.cayenne.modeler.project.ObjEntityOps;
@@ -99,6 +100,8 @@ public class ObjAttributePanel extends ProjectPanel 
implements ObjEntityDisplayL
         table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
         table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
         table.setDefaultRenderer(String.class, new CellRenderer());
+        table.setDefaultRenderer(Boolean.class,
+                new 
BooleanTableCellRenderer(table.getDefaultRenderer(Boolean.class)));
 
         JPopupMenu popup = new JPopupMenu();
         popup.add(editMenu);
diff --git 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/objentity/properties/ObjRelationshipPanel.java
 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/objentity/properties/ObjRelationshipPanel.java
index 1e0ba6a0a..90fbc2ebc 100644
--- 
a/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/objentity/properties/ObjRelationshipPanel.java
+++ 
b/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/objentity/properties/ObjRelationshipPanel.java
@@ -26,6 +26,7 @@ import 
org.apache.cayenne.modeler.event.model.ObjRelationshipEvent;
 import org.apache.cayenne.modeler.event.model.ObjEntityEvent;
 import org.apache.cayenne.modeler.event.model.ObjEntityListener;
 import org.apache.cayenne.modeler.event.model.ObjRelationshipListener;
+import org.apache.cayenne.modeler.toolkit.table.BooleanTableCellRenderer;
 import org.apache.cayenne.modeler.toolkit.ProjectPanel;
 import org.apache.cayenne.modeler.toolkit.combobox.CMComboBox;
 import org.apache.cayenne.modeler.toolkit.combobox.CMComboBoxCellEditor;
@@ -94,6 +95,8 @@ public class ObjRelationshipPanel extends ProjectPanel 
implements ObjEntityDispl
         table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
         table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
         table.setDefaultRenderer(String.class, new StringRenderer());
+        table.setDefaultRenderer(Boolean.class,
+                new 
BooleanTableCellRenderer(table.getDefaultRenderer(Boolean.class)));
         table.setDefaultRenderer(ObjEntity.class, new EntityRenderer());
 
         JPopupMenu popup = new JPopupMenu();

Reply via email to