/* * #%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); } } } } }