/*
* Freeplane - mind map editor
* Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev
*
* This file is created 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.main.application;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.LayoutManager;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.MessageFormat;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.RootPaneContainer;
import org.freeplane.core.resources.ResourceController;
import org.freeplane.core.ui.components.FreeplaneMenuBar;
import org.freeplane.core.ui.components.UITools;
import org.freeplane.core.util.Compat;
import org.freeplane.features.map.IMapSelection;
import org.freeplane.features.map.NodeModel;
import org.freeplane.features.mode.Controller;
import org.freeplane.features.ui.IMapViewManager;
import org.freeplane.features.ui.ViewController;
import org.freeplane.features.url.mindmapmode.FileOpener;
import org.freeplane.view.swing.ui.DefaultMapMouseListener;
import sun.net.www.ParseUtil;
class ApplicationViewController extends ViewController {
public static final String RESOURCES_USE_TABBED_PANE = "use_tabbed_pane";
private static final String SPLIT_PANE_LAST_LEFT_POSITION = "split_pane_last_left_position";
private static final String SPLIT_PANE_LAST_POSITION = "split_pane_last_position";
private static final String SPLIT_PANE_LAST_RIGHT_POSITION = "split_pane_last_right_position";
private static final String SPLIT_PANE_LAST_TOP_POSITION = "split_pane_last_top_position";
private static final String SPLIT_PANE_LEFT_POSITION = "split_pane_left_position";
private static final String SPLIT_PANE_POSITION = "split_pane_position";
private static final String SPLIT_PANE_RIGHT_POSITION = "split_pane_right_position";
private static final String SPLIT_PANE_TOP_POSITION = "split_pane_top_position";
// // final private Controller controller;
final private JFrame frame;
/** Contains the value where the Note Window should be displayed (right, left, top, bottom) */
private String mLocationPreferenceValue;
/** Contains the Note Window Component */
private JComponent mMindMapComponent;
final private JSplitPane mSplitPane;
final private NavigationNextMapAction navigationNextMap;
final private NavigationPreviousMapAction navigationPreviousMap;
final private ResourceController resourceController;
@SuppressWarnings("serial")
public ApplicationViewController( Controller controller, final IMapViewManager mapViewController,
final JFrame frame) {
super(controller, mapViewController, "");
// this.controller = controller;
navigationPreviousMap = new NavigationPreviousMapAction();
controller.addAction(navigationPreviousMap);
navigationNextMap = new NavigationNextMapAction();
controller.addAction(navigationNextMap);
resourceController = ResourceController.getResourceController();
this.frame = frame;
getContentPane().setLayout(new BorderLayout());
// --- Set Note Window Location ---
mLocationPreferenceValue = resourceController.getProperty("note_location", "bottom");
if (ResourceController.getResourceController().getBooleanProperty("no_scrollbar")) {
getScrollPane().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
getScrollPane().setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
else {
getScrollPane().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
getScrollPane().setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
}
// disable all hotkeys for JSplitPane
mSplitPane = new JSplitPane(){
@Override
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed){
return false;
}
};
setSplitPaneLayoutManager();
final JScrollPane contentComponent = getScrollPane();
mSplitPane.setLeftComponent(contentComponent);
mSplitPane.setRightComponent(null);
final boolean shouldUseTabbedPane = ResourceController.getResourceController().getBooleanProperty(
ApplicationViewController.RESOURCES_USE_TABBED_PANE);
if (shouldUseTabbedPane) {
new MapViewTabs(this, mSplitPane);
}
else {
getContentPane().add(mSplitPane, BorderLayout.CENTER);
final FileOpener fileOpener = new FileOpener();
new DropTarget(mSplitPane, fileOpener);
mSplitPane.addMouseListener(new DefaultMapMouseListener());
}
initFrame(frame);
}
/**
* Called from the Controller, when the Location of the Note Window is changed on the Menu->View->Note Window Location
*/
@Override
public void changeNoteWindowLocation() {
mLocationPreferenceValue = resourceController.getProperty("note_location");
if(mMindMapComponent != null){
insertComponentIntoSplitPane(mMindMapComponent);
}
}
public String getAdjustableProperty(final String label) {
return resourceController.getProperty(label);
}
/*
* (non-Javadoc)
* @see freeplane.main.FreeplaneMain#getContentPane()
*/
@Override
public RootPaneContainer getRootPaneContainer() {
return frame;
}
@Override
public FreeplaneMenuBar getFreeplaneMenuBar() {
return (FreeplaneMenuBar) frame.getJMenuBar();
}
/*
* (non-Javadoc)
* @see freeplane.main.FreeplaneMain#getJFrame()
*/
@Override
public JFrame getJFrame() {
return frame;
}
/*
* (non-Javadoc)
* @see freeplane.main.FreeplaneMain#getLayeredPane()
*/
public JLayeredPane getLayeredPane() {
return frame.getLayeredPane();
}
@Override
public void insertComponentIntoSplitPane(final JComponent pMindMapComponent) {
// --- Save the Component --
mMindMapComponent = pMindMapComponent;
// --- Devider position variables --
int splitPanePosition = -1;
int lastSplitPanePosition = -1;
final JScrollPane scrollPane = getScrollPane();
scrollPane.setVisible(true);
mSplitPane.setLeftComponent(null);
mSplitPane.setRightComponent(null);
if ("right".equals(mLocationPreferenceValue)) {
mSplitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
mSplitPane.setLeftComponent(scrollPane);
mSplitPane.setRightComponent(pMindMapComponent);
splitPanePosition = resourceController.getIntProperty(SPLIT_PANE_RIGHT_POSITION, -1);
lastSplitPanePosition = resourceController.getIntProperty(SPLIT_PANE_LAST_RIGHT_POSITION, -1);
}
else if ("left".equals(mLocationPreferenceValue)) {
mSplitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
mSplitPane.setLeftComponent(pMindMapComponent);
mSplitPane.setRightComponent(scrollPane);
splitPanePosition = resourceController.getIntProperty(SPLIT_PANE_LEFT_POSITION, -1);
lastSplitPanePosition = resourceController.getIntProperty(SPLIT_PANE_LAST_LEFT_POSITION, -1);
}
else if ("top".equals(mLocationPreferenceValue)) {
mSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
mSplitPane.setLeftComponent(pMindMapComponent);
mSplitPane.setRightComponent(scrollPane);
splitPanePosition = resourceController.getIntProperty(SPLIT_PANE_TOP_POSITION, -1);
lastSplitPanePosition = resourceController.getIntProperty(SPLIT_PANE_LAST_TOP_POSITION, -1);
}
else if ("bottom".equals(mLocationPreferenceValue)) {
mSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
mSplitPane.setLeftComponent(scrollPane);
mSplitPane.setRightComponent(pMindMapComponent);
splitPanePosition = resourceController.getIntProperty(SPLIT_PANE_POSITION, -1);
lastSplitPanePosition = resourceController.getIntProperty(SPLIT_PANE_LAST_POSITION, -1);
}
mSplitPane.setContinuousLayout(true);
mSplitPane.setOneTouchExpandable(false);
setSplitPaneLayoutManager();
/*
* This means that the mind map area gets all the space that results
* from resizing the window.
*/
mSplitPane.setResizeWeight(1.0d);
if (splitPanePosition != -1 && lastSplitPanePosition != -1) {
mSplitPane.setDividerLocation(splitPanePosition);
mSplitPane.setLastDividerLocation(lastSplitPanePosition);
}
else {
mSplitPane.setDividerLocation(0.5);
}
}
@Override
public boolean isApplet() {
return false;
}
@Override
public void openDocument(final URI uri) throws IOException {
String uriString = ParseUtil.decode(uri.toString());
final String UNC_PREFIX = "file:////";
if (uriString.startsWith(UNC_PREFIX)) {
uriString = "file://" + uriString.substring(UNC_PREFIX.length());
}
final String osName = System.getProperty("os.name");
if (osName.substring(0, 3).equals("Win")) {
String propertyString = "default_browser_command_windows";
if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) {
propertyString += "_9x";
}
else {
propertyString += "_nt";
}
String[] command = null;
try {
final Object[] messageArguments = { uriString };
final MessageFormat formatter = new MessageFormat(ResourceController.getResourceController()
.getProperty(propertyString));
final String browserCommand = formatter.format(messageArguments);
final String scheme = uri.getScheme();
if (scheme.equals("file") || scheme.equals("smb")) {
if(scheme.equals("smb")){
uriString = Compat.smbUri2unc(uri);
}
if (System.getProperty("os.name").startsWith("Windows 2000"))
command = new String[] { "rundll32", "shell32.dll,ShellExec_RunDLL", uriString };
else
command = new String[] { "rundll32", "url.dll,FileProtocolHandler", uriString };
}
else if (uriString.startsWith("mailto:")) {
command = new String[] { "rundll32", "url.dll,FileProtocolHandler", uriString };
}
else {
Controller.exec(browserCommand);
return;
}
Controller.exec(command);
}
catch (final IOException x) {
UITools
.errorMessage("Could not invoke browser.\n\nFreeplane excecuted the following statement on a command line:\n\""
+ command
+ "\".\n\nYou may look at the user or default property called '"
+ propertyString
+ "'.");
System.err.println("Caught: " + x);
}
}
else if (osName.startsWith("Mac OS")) {
String browserCommand = null;
try {
final Object[] messageArguments = { uriString, uriString };
final MessageFormat formatter = new MessageFormat(ResourceController.getResourceController()
.getProperty("default_browser_command_mac"));
browserCommand = formatter.format(messageArguments);
Controller.exec(browserCommand);
}
catch (final IOException ex2) {
UITools
.errorMessage("Could not invoke browser.\n\nFreeplane excecuted the following statement on a command line:\n\""
+ browserCommand
+ "\".\n\nYou may look at the user or default property called 'default_browser_command_mac'.");
System.err.println("Caught: " + ex2);
}
}
else {
String browserCommand = null;
try {
final Object[] messageArguments = { uriString, uriString };
final MessageFormat formatter = new MessageFormat(ResourceController.getResourceController()
.getProperty("default_browser_command_other_os"));
browserCommand = formatter.format(messageArguments);
Controller.exec(browserCommand);
}
catch (final IOException ex2) {
UITools
.errorMessage("Could not invoke browser.\n\nFreeplane excecuted the following statement on a command line:\n\""
+ browserCommand
+ "\".\n\nYou may look at the user or default property called 'default_browser_command_other_os'.");
System.err.println("Caught: " + ex2);
}
}
}
/**
* Open url in WWW browser. This method hides some differences between
* operating systems.
*/
@Override
public void openDocument(final URL url) throws Exception {
URI uri = null;
try {
uri = url.toURI();
}
catch (URISyntaxException e) {
uri = new URI(url.getProtocol(), url.getHost(), url.getPath(), url.getQuery(), url.getRef());
}
openDocument(uri);
}
@Override
public boolean quit() {
if (!super.quit()) {
return false;
}
frame.dispose();
return true;
}
@Override
public void removeSplitPane() {
saveSplitPanePosition();
final JScrollPane scrollPane = getScrollPane();
mMindMapComponent = null;
mSplitPane.setLeftComponent(null);
mSplitPane.setRightComponent(null);
mSplitPane.setLeftComponent(scrollPane);
setSplitPaneLayoutManager();
final Controller controller = Controller.getCurrentModeController().getController();
final IMapSelection selection = controller.getSelection();
if(selection == null){
return;
}
final NodeModel node = selection.getSelected();
EventQueue.invokeLater(new Runnable() {
public void run() {
final Component component = controller.getViewController().getComponent(node);
if (component != null) {
component.requestFocus();
}
}
});
}
private void setSplitPaneLayoutManager() {
final LayoutManager layout = mSplitPane.getLayout();
if(layout instanceof SplitPaneLayoutManagerDecorator){
return;
}
mSplitPane.setLayout(new SplitPaneLayoutManagerDecorator(layout));
}
@Override
public void saveProperties() {
saveSplitPanePosition();
resourceController.setProperty("map_view_zoom", Float.toString(getZoom()));
if (!isFullScreenEnabled()) {
final int winState = frame.getExtendedState() & ~Frame.ICONIFIED;
if (JFrame.MAXIMIZED_BOTH != (winState & JFrame.MAXIMIZED_BOTH)) {
resourceController.setProperty("appwindow_x", String.valueOf(frame.getX()));
resourceController.setProperty("appwindow_y", String.valueOf(frame.getY()));
resourceController.setProperty("appwindow_width", String.valueOf(frame.getWidth()));
resourceController.setProperty("appwindow_height", String.valueOf(frame.getHeight()));
}
resourceController.setProperty("appwindow_state", String.valueOf(winState));
}
}
private void saveSplitPanePosition() {
if (mSplitPane == null) {
return;
}
if ("right".equals(mLocationPreferenceValue)) {
resourceController.setProperty(SPLIT_PANE_RIGHT_POSITION, "" + mSplitPane.getDividerLocation());
resourceController.setProperty(SPLIT_PANE_LAST_RIGHT_POSITION, "" + mSplitPane.getLastDividerLocation());
}
else if ("left".equals(mLocationPreferenceValue)) {
resourceController.setProperty(SPLIT_PANE_LEFT_POSITION, "" + mSplitPane.getDividerLocation());
resourceController.setProperty(SPLIT_PANE_LAST_LEFT_POSITION, "" + mSplitPane.getLastDividerLocation());
}
else if ("top".equals(mLocationPreferenceValue)) {
resourceController.setProperty(SPLIT_PANE_TOP_POSITION, "" + mSplitPane.getDividerLocation());
resourceController.setProperty(SPLIT_PANE_LAST_TOP_POSITION, "" + mSplitPane.getLastDividerLocation());
}
else { // "bottom".equals(mLocationPreferenceValue) also covered
resourceController.setProperty(SPLIT_PANE_POSITION, "" + mSplitPane.getDividerLocation());
resourceController.setProperty(SPLIT_PANE_LAST_POSITION, "" + mSplitPane.getLastDividerLocation());
}
}
@Override
protected void setFreeplaneMenuBar(final FreeplaneMenuBar menuBar) {
frame.setJMenuBar(menuBar);
}
/*
* (non-Javadoc)
* @see freeplane.main.FreeplaneMain#setTitle(java.lang.String)
*/
@Override
public void setTitle(final String title) {
frame.setTitle(title);
}
@Override
public void setWaitingCursor(final boolean waiting) {
if (waiting) {
frame.getRootPane().getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
frame.getRootPane().getGlassPane().setVisible(true);
}
else {
frame.getRootPane().getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
frame.getRootPane().getGlassPane().setVisible(false);
}
}
@Override
protected void viewNumberChanged(final int number) {
navigationPreviousMap.setEnabled(number > 1);
navigationNextMap.setEnabled(number > 1);
}
public void initFrame(final JFrame frame) {
// Preserve the existing icon image under Mac OS X
if (!Compat.isMacOsX()) {
final ImageIcon mWindowIcon;
if (Compat.isLowerJdk(Compat.VERSION_1_6_0)) {
if(ResourceController.getResourceController().getProperty("ApplicationName", "Freeplane").equals("Docear")) {
mWindowIcon = new ImageIcon(ResourceController.getResourceController().getResource(
"/images/docear16.png"));
}
else {
mWindowIcon = new ImageIcon(ResourceController.getResourceController().getResource(
"/images/Freeplane_frame_icon.png"));
}
}
else {
if(ResourceController.getResourceController().getProperty("ApplicationName", "Freeplane").equals("Docear")) {
mWindowIcon = new ImageIcon(ResourceController.getResourceController().getResource(
"/images/docear32.png"));
}
else {
mWindowIcon = new ImageIcon(ResourceController.getResourceController().getResource(
"/images/Freeplane_frame_icon_32x32.png"));
}
}
frame.setIconImage(mWindowIcon.getImage());
}
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(final WindowEvent e) {
Controller.getCurrentController().quit(new ActionEvent(this, 0, "quit"));
}
/*
* fc, 14.3.2008: Completely removed, as it damaged the focus if for
* example the note window was active.
*/
});
frame.setFocusTraversalKeysEnabled(false);
final int win_width = ResourceController.getResourceController().getIntProperty("appwindow_width", -1);
final int win_height = ResourceController.getResourceController().getIntProperty("appwindow_height", -1);
final int win_x = ResourceController.getResourceController().getIntProperty("appwindow_x", -1);
final int win_y = ResourceController.getResourceController().getIntProperty("appwindow_y", -1);
UITools.setBounds(frame, win_x, win_y, win_width, win_height);
setFrameSize(frame.getBounds());
int win_state = Integer
.parseInt(ResourceController.getResourceController().getProperty("appwindow_state", "0"));
win_state = ((win_state & Frame.ICONIFIED) != 0) ? Frame.NORMAL : win_state;
frame.setExtendedState(win_state);
}
}