/*FreeMind - A Program for creating and viewing Mindmaps *Copyright (C) 2000-2006 Joerg Mueller, Daniel Polansky, Christian Foltin and others. *See COPYING for Details * *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, write to the Free Software *Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*$Id: FreeMind.java,v 1.32.14.28.2.147 2011/01/09 21:03:13 christianfoltin Exp $*/ package freemind.main; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.net.Authenticator; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.Socket; import java.net.URL; import java.text.MessageFormat; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.ResourceBundle; import java.util.StringTokenizer; import java.util.Vector; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; import javax.swing.ImageIcon; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.UIManager; import com.inet.jortho.SpellChecker; import freemind.controller.Controller; import freemind.controller.LastStateStorageManagement; import freemind.controller.MenuBar; import freemind.controller.actions.generated.instance.MindmapLastStateStorage; import freemind.main.FreeMindStarter.ProxyAuthenticator; import freemind.modes.ModeController; import freemind.preferences.FreemindPropertyListener; import freemind.view.MapModule; import freemind.view.mindmapview.MapView; public class FreeMind extends JFrame implements FreeMindMain { // OSSXP.COM: set output character set to utf-8. public static final String DEFAULT_CHARSET = "UTF-8"; public static final String J_SPLIT_PANE_SPLIT_TYPE = "JSplitPane.SPLIT_TYPE"; public static final String VERTICAL_SPLIT_BELOW = "vertical_split_below"; public static final String HORIZONTAL_SPLIT_RIGHT = "horizontal_split_right"; public static final String LOG_FILE_NAME = "log"; private static final String PORT_FILE = "portFile"; private static final String FREE_MIND_PROGRESS_LOAD_MAPS = "FreeMind.progress.loadMaps"; private static final String FREE_MIND_PROGRESS_LOAD_MAPS_NAME = "FreeMind.progress.loadNamedMaps"; private static final String SPLIT_PANE_POSITION = "split_pane_position"; private static final String SPLIT_PANE_LAST_POSITION = "split_pane_last_position"; public static final String RESOURCE_LOOKANDFEEL = "lookandfeel"; public static final String RESOURCES_SELECTION_METHOD = "selection_method"; public static final String RESOURCES_NODE_STYLE = "standardnodestyle"; public static final String RESOURCES_ROOT_NODE_STYLE = "standardrootnodestyle"; public static final String RESOURCES_NODE_TEXT_COLOR = "standardnodetextcolor"; public static final String RESOURCES_SELECTED_NODE_COLOR = "standardselectednodecolor"; public static final String RESOURCES_SELECTED_NODE_RECTANGLE_COLOR = "standardselectednoderectanglecolor"; public static final String RESOURCE_DRAW_RECTANGLE_FOR_SELECTION = "standarddrawrectangleforselection"; public static final String RESOURCES_EDGE_COLOR = "standardedgecolor"; public static final String RESOURCES_EDGE_STYLE = "standardedgestyle"; public static final String RESOURCES_CLOUD_COLOR = "standardcloudcolor"; public static final String RESOURCES_LINK_COLOR = "standardlinkcolor"; public static final String RESOURCES_BACKGROUND_COLOR = "standardbackgroundcolor"; public static final String RESOURCE_PRINT_ON_WHITE_BACKGROUND = "printonwhitebackground"; public static final String RESOURCES_WHEEL_VELOCITY = "wheel_velocity"; public static final String RESOURCES_USE_TABBED_PANE = "use_tabbed_pane"; public static final String RESOURCES_USE_SPLIT_PANE = "use_split_pane"; public static final String RESOURCES_DELETE_NODES_WITHOUT_QUESTION = "delete_nodes_without_question"; public static final String RESOURCES_RELOAD_FILES_WITHOUT_QUESTION = "reload_files_without_question"; private Logger logger = null; protected static final VersionInformation VERSION = new VersionInformation("1.0.1"); // OSSXP.COM: show mmx hacked version public static final String HACKEDVERSION = "(MMX Hack v19, 2005-2009, by Jiang Xin)"; public static final String XML_VERSION = "1.0.1"; public static final String RESOURCES_REMIND_USE_RICH_TEXT_IN_NEW_LONG_NODES = "remind_use_rich_text_in_new_long_nodes"; public static final String RESOURCES_EXECUTE_SCRIPTS_WITHOUT_ASKING = "resources_execute_scripts_without_asking"; public static final String RESOURCES_EXECUTE_SCRIPTS_WITHOUT_FILE_RESTRICTION = "resources_execute_scripts_without_file_restriction"; public static final String RESOURCES_EXECUTE_SCRIPTS_WITHOUT_NETWORK_RESTRICTION = "resources_execute_scripts_without_network_restriction"; public static final String RESOURCES_EXECUTE_SCRIPTS_WITHOUT_EXEC_RESTRICTION = "resources_execute_scripts_without_exec_restriction"; public static final String RESOURCES_SCRIPT_USER_KEY_NAME_FOR_SIGNING = "resources_script_user_key_name_for_signing"; public static final String RESOURCES_CONVERT_TO_CURRENT_VERSION = "resources_convert_to_current_version"; public static final String RESOURCES_CUT_NODES_WITHOUT_QUESTION = "resources_cut_nodes_without_question"; public static final String RESOURCES_DON_T_SHOW_NOTE_ICONS = "resources_don_t_show_note_icons"; public static final String RESOURCES_REMOVE_NOTES_WITHOUT_QUESTION = "resources_remove_notes_without_question"; public static final String RESOURCES_SAVE_FOLDING_STATE = "resources_save_folding_state"; public static final String RESOURCES_SIGNED_SCRIPT_ARE_TRUSTED = "resources_signed_script_are_trusted"; public static final String RESOURCES_USE_DEFAULT_FONT_FOR_NOTES_TOO = "resources_use_default_font_for_notes_too"; public static final String RESOURCES_USE_MARGIN_TOP_ZERO_FOR_NOTES = "resources_use_margin_top_zero_for_notes"; public static final String RESOURCES_DON_T_SHOW_CLONE_ICONS = "resources_don_t_show_clone_icons"; public static final String RESOURCES_DON_T_OPEN_PORT = "resources_don_t_open_port"; public static final String KEYSTROKE_MOVE_MAP_LEFT = "keystroke_MoveMapLeft"; public static final String KEYSTROKE_MOVE_MAP_RIGHT = "keystroke_MoveMapRight"; public static final String KEYSTROKE_PREVIOUS_MAP = "keystroke_previousMap"; public static final String KEYSTROKE_NEXT_MAP = "keystroke_nextMap"; public static final String RESOURCES_SEARCH_IN_NOTES_TOO = "resources_search_in_notes_too"; public static final String RESOURCES_DON_T_SHOW_NOTE_TOOLTIPS = "resources_don_t_show_note_tooltips"; public static final String RESOURCES_SEARCH_FOR_NODE_TEXT_WITHOUT_QUESTION = "resources_search_for_node_text_without_question"; public static final String RESOURCES_COMPLETE_CLONING = "complete_cloning"; public static final String RESOURCES_CLONE_TYPE_COMPLETE_CLONE = "COMPLETE_CLONE"; public static final String TOOLTIP_DISPLAY_TIME = "tooltip_display_time"; public static final String PROXY_PORT = "proxy.port"; public static final String PROXY_HOST = "proxy.host"; public static final String PROXY_PASSWORD = "proxy.password"; public static final String PROXY_USER = "proxy.user"; public static final String PROXY_IS_AUTHENTICATED = "proxy.is_authenticated"; public static final String PROXY_USE_SETTINGS = "proxy.use_settings"; public static final String RESOURCES_DISPLAY_FOLDING_BUTTONS = "resources_display_folding_buttons"; // public static final String defaultPropsURL = "freemind.properties"; // public static Properties defaultProps; public static Properties props; private static Properties defProps; private MenuBar menuBar; private JLabel status; private Map filetypes; // Hopefully obsolete. Used to store applications // used to open different file types private File autoPropertiesFile; private File patternsFile; Controller controller;// the one and only controller private FreeMindCommon mFreeMindCommon; private static FileHandler mFileHandler; private static boolean mFileHandlerError = false; private JScrollPane mScrollPane = null; private JSplitPane mSplitPane; private JComponent mContentComponent = null; private JTabbedPane mTabbedPane = null; private ImageIcon mWindowIcon; private boolean mStartupDone = false; private List mStartupDoneListeners = new Vector(); private EditServer mEditServer = null; private Vector mLoggerList = new Vector(); private static LogFileLogHandler sLogFileHandler; public FreeMind(Properties pDefaultPreferences, Properties pUserPreferences, File pAutoPropertiesFile) { super("FreeMind"); // Focus searcher System.setSecurityManager(new FreeMindSecurityManager()); defProps = pDefaultPreferences; props = pUserPreferences; autoPropertiesFile = pAutoPropertiesFile; if (logger == null) { logger = getLogger(FreeMind.class.getName()); StringBuffer info = new StringBuffer(); info.append("freemind_version = "); info.append(VERSION); info.append("; freemind_xml_version = "); info.append(XML_VERSION); try { String propsLoc = "version.properties"; URL versionUrl = this.getClass().getClassLoader() .getResource(propsLoc); Properties buildNumberPros = new Properties(); InputStream stream = versionUrl.openStream(); buildNumberPros.load(stream); info.append("\nBuild: " + buildNumberPros.getProperty("build.number") + "\n"); stream.close(); } catch (Exception e) { info.append("Problems reading build number file: " + e); } info.append("\njava_version = "); info.append(System.getProperty("java.version")); info.append("; os_name = "); info.append(System.getProperty("os.name")); info.append("; os_version = "); info.append(System.getProperty("os.version")); logger.info(info.toString()); } mFreeMindCommon = new FreeMindCommon(this); Resources.createInstance(this); } void init(FeedBack feedback) { /* This is only for apple but does not harm for the others. */ System.setProperty("apple.laf.useScreenMenuBar", "true"); patternsFile = new File(getFreemindDirectory(), getDefaultProperty("patternsfile")); feedback.increase("FreeMind.progress.updateLookAndFeel", null); updateLookAndFeel(); feedback.increase("FreeMind.progress.createController", null); setIconImage(mWindowIcon.getImage()); // Layout everything getContentPane().setLayout(new BorderLayout()); controller = new Controller(this); controller.init(); feedback.increase("FreeMind.progress.settingPreferences", null); // add a listener for the controller, resource bundle: Controller.addPropertyChangeListener(new FreemindPropertyListener() { public void propertyChanged(String propertyName, String newValue, String oldValue) { if (propertyName.equals(FreeMindCommon.RESOURCE_LANGUAGE)) { // re-read resources: mFreeMindCommon.clearLanguageResources(); getResources(); } } }); // fc, disabled with purpose (see java look and feel styleguides). // http://java.sun.com/products/jlf/ed2/book/index.html // // add a listener for the controller, look and feel: // Controller.addPropertyChangeListener(new FreemindPropertyListener() { // // public void propertyChanged(String propertyName, String newValue, // String oldValue) { // if (propertyName.equals(RESOURCE_LOOKANDFEEL)) { // updateLookAndFeel(); // } // } // }); controller.optionAntialiasAction .changeAntialias(getProperty(FreeMindCommon.RESOURCE_ANTIALIAS)); setupSpellChecking(); setupProxy(); feedback.increase("FreeMind.progress.propageteLookAndFeel", null); SwingUtilities.updateComponentTreeUI(this); // Propagate LookAndFeel to feedback.increase("FreeMind.progress.buildScreen", null); setScreenBounds(); // JComponents feedback.increase("FreeMind.progress.createInitialMode", null); controller.createNewMode(getProperty("initial_mode")); // EventQueue eventQueue = Toolkit.getDefaultToolkit() // .getSystemEventQueue(); // eventQueue.push(new MyEventQueue()); }// Constructor /** * */ private void updateLookAndFeel() { // set Look&Feel try { String lookAndFeel = props.getProperty(RESOURCE_LOOKANDFEEL); if (lookAndFeel.equals("windows")) { UIManager .setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } else if (lookAndFeel.equals("motif")) { UIManager .setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); } else if (lookAndFeel.equals("mac")) { // Only available on macOS UIManager.setLookAndFeel("javax.swing.plaf.mac.MacLookAndFeel"); } else if (lookAndFeel.equals("metal")) { UIManager .setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); } else if (lookAndFeel.equals("gtk")) { UIManager .setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); } else if (lookAndFeel.equals("nothing")) { } else if (lookAndFeel.indexOf('.') != -1) { // string contains a // dot UIManager.setLookAndFeel(lookAndFeel); // we assume class name } else { // default. logger.info("Default (System) Look & Feel: " + UIManager.getSystemLookAndFeelClassName()); UIManager.setLookAndFeel(UIManager .getSystemLookAndFeelClassName()); } } catch (Exception ex) { System.err.println("Unable to set Look & Feel."); } mFreeMindCommon.loadUIProperties(defProps); } public boolean isApplet() { return false; } public File getPatternsFile() { return patternsFile; } public VersionInformation getFreemindVersion() { return VERSION; } // maintain this methods to keep the last state/size of the window (PN) public int getWinHeight() { return getHeight(); } public int getWinWidth() { return getWidth(); } public int getWinX() { return getX(); } public int getWinY() { return getY(); } public int getWinState() { return getExtendedState(); } public URL getResource(String name) { return this.getClass().getClassLoader().getResource(name); } public String getProperty(String key) { return props.getProperty(key); } public int getIntProperty(String key, int defaultValue) { try { return Integer.parseInt(getProperty(key)); } catch (NumberFormatException nfe) { return defaultValue; } } public Properties getProperties() { return props; } public void setProperty(String key, String value) { props.setProperty(key, value); } public String getDefaultProperty(String key) { return defProps.getProperty(key); } public void setDefaultProperty(String key, String value) { defProps.setProperty(key, value); } public String getFreemindDirectory() { return System.getProperty("user.home") + File.separator + getProperty("properties_folder"); } public void saveProperties(boolean pIsShutdown) { try { OutputStream out = new FileOutputStream(autoPropertiesFile); final OutputStreamWriter outputStreamWriter = new OutputStreamWriter( out, "8859_1"); outputStreamWriter.write("#FreeMind "); outputStreamWriter.write(VERSION.toString()); outputStreamWriter.write('\n'); outputStreamWriter.flush(); //to save as few props as possible. Properties toBeStored = Tools.copyChangedProperties(props, defProps); toBeStored.store(out, null); out.close(); } catch (Exception ex) { Resources.getInstance().logException(ex); } getController().getFilterController().saveConditions(); if (pIsShutdown && mEditServer != null) { mEditServer.stopServer(); } } public MapView getView() { return controller.getView(); } public Controller getController() { return controller; } public void setView(MapView view) { mScrollPane.setViewportView(view); } public MenuBar getFreeMindMenuBar() { return menuBar; } public void out(String msg) { // TODO: Automatically remove old messages after a certain time. if (status != null) { status.setText(msg); // logger.info(msg); } } public void err(String msg) { if (status != null) { status.setText(msg); } // logger.info(msg); } /** * Open url in WWW browser. This method hides some differences between * operating systems. */ public void openDocument(URL url) throws Exception { // build string for default browser: String correctedUrl = new String(url.toExternalForm()); if (url.getProtocol().equals("file")) { correctedUrl = correctedUrl.replace('\\', '/').replaceAll(" ", "%20"); // ^ This is more of a heuristic than a "logical" code // and due to a java bug: // http://forum.java.sun.com/thread.jsp?forum=31&thread=363990 } // Originally, this method determined external application, with which // the document // should be opened. Which application should open which document type // was // configured in FreeMind properties file. As a result, FreeMind tried // to solve the // problem (of determining application for a file type), which should // better be // solved somewhere else. Indeed, on Windows, this problem is perfectly // solved by // Explorer. On KDE, this problem is solved by Konqueror default // browser. In // general, most WWW browsers have to solve this problem. // As a result, the only thing we do here, is to open URL in WWW // browser. String osName = System.getProperty("os.name"); String urlString = url.toString(); if (osName.substring(0, 3).equals("Win")) { String propertyString = new String( "default_browser_command_windows"); if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) { propertyString += "_9x"; } else { propertyString += "_nt"; } String browser_command = new String(); String command = new String(); // Here we introduce " around the parameter of explorer // command. This is not because of possible spaces in this // parameter - it is because of "=" character, which causes // problems. My understanding of MSDOS is not so good, but at // least I can say, that "=" is used in general for the purpose // of variable assignment. // String[] call = { browser_command, "\""+url.toString()+"\"" }; try { // This is working fine on Windows 2000 and NT as well // Below is a piece of code showing how to run executables // directly // without asking. However, we don't want to do that. Explorer // will run // executable, but ask before it actually runs it. // // Imagine you download a package of maps containing also nasty // executable. Let's say there is a map "index.mm". This map // contains a // link to that nasty executable, but the name of the link // appearing to the // user does not indicate at all that clicking the link leads to // execution // of a programm. This executable is located on your local // computer, so // asking before executing remote executable does not solve the // problem. You click the link and there you are running evil // executable. // build string for default browser: // ask for property about browser: fc, 26.11.2003. Object[] messageArguments = { urlString }; MessageFormat formatter = new MessageFormat( getProperty(propertyString)); browser_command = formatter.format(messageArguments); if (url.getProtocol().equals("file")) { final File file = Tools.urlToFile(url); if (!Tools.isBelowJava6()) { Class desktopClass = Class.forName("java.awt.Desktop"); Method getDesktopMethod = desktopClass.getMethod( "getDesktop", new Class[] {}); Object desktopObject = getDesktopMethod.invoke(null, new Object[] {}); Method openMethod = desktopObject.getClass().getMethod( "open", new Class[] { File.class }); logger.info("Opening file " + file); openMethod.invoke(desktopObject, new Object[] { file }); return; } // command = "rundll32 url.dll,FileProtocolHandler "+ // Tools.urlGetFile(url); // bug fix by Dan: command = "cmd /C rundll32 url.dll,FileProtocolHandler " + urlString; // see // http://rsb.info.nih.gov/ij/developer/source/ij/plugin/BrowserLauncher.java.html if (System.getProperty("os.name") .startsWith("Windows 2000")) command = "cmd /C rundll32 shell32.dll,ShellExec_RunDLL " + urlString; } else if (urlString.startsWith("mailto:")) { command = "cmd /C rundll32 url.dll,FileProtocolHandler " + urlString; } else { command = browser_command; } logger.info("Starting browser with " + command); // Runtime.getRuntime().exec(command); execWindows(command); } catch (IOException x) { controller .errorMessage("Could not invoke browser.\n\nFreemind 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")) { // logger.info("Opening URL "+urlString); String browser_command = new String(); try { // ask for property about browser: fc, 26.11.2003. Object[] messageArguments = { correctedUrl, urlString }; if ("file".equals(url.getProtocol())) { // Bug in the apple's open function. For files, a pure // filename must be given. final File file = Tools.urlToFile(url); String[] command = { getProperty("default_browser_command_mac_open"), "file:" + file.getAbsolutePath() }; logger.info("Starting command: " + Arrays.deepToString(command)); Runtime.getRuntime().exec(command, null, null); } else { MessageFormat formatter = new MessageFormat( getProperty("default_browser_command_mac")); browser_command = formatter.format(messageArguments); logger.info("Starting command: " + browser_command); Runtime.getRuntime().exec(browser_command); } } catch (IOException ex2) { controller .errorMessage("Could not invoke browser.\n\nFreemind excecuted the following statement on a command line:\n\"" + browser_command + "\".\n\nYou may look at the user or default property called 'default_browser_command_mac'."); System.err.println("Caught: " + ex2); } } else { // There is no '"' character around url.toString (compare to Windows // code // above). Putting '"' around does not work on Linux - instead, the // '"' // becomes part of URL, which is malformed, as a result. String browser_command = new String(); try { // ask for property about browser: fc, 26.11.2003. Object[] messageArguments = { correctedUrl, urlString }; MessageFormat formatter = new MessageFormat( getProperty("default_browser_command_other_os")); browser_command = formatter.format(messageArguments); logger.info("Starting command: " + browser_command); Runtime.getRuntime().exec(browser_command); } catch (IOException ex2) { controller .errorMessage("Could not invoke browser.\n\nFreemind excecuted the following statement on a command line:\n\"" + browser_command + "\".\n\nYou may look at the user or default property called 'default_browser_command_other_os'."); System.err.println("Caught: " + ex2); } } } /** * @param cmd * precondition: the command can be split by spaces and the last * argument is the only one that contains unicode chars. * Moreover, we are under Windows. THIS METHOD DOESN'T SEEM TO * WORK for UNICODE ARGUMENTS. * @throws IOException */ private void execWindows(String pCommand) throws IOException { // taken and adapted from // http://stackoverflow.com/questions/1876507/java-runtime-exec-on-windows-fails-with-unicode-in-arguments StringTokenizer st = new StringTokenizer(pCommand, " "); String[] cmd = new String[st.countTokens()]; int i = 0; while (st.hasMoreTokens()) { cmd[i++] = st.nextToken(); } Map newEnv = new HashMap(); newEnv.putAll(System.getenv()); // exchange last argument by environment String envName = "JENV_1"; newEnv.put(envName, cmd[cmd.length - 1]); cmd[cmd.length - 1] = "%" + envName + "%"; logger.info("Starting command array " + Arrays.toString(cmd) + ", and env for " + envName + " = " + HtmlTools.unicodeToHTMLUnicodeEntity( (String) newEnv.get(envName), true)); ProcessBuilder pb = new ProcessBuilder(cmd); Map env = pb.environment(); env.putAll(newEnv); final Process p = pb.start(); } private String transpose(String input, char findChar, String replaceString) { String res = new String(); for (int i = 0; i < input.length(); ++i) { char d = input.charAt(i); if (d == findChar) res += replaceString; else res += d; } return res; } public void setWaitingCursor(boolean waiting) { if (waiting) { getRootPane().getGlassPane().setCursor( Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); getRootPane().getGlassPane().setVisible(true); } else { getRootPane().getGlassPane().setCursor( Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); getRootPane().getGlassPane().setVisible(false); } } private String getProgramForFile(String type) { if (filetypes == null) { filetypes = new HashMap(); String raw = getProperty("filetypes"); if (raw == null || raw.equals("")) { return ""; } StringTokenizer tokens = new StringTokenizer(raw, ","); while (tokens.hasMoreTokens()) { StringTokenizer pair = new StringTokenizer(tokens.nextToken(), ":"); String key = pair.nextToken().trim().toLowerCase(); String value = pair.nextToken().trim(); filetypes.put(key, value); } } return (String) filetypes.get(type.trim().toLowerCase()); } /** Returns the ResourceBundle with the current language */ public ResourceBundle getResources() { return mFreeMindCommon.getResources(); } public String getResourceString(String resource) { return mFreeMindCommon.getResourceString(resource); } public String getResourceString(String key, String pDefault) { return mFreeMindCommon.getResourceString(key, pDefault); } public Logger getLogger(String forClass) { Logger loggerForClass = java.util.logging.Logger.getLogger(forClass); mLoggerList.add(loggerForClass); if (mFileHandler == null && !mFileHandlerError) { // initialize handlers using an old System.err: final Logger parentLogger = loggerForClass.getParent(); final Handler[] handlers = parentLogger.getHandlers(); for (int i = 0; i < handlers.length; i++) { final Handler handler = handlers[i]; if (handler instanceof ConsoleHandler) { parentLogger.removeHandler(handler); } } try { mFileHandler = new FileHandler(getFreemindDirectory() + File.separator + LOG_FILE_NAME, 1400000, 5, false); mFileHandler.setFormatter(new StdFormatter()); mFileHandler.setLevel(Level.INFO); parentLogger.addHandler(mFileHandler); final ConsoleHandler stdConsoleHandler = new ConsoleHandler(); stdConsoleHandler.setFormatter(new StdFormatter()); stdConsoleHandler.setLevel(Level.WARNING); parentLogger.addHandler(stdConsoleHandler); sLogFileHandler = new LogFileLogHandler(); sLogFileHandler.setFormatter(new SimpleFormatter()); sLogFileHandler.setLevel(Level.INFO); LoggingOutputStream los; Logger logger = Logger.getLogger(StdFormatter.STDOUT.getName()); los = new LoggingOutputStream(logger, StdFormatter.STDOUT); System.setOut(new PrintStream(los, true)); logger = Logger.getLogger(StdFormatter.STDERR.getName()); los = new LoggingOutputStream(logger, StdFormatter.STDERR); System.setErr(new PrintStream(los, true)); } catch (Exception e) { System.err.println("Error creating logging File Handler"); e.printStackTrace(); mFileHandlerError = true; // to avoid infinite recursion. // freemind.main.Resources.getInstance().logExecption(e); } if (false) { // Obtain a reference to the logger Logger focusLog = Logger.getLogger("java.awt.focus.Component"); // The logger should log all messages focusLog.setLevel(Level.ALL); // Create a new handler ConsoleHandler handler = new ConsoleHandler(); // The handler must handle all messages handler.setLevel(Level.ALL); // Add the handler to the logger focusLog.addHandler(handler); } } if (sLogFileHandler != null) { loggerForClass.addHandler(sLogFileHandler); } return loggerForClass; } public static void main(final String[] args, Properties pDefaultPreferences, Properties pUserPreferences, File pAutoPropertiesFile) { final FreeMind frame = new FreeMind(pDefaultPreferences, pUserPreferences, pAutoPropertiesFile); IFreeMindSplash splash = null; frame.checkForAnotherInstance(args); frame.initServer(); final FeedBack feedBack; // change here, if you don't like the splash if (true) { splash = new FreeMindSplashModern(frame); splash.setVisible(true); feedBack = splash.getFeedBack(); frame.mWindowIcon = splash.getWindowIcon(); } else { feedBack = new FeedBack() { int value = 0; public int getActualValue() { return value; } public void increase(String messageId, Object[] pMessageParameters) { progress(getActualValue() + 1, messageId, pMessageParameters); } public void progress(int act, String messageId, Object[] pMessageParameters) { frame.logger.info("Beginnig task:" + messageId); } public void setMaximumValue(int max) { } }; frame.mWindowIcon = new ImageIcon( frame.getResource("images/FreeMindWindowIcon.png")); } feedBack.setMaximumValue(10 + frame.getMaximumNumberOfMapsToLoad(args)); frame.init(feedBack); feedBack.increase("FreeMind.progress.startCreateController", null); final ModeController ctrl = frame.createModeController(args); feedBack.increase(FREE_MIND_PROGRESS_LOAD_MAPS, null); frame.loadMaps(args, ctrl, feedBack); Tools.waitForEventQueue(); feedBack.increase("FreeMind.progress.endStartup", null); // focus fix after startup. frame.addWindowFocusListener(new WindowFocusListener() { public void windowLostFocus(WindowEvent e) { } public void windowGainedFocus(WindowEvent e) { frame.getController().obtainFocusForSelected(); frame.removeWindowFocusListener(this); } }); frame.setVisible(true); if (splash != null) { splash.setVisible(false); } frame.fireStartupDone(); } private void setupSpellChecking() { boolean checkSpelling = // Resources.getInstance().getBoolProperty(FreeMindCommon.CHECK_SPELLING); Tools.safeEquals("true", props.getProperty(FreeMindCommon.CHECK_SPELLING)); if (checkSpelling) { try { // TODO filter languages in dictionaries.properties like this: // String[] languages = "en,de,es,fr,it,nl,pl,ru,ar".split(","); // for (int i = 0; i < languages.length; i++) { // System.out.println(new File("dictionary_" + languages[i] + ".ortho").exists()); // } String decodedPath = Tools.getFreeMindBasePath(); URL url = null; if (new File (decodedPath).exists()) { url = new URL("file", null, decodedPath); } SpellChecker.registerDictionaries(url, Locale.getDefault().getLanguage()); } catch (MalformedURLException e) { freemind.main.Resources.getInstance().logException(e); } catch (UnsupportedEncodingException e) { freemind.main.Resources.getInstance().logException(e); } } } private void setupProxy() { // proxy settings if("true".equals(props.getProperty(PROXY_USE_SETTINGS))) { if ("true".equals(props.getProperty(PROXY_IS_AUTHENTICATED))) { Authenticator.setDefault(new ProxyAuthenticator(props .getProperty(PROXY_USER), Tools.decompress(props .getProperty(PROXY_PASSWORD)))); } System.setProperty("http.proxyHost", props.getProperty(PROXY_HOST)); System.setProperty("http.proxyPort", props.getProperty(PROXY_PORT)); } } private class MyEventQueue extends EventQueue { public void postEvent(AWTEvent theEvent) { logger.info("Event Posted: " + theEvent); super.postEvent(theEvent); } } private void initServer() { String portFile = getPortFile(); if (portFile == null) { return; } mEditServer = new EditServer(portFile, this); mEditServer.start(); } private void checkForAnotherInstance(String[] pArgs) { String portFile = getPortFile(); if (portFile == null) { return; } // {{{ Try connecting to another running FreeMind instance if (portFile != null && new File(portFile).exists()) { try { BufferedReader in = new BufferedReader(new FileReader(portFile)); String check = in.readLine(); if (!check.equals("b")) throw new Exception("Wrong port file format"); int port = Integer.parseInt(in.readLine()); int key = Integer.parseInt(in.readLine()); Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), port); DataOutputStream out = new DataOutputStream( socket.getOutputStream()); out.writeInt(key); String script; // Put url to open here script = Tools.arrayToUrls(pArgs); out.writeUTF(script); logger.info("Waiting for server"); // block until its closed try { socket.getInputStream().read(); } catch (Exception e) { } in.close(); out.close(); System.exit(0); } catch (Exception e) { // ok, this one seems to confuse newbies // endlessly, so log it as NOTICE, not // ERROR logger.info("An error occurred" + " while connecting to the FreeMind server instance." + " This probably means that" + " FreeMind crashed and/or exited abnormally" + " the last time it was run." + " If you don't" + " know what this means, don't worry. Exception: "+e ); } } } /** * @return null, if no port should be opened. */ private String getPortFile() { if (mEditServer == null && Resources.getInstance().getBoolProperty( RESOURCES_DON_T_OPEN_PORT)) { return null; } return getFreemindDirectory() + File.separator + getProperty(PORT_FILE); } private void fireStartupDone() { mStartupDone = true; for (Iterator it = mStartupDoneListeners.iterator(); it.hasNext();) { StartupDoneListener listener = (StartupDoneListener) it.next(); listener.startupDone(); } } private void setScreenBounds() { // Create the MenuBar menuBar = new MenuBar(controller); setJMenuBar(menuBar); // Create the scroll pane mScrollPane = new MapView.ScrollPane(); if (Resources.getInstance().getBoolProperty("no_scrollbar")) { mScrollPane .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); mScrollPane .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); } else { mScrollPane .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); mScrollPane .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); } status = new JLabel("!"); status.setPreferredSize(status.getPreferredSize()); status.setText(""); mContentComponent = mScrollPane; boolean shouldUseTabbedPane = Resources.getInstance().getBoolProperty( RESOURCES_USE_TABBED_PANE); if (shouldUseTabbedPane) { // tabbed panes eat control up. This is corrected here. InputMap map; map = (InputMap) UIManager.get("TabbedPane.ancestorInputMap"); KeyStroke keyStrokeCtrlUp = KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.CTRL_DOWN_MASK); map.remove(keyStrokeCtrlUp); mTabbedPane = new JTabbedPane(); mTabbedPane.setFocusable(false); controller.addTabbedPane(mTabbedPane); getContentPane().add(mTabbedPane, BorderLayout.CENTER); } else { // don't use tabbed panes. getContentPane().add(mContentComponent, BorderLayout.CENTER); } getContentPane().add(status, BorderLayout.SOUTH); // Disable the default close button, instead use windowListener setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { controller.quit .actionPerformed(new ActionEvent(this, 0, "quit")); } /* * fc, 14.3.2008: Completely removed, as it damaged the focus if for * example the note window was active. */ // public void windowActivated(WindowEvent e) { // // This doesn't work the first time, it's called too early to // // get Focus // logger.info("windowActivated"); // if ((getView() != null) && (getView().getSelected() != null)) { // getView().getSelected().requestFocus(); // } // } }); if (Tools.safeEquals(getProperty("toolbarVisible"), "false")) { controller.setToolbarVisible(false); } if (Tools.safeEquals(getProperty("leftToolbarVisible"), "false")) { controller.setLeftToolbarVisible(false); } // first define the final layout of the screen: setFocusTraversalKeysEnabled(false); pack(); // and now, determine size, position and state. // set the default size (PN) int win_width = getIntProperty("appwindow_width", 0); int win_height = getIntProperty("appwindow_height", 0); int win_x = getIntProperty("appwindow_x", 0); int win_y = getIntProperty("appwindow_y", 0); win_width = (win_width > 0) ? win_width : 640; win_height = (win_height > 0) ? win_height : 440; final Toolkit defaultToolkit = Toolkit.getDefaultToolkit(); final Insets screenInsets = defaultToolkit .getScreenInsets(getGraphicsConfiguration()); Dimension screenSize = defaultToolkit.getScreenSize(); final int screenWidth = screenSize.width - screenInsets.left - screenInsets.right; win_width = Math.min(win_width, screenWidth); final int screenHeight = screenSize.height - screenInsets.top - screenInsets.bottom; win_height = Math.min(win_height, screenHeight); win_x = Math.max(screenInsets.left, win_x); win_x = Math.min(screenWidth + screenInsets.left - win_width, win_x); win_y = Math.max(screenInsets.top, win_y); win_y = Math.min(screenWidth + screenInsets.top - win_height, win_y); setBounds(win_x, win_y, win_width, win_height); // set the default state (normal/maximized) (PN) // (note: this must be done later when partucular // initalizations of the windows are ready, // perhaps after setVisible is it enough... :-? int win_state = Integer.parseInt(FreeMind.props.getProperty( "appwindow_state", "0")); win_state = ((win_state & ICONIFIED) != 0) ? NORMAL : win_state; setExtendedState(win_state); } private ModeController createModeController(final String[] args) { ModeController ctrl = controller.getModeController(); // try to load mac module: try { Class macClass = Class.forName("accessories.plugins.MacChanges"); // lazy programming. the mac class has exactly one // constructor // with a modeController. macClass.getConstructors()[0].newInstance(new Object[] { this }); } catch (Exception e1) { // freemind.main.Resources.getInstance().logExecption(e1); } return ctrl; } private int getMaximumNumberOfMapsToLoad(String[] args) { LastStateStorageManagement management = getLastStateStorageManagement(); return Math.max( args.length + management.getLastOpenList().size(), 1 ); } private void loadMaps(final String[] args, ModeController pModeController, FeedBack pFeedBack) { boolean fileLoaded = false; if (Tools.isPreferenceTrue(getProperty(FreeMindCommon.LOAD_LAST_MAPS_AND_LAYOUT))) { int index = 0; MapModule mapToFocus = null; LastStateStorageManagement management = getLastStateStorageManagement(); for (Iterator it = management.getLastOpenList().iterator(); it .hasNext();) { MindmapLastStateStorage store = (MindmapLastStateStorage) it .next(); String restorable = store.getRestorableName(); pFeedBack.increase(FREE_MIND_PROGRESS_LOAD_MAPS_NAME, new Object[] { restorable.replaceAll(".*/", "") }); try { if (controller.getLastOpenedList().open(restorable)) { if (index == management.getLastFocussedTab()) { mapToFocus = controller.getMapModule(); } } fileLoaded = true; } catch (Exception e) { freemind.main.Resources.getInstance().logException(e); } index++; } if (mapToFocus != null) { controller.getMapModuleManager().changeToMapModule( mapToFocus.getDisplayName()); } } for (int i = 0; i < args.length; i++) { String fileArgument = args[i]; pFeedBack.increase(FREE_MIND_PROGRESS_LOAD_MAPS_NAME, new Object[] { fileArgument.replaceAll(".*/", "") }); if (fileArgument.toLowerCase().endsWith( freemind.main.FreeMindCommon.FREEMIND_FILE_EXTENSION)) { if (!Tools.isAbsolutePath(fileArgument)) { fileArgument = System.getProperty("user.dir") + System.getProperty("file.separator") + fileArgument; } try { pModeController.load(new File(fileArgument)); fileLoaded = true; // logger.info("Attempting to load: " + // args[i]); } catch (Exception ex) { System.err.println("File " + fileArgument + " not found error"); } } } if (!fileLoaded) { fileLoaded = processLoadEventFromStartupPhase(); } if (!fileLoaded) { String restoreable = getProperty(FreeMindCommon.ON_START_IF_NOT_SPECIFIED); if (Tools .isPreferenceTrue(getProperty(FreeMindCommon.LOAD_LAST_MAP)) && restoreable != null && restoreable.length() > 0) { pFeedBack.increase(FREE_MIND_PROGRESS_LOAD_MAPS_NAME, new Object[] { restoreable.replaceAll(".*/", "") }); try { controller.getLastOpenedList().open(restoreable); controller.getModeController().getView().moveToRoot(); fileLoaded = true; } catch (Exception e) { freemind.main.Resources.getInstance().logException(e); out("An error occured on opening the file: " + restoreable + "."); } } } if (!fileLoaded && Tools.isPreferenceTrue(getProperty(FreeMindCommon.LOAD_NEW_MAP))) { /* * nothing loaded so far. Perhaps, we should display a new map... * According to Summary: On first start FreeMind should show new map * to newbies * https://sourceforge.net/tracker/?func=detail&atid=107118 * &aid=1752516&group_id=7118 */ pModeController.newMap(); pFeedBack.increase(FREE_MIND_PROGRESS_LOAD_MAPS, null); } } private LastStateStorageManagement getLastStateStorageManagement() { String lastStateMapXml = getProperty(FreeMindCommon.MINDMAP_LAST_STATE_MAP_STORAGE); LastStateStorageManagement management = new LastStateStorageManagement( lastStateMapXml); return management; } /** * Iterates over the load events from the startup phase * <p> * More than one file can be opened during startup. The filenames are stored * in numbered properties, i.e. * <ul> * loadEventDuringStartup0=/Users/alex/Desktop/test1.mm * loadEventDuringStartup1=/Users/alex/Desktop/test2.mm * </ul> * * @return true if at least one file has been loaded */ private boolean processLoadEventFromStartupPhase() { boolean atLeastOneFileHasBeenLoaded = false; int count = 0; while (true) { String propertyKey = FreeMindCommon.LOAD_EVENT_DURING_STARTUP + count; if (getProperty(propertyKey) == null) { break; } else { if (processLoadEventFromStartupPhase(propertyKey)) atLeastOneFileHasBeenLoaded = true; ++count; } } return atLeastOneFileHasBeenLoaded; } private boolean processLoadEventFromStartupPhase(String propertyKey) { String filename = getProperty(propertyKey); try { if (logger.isLoggable(Level.INFO)) { logger.info("Loading " + filename); } controller.getModeController().load( Tools.fileToUrl(new File(filename))); // remove temporary property because we do not want to store in a // file and survive restart getProperties().remove(propertyKey); return true; } catch (Exception e) { freemind.main.Resources.getInstance().logException(e); out("An error occured on opening the file: " + filename + "."); return false; } } /* * (non-Javadoc) * * @see freemind.main.FreeMindMain#getJFrame() */ public JFrame getJFrame() { return this; } public ClassLoader getFreeMindClassLoader() { return mFreeMindCommon.getFreeMindClassLoader(); } public String getFreemindBaseDir() { return mFreeMindCommon.getFreemindBaseDir(); } public String getAdjustableProperty(String label) { return mFreeMindCommon.getAdjustableProperty(label); } public JSplitPane insertComponentIntoSplitPane(JComponent pMindMapComponent) { if (mSplitPane != null) { // already present: return mSplitPane; } removeContentComponent(); int splitType = JSplitPane.VERTICAL_SPLIT; String splitProperty = getProperty(J_SPLIT_PANE_SPLIT_TYPE); if(Tools.safeEquals(splitProperty, HORIZONTAL_SPLIT_RIGHT)) { splitType = JSplitPane.HORIZONTAL_SPLIT; } else if(Tools.safeEquals(splitProperty, VERTICAL_SPLIT_BELOW)) { // default } else { logger.warning("Split type not known: " + splitProperty); } mSplitPane = new JSplitPane(splitType, mScrollPane, pMindMapComponent); mSplitPane.setContinuousLayout(true); mSplitPane.setOneTouchExpandable(false); /* * This means that the mind map area gets all the space that results * from resizing the window. */ mSplitPane.setResizeWeight(1.0d); // split panes eat F8 and F6. This is corrected here. Tools.correctJSplitPaneKeyMap(); mContentComponent = mSplitPane; setContentComponent(); // set divider position: int splitPanePosition = getIntProperty(SPLIT_PANE_POSITION, -1); int lastSplitPanePosition = getIntProperty(SPLIT_PANE_LAST_POSITION, -1); if (splitPanePosition != -1 && lastSplitPanePosition != -1) { mSplitPane.setDividerLocation(splitPanePosition); mSplitPane.setLastDividerLocation(lastSplitPanePosition); } return mSplitPane; } public void removeSplitPane() { if (mSplitPane != null) { setProperty(SPLIT_PANE_POSITION, "" + mSplitPane.getDividerLocation()); setProperty(SPLIT_PANE_LAST_POSITION, "" + mSplitPane.getLastDividerLocation()); removeContentComponent(); mContentComponent = mScrollPane; setContentComponent(); mSplitPane = null; } } private void removeContentComponent() { if (mTabbedPane != null) { if (mTabbedPane.getSelectedIndex() >= 0) { mTabbedPane.setComponentAt(mTabbedPane.getSelectedIndex(), new JPanel()); } } else { getContentPane().remove(mContentComponent); getRootPane().revalidate(); } } private void setContentComponent() { if (mTabbedPane != null) { if (mTabbedPane.getSelectedIndex() >= 0) { mTabbedPane.setComponentAt(mTabbedPane.getSelectedIndex(), mContentComponent); } } else { getContentPane().add(mContentComponent, BorderLayout.CENTER); getRootPane().revalidate(); } } public JScrollPane getScrollPane() { return mScrollPane; } public JComponent getContentComponent() { return mContentComponent; } public void registerStartupDoneListener( StartupDoneListener pStartupDoneListener) { if (!mStartupDone) mStartupDoneListeners.add(pStartupDoneListener); } public List getLoggerList() { return Collections.unmodifiableList(mLoggerList); } }