/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.visualvm.core.ui.components; import com.sun.tools.visualvm.uisupport.UISupport; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.plaf.basic.BasicSplitPaneUI; /** * UI component displaying single subtab for opened DataSource. * * @author Jiri Sedlacek */ public final class DataViewComponent extends JPanel { /** * Top left details area of the view. */ public static final int TOP_LEFT = 1; /** * Top right details area of the view. */ public static final int TOP_RIGHT = 2; /** * Bottom left details area of the view. */ public static final int BOTTOM_LEFT = 3; /** * Bottom right details area of the view. */ public static final int BOTTOM_RIGHT = 4; private boolean isMasterViewResizable; private JPanel masterPanel; private JPanel detailsPanel; private JExtendedSplitPane detailsTopHorizontalSplitter; private JExtendedSplitPane detailsBottomHorizontalSplitter; private JExtendedSplitPane detailsVerticalSplitter; private DisplayArea masterArea; private DisplayArea detailsTopLeftArea; private DisplayArea detailsTopRightArea; private DisplayArea detailsBottomLeftArea; private DisplayArea detailsBottomRightArea; private static final Color BACKGROUND_COLOR; private static final Color HIGHLIGHT_BACKGROUND; static { BACKGROUND_COLOR = UISupport.getDefaultBackground(); int darkerR = BACKGROUND_COLOR.getRed() - 20; if (darkerR < 0) darkerR += 40; int darkerG = BACKGROUND_COLOR.getGreen() - 20; if (darkerG < 0) darkerG += 40; int darkerB = BACKGROUND_COLOR.getBlue() - 20; if (darkerB < 0) darkerB += 40; HIGHLIGHT_BACKGROUND = new Color(darkerR, darkerG, darkerB); } /** * Creates new instance of DataViewComponent. * * @param masterView MasterView for the component. * @param masterAreaConfiguration MasterAreaConfiguration for the component. */ public DataViewComponent(MasterView masterView, MasterViewConfiguration masterAreaConfiguration) { initComponents(); createMasterView(masterView); configureMasterView(masterAreaConfiguration); } private void configureMasterView(MasterViewConfiguration masterViewConfiguration) { setMasterViewResizable(masterViewConfiguration.isMasterViewResizable()); } /** * Configures properties of the details view. * * @param detailsViewConfiguration configuration for the details view. */ public void configureDetailsView(DetailsViewConfiguration detailsViewConfiguration) { double topHorizontalDividerResizeWeight = detailsViewConfiguration.getTopHorizontalDividerResizeWeight(); if (topHorizontalDividerResizeWeight != -1) detailsTopHorizontalSplitter.setResizeWeight(topHorizontalDividerResizeWeight); double topHorizontalDividerLocation = detailsViewConfiguration.getTopHorizontalDividerLocation(); if (topHorizontalDividerLocation != -1) detailsTopHorizontalSplitter.setDividerLocation(topHorizontalDividerLocation); double bottomHorizontalDividerResizeWeight = detailsViewConfiguration.getBottomHorizontalDividerResizeWeight(); if (bottomHorizontalDividerResizeWeight != -1) detailsBottomHorizontalSplitter.setResizeWeight(bottomHorizontalDividerResizeWeight); double bottomHorizontalDividerLocation = detailsViewConfiguration.getBottomHorizontalDividerLocation(); if (bottomHorizontalDividerLocation != -1) detailsBottomHorizontalSplitter.setDividerLocation(bottomHorizontalDividerLocation); double verticalDividerResizeWeight = detailsViewConfiguration.getVerticalDividerResizeWeight(); if (verticalDividerResizeWeight != -1) detailsVerticalSplitter.setResizeWeight(verticalDividerResizeWeight); double verticalDividerLocation = detailsViewConfiguration.getVerticalDividerLocation(); if (verticalDividerLocation != -1) detailsVerticalSplitter.setDividerLocation(verticalDividerLocation); } /** * Shows details area on provided location. * * @param location location to be shown. */ public void showDetailsArea(int location) { DisplayArea displayArea = getDisplayArea(location); if (displayArea != null) displayArea.setVisible(true); } /** * Hides details area on provided location. * * @param location location to be hidden. */ public void hideDetailsArea(int location) { DisplayArea displayArea = getDisplayArea(location); if (displayArea != null) displayArea.setVisible(false); } /** * Returns true if the details area on provided location is currently shown, false otherwise. * @param location location to be checked. * @return true if the details area on provided location is currently shown, false otherwise. */ public boolean isDetailsAreaShown(int location) { DisplayArea displayArea = getDisplayArea(location); return displayArea != null ? displayArea.isVisible() : false; } /** * Configures properties of the details area. * * @param detailsAreaConfiguration configuration for the details area. * @param location location of the details area to be configured. */ public void configureDetailsArea(DetailsAreaConfiguration detailsAreaConfiguration, int location) { DisplayArea displayArea = getDisplayArea(location); if (displayArea != null) { displayArea.setCaption(detailsAreaConfiguration.getName()); displayArea.setClosable(detailsAreaConfiguration.isClosable()); } } private void createMasterView(MasterView masterView) { masterPanel.setVisible(true); JComponent[] options = new JComponent[] { detailsTopLeftArea.getPresenter(), detailsTopRightArea.getPresenter(), detailsBottomLeftArea.getPresenter(), detailsBottomRightArea.getPresenter() }; masterArea.addTab(new DisplayArea.Tab(masterView.getName(), masterView.getDescription(), 0, masterView.getView(), options)); } /** * Adds new details view. * * @param detailsView DetailsView to be added. * @param location location where the DetailsView will be added. */ public void addDetailsView(DetailsView detailsView, int location) { DisplayArea displayArea = getDisplayArea(location); if (displayArea != null) { if ("".equals(displayArea.getCaption())) displayArea.setCaption(detailsView.getTab().getName()); displayArea.addTab(detailsView.getTab()); revalidate(); repaint(); } } /** * Removes details view. * * @param detailsView DetailsView to be removed. */ public void removeDetailsView(DetailsView detailsView) { DisplayArea displayArea = getDisplayArea(detailsView.getTab()); if (displayArea != null) displayArea.removeTab(detailsView.getTab()); } /** * Returns true if DataViewComponent contains the DetailsView, false otherwise. * * @param detailsView DetailsView to check. * @return true if DataViewComponent contains the DetailsView, false otherwise. */ public boolean containsDetailsView(DetailsView detailsView) { return getDisplayArea(detailsView.getTab()) != null; } /** * Selects the DetailsView. * * @param detailsView DetailsView to be selected. */ public void selectDetailsView(DetailsView detailsView) { DisplayArea displayArea = getDisplayArea(detailsView.getTab()); if (displayArea != null) displayArea.setSelectedTab(detailsView.getTab()); } private void setMasterViewResizable(boolean isMasterViewResizable) { this.isMasterViewResizable = isMasterViewResizable; masterArea.setIgnoresContentsHeight(isMasterViewResizable); JComponent contents = null; if (isMasterViewResizable) { final CustomizedSplitPaneUI mainVerticalSplitterUI = new CustomizedSplitPaneUI(); JExtendedSplitPane mainVerticalSplitter = new JExtendedSplitPane(JSplitPane.VERTICAL_SPLIT, masterPanel, detailsPanel){ public void updateUI() { if (getUI() != mainVerticalSplitterUI) setUI(mainVerticalSplitterUI); setBorder(null); setOpaque(false); setDividerSize(6); setContinuousLayout(true); final BasicSplitPaneDivider divider = ((BasicSplitPaneUI) getUI()).getDivider(); divider.setBackground(BACKGROUND_COLOR); divider.setBorder(null); divider.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { divider.setBackground(HIGHLIGHT_BACKGROUND); divider.repaint(); } public void mouseExited(MouseEvent e) { divider.setBackground(BACKGROUND_COLOR); divider.repaint(); } }); } }; mainVerticalSplitter.setDividerLocation(0.5d); contents = mainVerticalSplitter; } else { JPanel containerPanel = new JPanel(new BorderLayout()); containerPanel.setOpaque(false); containerPanel.add(masterPanel, BorderLayout.NORTH); containerPanel.add(detailsPanel, BorderLayout.CENTER); contents = containerPanel; } synchronized (getTreeLock()) { removeAll(); add(contents, BorderLayout.CENTER); } revalidate(); repaint(); } private boolean isMasterAreaResizable() { return isMasterViewResizable; } private DisplayArea getDisplayArea(int location) { switch (location) { case TOP_LEFT: return detailsTopLeftArea; case TOP_RIGHT: return detailsTopRightArea; case BOTTOM_LEFT: return detailsBottomLeftArea; case BOTTOM_RIGHT: return detailsBottomRightArea; default: return null; } } private DisplayArea getDisplayArea(DisplayArea.Tab tab) { if (detailsTopLeftArea.containsTab(tab)) return detailsTopLeftArea; if (detailsTopRightArea.containsTab(tab)) return detailsTopRightArea; if (detailsBottomLeftArea.containsTab(tab)) return detailsBottomLeftArea; if (detailsBottomRightArea.containsTab(tab)) return detailsBottomRightArea; return null; } private void initComponents() { // Top details area detailsTopLeftArea = new DisplayArea(); detailsTopRightArea = new DisplayArea(); final JPanel detailsTopPanel = new JPanel(new BorderLayout()); detailsTopPanel.setOpaque(false); final CustomizedSplitPaneUI detailsTopHorizontalSplitterUI = new CustomizedSplitPaneUI(); detailsTopHorizontalSplitter = new JExtendedSplitPane(JSplitPane.HORIZONTAL_SPLIT, detailsTopLeftArea, detailsTopRightArea) { public void setVisible(boolean visible) { super.setVisible(visible); detailsTopPanel.setVisible(visible); revalidate(); repaint(); } public void updateUI() { if (getUI() != detailsTopHorizontalSplitterUI) setUI(detailsTopHorizontalSplitterUI); setBorder(null); setOpaque(false); setDividerSize(6); setContinuousLayout(true); final BasicSplitPaneDivider divider = ((BasicSplitPaneUI) getUI()).getDivider(); divider.setBackground(BACKGROUND_COLOR); divider.setBorder(null); divider.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { divider.setBackground(HIGHLIGHT_BACKGROUND); divider.repaint(); } public void mouseExited(MouseEvent e) { divider.setBackground(BACKGROUND_COLOR); divider.repaint(); } }); } }; detailsTopHorizontalSplitter.setResizeWeight(0.5d); detailsTopHorizontalSplitter.setDividerLocation(0.5d); detailsTopPanel.add(detailsTopHorizontalSplitter, BorderLayout.CENTER); // Bottom details area detailsBottomLeftArea = new DisplayArea(); detailsBottomRightArea = new DisplayArea(); final JPanel detailsBottomPanel = new JPanel(new BorderLayout()); detailsBottomPanel.setOpaque(false); final CustomizedSplitPaneUI detailsBottomHorizontalSplitterUI = new CustomizedSplitPaneUI(); detailsBottomHorizontalSplitter = new JExtendedSplitPane(JSplitPane.HORIZONTAL_SPLIT, detailsBottomLeftArea, detailsBottomRightArea) { public void setVisible(boolean visible) { super.setVisible(visible); detailsBottomPanel.setVisible(visible); revalidate(); repaint(); } public void updateUI() { if (getUI() != detailsBottomHorizontalSplitterUI) setUI(detailsBottomHorizontalSplitterUI); setBorder(null); setOpaque(false); setDividerSize(6); setContinuousLayout(true); final BasicSplitPaneDivider divider = ((BasicSplitPaneUI) getUI()).getDivider(); divider.setBackground(BACKGROUND_COLOR); divider.setBorder(null); divider.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { divider.setBackground(HIGHLIGHT_BACKGROUND); divider.repaint(); } public void mouseExited(MouseEvent e) { divider.setBackground(BACKGROUND_COLOR); divider.repaint(); } }); } }; detailsBottomHorizontalSplitter.setResizeWeight(0.5d); detailsBottomHorizontalSplitter.setDividerLocation(0.5d); detailsBottomPanel.add(detailsBottomHorizontalSplitter, BorderLayout.CENTER); // Details area detailsPanel = new JPanel(new BorderLayout()); detailsPanel.setOpaque(false); detailsPanel.setVisible(false); final CustomizedSplitPaneUI detailsVerticalSplitterUI = new CustomizedSplitPaneUI(); detailsVerticalSplitter = new JExtendedSplitPane(JSplitPane.VERTICAL_SPLIT, detailsTopPanel, detailsBottomPanel) { public void setVisible(boolean visible) { super.setVisible(visible); detailsPanel.setVisible(visible); revalidate(); repaint(); } public void updateUI() { if (getUI() != detailsVerticalSplitterUI) setUI(detailsVerticalSplitterUI); setBorder(null); setOpaque(false); setDividerSize(6); setContinuousLayout(true); final BasicSplitPaneDivider divider = ((BasicSplitPaneUI) getUI()).getDivider(); divider.setBackground(BACKGROUND_COLOR); divider.setBorder(null); divider.addMouseListener(new MouseAdapter() { public void mouseEntered(MouseEvent e) { divider.setBackground(HIGHLIGHT_BACKGROUND); divider.repaint(); } public void mouseExited(MouseEvent e) { divider.setBackground(BACKGROUND_COLOR); divider.repaint(); } }); } }; detailsVerticalSplitter.setResizeWeight(0.5d); detailsVerticalSplitter.setDividerLocation(0.5d); detailsPanel.add(detailsVerticalSplitter, BorderLayout.CENTER); // Master area masterArea = new DisplayArea(); masterArea.setClosable(false); masterPanel = new JPanel(new BorderLayout()); masterPanel.setOpaque(false); masterPanel.setVisible(false); masterPanel.add(masterArea, BorderLayout.CENTER); // DataView setOpaque(true); setBackground(BACKGROUND_COLOR); setLayout(new BorderLayout()); } private static class CustomizedSplitPaneUI extends BasicSplitPaneUI { public BasicSplitPaneDivider createDefaultDivider() { return new BasicSplitPaneDivider(this) { public void paint(Graphics g) { Dimension size = getSize(); g.setColor(getBackground()); g.fillRect(0, 0, size.width, size.height); } }; } } /** * Master view of DataViewComponent. This is the upper part of the component showing the master contents and/or * controls for details views. Master view is always shown for each DataViewComponent. */ public static class MasterView { private String name; private String description; private JComponent view; /** * Creates new instance of MasterView. * * @param name name of the view. * @param description description of the view. * @param view UI component to be shown. */ public MasterView(String name, String description, JComponent view) { this.name = name; this.description = description; this.view = view; } private String getName() { return name; } private String getDescription() { return description; } private JComponent getView() { return view; } } /** * Details view of DataViewComponent. This is an optional view of the DataViewComponent which can be hidden or * not used at all. */ public static class DetailsView { private DisplayArea.Tab tab; /** * Creates new instance of DetailsView. * * @param name name of the view. * @param description description of the view. * @param preferredPosition preferred position of the view among other views. * @param view UI component to be shown. * @param options components to be shown in view's caption. */ public DetailsView(String name, String description, int preferredPosition, JComponent view, JComponent[] options) { tab = new DisplayArea.Tab(name, description, preferredPosition, view, options); } private DisplayArea.Tab getTab() { return tab; } } /** * Configuration of the MasterView. */ public static class MasterViewConfiguration { private boolean isMasterViewResizable; /** * Creates new instance of MasterViewConfiguration. * * @param isMasterAreaResizable controls if master area can be dynamically resized in the UI (effective only if at least one details view is displayed). */ public MasterViewConfiguration(boolean isMasterAreaResizable) { this.isMasterViewResizable = isMasterAreaResizable; } private boolean isMasterViewResizable() { return isMasterViewResizable; } } /** * Configuration of the DetailsView. */ public static class DetailsViewConfiguration { private double topHorizontalDividerLocation; private double topHorizontalDividerResizeWeight; private double bottomHorizontalDividerLocation; private double bottomHorizontalDividerResizeWeight; private double verticalDividerLocation; private double verticalDividerResizeWeight; /** * Creates new instance of DetailsViewConfiguration. * * @param topHorizontalDividerLocation preferred relative location of top horizontal divider. * @param topHorizontalDividerResizeWeight preferred resize weight of top horizontal divider. * @param bottomHorizontalDividerLocation preferred relative location of bottom horizontal divider. * @param bottomHorizontalDividerResizeWeight preferred resize weight of bottom horizontal divider. * @param verticalDividerLocation preferred location of vertical divider. * @param verticalDividerResizeWeight preferred resize weight of vertical divider. */ public DetailsViewConfiguration(double topHorizontalDividerLocation, double topHorizontalDividerResizeWeight, double bottomHorizontalDividerLocation, double bottomHorizontalDividerResizeWeight, double verticalDividerLocation, double verticalDividerResizeWeight) { this.topHorizontalDividerLocation = topHorizontalDividerLocation; this.topHorizontalDividerResizeWeight = topHorizontalDividerResizeWeight; this.bottomHorizontalDividerLocation = bottomHorizontalDividerLocation; this.bottomHorizontalDividerResizeWeight = bottomHorizontalDividerResizeWeight; this.verticalDividerLocation = verticalDividerLocation; this.verticalDividerResizeWeight = verticalDividerResizeWeight; } /** * Returns preferred relative location of top horizontal divider. * @return preferred relative location of top horizontal divider. */ public double getTopHorizontalDividerLocation() { return topHorizontalDividerLocation; }; /** * Returns preferred resize weight of top horizontal divider. * @return preferred resize weight of top horizontal divider. */ public double getTopHorizontalDividerResizeWeight() { return topHorizontalDividerResizeWeight; }; /** * Returns preferred relative location of bottom horizontal divider. * @return preferred relative location of bottom horizontal divider. */ public double getBottomHorizontalDividerLocation() { return bottomHorizontalDividerLocation; }; /** * Returns preferred resize weight of bottom horizontal divider. * @return preferred resize weight of bottom horizontal divider. */ public double getBottomHorizontalDividerResizeWeight() { return bottomHorizontalDividerResizeWeight; }; /** * Returns preferred relative location of vertical divider. * @return preferred relative location of vertical divider. */ public double getVerticalDividerLocation() { return verticalDividerLocation; }; /** * Returns preferred resize weight of vertical divider. * @return preferred resize weight of vertical divider. */ public double getVerticalDividerResizeWeight() { return verticalDividerResizeWeight; }; } /** * Configuration for details area. */ public static class DetailsAreaConfiguration { private String name; private boolean closable; /** * Creates new instance of DetailsAreaConfiguration. * * @param name name of the details area. * @param closable controls if the details area is closable or always shown. */ public DetailsAreaConfiguration(String name, boolean closable) { this.name = name; this.closable = closable; } private String getName() { return name; } private boolean isClosable() { return closable; } } }