All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.flexdock.view.View Maven / Gradle / Ivy

/*
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.flexdock.view;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JPanel;

import org.flexdock.docking.Dockable;
import org.flexdock.docking.DockingConstants;
import org.flexdock.docking.DockingManager;
import org.flexdock.docking.DockingPort;
import org.flexdock.docking.DockingStrategy;
import org.flexdock.docking.defaults.DefaultDockingStrategy;
import org.flexdock.docking.event.DockingEvent;
import org.flexdock.docking.event.DockingListener;
import org.flexdock.docking.props.DockablePropertySet;
import org.flexdock.docking.props.PropertyManager;
import org.flexdock.plaf.PlafManager;
import org.flexdock.plaf.theme.ViewUI;
import org.flexdock.util.DockingUtility;
import org.flexdock.util.ResourceManager;
import org.flexdock.util.SwingUtility;

/**
 * The {@code View} class is slightly incompatible with {@code JComponent}.
 * Similar to JFC/Swing top-level containers, a {@code View} contains only a
 * content pane {@code Container} and a {@code Titlebar}. The content pane
 * should contain all the components displayed by the {@code View}. As a
 * convenience {@code add} and its variants, {@code remove(Component)} and
 * {@code setLayout} have been overridden to forward to the
 * {@code contentPane} as necessary. This means you can write:
 *
 * 
 * view.add(child);
 * 
* * And the child will be added to the contentPane. The content pane will always * be non-null. Attempting to set it to null will cause the View to throw an * exception. The default content pane will not have a layout manager set. * * @see javax.swing.JFrame * @see javax.swing.JRootPane * * @author Christopher Butler * @author Karl Schaefer */ public class View extends JComponent implements Dockable, DockingConstants { protected class ViewLayout implements LayoutManager2, Serializable { /** * Returns the amount of space the layout would like to have. * * @param parent * the Container for which this layout manager is being used * @return a Dimension object containing the layout's preferred size */ @Override public Dimension preferredLayoutSize(Container parent) { Dimension rd, tpd; Insets i = getInsets(); if (contentPane != null) { rd = contentPane.getPreferredSize(); } else { rd = parent.getSize(); } if (titlepane != null && titlepane.isVisible()) { tpd = titlepane.getPreferredSize(); } else { tpd = new Dimension(0, 0); } return new Dimension(Math.max(rd.width, tpd.width) + i.left + i.right, rd.height + tpd.height + i.top + i.bottom); } /** * Returns the minimum amount of space the layout needs. * * @param parent * the Container for which this layout manager is being used * @return a Dimension object containing the layout's minimum size */ @Override public Dimension minimumLayoutSize(Container parent) { Dimension rd, tpd; Insets i = getInsets(); if (contentPane != null) { rd = contentPane.getMinimumSize(); } else { rd = parent.getSize(); } if (titlepane != null && titlepane.isVisible()) { tpd = titlepane.getMinimumSize(); } else { tpd = new Dimension(0, 0); } return new Dimension(Math.max(rd.width, tpd.width) + i.left + i.right, rd.height + tpd.height + i.top + i.bottom); } /** * Returns the maximum amount of space the layout can use. * * @param target * the Container for which this layout manager is being used * @return a Dimension object containing the layout's maximum size */ @Override public Dimension maximumLayoutSize(Container target) { Dimension rd, tpd; Insets i = getInsets(); if (titlepane != null && titlepane.isVisible()) { tpd = titlepane.getMaximumSize(); } else { tpd = new Dimension(0, 0); } if (contentPane != null) { rd = contentPane.getMaximumSize(); } else { // This is silly, but should stop an overflow error rd = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE - i.top - i.bottom - tpd.height - 1); } return new Dimension(Math.min(rd.width, tpd.width) + i.left + i.right, rd.height + tpd.height + i.top + i.bottom); } /** * Instructs the layout manager to perform the layout for the specified * container. * * @param parent * the Container for which this layout manager is being used */ @Override public void layoutContainer(Container parent) { Rectangle b = parent.getBounds(); Insets i = getInsets(); int contentY = 0; int w = b.width - i.right - i.left; int h = b.height - i.top - i.bottom; if (titlepane != null && titlepane.isVisible()) { Dimension mbd = titlepane.getPreferredSize(); titlepane.setBounds(0, 0, w, mbd.height); contentY += mbd.height; } if (contentPane != null) { contentPane.setBounds(0, contentY, w, h - contentY); } } @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public void addLayoutComponent(Component comp, Object constraints) { } @Override public float getLayoutAlignmentX(Container target) { return 0.0f; } @Override public float getLayoutAlignmentY(Container target) { return 0.0f; } @Override public void invalidateLayout(Container target) { } } public static final String UI_CLASS_ID = "Flexdock.view"; public static final String ACTION_TOGGLE_NEXT = "toggleNextView"; public static final String ACTION_TOGGLE_PREVIOUS = "togglePreviousView"; static final DockingStrategy VIEW_DOCKING_STRATEGY = createDockingStrategy(); private String persistentId; protected Titlebar titlepane; protected Container contentPane; protected boolean contentPaneCheckingEnabled; protected ArrayList dockingListeners; // protected boolean active; protected ArrayList dragSources; protected HashSet frameDragSources; private transient HashSet blockedActions; static { DockingManager.setDockingStrategy(View.class, VIEW_DOCKING_STRATEGY); PropertyManager.setDockablePropertyType(View.class, ViewProps.class); } public View(String persistentId) { this(persistentId, null); } public View(String persistentId, String title) { this(persistentId, title, null); } public View(String persistentId, String title, String tabText) { if (persistentId == null) { throw new IllegalArgumentException( "The 'persistentId' parameter cannot be null."); } this.persistentId = persistentId; dragSources = new ArrayList(1); frameDragSources = new HashSet(1); dockingListeners = new ArrayList(1); setContentPane(createContentPane()); setTitlebar(createTitlebar()); setLayout(createLayout()); setContentPaneCheckingEnabled(true); if (title == null) { title = ""; } setTitle(title); if (tabText == null) { tabText = title; } setTabText(tabText); addHierarchyListener(new HierarchyListener() { @Override public void hierarchyChanged(HierarchyEvent e) { clearButtonRollovers(); } }); updateUI(); DockingManager.registerDockable((Dockable) this); getActionMap().put(ACTION_TOGGLE_NEXT, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { SwingUtility.toggleFocus(+1); } }); getActionMap().put(ACTION_TOGGLE_PREVIOUS, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { SwingUtility.toggleFocus(-1); } }); } private static DockingStrategy createDockingStrategy() { return new DefaultDockingStrategy() { @Override protected DockingPort createDockingPortImpl(DockingPort base) { return new Viewport(); } }; } public static View getInstance(String viewId) { Dockable view = DockingManager.getDockable(viewId); return view instanceof View ? (View) view : null; } protected Container createContentPane() { return new JPanel(); } protected LayoutManager createLayout() { return new ViewLayout(); } protected Titlebar createTitlebar() { Titlebar t = new Titlebar(); t.setView(this); return t; } public Container getContentPane() { return contentPane; } public Titlebar getTitlebar() { return titlepane; } @Override public DockablePropertySet getDockingProperties() { return PropertyManager.getDockablePropertySet(this); } public ViewProps getViewProperties() { return (ViewProps) getDockingProperties(); } public void addAction(Action action) { if (titlepane != null) { titlepane.addAction(action); } } public void addAction(String action) { if (titlepane != null) { titlepane.addAction(action); } } public void removeActions() { if (titlepane != null) { titlepane.removeAllActions(); } } public void setIcon(Icon icon) { if (titlepane != null) { titlepane.setIcon(icon); } } public void setIcon(String imgUri) { Icon icon = imgUri == null ? null : ResourceManager.createIcon(imgUri); setIcon(icon); } /** * Sets the content pane for this view. * * @param c * the container to use as the content pane. * @throws IllegalArgumentException * if {@code c} is {@code null} or if {@code c} is the * {@code titlepane}. * @see #titlepane * @see #getTitlePane() */ public void setContentPane(Container c) throws IllegalArgumentException { if (c == null) { throw new IllegalArgumentException( "Unable to set a null content pane."); } if (c == titlepane) { throw new IllegalArgumentException( "Cannot use the same component as both content pane and titlebar."); } if (contentPane != null) { remove(contentPane); } contentPane = c; if (contentPane != null) { boolean checkingEnabled = isContentPaneCheckingEnabled(); try { setContentPaneCheckingEnabled(false); add(contentPane); } finally { setContentPaneCheckingEnabled(checkingEnabled); } } } protected String getPreferredTitlebarUIName() { return ui instanceof ViewUI ? ((ViewUI) ui).getPreferredTitlebarUI() : null; } public void setTitlebar(Titlebar titlebar) { if (titlebar != null && titlebar == contentPane) { throw new IllegalArgumentException( "Cannot use the same component as both content pane and titlebar."); } if (titlepane != null) { remove(titlepane); titlepane.setView(null); dragSources.remove(titlepane); frameDragSources.remove(titlepane); DockingManager.removeDragListeners(titlepane); } titlepane = titlebar; if (titlepane != null) { boolean checkingEnabled = isContentPaneCheckingEnabled(); try { setContentPaneCheckingEnabled(false); add(titlepane); } finally { setContentPaneCheckingEnabled(checkingEnabled); } dragSources.add(titlepane); frameDragSources.add(titlepane); DockingManager.updateDragListeners(this); } } protected Component getTitlePane() { return titlepane; } public void setTitle(String title) { if (titlepane != null) { titlepane.setText(title); } } public void setTitle(String title, boolean alsoTabText) { setTitle(title); if (alsoTabText) { setTabText(title); } } public String getTitle() { Titlebar tbar = getTitlebar(); return tbar == null ? null : tbar.getText(); } @Override public void updateUI() { setUI(PlafManager.getUI(this)); } @Override public String getUIClassID() { return UI_CLASS_ID; } /** * {@inheritDoc} */ @Override protected void addImpl(Component comp, Object constraints, int index) { if (comp instanceof Titlebar) { ((Titlebar) comp).setView(this); } if (isContentPaneCheckingEnabled()) { getContentPane().add(comp, constraints, index); } else { super.addImpl(comp, constraints, index); } } @Override public void remove(Component comp) { if (comp == contentPane) { super.remove(comp); } else { getContentPane().remove(comp); } } public AbstractButton getActionButton(String actionName) { Titlebar tbar = getTitlebar(); return tbar == null ? null : tbar.getActionButton(actionName); } @Override public Component getComponent() { return this; } @Override public List getDragSources() { return dragSources; } @Override public Set getFrameDragSources() { return frameDragSources; } @Override public String getPersistentId() { return persistentId; } public boolean isTerritoryBlocked(Dockable dockable, String region) { return getDockingProperties().isTerritoryBlocked(region).booleanValue(); } public void setTerritoryBlocked(String region, boolean blocked) { getDockingProperties().setTerritoryBlocked(region, blocked); } public String getTabText() { String txt = getDockingProperties().getDockableDesc(); return txt == null ? getTitle() : txt; } public void setTabText(String tabText) { getDockingProperties().setDockableDesc(tabText); } public Icon getTabIcon() { return getDockingProperties().getTabIcon(); } public void setTabIcon(Icon icon) { getDockingProperties().setTabIcon(icon); } @Override public boolean dock(Dockable dockable) { return dock(dockable, CENTER_REGION); } @Override public DockingPort getDockingPort() { return DockingManager.getDockingPort((Dockable) this); } public Dockable getSibling(String region) { return DefaultDockingStrategy.getSibling(this, region); } public Viewport getViewport() { DockingPort port = getDockingPort(); return port instanceof Viewport ? (Viewport) port : null; } @Override public boolean dock(Dockable dockable, String relativeRegion) { return DockingManager.dock(dockable, this, relativeRegion); } @Override public boolean dock(Dockable dockable, String relativeRegion, float ratio) { return DockingManager.dock(dockable, this, relativeRegion, ratio); } public void setActive(boolean b) { getViewProperties().setActive(b); } public boolean isActive() { return getViewProperties().isActive().booleanValue(); } public void setActiveStateLocked(boolean b) { getViewProperties().setActiveStateLocked(b); } public boolean isActiveStateLocked() { return getViewProperties().isActiveStateLocked().booleanValue(); } public boolean isMinimized() { return DockingUtility.isMinimized(this); } public int getMinimizedConstraint() { return DockingUtility.getMinimizedConstraint(this); } @Override public void addDockingListener(DockingListener listener) { dockingListeners.add(listener); } @Override public DockingListener[] getDockingListeners() { return (DockingListener[]) dockingListeners .toArray(new DockingListener[0]); } @Override public void removeDockingListener(DockingListener listener) { dockingListeners.remove(listener); } @Override public void dockingCanceled(DockingEvent evt) { } @Override public void dockingComplete(DockingEvent evt) { setActionBlocked(DockingConstants.PIN_ACTION, isFloating()); if (titlepane != null) { titlepane.revalidate(); } } @Override public void dragStarted(DockingEvent evt) { } @Override public void dropStarted(DockingEvent evt) { } @Override public void undockingComplete(DockingEvent evt) { clearButtonRollovers(); } @Override public void undockingStarted(DockingEvent evt) { } private void clearButtonRollovers() { if (titlepane == null) { return; } Component[] comps = titlepane.getComponents(); for (int i = 0; i < comps.length; i++) { Button button = comps[i] instanceof Button ? (Button) comps[i] : null; if (button != null) { button.getModel().setRollover(false); } } } public void setActionBlocked(String actionName, boolean blocked) { if (actionName == null) { return; } Set actions = getBlockedActions(); if (blocked) { actions.add(actionName); } else { if (actions != null) { actions.remove(actionName); } } } public boolean isActionBlocked(String actionName) { return actionName == null || blockedActions == null ? false : blockedActions.contains(actionName); } private HashSet getBlockedActions() { if (blockedActions == null) { blockedActions = new HashSet(1); } return blockedActions; } public boolean isFloating() { return DockingUtility.isFloating(this); } @Override protected String paramString() { return "id=" + getPersistentId() + "," + super.paramString(); } /** * @return the contentPaneCheckingEnabled */ protected boolean isContentPaneCheckingEnabled() { return contentPaneCheckingEnabled; } /** * @param contentPaneCheckingEnabled * the contentPaneCheckingEnabled to set */ protected void setContentPaneCheckingEnabled( boolean contentPaneCheckingEnabled) { this.contentPaneCheckingEnabled = contentPaneCheckingEnabled; } /** * Sets the LayoutManager. Overridden to conditionally * forward the call to the contentPane. * * @param manager * the LayoutManager * @see #setContentPaneCheckingEnabled */ @Override public void setLayout(LayoutManager manager) { if (isContentPaneCheckingEnabled()) { getContentPane().setLayout(manager); } else { super.setLayout(manager); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy