protected void createTableViewer(Composite parent) {
    viewer =
        new FeatureTableViewer(
            parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);

    contentProvider = new LazyFeatureContentProvider();
    contentProvider.filter(featureLayer.filter());
    viewer.setContentProvider(contentProvider);

    // add columns
    DefaultFeatureTableColumn first = null;
    for (PropertyDescriptor prop : fs.getSchema().getDescriptors()) {
      if (Geometry.class.isAssignableFrom(prop.getType().getBinding())) {
        // skip Geometry
      } else {
        DefaultFeatureTableColumn column = new DefaultFeatureTableColumn(prop);
        // disable default sorting behaviour
        // column.setSortable( false );
        viewer.addColumn(column);
        first = first != null ? first : column;

        //                column.getViewerColumn().getColumn().addSelectionListener( new
        // SelectionAdapter() {
        //                    private SortOrder currentOrder = SortOrder.ASCENDING;
        //                    @Override
        //                    public void widgetSelected( SelectionEvent ev ) {
        //                        // with selection RAP produces huge JS which fails in browser
        //                        viewer.setSelection( StructuredSelection.EMPTY );
        //                        currentOrder = currentOrder.equals( ASCENDING ) ? DESCENDING :
        // ASCENDING;
        //                        contentProvider.sort( column, currentOrder );
        //                    }
        //                });
      }
    }

    // it is important to sort any column; otherwise preserving selection during refresh()
    // always selects a new element, which causes an event, which causes a refresh() ...
    first.sort(SWT.UP);

    //
    viewer.setInput(fs);

    // selection -> FeaturePanel
    viewer.addSelectionChangedListener(
        ev -> {
          on(ev.getSelection())
              .first(IFeatureTableElement.class)
              .ifPresent(
                  elm -> {
                    log.info("selection: " + elm);
                    featureLayer.setClicked(elm.unwrap(Feature.class).get());

                    BatikApplication.instance()
                        .getContext()
                        .openPanel(panel.site().path(), FeaturePanel.ID);
                  });
        });
  }
 @EventHandler(display = true)
 protected void onFeatureChange(FeatureEvent ev) {
   if (!viewer.getTable().isDisposed()) {
     // XXX this tries to preserve selection; this is index based; it causes
     // a selection event; if sort has changed, another element ist selected!
     viewer.refresh();
   } else {
     EventManager.instance().unsubscribe(this);
   }
 }
 @EventHandler(display = true)
 protected void onFeatureClick(FeatureClickEvent ev) throws IOException {
   if (!viewer.getTable().isDisposed()) {
     IFeatureTableElement[] selected = viewer.getSelectedElements();
     String clickedFid = ev.clicked.get().getIdentifier().getID();
     if (selected.length != 1 || !selected[0].fid().equals(clickedFid)) {
       // viewer.setSelection() does not work with LazyContentProvider
       int index = contentProvider.indexOfFid(clickedFid);
       viewer.getTable().select(index);
       viewer.getTable().showSelection();
     }
   } else {
     EventManager.instance().unsubscribe(this);
   }
 }
  public FeatureSelectionTable(Composite parent, FeatureLayer featureLayer, IPanel panel) {
    BatikApplication.instance().getContext().propagate(this);
    this.featureLayer = featureLayer;
    this.fs = featureLayer.featureSource();
    this.panel = panel;

    parent.setLayout(FormLayoutFactory.defaults().create());

    // topbar
    Composite topbar = on(tk().createComposite(parent)).fill().noBottom().height(36).control();
    topbar.setLayout(FormLayoutFactory.defaults().spacing(10).margins(3).create());

    // closeBtn
    Button closeBtn = tk().createButton(topbar, "", SWT.PUSH, SWT.FLAT);
    closeBtn.setToolTipText("Close table");
    closeBtn.setImage(
        BatikPlugin.images()
            .svgImage(
                "close.svg", SvgImageRegistryHelper.NORMAL12)); // P4Plugin.TOOLBAR_ICON_CONFIG ) );
    on(closeBtn).left(0).top(0).height(26).width(28);
    closeBtn.addSelectionListener(
        selectionListener(
            ev -> {
              close();
            }));

    // title
    Label title = tk().createLabel(topbar, abbreviate(featureLayer.layer().label.get(), 20));
    title.setFont(MdAppDesign.font(FontStyle.Title));
    on(title).top(0, 1).left(closeBtn, -8);

    // seach
    createTextSearch(topbar);
    on(searchText.getControl()).left(title).top(0).width(250);

    // toolbar
    toolbar = tk().createToolbar(topbar, SWT.FLAT);
    on(toolbar.getControl()).fill().noLeft().right(100);
    ContributionManager.instance().contributeTo(toolbar, panel, TOOLBAR_TAG);

    // table viewer
    createTableViewer(parent);
    on(viewer.getTable()).fill().top(topbar);

    // listen to commit events
    EventManager.instance()
        .subscribe(
            this,
            TypeEventFilter.ifType(
                FeatureEvent.class,
                ev -> ev.getType() == Type.COMMIT && ev.getFeatureSource() == fs));

    // listen to click events
    EventManager.instance()
        .subscribe(
            this,
            TypeEventFilter.ifType(
                FeatureClickEvent.class,
                ev -> ev.getSource() == this.featureLayer && ev.clicked.isPresent()));
  }
 protected void doSearch() {
   Filter filter = featureLayer.filter();
   String s = searchText.getText().getText();
   if (!StringUtils.isBlank(s)) {
     if (!s.contains("*") && !s.contains("?")) {
       s = s + "*";
     }
     for (PropertyDescriptor prop : fs.getSchema().getDescriptors()) {
       if (String.class.isAssignableFrom(prop.getType().getBinding())) {
         PropertyIsLike isLike = ff.like(ff.property(prop.getName()), s, "*", "?", "\\");
         filter = filter == Filter.INCLUDE ? isLike : ff.or(filter, isLike);
       }
     }
   }
   viewer.setSelection(StructuredSelection.EMPTY);
   log.info("FILTER: " + filter);
   //        FeatureCollection filtered = features.subCollection( filter );
   //        log.info( "RESULT: "  + filtered.size() );
   contentProvider.filter(filter);
 }