/* * Freeplane - mind map editor * Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev * * This file is modified by Dimitry Polivaev in 2008. * * 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 2 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/>. */ package org.freeplane.features.ui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.KeyboardFocusManager; import java.awt.LayoutManager; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URL; import java.util.HashMap; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ComboBoxEditor; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.RootPaneContainer; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.plaf.basic.BasicComboBoxEditor; import javax.swing.plaf.metal.MetalFileChooserUI; import org.freeplane.core.resources.NamedObject; import org.freeplane.core.resources.ResourceController; import org.freeplane.core.ui.FixedBasicComboBoxEditor; import org.freeplane.core.ui.IUserInputListenerFactory; import org.freeplane.core.ui.components.ContainerComboBoxEditor; import org.freeplane.core.ui.components.FreeplaneMenuBar; import org.freeplane.core.ui.components.JResizer.Direction; import org.freeplane.core.ui.components.OneTouchCollapseResizer; import org.freeplane.core.ui.components.OneTouchCollapseResizer.CollapseDirection; import org.freeplane.core.ui.components.OneTouchCollapseResizer.ComponentCollapseListener; import org.freeplane.core.ui.components.ResizeEvent; import org.freeplane.core.ui.components.ResizerListener; import org.freeplane.core.ui.components.UITools; import org.freeplane.core.util.Compat; import org.freeplane.core.util.LogUtils; import org.freeplane.features.format.FormattedDate; import org.freeplane.features.format.FormattedObject; import org.freeplane.features.format.ScannerController; import org.freeplane.features.mode.Controller; import org.freeplane.features.mode.ModeController; import org.freeplane.features.styles.StyleNamedObject; import org.freeplane.features.time.TimeComboBoxEditor; /** * @author Dimitry Polivaev */ abstract public class FrameController implements ViewController { private final class HorizontalToolbarPanel extends JPanel { /** * */ private static final long serialVersionUID = 1L; private HorizontalToolbarPanel(final LayoutManager layout) { super(layout); } @Override public void validateTree() { if (!isValid()) { super.validateTree(); resizeToolbarPane(); } } private void resizeToolbarPane() { if (getWidth() == 0) { return; } int lastComponent = getComponentCount() - 1; while (lastComponent >= 0 && !getComponent(lastComponent).isVisible()) { lastComponent--; } final Dimension oldPreferredSize = getPreferredSize(); final Dimension preferredSize; if (lastComponent >= 0) { final Component component = getComponent(lastComponent); preferredSize = new Dimension(getWidth(), component.getY() + component.getHeight()); } else { preferredSize = new Dimension(0, 0); } if (oldPreferredSize.height != preferredSize.height) { setPreferredSize(preferredSize); EventQueue.invokeLater(new Runnable() { public void run() { getParent().invalidate(); ((JComponent) getContentPane()).revalidate(); } }); } } } // // final private Controller controller; final private JLabel status; final private Map<String, Component> statusInfos; final private JPanel statusPanel; final private JComponent toolbarPanel[]; private Rectangle frameSize; public Rectangle getFrameSize() { return frameSize; } public void setFrameSize(final Rectangle frameSize) { this.frameSize = frameSize; } private int winState; final private String propertyKeyPrefix; public static Icon textIcon; public static Icon numberIcon; public static Icon dateIcon; public static Icon dateTimeIcon; public static Icon linkIcon; public static Icon localLinkIcon; public FrameController(Controller controller, final IMapViewManager mapViewManager, final String propertyKeyPrefix) { super(); final ResourceController resourceController = ResourceController.getResourceController(); if(textIcon == null){ FrameController.textIcon = new ImageIcon(resourceController.getResource("/images/text.png")); FrameController.numberIcon = new ImageIcon(resourceController.getResource("/images/number.png")); FrameController.dateIcon = new ImageIcon(resourceController.getResource("/images/calendar_red.png")); FrameController.dateTimeIcon = new ImageIcon(resourceController.getResource("/images/calendar_clock_red.png")); FrameController.linkIcon = new ImageIcon(resourceController.getResource("/images/" + resourceController.getProperty("link_icon"))); FrameController.localLinkIcon = new ImageIcon(resourceController.getResource("/images/" + resourceController.getProperty("link_local_icon"))); } this.propertyKeyPrefix = propertyKeyPrefix; statusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 3, 0)); statusPanel.putClientProperty(VISIBLE_PROPERTY_KEY, "status_visible"); status = new JLabel(); status.setBorder(BorderFactory.createEtchedBorder()); statusPanel.add(status); statusInfos = new HashMap<String, Component>(); statusInfos.put(STANDARD_STATUS_INFO_KEY, status); // this.controller = controller; controller.setViewController(this); controller.addAction(new ToggleFullScreenAction(this)); controller.addAction(new ToggleRibbonAction()); controller.addAction(new CloseAction()); controller.addAction(new ToggleMenubarAction(this)); controller.addAction(new ToggleScrollbarsAction(this)); controller.addAction(new ToggleToolbarAction("ToggleToolbarAction", "/main_toolbar")); controller.addAction(new ToggleToolbarAction("ToggleStatusAction", "/status")); toolbarPanel = new JComponent[4]; toolbarPanel[TOP] = new HorizontalToolbarPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); toolbarPanel[BOTTOM] = Box.createHorizontalBox(); toolbarPanel[LEFT] = Box.createHorizontalBox(); toolbarPanel[RIGHT] = Box.createVerticalBox(); } public void changeNoteWindowLocation() { } public void err(final String msg) { status.setText(msg); } /** * @return */ abstract public RootPaneContainer getRootPaneContainer(); public Container getContentPane(){ return getRootPaneContainer().getContentPane(); } protected Controller getController() { return Controller.getCurrentController(); } public Frame getFrame() { return JOptionPane.getFrameForComponent(getContentPane()); } abstract public FreeplaneMenuBar getFreeplaneMenuBar(); /** * @return */ abstract public JFrame getJFrame(); /** */ public JComponent getStatusBar() { return statusPanel; } public void init(Controller controller) { //RIBBONS impl: find ribbon in layout final Component comp = ((BorderLayout)getContentPane().getLayout()).getLayoutComponent(BorderLayout.NORTH); if(comp == null) { getContentPane().add(toolbarPanel[TOP], BorderLayout.NORTH); } else { JPanel northPanel = new JPanel(); northPanel.setLayout(new BorderLayout()); Box resizableTabs = Box.createVerticalBox(); resizableTabs.add(comp); final OneTouchCollapseResizer otcr = new OneTouchCollapseResizer(Direction.UP, CollapseDirection.COLLAPSE_UP); otcr.setSliderLocked(true); resizableTabs.add(otcr); otcr.addResizerListener(new ResizerListener() { public void componentResized(ResizeEvent event) { if(comp.getHeight() > 151) { comp.setMaximumSize(new Dimension(Integer.MAX_VALUE, 151)); comp.setPreferredSize(new Dimension(comp.getWidth(), 151)); } } }); otcr.addCollapseListener(new ComponentCollapseListener() { public void componentExpanded(ResizeEvent event) { comp.setPreferredSize(null); otcr.recalibrate(); } public void componentCollapsed(ResizeEvent event) { } }); northPanel.add(resizableTabs, BorderLayout.NORTH); northPanel.add(toolbarPanel[TOP], BorderLayout.CENTER); getContentPane().add(northPanel, BorderLayout.NORTH); } getContentPane().add(toolbarPanel[LEFT], BorderLayout.WEST); getContentPane().add(toolbarPanel[RIGHT], BorderLayout.EAST); getContentPane().add(toolbarPanel[BOTTOM], BorderLayout.SOUTH); // status.setPreferredSize(status.getPreferredSize()); status.setText(""); getRootPaneContainer().getRootPane().putClientProperty(Controller.class, controller); final Frame frame = getFrame(); frame.addComponentListener(new ComponentAdapter() { @Override public void componentResized(final ComponentEvent e) { final Frame frame = (Frame) e.getComponent(); if (frame.getExtendedState() != Frame.NORMAL || isFullScreenEnabled()) { return; } frameSize = frame.getBounds(); } @Override public void componentMoved(final ComponentEvent e) { componentResized(e); } }); } abstract public void insertComponentIntoSplitPane(JComponent noteViewerComponent); abstract public boolean isApplet(); public boolean isMenubarVisible() { return isComponentVisible("menubar"); } public boolean areScrollbarsVisible() { return isComponentVisible("scrollbars"); } private boolean isComponentVisible(String component) { final String property; if (isFullScreenEnabled()) { property = component+"Visible.fullscreen"; } else { property = component +"Visible"; } final boolean booleanProperty = ResourceController.getResourceController().getBooleanProperty( getPropertyKeyPrefix() + property); return booleanProperty; } abstract public void openDocument(URI uri) throws IOException; abstract public void openDocument(URL fileToUrl) throws Exception; public void out(final String msg) { status.setText(msg); } public void addStatusInfo(final String key, final String info) { addStatusInfo(key, info, null, null); } public void addStatusInfo(final String key, Icon icon) { addStatusInfo(key, null, icon, null); } public void addStatusInfo(final String key, final String info, Icon icon) { addStatusInfo(key, info, icon, null); } public void addStatusInfo(final String key, final String info, Icon icon, final String tooltip) { JLabel label = (JLabel) statusInfos.get(key); if (label == null) { label = new JLabel(info); label.setBorder(BorderFactory.createEtchedBorder()); statusInfos.put(key, label); statusPanel.add(label, statusPanel.getComponentCount() - 1); } else { label.setText(info); label.revalidate(); label.repaint(); } label.setIcon(icon); label.setToolTipText(tooltip); label.setVisible(info != null || icon != null); } public void addStatusComponent(final String key, Component component) { Component oldComponent = statusInfos.put(key, component); if (oldComponent == null) { statusPanel.add(component, statusPanel.getComponentCount() - 1); } else { final int index = UITools.getComponentIndex(component); statusPanel.remove(index); statusPanel.add(component, index); } } public void removeStatus(final String key) { final Component oldComponent = statusInfos.remove(key); if (oldComponent == null) { return; } statusPanel.remove(oldComponent); } /** * */ abstract public void removeSplitPane(); public void saveProperties() { } public void selectMode( final ModeController oldModeController, final ModeController newModeController) { if (oldModeController == newModeController) { return; } if (oldModeController != null) { final IUserInputListenerFactory userInputListenerFactory = oldModeController.getUserInputListenerFactory(); for (int j = 0; j < 4; j++) { final Iterable<JComponent> modeToolBars = userInputListenerFactory.getToolBars(j); if (modeToolBars != null) { for (final Component toolBar : modeToolBars) { toolbarPanel[j].remove(toolBar); } toolbarPanel[j].revalidate(); } } } final IUserInputListenerFactory newUserInputListenerFactory = newModeController.getUserInputListenerFactory(); for (int j = 0; j < 4; j++) { final Iterable<JComponent> newToolBars = newUserInputListenerFactory.getToolBars(j); if (newToolBars != null) { int i = 0; for (final JComponent toolBar : newToolBars) { toolBar.setVisible(isToolbarVisible(toolBar)); toolbarPanel[j].add(toolBar, i++); } toolbarPanel[j].revalidate(); toolbarPanel[j].repaint(); } } //RIBBONS - todo: refactor later // setFreeplaneMenuBar(newUserInputListenerFactory.getMenuBar()); newUserInputListenerFactory.getRibbonBuilder().buildRibbon(); setUIComponentsVisible(newModeController.getController().getMapViewManager()); } private void setUIComponentsVisible(IMapViewManager iMapViewManager) { //getFreeplaneMenuBar().setVisible(isMenubarVisible()); final boolean areScrollbarsVisible = areScrollbarsVisible(); iMapViewManager.setScrollbarsVisible(areScrollbarsVisible); } abstract protected void setFreeplaneMenuBar(FreeplaneMenuBar menuBar); public void setMenubarVisible(final boolean visible) { final FreeplaneMenuBar freeplaneMenuBar = getFreeplaneMenuBar(); setComponentVisibleProperty("menubar", visible); freeplaneMenuBar.setVisible(visible); } public void setScrollbarsVisible(final boolean visible) { setComponentVisibleProperty("scrollbars", visible); Controller.getCurrentController().getMapViewManager().setScrollbarsVisible(visible); } private void setComponentVisibleProperty(final String componentName, final boolean visible) { final String property; if (isFullScreenEnabled()) { property = componentName+"Visible.fullscreen"; } else { property = componentName+"Visible"; } ResourceController.getResourceController().setProperty(getPropertyKeyPrefix() + property, visible); } /** * Set the Frame title with mode and file if exist */ abstract public void setTitle(String title); /** * @param b */ abstract public void setWaitingCursor(boolean b); public void viewNumberChanged(final int number) { } void setFullScreen(final boolean fullScreen) { final Frame frame = getFrame(); final Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); if (fullScreen == isFullScreenEnabled()) { return; } final Controller controller = getController(); if (fullScreen) { winState = frame.getExtendedState(); frame.dispose(); frame.setExtendedState(Frame.MAXIMIZED_BOTH); final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); frame.setBounds(0, 0, screenSize.width, screenSize.height); frame.setUndecorated(true); frame.setResizable(false); setUIComponentsVisible(controller.getMapViewManager()); // for (int j = 0; j < 4; j++) { // final Iterable<JComponent> toolBars = controller.getModeController().getUserInputListenerFactory() // .getToolBars(j); // for (final JComponent toolBar : toolBars) { // toolBar.setVisible(isToolbarVisible(toolBar)); // } // } frame.setVisible(true); } else { frame.dispose(); frame.setUndecorated(false); frame.setResizable(true); frame.setBounds(frameSize); frame.setExtendedState(winState); setUIComponentsVisible(controller.getMapViewManager()); for (int j = 0; j < 4; j++) { final Iterable<JComponent> toolBars = controller.getModeController().getUserInputListenerFactory() .getToolBars(j); for (final JComponent toolBar : toolBars) { toolBar.setVisible(isToolbarVisible(toolBar)); } } frame.setVisible(true); } if(focusOwner != null) focusOwner.requestFocus(); } boolean isToolbarVisible(final JComponent toolBar) { final String completeKeyString = completeVisiblePropertyKey(toolBar); if (completeKeyString == null) { return true; } return !"false".equals(ResourceController.getResourceController().getProperty(completeKeyString, "true")); } public String completeVisiblePropertyKey(final JComponent toolBar) { if(toolBar == null) { return null; } final Object key = toolBar.getClientProperty(VISIBLE_PROPERTY_KEY); if (key == null) { return null; } final String keyString = key.toString(); final String completeKeyString; if (isFullScreenEnabled()) { completeKeyString = keyString + ".fullscreen"; } else { completeKeyString = keyString; } return getPropertyKeyPrefix() + completeKeyString; } protected boolean isFullScreenEnabled() { return !getFrame().isResizable(); } protected String getPropertyKeyPrefix() { return propertyKeyPrefix; } public static void setLookAndFeel(final String lookAndFeel) { try { if (Compat.isMacOsX() || lookAndFeel.equals("default")) { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } else { LookAndFeelInfo[] lafInfos = UIManager.getInstalledLookAndFeels(); boolean setLnF = false; setLnF = setLookAndFeel(lookAndFeel, lafInfos); if(!setLnF){ if (!"freeplane".equals(Controller.getCurrentController().getResourceController().getProperty("ApplicationName", "freeplane").toLowerCase())) { setLookAndFeel("nimbus", lafInfos); } else { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); Controller.getCurrentController().getResourceController().setProperty("lookandfeel", "default"); } } } } catch (final Exception ex) { LogUtils.warn("Error while setting Look&Feel" + lookAndFeel); } UIManager.put("Button.defaultButtonFollowsFocus", Boolean.TRUE); // Workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7077418 // NullPointerException in WindowsFileChooserUI when system icons missing/invalid // set FileChooserUI to MetalFileChooserUI if no JFileChooser can be created try{ new JFileChooser(); } catch (Throwable t){ try{ UIManager.getLookAndFeelDefaults().put("FileChooserUI", MetalFileChooserUI.class.getName()); } catch (Throwable t1){ } } } private static boolean setLookAndFeel(final String lookAndFeel, LookAndFeelInfo[] lafInfos) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException { boolean setLnF = false; for(LookAndFeelInfo lafInfo : lafInfos){ if(lafInfo.getName().equalsIgnoreCase(lookAndFeel)){ UIManager.setLookAndFeel(lafInfo.getClassName()); Controller.getCurrentController().getResourceController().setProperty("lookandfeel", lafInfo.getClassName()); setLnF = true; break; } if(lafInfo.getClassName().equals(lookAndFeel)){ UIManager.setLookAndFeel(lafInfo.getClassName()); setLnF = true; break; } } return setLnF; } public void addObjectTypeInfo(Object value) { if (value instanceof FormattedObject) { value = ((FormattedObject) value).getObject(); } if (value instanceof String || value instanceof StyleNamedObject) { addStatusInfo(ResourceController.OBJECT_TYPE, null, FrameController.textIcon); } else if (value instanceof FormattedDate) { final FormattedDate fd = (FormattedDate) value; if (fd.containsTime()) { addStatusInfo(ResourceController.OBJECT_TYPE, null, FrameController.dateTimeIcon); } else { addStatusInfo(ResourceController.OBJECT_TYPE, null, FrameController.dateIcon); } } else if (value instanceof Number) { addStatusInfo(ResourceController.OBJECT_TYPE, null, FrameController.numberIcon); } else if (value instanceof URI) { addStatusInfo(ResourceController.OBJECT_TYPE, null, FrameController.linkIcon); } else { addStatusInfo(ResourceController.OBJECT_TYPE, null, null); } } public static ComboBoxEditor getTextDateTimeEditor() { final ContainerComboBoxEditor editor = new ContainerComboBoxEditor(); final NamedObject keyText = new NamedObject("text", "1Ab"); final BasicComboBoxEditor textEditor = new FixedBasicComboBoxEditor(){ private Object oldItem; @Override public void setItem(Object object) { oldItem = object; if(object instanceof FormattedDate) super.setItem(""); else super.setItem(object); } @Override public Object getItem() { final Object item = super.getItem(); final Object oldItem = this.oldItem; this.oldItem = null; if(item != null && oldItem != null && item.toString().equals(oldItem.toString())) return oldItem; if(ResourceController.getResourceController().getBooleanProperty("parse_data") && item instanceof String){ final Object scannedObject = ScannerController.getController().parse((String)item); return scannedObject; } return item; } }; editor.put(keyText, textEditor); final NamedObject keyDate = new NamedObject("date", ""); keyDate.setIcon(dateIcon); final TimeComboBoxEditor dateComboBoxEditor = new TimeComboBoxEditor(false){ @Override public void setItem(Object object) { if(object instanceof FormattedDate && !((FormattedDate)object).containsTime()) super.setItem(object); else super.setItem(null); } }; dateComboBoxEditor.setItem(); editor.put(keyDate, dateComboBoxEditor); final NamedObject keyDateTime = new NamedObject("date_time", ""); keyDateTime.setIcon(dateTimeIcon); final TimeComboBoxEditor dateTimeComboBoxEditor = new TimeComboBoxEditor(true){ @Override public void setItem(Object object) { if(object instanceof FormattedDate && ((FormattedDate)object).containsTime()) super.setItem(object); else super.setItem(null); } }; dateTimeComboBoxEditor.setItem(); editor.put(keyDateTime, dateTimeComboBoxEditor); return editor; } public boolean quit() { return getController().getMapViewManager().closeAllMaps(); } public boolean isDispatchThread() { return EventQueue.isDispatchThread(); } public void invokeLater(Runnable runnable) { EventQueue.invokeLater(runnable); } public void invokeAndWait(Runnable runnable) throws InterruptedException, InvocationTargetException { EventQueue.invokeAndWait(runnable); } public boolean isHeadless() { return false; } }