Search code examples
filterhyperlinkswttableviewer

How to filter hyperlink cells in swt tableviewer


I have a problem i can't solve and I have spent a lot of time. I have a table and I have a column with hyperlinks. I have some filters added to the table and when I activate one of those filters, the hyperlink column doesn't refresh correctly. The code I implemented is showing above. This is an example you can copy&paste, run & reproduce the bug:

The Table class:

package borrar;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.Hyperlink;

public class Example {

    private static TableViewer tViewer = null;
    private static Table tblTrades = null;
    private static Text txtTicker;
    public static TickerFilter2 tickerFilter = new TickerFilter2();

    public static void main(String[] args) {

        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setLayout(new FillLayout());
        Composite parent = new Composite(shell, SWT.NONE);
        parent.setLayout(new FillLayout());
        parent.setLayout(new GridLayout(1, true));
        Label lblTicker = new Label(parent, SWT.NONE);
        lblTicker.setText("Search: ");
        txtTicker = new Text(parent, SWT.NONE);
        txtTicker.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
        txtTicker.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent ke) {
                tickerFilter.setSearchText(txtTicker.getText());
                tViewer.refresh();
            }
        });
        tViewer = new TableViewer(parent, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION | SWT.CENTER);
        String[] titles = { "Ticker", "Hyperlinks" };
        createColumns(titles);
        tblTrades = tViewer.getTable();
        tViewer.setContentProvider(new ArrayContentProvider());
        List<DataTable> dt = Arrays.asList(new DataTable("AAA", "ImagePath"), new DataTable("ABBBBBB", "ImagePath"),
                new DataTable("BBBBBBB", "ImagePath"));
        tViewer.setInput(dt);
        tblTrades.setHeaderVisible(true);
        tViewer.addFilter(tickerFilter);

        shell.open();
        shell.pack();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }

    private static void createColumns(String[] titles) {
        TableViewerColumn col = createTableViewerColumn(titles[0], 70);
        col.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                DataTable op = (DataTable) element;
                if (op.getTicker() != null) {
                    return op.getTicker();
                } else {
                    return "";
                }
            }
        });
        col = createTableViewerColumn(titles[1], 80);
        col.setLabelProvider(new ColumnLabelProvider() {
            Map<Object, Hyperlink> hyperlinks = new HashMap<Object, Hyperlink>();

            @Override
            public void update(ViewerCell cell) {
                TableItem item = (TableItem) cell.getItem();
                final Hyperlink hyperlink;
                if (hyperlinks.containsKey(cell.getElement()) && !hyperlinks.get(cell.getElement()).isDisposed()) {
                    hyperlink = hyperlinks.get(cell.getElement());
                } else {
                    hyperlink = new Hyperlink((Composite) (cell.getViewerRow().getControl()), SWT.NONE);
                    if (cell.getElement() instanceof DataTable) {
                        DataTable trade = (DataTable) cell.getElement();
                        if (trade.getPath() != null && !trade.getPath().equals("")) {
                            hyperlink.setText(trade.getPath() + "-" + trade.getTicker());
                            hyperlink.setHref(trade.getPath());
                        }
                    }
                    hyperlink.addHyperlinkListener(new HyperlinkAdapter() {
                        public void linkActivated(HyperlinkEvent e) {
                            org.eclipse.swt.program.Program.launch(hyperlink.getHref().toString());
                        }
                    });
                    hyperlinks.put(cell.getElement(), hyperlink);
                }
                TableEditor editor = new TableEditor(item.getParent());
                editor.grabHorizontal = true;
                editor.grabVertical = true;
                editor.setEditor(hyperlink, item, cell.getColumnIndex());
                editor.layout();
            }
        });
    }

    private static TableViewerColumn createTableViewerColumn(String title, int bound) {
        final TableViewerColumn viewerColumn = new TableViewerColumn(tViewer, SWT.CENTER);
        final TableColumn column = viewerColumn.getColumn();
        column.setText(title);
        column.setWidth(bound);
        column.setResizable(true);
        column.setMoveable(true);
        return viewerColumn;
    }

    public void refreshTable() {
        tViewer.refresh();
    }
}


My filter class:

package borrar;

import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;

public class TickerFilter2 extends ViewerFilter {
    private String searchString;

    public void setSearchText(String s) {
        // ensure that the value can be used for matching
        this.searchString = ".*" + s + ".*";
    }

    @Override
    public boolean select(Viewer viewer, Object parentElement, Object element) {
        if (searchString == null || searchString.length() == 0) {
            return true;
        }
        DataTable trade = (DataTable) element;
        if (trade.getTicker().matches(searchString)) {
            return true;
        }
        return false;
    }

}

The data table object:

package borrar;

public class DataTable {

    String ticker;
    String path;

    public DataTable(String ticker, String path) {
        super();
        this.ticker = ticker;
        this.path = path;
    }

    public String getTicker() {
        return ticker;
    }

    public void setTicker(String ticker) {
        this.ticker = ticker;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}

If you put in the search field a "z" or "l" for example, you can see that the hyperlink column does not filter.

The filter class filters data correctly but the hyperlinks column doesn't filter fine.

Here you have an image with the bad result: Here you have an image with the bad result


Solution

  • The TableViewer doesn't really know about the TableEditor code so it doesn't deal with them when filtering.

    You could perhaps use the EditingSupport code. The following code defines a cell editor which does an action as soon as you click on the column cell.

    For the column definition replace your code with something like:

       col.setLabelProvider(new ColumnLabelProvider() {
          @Override
          public String getText(final Object element) {
              // Whatever you want for the cell text here
              final DataTable op = (DataTable) element;
              return op.getPath();
           }
        });
    
        col.setEditingSupport(new HyperlinkEditingSupport(tViewer));
    

    The editing support class:

    public class HyperlinkEditingSupport extends EditingSupport
    {
      private final HyperlinkCellEditor _editor;
    
      public HyperlinkEditingSupport(final TableViewer viewer)
      {
        super(viewer);
    
        _editor = new HyperlinkCellEditor(viewer.getTable());
      }
    
      @Override
      protected CellEditor getCellEditor(final Object element)
      {
        return _editor;
      }
    
      @Override
      protected boolean canEdit(final Object element)
      {
        return true;
      }
    
      @Override
      protected Object getValue(final Object element)
      {
        // Return the value you want launched
        return ((DataTable)element).getPath();
      }
    
      @Override
      protected void setValue(final Object element, final Object value)
      {
        // no action
      }
    }
    

    And the CellEditor. This is a bit odd because we are just going to launch as soon as the editor is invoked so we don't really need a separate editor:

    public class HyperlinkCellEditor extends CellEditor
    {
      public HyperlinkCellEditor(final Composite parent)
      {
        super(parent);
      }
    
      @Override
      protected Control createControl(final Composite parent)
      {
        return new Composite(parent, SWT.None);
      }
    
      @Override
      protected Object doGetValue()
      {
        return new Object[0];
      }
    
      @Override
      protected void doSetFocus()
      {
        // no action
      }
    
      @Override
      protected void doSetValue(final Object value)
      {
        final String path = (String)value;
    
        // TODO Open the link
      }
    }