In the KNIME framework the technique known as "linking and brushing" is called HiLiting. This means that whenever a datapoint is hilited in one view it is immediately also hilited in all other views displaying this data point. If we had a view that displayed the datapoints directly we would have to implement the
But since we have an aggregated view of the datapoints it does not make much sense to implement the
The next step is to listen to the mouse events to be informed about whether a bin is selected or not. Then the bin has to know its graphical representation, i.e. the painted rectangle (otherwise we cannot know if it is clicked or not):
In order to listen to mouse events we have to add a
So far we are able to select one or more bins. If we want to hilite them we have to add a menu to the
The
So far we are able to select and hilite (unhilite) the bins and the contained rows. But if you run the code implemented so far you immediately encounter the frustrating fact, that in our view you cannot distinguish between selected, hilited and normal bins. Thus, we have to add this to the paint method of the drawing component, the
Now the view looks good and is correctly displayed if a bin is selected, hilited, both, or none. We use the KNIME standard colors defined in the
HiLiteListener
interface to be informed about any change in the hiliting. The HiLiteListener
interface has three methods:
/** * Invoked when some item(s) were hilit. * * @param event contains a list of row keys that were hilit */void
hiLite(final
KeyEvent event); /** * Invoked when some item(s) were unhilit. * * @param event contains a list of row keys that were unhilit */void
unHiLite(final
KeyEvent event); /** * Invoked, when everything (all rows) are unhilit. */void
unHiLiteAll();
HiLiteListener
interface. We would rather hilite a bin and see all the datapoints in that bin hilited in other views. In the following we explain how this is implemented. First of all we have to prepare our NumericBin
to know when it has been selected and if it is hilited. Therefore we simply introduce two flags to indicate the status:
/** * @param isHilite sets the hilite status of this bin. */public void
setHilited(final boolean
isHilite) { m_isHilite = isHilite; } /** * * @return true if this bin contains hilited keys, false otherwise. */public boolean
isHilited() {return
m_isHilite; } /** * * @return true if this bin is selected. false otherwise. */public boolean
isSelected() {return
m_isSelected; } /** * * @param selected true, if the bin is selected, false otherwise. */public void
setSelected(final boolean
selected) { m_isSelected = selected; }
/** * * @return the graphical representation as a rectangle. */public
Rectangle getViewRepresentation() {return
m_viewRepresentation; } /** * The graphical representation can only be calculated outside with the * knowledge of the number of bins, the maximal and minimal size * and the available width and height. This is done in the * {@link NumericBinnerViewPanel#paint(java.awt.Graphics)} * * @param rectangle the graphical representation */public void
setViewRepresentation(final
Rectangle rectangle) { m_viewRepresentation = rectangle; }
MouseListener
in the NodeView
's constructor to the drawing component. The selected bins are stored in a local datastructure m_selected:
... m_selected =new
HashSet(); m_panel.addMouseListener( new
MouseAdapter() { /** * @see java.awt.event.MouseAdapter#mouseReleased( * java.awt.event.MouseEvent) */ @Overridepublic void
mouseReleased(final
MouseEvent e) {if
(!e.isControlDown()) { m_selected.clear();for
(NumericBin bin : m_panel.getBins()) { bin.setSelected(false
); } }for
(NumericBin bin : m_panel.getBins()) {if
(bin.getViewRepresentation() !=null
&& bin.getViewRepresentation().contains( e.getX(), e.getY())){ bin.setSelected(true
); m_selected.add(bin);break
; } } ...
NodeView
, to enable us to hilite or unhilite the selected bins, or clear the hiliting.
// create the hilite menu // the HiliteHandler provides standard names m_hilite =new
JMenuItem(HiLiteHandler.HILITE_SELECTED); m_hilite.setEnabled(false
); m_hilite.addActionListener(new
ActionListener() { /** * @see java.awt.event.ActionListener#actionPerformed( * java.awt.event.ActionEvent) */public void
actionPerformed(final
ActionEvent e) { SettoBeHilited = new
HashSet(); for
(NumericBin bin : m_selected) { // store all row ids from the selected bin toBeHilited.addAll(bin.getContainedRowIds()); // set the bin hilited bin.setHilited(true
); // count the number of hilited bins for a // correct menu display (see below) m_numberOfHilitedBins++; } // now get the hilite handler and hilite the rows getNodeModel().getInHiLiteHandler( NumericBinnerNodeModel.IN_PORT).fireHiLiteEvent(toBeHilited); // and repaint to have the hilited bins displayed correctly m_panel.repaint(); } }); m_unhilite =new
JMenuItem(HiLiteHandler.UNHILITE_SELECTED); m_unhilite.setEnabled(false
); m_unhilite.addActionListener(new
ActionListener() { /** * @see java.awt.event.ActionListener#actionPerformed( * java.awt.event.ActionEvent) */public void
actionPerformed(final
ActionEvent e) { SettoBeUnhilited = new
HashSet(); for
(NumericBin bin : m_selected) { // store all row ids that should be unhilited toBeUnhilited.addAll(bin.getContainedRowIds()); // unhilite the bin bin.setHilited(false
); // decrease the number of hilited bins m_numberOfHilitedBins--; } // get the hilite handler and unhilite the rows getNodeModel().getInHiLiteHandler( NumericBinnerNodeModel.IN_PORT).fireUnHiLiteEvent(toBeUnhilited); // repaint to have the bins displayed correctly m_panel.repaint(); } }); JMenuItem clear =new
JMenuItem(HiLiteHandler.CLEAR_HILITE); clear.addActionListener(new
ActionListener() { /** * @see java.awt.event.ActionListener#actionPerformed( * java.awt.event.ActionEvent) */public void
actionPerformed(final
ActionEvent e) { // get the hilite handler and unhilite all getNodeModel().getInHiLiteHandler( NumericBinnerNodeModel.IN_PORT).fireClearHiLiteEvent(); // unhilite all binsfor
(NumericBin bin : m_panel.getBins()) { bin.setHilited(false
); } // no bin is hilited anymore m_numberOfHilitedBins = 0; // repaint to display the bins correctly m_panel.repaint(); } }); // create the menu and all the menu items to it JMenu menu =new
JMenu(HiLiteHandler.HILITE); menu.add(m_hilite); menu.add(m_unhilite); menu.add(clear); // get the JMenu bar of the NodeView and add this menu to it getJMenuBar().add(menu); ...
HiLiteHandler
provides standard names for the menu items. The getJMenuBar
method returns the MenuBar
of the NodeView
to which the additional menu can be added. To further improve our small human computer interface we can enable and disable the menu items dependent on the current selection and hilite status, i.e. the hilite menu entry should only be enabled when some bins are selected. And accourdingly should the unhilite menu entry only be enabled when some bins are selected and hilited. We add this functionality to the MouseListener
. (By the way this is the reason, why the two menu items are local fields.)
public void
mouseReleased(final
MouseEvent e) { ... // update the hilite menuif
(m_selected.size() > 0) { m_hilite.setEnabled(true
); }else
{ m_hilite.setEnabled(false
); }if
(m_numberOfHilitedBins > 0 && m_selected.size() > 0) { m_unhilite.setEnabled(true
); }else
{ m_unhilite.setEnabled(false
); } m_panel.repaint(); } });
NumericBinnerViewPanel
(it also shows how the graphical rectangle of the bins is updated in every paint):
... Rectangle rect =new
Rectangle(x, height - binHeight, binWidth, binHeight); m_bins[i].setViewRepresentation(rect); // draw a border in white to make the bins distinguishable Color color = Color.BLACK;if
(m_bins[i].isHilited()) { color = ColorAttr.HILITE; }if
(m_bins[i].isSelected()) { color = ColorAttr.SELECTED; }if
(m_bins[i].isHilited() && m_bins[i].isSelected()) { color = ColorAttr.SELECTED_HILITE; } Graphics2D g2 = (Graphics2D)g; g2.setColor(color); g2.fillRect(rect.x+2, rect.y+2, rect.width-2, rect.height-2); g2.setColor(Color.WHITE); g2.setStroke(new
BasicStroke(2)); g2.drawRect(rect.x, rect.y, rect.width, rect.height); ...
ColorAttr
to have a uniform coloring in all views.