Bug 581506 - Incorrect groups after loadState()

Fixed additional issues related to non-consecutive columns at creation
time and states that were applied after group creation.

Signed-off-by: Dirk Fauth <dirk.fauth@googlemail.com>

Change-Id: I52c81bde2616503def51622967b7064773f84968
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayerTest.java
index f457362..5b92546 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayerTest.java
@@ -14151,7 +14151,7 @@
 
         verifyCleanState();
 
-        // load single collapsed
+        // load reordered
         this.gridLayer.loadState("one", properties);
 
         group = this.columnGroupHeaderLayer.getGroupByPosition(2);
@@ -14169,4 +14169,148 @@
 
         verifyCleanState();
     }
+
+    @Test
+    public void shouldLoadStateWithHiddenFirstColumn() {
+        verifyCleanState();
+
+        Properties properties = new Properties();
+        this.gridLayer.saveState("clean", properties);
+
+        // remove all groups
+        this.gridLayer.doCommand(new RemoveColumnGroupCommand(0));
+        this.gridLayer.doCommand(new RemoveColumnGroupCommand(4));
+        this.gridLayer.doCommand(new RemoveColumnGroupCommand(8));
+        this.gridLayer.doCommand(new RemoveColumnGroupCommand(11));
+
+        assertEquals(0, this.columnGroupHeaderLayer.getGroupModel().getGroups().size());
+
+        // create column group
+        this.selectionLayer.selectColumn(2, 0, true, true);
+        this.selectionLayer.selectColumn(3, 0, true, true);
+        this.selectionLayer.selectColumn(4, 0, true, true);
+        this.gridLayer.doCommand(new CreateColumnGroupCommand("Test"));
+
+        // hide first column in group
+        this.gridLayer.doCommand(new ColumnHideCommand(this.selectionLayer, 2));
+
+        Group group = this.columnGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertFalse(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(3, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(3, group.getOriginalSpan());
+        assertEquals(2, group.getVisibleSpan());
+
+        this.gridLayer.saveState("one", properties);
+
+        // restore the clean state again
+        this.gridLayer.loadState("clean", properties);
+
+        verifyCleanState();
+
+        // load hidden
+        this.gridLayer.loadState("one", properties);
+
+        group = this.columnGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertFalse(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(3, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(3, group.getOriginalSpan());
+        assertEquals(2, group.getVisibleSpan());
+
+        // collapse
+        this.gridLayer.doCommand(new ColumnGroupExpandCollapseCommand(this.selectionLayer, 2));
+
+        group = this.columnGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertTrue(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(3, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(3, group.getOriginalSpan());
+        assertEquals(1, group.getVisibleSpan());
+
+        // restore the clean state again
+        this.gridLayer.loadState("clean", properties);
+
+        verifyCleanState();
+    }
+
+    @Test
+    public void shouldLoadStateWithCollapsedNonConsecutive() {
+        verifyCleanState();
+
+        Properties properties = new Properties();
+        this.gridLayer.saveState("clean", properties);
+
+        // remove all groups
+        this.gridLayer.doCommand(new RemoveColumnGroupCommand(0));
+        this.gridLayer.doCommand(new RemoveColumnGroupCommand(4));
+        this.gridLayer.doCommand(new RemoveColumnGroupCommand(8));
+        this.gridLayer.doCommand(new RemoveColumnGroupCommand(11));
+
+        assertEquals(0, this.columnGroupHeaderLayer.getGroupModel().getGroups().size());
+
+        // create column group
+        this.selectionLayer.selectColumn(2, 0, false, true);
+        this.selectionLayer.selectColumn(3, 0, false, true);
+        this.selectionLayer.selectColumn(5, 0, false, true);
+        this.selectionLayer.selectColumn(6, 0, false, true);
+        this.gridLayer.doCommand(new CreateColumnGroupCommand("Test"));
+
+        Group group = this.columnGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertFalse(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(2, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(4, group.getOriginalSpan());
+        assertEquals(4, group.getVisibleSpan());
+
+        // collapse
+        this.gridLayer.doCommand(new ColumnGroupExpandCollapseCommand(this.selectionLayer, 2));
+
+        group = this.columnGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertTrue(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(2, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(4, group.getOriginalSpan());
+        assertEquals(1, group.getVisibleSpan());
+
+        this.gridLayer.saveState("one", properties);
+
+        // restore the clean state again
+        this.gridLayer.loadState("clean", properties);
+
+        verifyCleanState();
+
+        // load collapsed
+        this.gridLayer.loadState("one", properties);
+
+        group = this.columnGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertTrue(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(2, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(4, group.getOriginalSpan());
+        assertEquals(1, group.getVisibleSpan());
+
+        // restore the clean state again
+        this.gridLayer.loadState("clean", properties);
+
+        verifyCleanState();
+    }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModelTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModelTest.java
index 4786501..f0e84bd 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModelTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModelTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019, 2020 Dirk Fauth.
+ * Copyright (c) 2019, 2023 Dirk Fauth.
  *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
@@ -1139,9 +1139,9 @@
 
         assertEquals(1, properties.size());
         assertEquals(
-                "testGroupName=0:0:0:4:4:expanded:uncollapseable:breakable:1,2|"
-                        + "testGroupName2=5:6:5:3:2:expanded:collapseable:unbreakable|"
-                        + "testGroupName3=12:12:12:2:2:collapsed:collapseable:breakable|",
+                "testGroupName=0:0:0:4:4:expanded:uncollapseable:breakable:1,2:members[0,1,2,3]|"
+                        + "testGroupName2=5:6:5:3:2:expanded:collapseable:unbreakable:members[5,6,7]|"
+                        + "testGroupName3=12:12:12:2:2:collapsed:collapseable:breakable:members[12,13]|",
                 properties.getProperty("prefix.groupModel"));
     }
 
@@ -1200,4 +1200,69 @@
         assertFalse(group3.isUnbreakable());
         assertEquals(0, group3.getStaticIndexes().length);
     }
+
+    @Test
+    public void shouldLoadStateWithMembers() {
+        Properties properties = new Properties();
+        properties.setProperty(
+                "prefix.groupModel",
+                "testGroupName=0:0:0:4:4:expanded:uncollapseable:breakable:1,2:members[0,1,3,4]|"
+                        + "testGroupName2=5:6:5:3:2:expanded:collapseable:unbreakable:members[5,2,7]|"
+                        + "testGroupName3=12:12:12:2:2:collapsed:collapseable:breakable:members[14,13]|");
+
+        GroupModel tempModel = new GroupModel();
+        tempModel.loadState("prefix", properties);
+
+        assertTrue(tempModel.isPartOfAGroup(0));
+        assertTrue(tempModel.isPartOfAGroup(5));
+        assertTrue(tempModel.isPartOfAGroup(12));
+
+        Group group1 = tempModel.getGroupByPosition(0);
+        Group group2 = tempModel.getGroupByPosition(5);
+        Group group3 = tempModel.getGroupByPosition(12);
+
+        assertEquals("testGroupName", group1.getName());
+        assertEquals(0, group1.getStartIndex());
+        assertEquals(0, group1.getVisibleStartIndex());
+        assertEquals(0, group1.getVisibleStartPosition());
+        assertEquals(4, group1.getOriginalSpan());
+        assertEquals(4, group1.getVisibleSpan());
+        assertFalse(group1.isCollapsed());
+        assertFalse(group1.isCollapseable());
+        assertFalse(group1.isUnbreakable());
+        assertEquals(2, group1.getStaticIndexes().length);
+        assertTrue(group1.containsStaticIndex(1));
+        assertTrue(group1.containsStaticIndex(2));
+        assertTrue(group1.hasMember(0));
+        assertTrue(group1.hasMember(1));
+        assertTrue(group1.hasMember(3));
+        assertTrue(group1.hasMember(4));
+
+        assertEquals("testGroupName2", group2.getName());
+        assertEquals(5, group2.getStartIndex());
+        assertEquals(6, group2.getVisibleStartIndex());
+        assertEquals(5, group2.getVisibleStartPosition());
+        assertEquals(3, group2.getOriginalSpan());
+        assertEquals(2, group2.getVisibleSpan());
+        assertFalse(group2.isCollapsed());
+        assertTrue(group2.isCollapseable());
+        assertTrue(group2.isUnbreakable());
+        assertEquals(0, group2.getStaticIndexes().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(2));
+        assertTrue(group2.hasMember(7));
+
+        assertEquals("testGroupName3", group3.getName());
+        assertEquals(12, group3.getStartIndex());
+        assertEquals(12, group3.getVisibleStartIndex());
+        assertEquals(12, group3.getVisibleStartPosition());
+        assertEquals(2, group3.getOriginalSpan());
+        assertEquals(2, group3.getVisibleSpan());
+        assertTrue(group3.isCollapsed());
+        assertTrue(group3.isCollapseable());
+        assertFalse(group3.isUnbreakable());
+        assertEquals(0, group3.getStaticIndexes().length);
+        assertTrue(group3.hasMember(13));
+        assertTrue(group3.hasMember(14));
+    }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayerTest.java
index 7e30898..7b92d7d 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayerTest.java
@@ -14129,7 +14129,7 @@
 
         verifyCleanState();
 
-        // load single collapsed
+        // load reordered
         this.gridLayer.loadState("one", properties);
 
         group = this.rowGroupHeaderLayer.getGroupByPosition(2);
@@ -14148,4 +14148,148 @@
         verifyCleanState();
     }
 
+    @Test
+    public void shouldLoadStateWithHiddenFirstRow() {
+        verifyCleanState();
+
+        Properties properties = new Properties();
+        this.gridLayer.saveState("clean", properties);
+
+        // remove all groups
+        this.gridLayer.doCommand(new RemoveRowGroupCommand(0));
+        this.gridLayer.doCommand(new RemoveRowGroupCommand(4));
+        this.gridLayer.doCommand(new RemoveRowGroupCommand(8));
+        this.gridLayer.doCommand(new RemoveRowGroupCommand(11));
+
+        assertEquals(0, this.rowGroupHeaderLayer.getGroupModel().getGroups().size());
+
+        // create row group
+        this.selectionLayer.selectRow(0, 2, true, true);
+        this.selectionLayer.selectRow(0, 3, true, true);
+        this.selectionLayer.selectRow(0, 4, true, true);
+        this.gridLayer.doCommand(new CreateRowGroupCommand("Test"));
+
+        // hide first row in group
+        this.gridLayer.doCommand(new RowHideCommand(this.selectionLayer, 2));
+
+        Group group = this.rowGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertFalse(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(3, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(3, group.getOriginalSpan());
+        assertEquals(2, group.getVisibleSpan());
+
+        this.gridLayer.saveState("one", properties);
+
+        // restore the clean state again
+        this.gridLayer.loadState("clean", properties);
+
+        verifyCleanState();
+
+        // load hidden
+        this.gridLayer.loadState("one", properties);
+
+        group = this.rowGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertFalse(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(3, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(3, group.getOriginalSpan());
+        assertEquals(2, group.getVisibleSpan());
+
+        // collapse
+        this.gridLayer.doCommand(new RowGroupExpandCollapseCommand(this.selectionLayer, 2));
+
+        group = this.rowGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertTrue(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(3, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(3, group.getOriginalSpan());
+        assertEquals(1, group.getVisibleSpan());
+
+        // restore the clean state again
+        this.gridLayer.loadState("clean", properties);
+
+        verifyCleanState();
+    }
+
+    @Test
+    public void shouldLoadStateWithCollapsedNonConsecutive() {
+        verifyCleanState();
+
+        Properties properties = new Properties();
+        this.gridLayer.saveState("clean", properties);
+
+        // remove all groups
+        this.gridLayer.doCommand(new RemoveRowGroupCommand(0));
+        this.gridLayer.doCommand(new RemoveRowGroupCommand(4));
+        this.gridLayer.doCommand(new RemoveRowGroupCommand(8));
+        this.gridLayer.doCommand(new RemoveRowGroupCommand(11));
+
+        assertEquals(0, this.rowGroupHeaderLayer.getGroupModel().getGroups().size());
+
+        // create row group
+        this.selectionLayer.selectRow(0, 2, false, true);
+        this.selectionLayer.selectRow(0, 3, false, true);
+        this.selectionLayer.selectRow(0, 5, false, true);
+        this.selectionLayer.selectRow(0, 6, false, true);
+        this.gridLayer.doCommand(new CreateRowGroupCommand("Test"));
+
+        Group group = this.rowGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertFalse(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(2, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(4, group.getOriginalSpan());
+        assertEquals(4, group.getVisibleSpan());
+
+        // collapse
+        this.gridLayer.doCommand(new RowGroupExpandCollapseCommand(this.selectionLayer, 2));
+
+        group = this.rowGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertTrue(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(2, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(4, group.getOriginalSpan());
+        assertEquals(1, group.getVisibleSpan());
+
+        this.gridLayer.saveState("one", properties);
+
+        // restore the clean state again
+        this.gridLayer.loadState("clean", properties);
+
+        verifyCleanState();
+
+        // load collapsed
+        this.gridLayer.loadState("one", properties);
+
+        group = this.rowGroupHeaderLayer.getGroupByPosition(2);
+        assertNotNull(group);
+        assertEquals("Test", group.getName());
+        assertTrue(group.isCollapsed());
+        assertEquals(2, group.getStartIndex());
+        assertEquals(2, group.getVisibleStartIndex());
+        assertEquals(2, group.getVisibleStartPosition());
+        assertEquals(4, group.getOriginalSpan());
+        assertEquals(1, group.getVisibleSpan());
+
+        // restore the clean state again
+        this.gridLayer.loadState("clean", properties);
+
+        verifyCleanState();
+    }
+
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupExpandCollapseLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupExpandCollapseLayer.java
index 3009ff4..e5ea1e8 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupExpandCollapseLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupExpandCollapseLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019, 2020 Dirk Fauth.
+ * Copyright (c) 2019, 2023 Dirk Fauth.
  *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
@@ -101,7 +101,7 @@
                     for (int member : group.getMembers()) {
                         int pos = groupModel.getPositionByIndex(member);
                         if (pos > -1) {
-                            columnIndexes.add(pos);
+                            columnIndexes.add(member);
                         }
                     }
                 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModel.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModel.java
index 8ea11f6..80c0b8d 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModel.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModel.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019, 2020 Dirk Fauth.
+ * Copyright (c) 2019, 2023 Dirk Fauth.
  *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
@@ -43,6 +43,11 @@
     private static final String PERSISTENCE_KEY_GROUP_MODEL = ".groupModel"; //$NON-NLS-1$
 
     /**
+     * Prefix for GroupModel member values.
+     */
+    private static final String MEMBER_PERSISTENCE_PREFIX = "members["; //$NON-NLS-1$
+
+    /**
      * Converter to support layer based position-index conversion.
      */
     protected IndexPositionConverter indexPositionConverter;
@@ -185,6 +190,11 @@
                 strBuilder.append(':').append(group.staticIndexes.toSortedList().makeString(IPersistable.VALUE_SEPARATOR));
             }
 
+            // we need to store the members also, as in case of reordering or
+            // hidden columns in the underlying layer, it is not possible to
+            // calculate the correct member indexes again
+            strBuilder.append(':').append(MEMBER_PERSISTENCE_PREFIX).append(group.members.toSortedList().makeString(IPersistable.VALUE_SEPARATOR)).append("]"); //$NON-NLS-1$
+
             strBuilder.append('|');
         }
 
@@ -260,12 +270,31 @@
                     throw new IllegalArgumentException(state + " not one of 'breakable' or 'unbreakable'"); //$NON-NLS-1$
                 }
 
-                if (groupProperties.length == 9) {
-                    String statics = groupProperties[8];
-                    StringTokenizer staticTokenizer = new StringTokenizer(statics, ","); //$NON-NLS-1$
-                    while (staticTokenizer.hasMoreTokens()) {
-                        int index = Integer.parseInt(staticTokenizer.nextToken());
-                        group.staticIndexes.add(index);
+                if (groupProperties.length >= 9) {
+                    String members = null;
+                    if (!groupProperties[8].startsWith(MEMBER_PERSISTENCE_PREFIX)) {
+                        String statics = groupProperties[8];
+                        StringTokenizer staticTokenizer = new StringTokenizer(statics, ","); //$NON-NLS-1$
+                        while (staticTokenizer.hasMoreTokens()) {
+                            int index = Integer.parseInt(staticTokenizer.nextToken());
+                            group.staticIndexes.add(index);
+                        }
+
+                        if (groupProperties.length == 10) {
+                            members = groupProperties[9];
+                        }
+                    } else {
+                        members = groupProperties[8];
+                    }
+
+                    if (members != null) {
+                        members = members.substring(MEMBER_PERSISTENCE_PREFIX.length(), members.length() - 1);
+                        StringTokenizer memberTokenizer = new StringTokenizer(members, ","); //$NON-NLS-1$
+                        group.members.clear();
+                        while (memberTokenizer.hasMoreTokens()) {
+                            int index = Integer.parseInt(memberTokenizer.nextToken());
+                            group.members.add(index);
+                        }
                     }
                 }
             }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupExpandCollapseLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupExpandCollapseLayer.java
index 293181e..196a53e 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupExpandCollapseLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupExpandCollapseLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019, 2020 Dirk Fauth.
+ * Copyright (c) 2019, 2023 Dirk Fauth.
  *
  * This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License 2.0
@@ -102,7 +102,7 @@
                     for (int member : group.getMembers()) {
                         int pos = groupModel.getPositionByIndex(member);
                         if (pos > -1) {
-                            rowIndexes.add(pos);
+                            rowIndexes.add(member);
                         }
                     }
                 }
diff --git a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_818_SortableAllFilterPerformanceColumnGroupExample.java b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_818_SortableAllFilterPerformanceColumnGroupExample.java
index c066b51..5699dbe 100644
--- a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_818_SortableAllFilterPerformanceColumnGroupExample.java
+++ b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_818_SortableAllFilterPerformanceColumnGroupExample.java
@@ -91,6 +91,7 @@
 import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
 import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
 import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.group.performance.ColumnGroupExpandCollapseLayer;
 import org.eclipse.nebula.widgets.nattable.group.performance.ColumnGroupHeaderLayer;
 import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
 import org.eclipse.nebula.widgets.nattable.hover.HoverLayer;
@@ -767,7 +768,6 @@
         private final ListDataProvider<T> bodyDataProvider;
         private final DataLayer bodyDataLayer;
         private final GlazedListsEventLayer<T> glazedListsEventLayer;
-        // private final DetailGlazedListsEventLayer<T> glazedListsEventLayer;
 
         private final SelectionLayer selectionLayer;
 
@@ -790,14 +790,15 @@
 
             // layer for event handling of GlazedLists and PropertyChanges
             this.glazedListsEventLayer =
-                    // new DetailGlazedListsEventLayer<>(this.bodyDataLayer,
-                    // this.filterList);
                     new GlazedListsEventLayer<>(this.bodyDataLayer, this.filterList);
 
             ColumnReorderLayer reorderLayer = new ColumnReorderLayer(this.glazedListsEventLayer);
             ColumnHideShowLayer hideShowLayer = new ColumnHideShowLayer(reorderLayer);
 
-            this.selectionLayer = new SelectionLayer(hideShowLayer);
+            ColumnGroupExpandCollapseLayer columnGroupExpandCollapseLayer =
+                    new ColumnGroupExpandCollapseLayer(hideShowLayer);
+
+            this.selectionLayer = new SelectionLayer(columnGroupExpandCollapseLayer);
             ViewportLayer viewportLayer = new ViewportLayer(this.selectionLayer);
 
             FreezeLayer freezeLayer = new FreezeLayer(this.selectionLayer);