/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo 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. * * OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.view; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; import java.awt.RadialGradientPaint; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ScrollPaneConstants; import javax.swing.border.MatteBorder; import javax.swing.event.ChangeListener; import org.openflexo.AdvancedPrefs; import org.openflexo.ch.FCH; import org.openflexo.icon.IconFactory; import org.openflexo.icon.IconLibrary; import org.openflexo.swing.TabbedPane; import org.openflexo.swing.TabbedPane.TabHeaderRenderer; import org.openflexo.swing.layout.JXMultiSplitPane; import org.openflexo.swing.layout.JXMultiSplitPane.DividerPainter; import org.openflexo.swing.layout.MultiSplitLayout; import org.openflexo.swing.layout.MultiSplitLayout.Divider; import org.openflexo.swing.layout.MultiSplitLayout.Leaf; import org.openflexo.swing.layout.MultiSplitLayout.Node; import org.openflexo.swing.layout.MultiSplitLayout.Split; import org.openflexo.toolbox.PropertyChangeListenerRegistrationManager; import org.openflexo.view.controller.FlexoController; import org.openflexo.view.controller.model.ControllerModel; import org.openflexo.view.controller.model.FlexoPerspective; import org.openflexo.view.controller.model.Location; /** * Abstract view managing global layout of a FlexoModule * * @author sguerin */ public class FlexoMainPane extends JPanel implements PropertyChangeListener { private static final MatteBorder MODULE_VIEW_BORDER = BorderFactory.createMatteBorder(0, 1, 1, 1, Color.LIGHT_GRAY); protected static final Logger logger = Logger.getLogger(FlexoMainPane.class.getPackage().getName()); public static enum LayoutPosition { TOP_LEFT, MIDDLE_LEFT, BOTTOM_LEFT, TOP_CENTER, MIDDLE_CENTER, BOTTOM_CENTER, TOP_RIGHT, MIDDLE_RIGHT, BOTTOM_RIGHT; } public static enum LayoutColumns { LEFT, CENTER, RIGHT; } private class EmptyPanel extends JPanel { @Override protected final void addImpl(Component comp, Object constraints, int index) { } } private FlexoController controller; private FlexoPerspective perspective; private ModuleView<?> moduleView; private MainPaneTopBar topBar; private JXMultiSplitPane centerPanel; private JComponent moduleViewContainerPanel; private JComponent footer; private MultiSplitLayout centerLayout; private PropertyChangeListenerRegistrationManager registrationManager; private TabbedPane<Location> tabbedPane; private static final int KNOB_SIZE = 5; private static final int KNOB_SPACE = 2; private static final int DIVIDER_SIZE = KNOB_SIZE + 2 * KNOB_SPACE; private static final int DIVIDER_KNOB_SIZE = 3 * KNOB_SIZE + 2 * KNOB_SPACE; private static final Paint KNOB_PAINTER = new RadialGradientPaint(new Point((KNOB_SIZE - 1) / 2, (KNOB_SIZE - 1) / 2), (KNOB_SIZE - 1) / 2, new float[] { 0.0f, 1.0f }, new Color[] { Color.GRAY, Color.LIGHT_GRAY }); public FlexoMainPane(FlexoController controller) { super(new BorderLayout()); this.controller = controller; this.centerLayout = new MultiSplitLayout(false); this.centerLayout.setLayoutMode(MultiSplitLayout.NO_MIN_SIZE_LAYOUT); registrationManager = new PropertyChangeListenerRegistrationManager(); registrationManager.new PropertyChangeListenerRegistration(ControllerModel.LOCATIONS, this, controller.getControllerModel()); registrationManager.new PropertyChangeListenerRegistration(ControllerModel.CURRENT_LOCATION, this, controller.getControllerModel()); registrationManager.new PropertyChangeListenerRegistration(ControllerModel.CURRENT_EDITOR, this, controller.getControllerModel()); registrationManager.new PropertyChangeListenerRegistration(ControllerModel.LEFT_VIEW_VISIBLE, this, controller.getControllerModel()); registrationManager.new PropertyChangeListenerRegistration(ControllerModel.RIGHT_VIEW_VISIBLE, this, controller.getControllerModel()); perspective = controller.getCurrentPerspective(); tabbedPane = new TabbedPane<Location>(new TabHeaderRenderer<Location>() { @Override public Icon getTabHeaderIcon(Location tab) { ImageIcon iconForObject = getController().iconForObject(tab.getObject()); if (iconForObject != null && tab.getObject() != null && (tab.getEditor() == null || tab.getObject().getProject() != tab.getEditor().getProject())) { iconForObject = IconFactory.getImageIcon(iconForObject, IconLibrary.LOCKED); } return iconForObject; } @Override public String getTabHeaderTitle(Location tab) { return getController().getWindowTitleforObject(tab.getObject()); } @Override public String getTabHeaderTooltip(Location tab) { return null; } @Override public boolean isTabHeaderVisible(Location tab) { if (tab == null) { return false; } if (tab.getObject() == null || tab.getObject().isDeleted()) { return false; } if (!AdvancedPrefs.getShowAllTabs() && (tab.getEditor() == null || !tab.getEditor().equals(getController().getControllerModel().getCurrentEditor()))) { return false; } ModuleView<?> view = getController().moduleViewForLocation(tab, true); return view != null && !(view instanceof EmptyPanel); } }); tabbedPane.setUseTabBody(false); tabbedPane.addToTabListeners(new TabbedPane.TabListener<Location>() { @Override public void tabSelected(Location tab) { FlexoMainPane.this.controller.getControllerModel().setCurrentLocation(tab); if (tab != null) { setModuleView(getController().moduleViewForLocation(tab, true)); } else { setModuleView(null); } } @Override public void tabClosed(Location tab) { FlexoMainPane.this.controller.getControllerModel().removeFromLocations(tab); } }); add(topBar = new MainPaneTopBar(controller), BorderLayout.NORTH); add(centerPanel = new JXMultiSplitPane(centerLayout)); centerPanel.setDividerSize(DIVIDER_SIZE); centerPanel.setDividerPainter(new DividerPainter() { @Override protected void doPaint(Graphics2D g, Divider divider, int width, int height) { if (!divider.isVisible()) { return; } if (divider.isVertical()) { int x = (width - KNOB_SIZE) / 2; int y = (height - DIVIDER_KNOB_SIZE) / 2; for (int i = 0; i < 3; i++) { Graphics2D graph = (Graphics2D) g.create(x, y + i * (KNOB_SIZE + KNOB_SPACE), KNOB_SIZE + 1, KNOB_SIZE + 1); graph.setPaint(KNOB_PAINTER); graph.fillOval(0, 0, KNOB_SIZE, KNOB_SIZE); } } else { int x = (width - DIVIDER_KNOB_SIZE) / 2; int y = (height - KNOB_SIZE) / 2; for (int i = 0; i < 3; i++) { Graphics2D graph = (Graphics2D) g.create(x + i * (KNOB_SIZE + KNOB_SPACE), y, KNOB_SIZE + 1, KNOB_SIZE + 1); graph.setPaint(KNOB_PAINTER); graph.fillOval(0, 0, KNOB_SIZE, KNOB_SIZE); } } } }); moduleViewContainerPanel = new JPanel(); moduleViewContainerPanel.setBorder(BorderFactory.createEmptyBorder(centerPanel.getDividerSize(), 0, 0, 0)); moduleViewContainerPanel.setLayout(new BorderLayout()); moduleViewContainerPanel.add(tabbedPane.getTabHeaders(), BorderLayout.NORTH); } public FlexoController getController() { return controller; } public void dispose() { saveLayout(); topBar.delete(); registrationManager.delete(); } public void resetModuleView() { setModuleView(null); } private void setModuleView(ModuleView<?> moduleView) { if (this.moduleView == moduleView) { return; } if (logger.isLoggable(Level.FINE)) { logger.fine("setModuleView() with " + moduleView + " perspective " + moduleView.getPerspective()); } try { if (this.moduleView != null) { this.moduleView.willHide(); } } catch (RuntimeException e) { e.printStackTrace(); if (logger.isLoggable(Level.SEVERE)) { logger.severe("willHide call failed on " + moduleView); } } if (this.moduleView != null && this.moduleView instanceof SelectionSynchronizedModuleView) { controller.getSelectionManager().removeFromSelectionListeners( ((SelectionSynchronizedModuleView<?>) this.moduleView).getSelectionListeners()); } this.moduleView = moduleView; if (moduleView != null && moduleView instanceof SelectionSynchronizedModuleView) { controller.getSelectionManager().addToSelectionListeners( ((SelectionSynchronizedModuleView<?>) moduleView).getSelectionListeners()); } JComponent newCenterView = null; if (moduleView != null) { if (moduleView.isAutoscrolled()) { newCenterView = (JComponent) moduleView; } else { JScrollPane scrollPane = new JScrollPane((JComponent) moduleView, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane.setBorder(BorderFactory.createEmptyBorder()); if (scrollPane.getVerticalScrollBar() != null) { scrollPane.getVerticalScrollBar().setUnitIncrement(10); scrollPane.getVerticalScrollBar().setBlockIncrement(50); } if (scrollPane.getHorizontalScrollBar() != null) { scrollPane.getHorizontalScrollBar().setUnitIncrement(10); scrollPane.getHorizontalScrollBar().setBlockIncrement(50); } scrollPane.getHorizontalScrollBar().setFocusable(false); scrollPane.getVerticalScrollBar().setFocusable(false); if (moduleView instanceof ChangeListener) { scrollPane.getViewport().addChangeListener((ChangeListener) moduleView); } newCenterView = scrollPane; } try { moduleView.willShow(); } catch (RuntimeException e) { e.printStackTrace(); if (logger.isLoggable(Level.SEVERE)) { logger.severe("willShow call failed on " + moduleView); } } FCH.setHelpItem((JComponent) moduleView, FCH.getModuleViewItemFor(controller.getModule(), moduleView)); newCenterView.setBorder(MODULE_VIEW_BORDER); } else { newCenterView = new JPanel(); } updateLayoutForPerspective(); Component centerComponent = ((BorderLayout) moduleViewContainerPanel.getLayout()).getLayoutComponent(BorderLayout.CENTER); if (centerComponent != null) { moduleViewContainerPanel.remove(centerComponent); } moduleViewContainerPanel.add(newCenterView); moduleViewContainerPanel.revalidate(); moduleViewContainerPanel.repaint(); updateComponent(moduleViewContainerPanel, LayoutPosition.MIDDLE_CENTER); centerPanel.revalidate(); revalidate(); repaint(); controller.getFlexoFrame().updateTitle(); if (moduleView != null) { controller.getCurrentPerspective().notifyModuleViewDisplayed(moduleView); } if (controller.getFlexoFrame().isValid()) { FCH.validateWindow(controller.getFlexoFrame()); } } private boolean updateBottomCenterView() { JComponent newBottomCenterView = perspective.getBottomCenterView(); updateComponent(newBottomCenterView, LayoutPosition.BOTTOM_CENTER); return newBottomCenterView != null; } private boolean updateBottomRightView() { JComponent newBottomRightView = perspective.getBottomRightView(); updateComponent(newBottomRightView, LayoutPosition.BOTTOM_RIGHT); return newBottomRightView != null; } private boolean updateBottomLeftView() { JComponent newBottomLeftView = perspective.getBottomLeftView(); updateComponent(newBottomLeftView, LayoutPosition.BOTTOM_LEFT); return newBottomLeftView != null; } private boolean updateMiddleRightView() { JComponent newMiddleRightView = perspective.getMiddleRightView(); updateComponent(newMiddleRightView, LayoutPosition.MIDDLE_RIGHT); return newMiddleRightView != null; } private boolean updateMiddleLeftView() { JComponent newMiddleLeftView = perspective.getMiddleLeftView(); updateComponent(newMiddleLeftView, LayoutPosition.MIDDLE_LEFT); return newMiddleLeftView != null; } private boolean updateTopCenterView() { JComponent newTopCenterView = perspective.getTopCenterView(); updateComponent(newTopCenterView, LayoutPosition.TOP_CENTER); return newTopCenterView != null; } private boolean updateTopRightView() { JComponent newTopRightView = perspective.getTopRightView(); updateComponent(newTopRightView, LayoutPosition.TOP_RIGHT); return newTopRightView != null; } private boolean updateTopLeftView() { JComponent newTopLeftView = perspective.getTopLeftView(); updateComponent(newTopLeftView, LayoutPosition.TOP_LEFT); return newTopLeftView != null; } private void updateFooter() { if (footer != controller.getCurrentPerspective().getFooter()) { if (footer != null) { remove(footer); } if (controller.getCurrentPerspective().getFooter() != null) { add(controller.getCurrentPerspective().getFooter(), BorderLayout.SOUTH); } footer = controller.getCurrentPerspective().getFooter(); } } private void updateHeader() { if (moduleView != null) { topBar.setHeader(controller.getCurrentPerspective().getHeader()); } else { topBar.setHeader(null); } } private void updateComponent(JComponent next, LayoutPosition position) { JComponent previous = getComponentForPosition(position); JComponent toAdd = next != null ? next : new EmptyPanel(); if (previous != toAdd) { if (previous != null) { centerPanel.remove(previous); } // toAdd.setPreferredSize(new Dimension(0, 0)); centerPanel.add(toAdd, position.name()); // boolean wasVisible = centerLayout.getNodeForName(position.name()).isVisible(); boolean visible = next != null; centerLayout.displayNode(position.name(), visible); Node node = centerLayout.getNodeForName(position.name()); Split parent = node.getParent(); if (parent != centerLayout.getNodeForName(LayoutColumns.CENTER.name())) { fixWeightForNodeChildren(parent); } /* if (!wasVisible && visible) { for (Node child : parent.getChildren()) { child.setBounds(new Rectangle()); } } */ centerPanel.revalidate(); } } protected void fixWeightForNodeChildren(Split split) { int visibleChildren = 0; // double weight = 0.0; for (Node child : split.getChildren()) { if (!(child instanceof Divider) && child.isVisible()) { visibleChildren++; // weight+=child.getWeight(); } } for (Node child : split.getChildren()) { if (!(child instanceof Divider) && child.isVisible()) { child.setWeight(1.0 / visibleChildren); } } } private void saveLayout() { if (perspective != null) { getController().getControllerModel().setLayoutForPerspective(perspective, centerLayout.getModel()); } } private void restoreLayout() { if (perspective == null) { return; } Node layoutModel = getController().getControllerModel().getLayoutForPerspective(perspective); if (layoutModel == null) { layoutModel = getDefaultLayout(); perspective.setupDefaultLayout(layoutModel); } centerLayout.setModel(layoutModel); centerPanel.revalidate(); } private Split getDefaultLayout() { Split root = new Split(); root.setName("ROOT"); Split left = getVerticalSplit(LayoutPosition.TOP_LEFT, LayoutPosition.MIDDLE_LEFT, LayoutPosition.BOTTOM_LEFT); left.setWeight(0.2); left.setName(LayoutColumns.LEFT.name()); Split center = getVerticalSplit(LayoutPosition.TOP_CENTER, LayoutPosition.MIDDLE_CENTER, LayoutPosition.BOTTOM_CENTER); center.setWeight(0.6); center.setName(LayoutColumns.CENTER.name()); Split right = getVerticalSplit(LayoutPosition.TOP_RIGHT, LayoutPosition.MIDDLE_RIGHT, LayoutPosition.BOTTOM_RIGHT); right.setWeight(0.2); right.setName(LayoutColumns.RIGHT.name()); root.setChildren(left, new Divider(), center, new Divider(), right); return root; } private Split getVerticalSplit(LayoutPosition position1, LayoutPosition position2, LayoutPosition position3) { Split split = new Split(); split.setRowLayout(false); Leaf l1 = new Leaf(position1.name()); l1.setWeight(0.2); Leaf l2 = new Leaf(position2.name()); l2.setWeight(0.6); Leaf l3 = new Leaf(position3.name()); l3.setWeight(0.2); split.setChildren(l1, new Divider(), l2, new Divider(), l3); return split; } private Node getNodeForName(Node root, String name) { if (root instanceof Split) { Split split = (Split) root; if (name.equals(split.getName())) { return split; } for (Node child : split.getChildren()) { Node n = getNodeForName(child, name); if (n != null) { return n; } } } else if (root instanceof Leaf) { Leaf leaf = (Leaf) root; if (name.equals(leaf.getName())) { return leaf; } } return null; } private JComponent getComponentForPosition(LayoutPosition position) { Node node = centerLayout.getNodeForName(position.name()); if (node != null) { return (JComponent) centerLayout.getComponentForNode(node); } else { return null; } } public ModuleView<?> getModuleView() { return moduleView; } public void showControlPanel() { topBar.setVisible(true); } public void hideControlPanel() { topBar.setVisible(false); } private void updateLeftViewVisibility() { Node left = getNodeForName(centerLayout.getModel(), LayoutColumns.LEFT.name()); updateVisibility(left, controller.getControllerModel().isLeftViewVisible()); centerPanel.revalidate(); centerPanel.repaint(); } private void updateRightViewVisibility() { Node right = getNodeForName(centerLayout.getModel(), LayoutColumns.RIGHT.name()); updateVisibility(right, controller.getControllerModel().isRightViewVisible()); centerPanel.revalidate(); centerPanel.repaint(); } private void updateVisibility(Node root, boolean visible) { if (root instanceof Leaf) { Component componentForNode = centerLayout.getComponentForNode(root); if (componentForNode instanceof EmptyPanel) { // EmptyPanel means that there is nothing to display/hide here } else { centerLayout.displayNode(((Leaf) root).getName(), visible); } } else if (root instanceof Split) { Split split = (Split) root; centerLayout.displayNode(split.getName(), visible); for (Node child : split.getChildren()) { updateVisibility(child, visible); } fixWeightForNodeChildren(split); } centerPanel.revalidate(); centerPanel.repaint(); } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getSource() == controller.getControllerModel()) { if (evt.getPropertyName().equals(ControllerModel.CURRENT_LOCATION)) { Location previous = (Location) evt.getOldValue(); Location next = (Location) evt.getNewValue(); FlexoPerspective previousPerspective = previous != null ? previous.getPerspective() : null; FlexoPerspective nextPerspective = next != null ? next.getPerspective() : null; if (previousPerspective != nextPerspective) { saveLayout(); perspective = nextPerspective; restoreLayout(); } if (next != null && next.getObject() != null) { tabbedPane.selectTab(next); } else { tabbedPane.selectTab(null); } updatePropertyChangeListener(previousPerspective, nextPerspective); updateLayoutForPerspective(); } else if (evt.getPropertyName().equals(ControllerModel.LEFT_VIEW_VISIBLE)) { updateLeftViewVisibility(); } else if (evt.getPropertyName().equals(ControllerModel.RIGHT_VIEW_VISIBLE)) { updateRightViewVisibility(); } else if (evt.getPropertyName().equals(ControllerModel.LOCATIONS)) { if (evt.getNewValue() != null) { Location newValue = (Location) evt.getNewValue(); if (newValue.getObject() != null) { if (perspective == null) { perspective = newValue.getPerspective(); restoreLayout(); } tabbedPane.addTab(newValue); registrationManager.addListener("name", this, newValue.getObject()); } } else if (evt.getOldValue() != null) { Location oldValue = (Location) evt.getOldValue(); if (oldValue.getObject() != null) { tabbedPane.removeTab(oldValue); registrationManager.addListener("name", this, oldValue.getObject()); } } } else if (evt.getPropertyName().equals(ControllerModel.CURRENT_EDITOR)) { tabbedPane.refreshTabHeaders(); } } else if (evt.getSource() == controller.getCurrentPerspective()) { if (evt.getPropertyName().equals(FlexoPerspective.HEADER)) { updateHeader(); } else if (evt.getPropertyName().equals(FlexoPerspective.FOOTER)) { updateFooter(); } else if (evt.getPropertyName().equals(FlexoPerspective.TOP_LEFT_VIEW)) { updateTopLeftView(); } else if (evt.getPropertyName().equals(FlexoPerspective.TOP_RIGHT_VIEW)) { updateTopRightView(); } else if (evt.getPropertyName().equals(FlexoPerspective.TOP_CENTER_VIEW)) { updateTopCenterView(); } else if (evt.getPropertyName().equals(FlexoPerspective.MIDDLE_LEFT_VIEW)) { updateMiddleLeftView(); } else if (evt.getPropertyName().equals(FlexoPerspective.MIDDLE_RIGHT_VIEW)) { updateMiddleRightView(); } else if (evt.getPropertyName().equals(FlexoPerspective.BOTTOM_LEFT_VIEW)) { updateBottomLeftView(); } else if (evt.getPropertyName().equals(FlexoPerspective.BOTTOM_RIGHT_VIEW)) { updateBottomRightView(); } else if (evt.getPropertyName().equals(FlexoPerspective.BOTTOM_CENTER_VIEW)) { updateBottomCenterView(); } } else if (evt.getSource() == controller) { } else if (evt.getPropertyName().equals("name")) { tabbedPane.refreshTabHeaders(); } } private void updateLayoutForPerspective() { if (perspective == null) { return; } boolean hasLeftView = false; boolean hasRightView = false; hasLeftView |= updateTopLeftView(); hasRightView |= updateTopRightView(); updateTopCenterView(); hasLeftView |= updateMiddleLeftView(); hasRightView |= updateMiddleRightView(); hasLeftView |= updateBottomLeftView(); hasRightView |= updateBottomRightView(); updateBottomCenterView(); updateHeader(); updateFooter(); updateLeftViewVisibility(); updateRightViewVisibility(); topBar.setLeftViewToggle(hasLeftView); topBar.setRightViewToggle(hasRightView); } private void updatePropertyChangeListener(FlexoPerspective previous, FlexoPerspective next) { if (previous != null) { for (String property : FlexoPerspective.PROPERTIES) { registrationManager.removeListener(property, this, next); } } if (next != null) { for (String property : FlexoPerspective.PROPERTIES) { registrationManager.new PropertyChangeListenerRegistration(property, this, next); } } } }