javaeclipsenattable

RowSelection in NatTable with column group header


I created an Eclipse application where I implemented the row selection for the NatTable with a single column header. The row selection works correctly. Later I added a NatTable with a column group header to the application. Here comes problem, after clicking on cell the row selection doesn't work. Only clicked cell is selected.

I define the row selection in RowSelectionAction extends AbstractSelectionAction class. RowSelectionAction object is created in overrided configureBodyMouseClickBindings() method of class extends DefaultSelectionBindings. This method is called during NatTable.configure() method. But it seems configureBodyMouseClickBindings() method isn't called at NatTable created with column groupd headers.

Here is code for creating NatTable with simple column header:

private NatTable createTable(Composite parent, List<TableLine> tLines, String[][] propertyNames,
  PropertyToLabels[] propToLabels, TableParams params, TextMatcherEditor<TableLine>editor) {

BodyLayerStack bodyLayerStack =
    new BodyLayerStack(
            tLines,
            new LineDataProviderColumnAccessor(propertyNames[0].length),
            params.getColumnIndicesForRowHeaders());

DataLayer bodyDataLayer = bodyLayerStack.getBodyDataLayer();
Integer[] rowHeights = params.getRowHeights();

if( rowHeights != null && rowHeights.length > 0 ) {
  for( int i = 0; i < rowHeights.length; i++ ) {
    if( rowHeights[i] != null )
      bodyDataLayer.setRowHeightByPosition(i, rowHeights[i]);
  }
}

Integer[] colWidths = params.getColumnWidths();
if( colWidths != null && colWidths.length > 0 ) {
  for( int i = 0; i < colWidths.length; i++ )
    if( colWidths[i] != null )
      bodyDataLayer.setColumnWidthByPosition(i, colWidths[i]);
}

IDataProvider columnHeaderDataProvider =
    new DefaultColumnHeaderDataProvider(propertyNames[0], propToLabels[0].getPropertyToLabels());
DataLayer columnHeaderDataLayer =
    new DefaultColumnHeaderDataLayer(columnHeaderDataProvider);

ILayer columnHeaderLayer =
    new ColumnHeaderLayer(
        columnHeaderDataLayer,
        bodyLayerStack,
        (SelectionLayer)null);

Integer[] hrHeights = params.getHeaderRowHeights();
if( hrHeights != null && hrHeights.length > 0 )
  columnHeaderDataLayer.setRowHeightByPosition(0, hrHeights[0]);

CompositeLayer composite = new CompositeLayer(1, 2);
composite.setChildLayer(GridRegion.COLUMN_HEADER, columnHeaderLayer, 0, 0);
composite.setChildLayer(GridRegion.BODY, bodyLayerStack, 0, 1);

NatTable natTable = new NatTable(parent, composite, false);
natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
natTable.addConfiguration(new ContextMenuConfiguration(natTable));
natTable.addConfiguration(new AbstractRegistryConfiguration() {
  
  @Override
  public void configureRegistry(IConfigRegistry configRegistry) {
    Style cellStyle = new Style();
    cellStyle.setAttributeValue(
            CellStyleAttributes.BACKGROUND_COLOR,
            GUIHelper.COLOR_WIDGET_BACKGROUND);
    configRegistry.registerConfigAttribute(
            CellConfigAttributes.CELL_STYLE, cellStyle,
            DisplayMode.NORMAL, RowHeaderLabel);
  }
});
natTable.configure();

editor.setFilterator(new TextFilterator<TableLine>() {
  @Override
  public void getFilterStrings(List<String> baseList, TableLine element) {
    for( int i = 0; i < element.getLength(); i++ )
      baseList.add("" + element.getObjectByColumn(i));
  }
});
editor.setMode(TextMatcherEditor.REGULAR_EXPRESSION);
bodyLayerStack.getFilterList().setMatcherEditor(editor);

return natTable;}

BodyLayerStack.class content:

class BodyLayerStack extends AbstractLayerTransform {

private final FilterList<TableLine> filterList;
private final SelectionLayer selectionLayer;
private final DataLayer bodyDataLayer;
private final IRowDataProvider<TableLine> bodyDataProvider;

public DataLayer getBodyDataLayer() {
  return bodyDataLayer;
}

public SelectionLayer getSelectionLayer() {
  return selectionLayer;
}

public IRowDataProvider<TableLine> getBodyDataProvider() {
  return bodyDataProvider;
}

public BodyLayerStack(List<TableLine> values, IColumnAccessor<TableLine> columnAccessor, Integer[] columnIndicesForRowHeaders) {
    EventList<TableLine> eventList = GlazedLists.eventList(values);
    TransformedList<TableLine, TableLine> rowObjectsGlazedList = GlazedLists.threadSafeList(eventList);

    this.filterList = new FilterList<>(rowObjectsGlazedList);

    this.bodyDataProvider = new ListDataProvider<TableLine>(this.filterList, columnAccessor);
    bodyDataLayer = new DataLayer(this.bodyDataProvider);
    
    ColumnOverrideLabelAccumulator bodyLabelAccumulator = new ColumnOverrideLabelAccumulator(bodyDataLayer);
    bodyDataLayer.setConfigLabelAccumulator(bodyLabelAccumulator);

    if( columnIndicesForRowHeaders != null ) {
      for ( int i = 0; i < columnIndicesForRowHeaders.length; i++ ) {
        bodyLabelAccumulator.registerColumnOverrides(
            columnIndicesForRowHeaders[i],
            RowHeaderLabel);
      }
    }

    GlazedListsEventLayer<TableLine> glazedListsEventLayer =
            new GlazedListsEventLayer<>(bodyDataLayer, this.filterList);

    this.selectionLayer = new SelectionLayer(glazedListsEventLayer, false);
    selectionLayer.setSelectionModel(new RowSelectionModel<TableLine>(selectionLayer, this.bodyDataProvider, new IRowIdAccessor<TableLine>()
    {
      @Override
      public Serializable getRowId(TableLine line)
      {
        return line.getId();
      }
    }));

    selectionLayer.addConfiguration(new DefaultSelectionLayerConfiguration()
    {
      @Override
      protected void addSelectionUIBindings()
      {
        addConfiguration(new SelectionBindings());
      }

      @SuppressWarnings("rawtypes")
      @Override
      protected void addMoveSelectionConfig()
      {
        addConfiguration(new RowOnlySelectionConfiguration());
      }
    });

    ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
    setUnderlyingLayer(viewportLayer);
}

public FilterList<TableLine> getFilterList() {
    return this.filterList;
}

}

Here is code for creating column group header NatTable:

private NatTable createTableWithColumnHeaderGroups(Composite parent, List<TableLine> tLines, TableParams params,
  TextMatcherEditor<TableLine> editor) {
String[][] columnHeaders = params.getColumnHeaders();
if( columnHeaders == null || columnHeaders.length != 2 )
  return null;
String[] propertyNames = new String[columnHeaders[1].length];
PropertyToLabels propToLabels = new PropertyToLabels();
for( int i = 0; i < propertyNames.length; i++ ) {
  propertyNames[i] = String.valueOf(i+1) + "_" + columnHeaders[1][i];
  propToLabels.add(propertyNames[i], columnHeaders[1][i]);
}

BodyLayerStack bodyLayerStack =
    new BodyLayerStack(
            tLines,
            new LineDataProviderColumnAccessor(propertyNames.length),
            params.getColumnIndicesForRowHeaders());

ColumnGroupModel columnGroupModel = new ColumnGroupModel();

IRowDataProvider<TableLine> bodyDataProvider =
    new ListDataProvider<TableLine>(tLines, new LineDataProviderColumnAccessor(propertyNames.length));
ColumnGroupBodyLayerStack bodyLayer =
    new ColumnGroupBodyLayerStack(bodyLayerStack.getBodyDataLayer(), columnGroupModel);

Integer[] rowHeights = params.getRowHeights();
if( rowHeights != null && rowHeights.length > 0 ) {
  for( int i = 0; i < rowHeights.length; i++ ) {
    if( rowHeights[i] != null )
      bodyLayer.doCommand(new RowResizeCommand(bodyLayer, i, rowHeights[i]));
  }
}
Integer[] colWidths = params.getColumnWidths();
if( colWidths != null && colWidths.length > 0 ) {
  for( int i = 0; i < colWidths.length; i++ )
    if( colWidths[i] != null )
      bodyLayer.doCommand(new ColumnResizeCommand(bodyLayer, i, colWidths[i]));
}

// Column header
DefaultColumnHeaderDataProvider defaultColumnHeaderDataProvider =
    new DefaultColumnHeaderDataProvider(propertyNames, propToLabels.getPropertyToLabels());
DefaultColumnHeaderDataLayer columnHeaderDataLayer =
    new DefaultColumnHeaderDataLayer(defaultColumnHeaderDataProvider);
ColumnHeaderLayer columnHeaderLayer =
    new ColumnHeaderLayer(columnHeaderDataLayer, bodyLayer, bodyLayer.getSelectionLayer());
ColumnGroupHeaderLayer columnGroupHeaderLayer =
    new ColumnGroupHeaderLayer(
        columnHeaderLayer,
        bodyLayer.getSelectionLayer(),
        columnGroupModel);

Integer[] hrHeights = params.getHeaderRowHeights();
if( hrHeights != null && hrHeights.length > 0 )
  for( int i = 0; i < hrHeights.length; i++ )
    if( hrHeights[i] != null )
      columnGroupHeaderLayer.doCommand(new RowResizeCommand(columnGroupHeaderLayer, i, hrHeights[i]));

List<ColumnHeaderGroup> groups = params.getColumnHeaderGroups();
for( ColumnHeaderGroup group : groups ) {
  List<Integer> indices = group.getIndices();
  int[] indicesArray = new int[indices.size()];
  for( int i = 0; i < indices.size(); i++ )
    indicesArray[i] = indices.get(i);
  columnGroupHeaderLayer.addColumnsIndexesToGroup(group.getName(), indicesArray);
}

// Row header
final DefaultRowHeaderDataProvider rowHeaderDataProvider =
    new DefaultRowHeaderDataProvider(bodyDataProvider);
DefaultRowHeaderDataLayer rowHeaderDataLayer =
    new DefaultRowHeaderDataLayer(rowHeaderDataProvider);
ILayer rowHeaderLayer =
    new RowHeaderLayer(rowHeaderDataLayer, bodyLayer, bodyLayer.getSelectionLayer());

// Corner
final DefaultCornerDataProvider cornerDataProvider =
    new DefaultCornerDataProvider(defaultColumnHeaderDataProvider, rowHeaderDataProvider);
DataLayer cornerDataLayer =
    new DataLayer(cornerDataProvider);
ILayer cornerLayer =
    new CornerLayer(cornerDataLayer, rowHeaderLayer, columnGroupHeaderLayer);

// Grid
GridLayer gridLayer =
    new GridLayer(bodyLayer, columnGroupHeaderLayer, rowHeaderLayer, cornerLayer);

NatTable natTable = new NatTable(parent, gridLayer, false);

editor.setFilterator(new TextFilterator<TableLine>() {

  @Override
  public void getFilterStrings(List<String> baseList, TableLine element) {
    for( int i = 0; i < element.getLength(); i++ )
      baseList.add("" + element.getObjectByColumn(i));
  }
});
editor.setMode(TextMatcherEditor.REGULAR_EXPRESSION);
bodyLayerStack.getFilterList().setMatcherEditor(editor);

natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
natTable.addConfiguration(new ContextMenuConfiguration(natTable));
natTable.addConfiguration(new AbstractRegistryConfiguration() {
  
  @Override
  public void configureRegistry(IConfigRegistry configRegistry) {
    Style cellStyle = new Style();
    cellStyle.setAttributeValue(
            CellStyleAttributes.BACKGROUND_COLOR,
            GUIHelper.COLOR_WIDGET_BACKGROUND);
    configRegistry.registerConfigAttribute(
            CellConfigAttributes.CELL_STYLE, cellStyle,
            DisplayMode.NORMAL, RowHeaderLabel);
  }
});
natTable.configure();
return natTable;  }

Eventually, the problem may be in nat table layer implementation at nat table with two-row column headers creation.


Solution

  • The issue is in your code. In the version with the ColumnGroupHeader you create two body layer stacks. First the BodyLayerStack that you also show. But that one is not used with the column group example. There you use the second one named ColumnGroupBodyLayerStack. As that one is not shown I can't tell if that class is missing the row selection configuration. But probably it is, as there is no issue in the NatTable layer implementation with regards to that feature.

    BTW, I would suggest to use the performance column grouping introduced with NatTable 1.6 instead of the old ColumnGroupModel based implementation.