/*
* #%L
* gitools-ui-app
* %%
* Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
package org.gitools.ui.app.heatmap.panel;
import org.gitools.api.matrix.MatrixDimensionKey;
import org.gitools.heatmap.Heatmap;
import org.gitools.heatmap.HeatmapDimension;
import org.gitools.heatmap.HeatmapLayer;
import org.gitools.heatmap.header.HeatmapHeader;
import org.gitools.heatmap.header.HierarchicalClusterHeatmapHeader;
import org.gitools.ui.app.heatmap.panel.details.boxes.HeatmapInfoBox;
import org.gitools.ui.app.heatmap.popupmenus.PopupMenuActions;
import org.gitools.ui.core.HeatmapPosition;
import org.gitools.ui.core.actions.ActionSetUtils;
import org.gitools.ui.core.actions.dynamicactions.DynamicActionsManager;
import org.gitools.ui.core.actions.dynamicactions.IHeatmapDimensionAction;
import org.gitools.ui.core.actions.dynamicactions.IHeatmapHeaderAction;
import org.gitools.ui.core.components.boxes.BoxManager;
import org.gitools.ui.core.components.boxes.SelectionBox;
import org.gitools.ui.platform.component.scrollbar.ThinScrollBar;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import static org.gitools.utils.events.EventUtils.isAny;
public class HeatmapPanel extends JPanel implements PropertyChangeListener {
// Bean
private Heatmap heatmap;
// Components
private HeatmapBodyPanel bodyPanel;
private HeatmapHeaderPanel columnHeaderPanel;
private HeatmapHeaderPanel rowHeaderPanel;
private HeatmapHeaderIntersectionPanel headerIntersectPanel;
private HeatmapPanelInputProcessor inputProcessor;
private JPopupMenu popupMenuRows;
private JPopupMenu popupMenuColumns;
private JViewport bodyVP;
private JViewport colVP;
private JViewport rowVP;
private JViewport intersectVP;
private JScrollBar colSB;
private JScrollBar rowSB;
public HeatmapPanel(Heatmap heatmap) {
this.heatmap = heatmap;
createComponents();
// Listen
heatmap.addPropertyChangeListener(this);
heatmap.getColumns().addPropertyChangeListener(this);
heatmap.getRows().addPropertyChangeListener(this);
heatmap.getLayers().addPropertyChangeListener(this);
heatmap.getLayers().getTopLayer().addPropertyChangeListener(this);
setFocusable(true);
setBackground(Color.WHITE);
}
public Heatmap getHeatmap() {
return heatmap;
}
private void createComponents() {
bodyPanel = new HeatmapBodyPanel(heatmap);
columnHeaderPanel = new HeatmapHeaderPanel(heatmap, heatmap.getColumns());
rowHeaderPanel = new HeatmapHeaderPanel(heatmap, heatmap.getRows());
headerIntersectPanel = new HeatmapHeaderIntersectionPanel(heatmap, columnHeaderPanel.getHeaderDrawer(), rowHeaderPanel.getHeaderDrawer());
bodyVP = new JViewport();
bodyVP.setView(bodyPanel);
colVP = new JViewport();
colVP.setView(columnHeaderPanel);
rowVP = new JViewport();
rowVP.setView(rowHeaderPanel);
intersectVP = new JViewport();
intersectVP.setView(headerIntersectPanel);
inputProcessor = new HeatmapPanelInputProcessor(this);
colSB = new ThinScrollBar(JScrollBar.HORIZONTAL);
colSB.addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
updateViewPorts();
}
});
rowSB = new ThinScrollBar(JScrollBar.VERTICAL);
rowSB.addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
updateViewPorts();
}
});
setLayout(new HeatmapLayout());
add(colVP);
add(rowVP);
add(bodyVP);
add(colSB);
add(rowSB);
add(intersectVP);
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
updateScrolls();
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
heatmap.getRows().setFocus(null);
heatmap.getColumns().setFocus(null);
heatmap.getColumns().getSelected().clear();
heatmap.getRows().getSelected().clear();
}
});
popupMenuRows = ActionSetUtils.createPopupMenu(PopupMenuActions.ROWS);
popupMenuColumns = ActionSetUtils.createPopupMenu(PopupMenuActions.COLUMNS);
}
private void updateScrolls() {
Dimension totalSize = bodyPanel.getDrawer().getSize();
Dimension visibleSize = bodyVP.getSize();
int scrollWidth = totalSize.width - visibleSize.width;
int scrollHeight = totalSize.height - visibleSize.height;
// Update columns scroll bar
colSB.setValueIsAdjusting(true);
colSB.setMinimum(0);
colSB.setMaximum(totalSize.width - 1);
if (colSB.getValue() > scrollWidth) {
colSB.setValue(scrollWidth);
}
colSB.setVisibleAmount(visibleSize.width);
colSB.setValueIsAdjusting(false);
// Update rows scroll bar
rowSB.setValueIsAdjusting(true);
rowSB.setMinimum(0);
rowSB.setMaximum(totalSize.height - 1);
if (rowSB.getValue() > scrollHeight) {
rowSB.setValue(scrollHeight);
}
rowSB.setVisibleAmount(visibleSize.height);
rowSB.setValueIsAdjusting(false);
}
public void makeColumnFocusVisible() {
checkFocusOutOfVisibleArea(heatmap.getColumns(), colSB, bodyVP.getSize().width);
}
public void makeRowFocusVisible() {
checkFocusOutOfVisibleArea(heatmap.getRows(), rowSB, bodyVP.getSize().height);
}
private void checkFocusOutOfVisibleArea(HeatmapDimension dimension, JScrollBar scrollBar, int visibleSize) {
int leadPoint = (dimension.indexOf(dimension.getFocus()) * dimension.getFullCellSize());
int min = 0;
int max = dimension.getFullCellSize() * dimension.size();
int margin = (dimension.getFullCellSize() * 3 < 10) ? dimension.getFullCellSize() * 3 : 10;
int scrollPosition = scrollBar.getValue();
if (leadPoint - margin < scrollPosition) {
int topFocus = leadPoint - margin;
scrollBar.setValue(topFocus < min ? min : topFocus);
} else if (leadPoint + margin > scrollPosition + visibleSize) {
int bottomFocus = leadPoint - visibleSize + margin;
scrollBar.setValue(bottomFocus < max ? bottomFocus : max - visibleSize);
}
}
private void updateViewPorts() {
Dimension totalSize = bodyVP.getViewSize();
Dimension visibleSize = bodyVP.getSize();
int colValue = colSB.getValue();
if (colValue + visibleSize.width > totalSize.width) {
colValue = totalSize.width - visibleSize.width;
}
int rowValue = rowSB.getValue();
if (rowValue + visibleSize.height > totalSize.height) {
rowValue = totalSize.height - visibleSize.height;
}
colVP.setViewPosition(new Point(colValue, 0));
rowVP.setViewPosition(new Point(0, rowValue));
bodyVP.setViewPosition(new Point(colValue, rowValue));
intersectVP.setViewPosition(new Point(0, 0));
}
public JViewport getBodyViewPort() {
return bodyVP;
}
public JViewport getColumnViewPort() {
return colVP;
}
public JViewport getRowViewPort() {
return rowVP;
}
public HeatmapBodyPanel getBodyPanel() {
return bodyPanel;
}
public HeatmapHeaderPanel getColumnPanel() {
return columnHeaderPanel;
}
public HeatmapHeaderPanel getRowPanel() {
return rowHeaderPanel;
}
public HeatmapPosition getScrollPosition() {
Point pos = new Point(colSB.getValue(), rowSB.getValue());
return bodyPanel.getDrawer().getPosition(pos);
}
public Point getScrollValue() {
return new Point(colSB.getValue(), rowSB.getValue());
}
public void setScrollColumnPosition(int index) {
Point pos = bodyPanel.getDrawer().getPoint(new HeatmapPosition(heatmap, 0, index));
colSB.setValue(pos.x);
}
public void setScrollColumnValue(int value) {
colSB.setValue(value);
}
public void setScrollRowPosition(int index) {
Point pos = bodyPanel.getDrawer().getPoint(new HeatmapPosition(heatmap, index, 0));
rowSB.setValue(pos.y);
}
public void setScrollRowValue(int value) {
rowSB.setValue(value);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension sz = getSize();
Rectangle r = new Rectangle(new Point(0, 0), sz);
g.setColor(Color.WHITE);
g.fillRect(r.x, r.y, r.width, r.height);
}
public void addHeatmapMouseListener(HeatmapMouseListener listener) {
inputProcessor.addHeatmapMouseListener(listener);
}
public void mouseReleased(MouseEvent e) {
if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) {
showPopup(e);
}
}
public void configureHeaders(MouseEvent e) {
if (e.getComponent() == this.rowVP) {
HeatmapHeader header = rowHeaderPanel.getHeaderDrawer().getHeader(e.getPoint());
if (header instanceof HierarchicalClusterHeatmapHeader) {
e.consume();
}
}
if (e.getComponent() == this.colVP) {
HeatmapHeader header = columnHeaderPanel.getHeaderDrawer().getHeader(e.getPoint());
if (header instanceof HierarchicalClusterHeatmapHeader) {
e.consume();
}
}
}
private void showPopup(MouseEvent e) {
if (e.getComponent() == this.rowVP) {
Point point = e.getPoint();
HeatmapHeader header = rowHeaderPanel.getHeaderDrawer().getHeader(point);
Point viewPosition = rowVP.getViewPosition();
point.translate(viewPosition.x, viewPosition.y);
HeatmapPosition headerPostion = rowHeaderPanel.getHeaderDrawer().getPosition(point);
DynamicActionsManager.updatePopupMenu(popupMenuRows, IHeatmapDimensionAction.class, heatmap.getRows(), headerPostion);
DynamicActionsManager.updatePopupMenu(popupMenuRows, IHeatmapHeaderAction.class, header, headerPostion);
popupMenuRows.show(e.getComponent(), e.getX(), e.getY());
}
if (e.getComponent() == this.colVP) {
Point point = e.getPoint();
HeatmapHeader header = columnHeaderPanel.getHeaderDrawer().getHeader(point);
Point viewPosition = colVP.getViewPosition();
point.translate(viewPosition.x, viewPosition.y);
HeatmapPosition headerPostion = columnHeaderPanel.getHeaderDrawer().getPosition(point);
DynamicActionsManager.updatePopupMenu(popupMenuColumns, IHeatmapDimensionAction.class, heatmap.getColumns(), headerPostion);
DynamicActionsManager.updatePopupMenu(popupMenuColumns, IHeatmapHeaderAction.class, header, headerPostion);
popupMenuColumns.show(e.getComponent(), e.getX(), e.getY());
}
}
@Override
public void updateUI() {
super.updateUI();
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
boolean updateSelection =
isAny(evt, HeatmapDimension.class,
HeatmapDimension.PROPERTY_SELECTED);
boolean updateVisible =
isAny(evt, HeatmapDimension.class,
HeatmapDimension.PROPERTY_VISIBLE);
boolean updateAll =
isAny(evt, HeatmapDimension.class,
HeatmapDimension.PROPERTY_CELL_SIZE,
HeatmapDimension.PROPERTY_VISIBLE,
HeatmapDimension.PROPERTY_GRID_SIZE,
HeatmapDimension.PROPERTY_GRID_COLOR
) ||
isAny(evt, HeatmapLayer.class,
HeatmapLayer.PROPERTY_DECORATOR,
HeatmapLayer.PROPERTY_SHORT_FORMATTER
);
if (updateAll) {
bodyPanel.updateSize();
rowHeaderPanel.updateSize();
columnHeaderPanel.updateSize();
headerIntersectPanel.updateSize();
updateScrolls();
revalidate();
repaint();
}
/// DETAIL BOXES
if (updateVisible) {
BoxManager.openOnly(HeatmapInfoBox.ID);
} else if (updateSelection) {
if (evt.getSource() instanceof HeatmapDimension) {
if (((HeatmapDimension) evt.getSource()).getSelected().size() > 0) {
MatrixDimensionKey key = ((HeatmapDimension) evt.getSource()).getId();
BoxManager.openOnly(SelectionBox.ID, key.name());
} else {
BoxManager.openOnly(SelectionBox.ID);
}
}
}
}
}