/*FreeMind - A Program for creating and viewing Mindmaps *Copyright (C) 2000-2004 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. * * Created on 08.08.2004 */ /*$Id: MapModuleManager.java,v 1.1.4.4.2.14 2008/05/31 10:55:04 dpolivaev Exp $*/ package freemind.controller; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Vector; import freemind.main.Tools; import freemind.modes.MindMap; import freemind.modes.Mode; import freemind.modes.ModeController; import freemind.view.MapModule; import freemind.view.mindmapview.MapView; /** * Manages the list of MapModules. As this task is very complex, I exported it * from Controller to this class to keep Controller simple. * * The information exchange between controller and this class is managed by * oberser pattern (the controller observes changes to the map modules here). * * TODO: Use an vector with the map modules ordered by the screen order. */ public class MapModuleManager { public static interface MapModuleChangeObserver { /** * The params may be null to indicate the there was no previous map, or * that the last map is closed now. */ boolean isMapModuleChangeAllowed(MapModule oldMapModule, Mode oldMode, MapModule newMapModule, Mode newMode); void beforeMapModuleChange(MapModule oldMapModule, Mode oldMode, MapModule newMapModule, Mode newMode); void afterMapClose(MapModule oldMapModule, Mode oldMode); void afterMapModuleChange(MapModule oldMapModule, Mode oldMode, MapModule newMapModule, Mode newMode); /** * To enable/disable the previous/next map actions. * * @param pIndex * TODO */ void numberOfOpenMapInformation(int number, int pIndex); } public static class MapModuleChangeObserverCompound implements MapModuleChangeObserver { private HashSet listeners = new HashSet(); public void addListener(MapModuleChangeObserver listener) { listeners.add(listener); } public void removeListener(MapModuleChangeObserver listener) { listeners.remove(listener); } public boolean isMapModuleChangeAllowed(MapModule oldMapModule, Mode oldMode, MapModule newMapModule, Mode newMode) { boolean returnValue = true; for (Iterator iter = new Vector(listeners).iterator(); iter .hasNext();) { MapModuleChangeObserver observer = (MapModuleChangeObserver) iter .next(); returnValue = observer.isMapModuleChangeAllowed(oldMapModule, oldMode, newMapModule, newMode); if (!returnValue) { break; } } return returnValue; } public void beforeMapModuleChange(MapModule oldMapModule, Mode oldMode, MapModule newMapModule, Mode newMode) { for (Iterator iter = new Vector(listeners).iterator(); iter .hasNext();) { MapModuleChangeObserver observer = (MapModuleChangeObserver) iter .next(); observer.beforeMapModuleChange(oldMapModule, oldMode, newMapModule, newMode); } } public void afterMapModuleChange(MapModule oldMapModule, Mode oldMode, MapModule newMapModule, Mode newMode) { for (Iterator iter = new Vector(listeners).iterator(); iter .hasNext();) { MapModuleChangeObserver observer = (MapModuleChangeObserver) iter .next(); observer.afterMapModuleChange(oldMapModule, oldMode, newMapModule, newMode); } } public void numberOfOpenMapInformation(int number, int pIndex) { for (Iterator iter = new Vector(listeners).iterator(); iter .hasNext();) { MapModuleChangeObserver observer = (MapModuleChangeObserver) iter .next(); observer.numberOfOpenMapInformation(number, pIndex); } } public void afterMapClose(MapModule pOldMapModule, Mode pOldMode) { for (Iterator iter = new Vector(listeners).iterator(); iter .hasNext();) { MapModuleChangeObserver observer = (MapModuleChangeObserver) iter .next(); observer.afterMapClose(pOldMapModule, pOldMode); } } } /** * You can register yourself to this listener at the main controller. */ public static interface MapTitleChangeListener { void setMapTitle(String pNewMapTitle, MapModule pMapModule, MindMap pModel); } MapModuleChangeObserverCompound listener = new MapModuleChangeObserverCompound(); public void addListener(MapModuleChangeObserver pListener) { listener.addListener(pListener); } public void removeListener(MapModuleChangeObserver pListener) { listener.removeListener(pListener); } /** * You can register yourself as a contributor to the title at the main * controller. */ public static interface MapTitleContributor { /** * @param pOldTitle * The current title * @param pMapModule * @param pModel * @return The current title can be changed or something can be added, * but it must be returned as a whole. */ String getMapTitle(String pOldTitle, MapModule pMapModule, MindMap pModel); } // /** Contains pairs String (key+extension) => MapModule instances. // * The instances of mode, ie. the Model/View pairs. Normally, the // * order should be the order of insertion, but such a Map is not // * available. */ // private Map mapModules = new HashMap(); /** * A vector of MapModule instances. They are ordered according to their * screen order. */ private Vector mapModuleVector = new Vector(); /** reference to the current mapmodule; null is allowed, too. */ private MapModule mapModule; /** * Reference to the current mode as the mapModule may be null. */ private Mode mCurrentMode = null; private Controller mController; MapModuleManager(Controller c) { this.mController = c; } /** * @return a map of String to MapModule elements. * @deprecated use getMapModuleVector instead (and get the displayname as * MapModule.getDisplayName(). */ public Map getMapModules() { HashMap returnValue = new HashMap(); for (Iterator iterator = mapModuleVector.iterator(); iterator.hasNext();) { MapModule module = (MapModule) iterator.next(); returnValue.put(module.getDisplayName(), module); } return Collections.unmodifiableMap(returnValue); } public List getMapModuleVector() { return Collections.unmodifiableList(mapModuleVector); } /** @return an unmodifiable set of all display names of current opened maps. */ public List getMapKeys() { LinkedList returnValue = new LinkedList(); for (Iterator iterator = mapModuleVector.iterator(); iterator.hasNext();) { MapModule module = (MapModule) iterator.next(); returnValue.add(module.getDisplayName()); } return Collections.unmodifiableList(returnValue); } public MapModule getMapModule() { return mapModule; } public void newMapModule(MindMap map, ModeController modeController) { MapModule mapModule = new MapModule(map, new MapView(map, mController), modeController.getMode(), modeController); addToOrChangeInMapModules(mapModule.toString(), mapModule); setMapModule(mapModule, modeController.getMode()); } public MapModule getModuleGivenModeController(ModeController pModeController) { MapModule mapModule = null; for (Iterator iter = getMapModules().entrySet().iterator(); iter .hasNext();) { Map.Entry mapEntry = (Map.Entry) iter.next(); mapModule = (MapModule) mapEntry.getValue(); if (pModeController.equals(mapModule.getModeController())) { break; } mapModule = null; } return mapModule; } public void updateMapModuleName() { // removeFromViews() doesn't work because MapModuleChanged() // must not be called at this state getMapModule().rename(); addToOrChangeInMapModules(getMapModule().toString(), getMapModule()); setMapModule(getMapModule(), getMapModule().getMode()); } void nextMapModule() { int index; int size = mapModuleVector.size(); if (getMapModule() != null) index = mapModuleVector.indexOf(getMapModule()); else index = size - 1; if (index + 1 < size && index >= 0) { changeToMapModule((MapModule) mapModuleVector.get(index + 1)); } else if (size > 0) { // Change to the first in the list changeToMapModule((MapModule) mapModuleVector.get(0)); } } void previousMapModule() { int index; int size = mapModuleVector.size(); if (getMapModule() != null) index = mapModuleVector.indexOf(getMapModule()); else index = 0; if (index > 0) { changeToMapModule((MapModule) mapModuleVector.get(index - 1)); } else { if (size > 0) { changeToMapModule((MapModule) mapModuleVector.get(size - 1)); } } } // Change MapModules /** * This is the question whether the map is already opened. If this is the * case, the map is automatically opened + returns true. Otherwise does * nothing + returns false. */ public boolean tryToChangeToMapModule(String mapModule) { if (mapModule != null && getMapKeys().contains(mapModule)) { changeToMapModule(mapModule); return true; } else { return false; } } /** * * Checks, whether or not a given url is already opened. Unlike * tryToChangeToMapModule, it does not consider the map+extension * identifiers nor switches to the module. * * @return null, if not found, the map+extension identifier otherwise. */ public String checkIfFileIsAlreadyOpened(URL urlToCheck) throws MalformedURLException { for (Iterator iter = mapModuleVector.iterator(); iter.hasNext();) { MapModule module = (MapModule) iter.next(); if (module.getModel() != null) { final URL moduleUrl = module.getModel().getURL(); if (sameFile(urlToCheck, moduleUrl)) return module.getDisplayName(); } } return null; } private boolean sameFile(URL urlToCheck, final URL moduleUrl) { if (moduleUrl == null) { return false; } if (urlToCheck.getProtocol().equals("file") && moduleUrl.getProtocol().equals("file")) { return (new File(urlToCheck.getFile())).equals(new File(moduleUrl .getFile())); } return urlToCheck.sameFile(moduleUrl); } public boolean changeToMapModule(String mapModuleDisplayName) { MapModule mapModuleCandidate = null; for (Iterator iterator = mapModuleVector.iterator(); iterator.hasNext();) { MapModule mapMod = (MapModule) iterator.next(); if (Tools.safeEquals(mapModuleDisplayName, mapMod.getDisplayName())) { mapModuleCandidate = mapMod; break; } } if (mapModuleCandidate == null) { throw new IllegalArgumentException("Map module " + mapModuleDisplayName + " not found."); } return changeToMapModule(mapModuleCandidate); } public boolean changeToMapModule(MapModule mapModuleCandidate) { return setMapModule(mapModuleCandidate, mapModuleCandidate.getMode()); } public void changeToMapOfMode(Mode mode) { for (Iterator iterator = mapModuleVector.iterator(); iterator.hasNext();) { MapModule mapMod = (MapModule) iterator.next(); if (mapMod.getMode() == mode) { changeToMapModule(mapMod); return; } } // there is no map with the given mode open. We have to create an empty // one? setMapModule(null, mode); // FIXME: Is getting here an error? fc, 25.11.2005. } /** * is null if the old mode should be closed. * * @return true if the set command was successful. */ boolean setMapModule(MapModule newMapModule, Mode newMode) { // allowed? MapModule oldMapModule = this.mapModule; Mode oldMode = mCurrentMode; if (!listener.isMapModuleChangeAllowed(oldMapModule, oldMode, newMapModule, newMode)) { return false; } listener.beforeMapModuleChange(oldMapModule, oldMode, newMapModule, newMode); this.mapModule = newMapModule; this.mCurrentMode = newMode; listener.afterMapModuleChange(oldMapModule, oldMode, newMapModule, newMode); fireNumberOfOpenMapInformation(); return true; } private void fireNumberOfOpenMapInformation() { listener.numberOfOpenMapInformation(mapModuleVector.size(), mapModuleVector.indexOf(getMapModule())); } // private private void addToOrChangeInMapModules(String key, MapModule newOrChangedMapModule) { // begin bug fix, 20.12.2003, fc. // check, if already present: String extension = ""; int count = 1; List mapKeys = getMapKeys(); while (mapKeys.contains(key + extension)) { extension = "<" + (++count) + ">"; } // rename map: newOrChangedMapModule.setName(key + extension); newOrChangedMapModule.setDisplayName(key + extension); if (!mapModuleVector.contains(newOrChangedMapModule)) { mapModuleVector.add(newOrChangedMapModule); } // end bug fix, 20.12.2003, fc. } /** * Close the currently active map, return false if closing canceled. * * @param force * forces the closing without any save actions. * @param pRestorable * is a buffer, if the name of the restorable is needed after * saving. * @return false if saving was canceled. */ public boolean close(boolean force, StringBuffer pRestorable) { // (DP) The mode controller does not close the map MapModule module = getMapModule(); // FIXME: This is not correct, as this class should not ask somebody. // This class is only a list! boolean closingNotCancelled = module.getModeController().close(force, this); if (!closingNotCancelled) { return false; } if (pRestorable != null) { pRestorable.append(module.getModel().getRestorable()); } int index = mapModuleVector.indexOf(module); mapModuleVector.remove(module); if (mapModuleVector.isEmpty()) { /* Keep the current running mode */ setMapModule(null, module.getMode()); } else { if (index >= mapModuleVector.size() || index < 0) { index = mapModuleVector.size() - 1; } changeToMapModule((MapModule) mapModuleVector.get(index)); } listener.afterMapClose(module, module.getMode()); return true; } // }} public void swapModules(int src, int dst) { Tools.swapVectorPositions(mapModuleVector, src, dst); fireNumberOfOpenMapInformation(); } }