Code Search for Developers
 
 
  

Client.java from Magellan-Client at Krugle


Show Client.java syntax highlighted

/*
 *  Copyright (C) 2000-2004 Roger Butenuth, Andreas Gampe,
 *                          Stefan Goetz, Sebastian Pappert,
 *                          Klaas Prause, Enno Rehling,
 *                          Sebastian Tusk, Ulrich Kuester,
 *                          Ilja Pavkovic
 *
 * This file is part of the Eressea Java Code Base, see the
 * file LICENSING for the licensing information applying to
 * this file.
 *
 */

package com.eressea.demo;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.zip.ZipEntry;

import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.plaf.FontUIResource;

import com.eressea.CoordinateID;
import com.eressea.EntityID;
import com.eressea.Faction;
import com.eressea.GameData;
import com.eressea.IntegerID;
import com.eressea.Message;
import com.eressea.MissingData;
import com.eressea.Unit;
import com.eressea.demo.actions.AbortAction;
import com.eressea.demo.actions.AddCRAction;
import com.eressea.demo.actions.AddSelectionAction;
import com.eressea.demo.actions.ArmyStatsAction;
import com.eressea.demo.actions.ChangeFactionConfirmationAction;
import com.eressea.demo.actions.ConfirmAction;
import com.eressea.demo.actions.ECheckAction;
import com.eressea.demo.actions.ExpandSelectionAction;
import com.eressea.demo.actions.ExportCRAction;
import com.eressea.demo.actions.ExternalModuleAction;
import com.eressea.demo.actions.FactionStatsAction;
import com.eressea.demo.actions.FileSaveAction;
import com.eressea.demo.actions.FileSaveAsAction;
import com.eressea.demo.actions.FillSelectionAction;
import com.eressea.demo.actions.FindAction;
import com.eressea.demo.actions.FindPreviousUnconfirmedAction;
import com.eressea.demo.actions.HelpAction;
import com.eressea.demo.actions.InfoAction;
import com.eressea.demo.actions.InvertSelectionAction;
import com.eressea.demo.actions.IslandAction;
import com.eressea.demo.actions.MapSaveAction;
import com.eressea.demo.actions.MenuAction;
import com.eressea.demo.actions.OpenCRAction;
import com.eressea.demo.actions.OpenOrdersAction;
import com.eressea.demo.actions.OpenSelectionAction;
import com.eressea.demo.actions.OptionAction;
import com.eressea.demo.actions.QuitAction;
import com.eressea.demo.actions.RedoAction;
import com.eressea.demo.actions.RepaintAction;
import com.eressea.demo.actions.SaveOrdersAction;
import com.eressea.demo.actions.SaveSelectionAction;
import com.eressea.demo.actions.SelectAllAction;
import com.eressea.demo.actions.SelectIslandsAction;
import com.eressea.demo.actions.SelectNothingAction;
import com.eressea.demo.actions.SetOriginAction;
import com.eressea.demo.actions.TaskTableAction;
import com.eressea.demo.actions.TileSetAction;
import com.eressea.demo.actions.TipOfTheDayAction;
import com.eressea.demo.actions.TradeOrganizerAction;
import com.eressea.demo.actions.UnconfirmAction;
import com.eressea.demo.actions.UndoAction;
import com.eressea.demo.actions.VorlageAction;
import com.eressea.demo.desktop.DesktopEnvironment;
import com.eressea.demo.desktop.MagellanDesktop;
import com.eressea.demo.desktop.ShortcutListener;
import com.eressea.event.EventDispatcher;
import com.eressea.event.GameDataEvent;
import com.eressea.event.GameDataListener;
import com.eressea.event.OrderConfirmEvent;
import com.eressea.event.OrderConfirmListener;
import com.eressea.event.SelectionEvent;
import com.eressea.event.SelectionListener;
import com.eressea.event.TempUnitEvent;
import com.eressea.event.TempUnitListener;
import com.eressea.event.UnitOrdersEvent;
import com.eressea.event.UnitOrdersListener;
import com.eressea.extern.ExternalModule;
import com.eressea.extern.ExternalModule2;
import com.eressea.extern.ExternalModule3;
import com.eressea.extern.ExternalModuleLoader;
import com.eressea.extern.ExternalModule3Loader;
import com.eressea.io.GameDataReader;
import com.eressea.io.file.FileBackup;
import com.eressea.io.file.FileTypeFactory;
import com.eressea.main.MagellanContext;
import com.eressea.resource.ResourcePathClassLoader;
import com.eressea.resource.ResourceSettingsFactory;
import com.eressea.rules.EresseaDate;
import com.eressea.swing.InternationalizedDataPanel;
import com.eressea.swing.MagellanLookAndFeel;
import com.eressea.swing.MapperPanel;
import com.eressea.swing.MenuProvider;
import com.eressea.swing.MessagePanel;
import com.eressea.swing.StartWindow;
import com.eressea.swing.TipOfTheDay;
import com.eressea.swing.map.CellGeometry;
import com.eressea.swing.preferences.PreferencesAdapter;
import com.eressea.swing.preferences.PreferencesFactory;
import com.eressea.swing.tree.IconAdapterFactory;
import com.eressea.util.BookmarkManager;
import com.eressea.util.CollectionFactory;
import com.eressea.util.FileHistory;
import com.eressea.util.JVMUtilities;
import com.eressea.util.LanguageDialog;
import com.eressea.util.Locales;
import com.eressea.util.Log;
import com.eressea.util.MagellanFinder;
import com.eressea.util.NameGenerator;
import com.eressea.util.PropertiesHelper;
import com.eressea.util.RendererLoader;
import com.eressea.util.SelectionHistory;
import com.eressea.util.SelfCleaningProperties;
import com.eressea.util.Translations;
import com.eressea.util.TrustLevels;
import com.eressea.util.VersionInfo;
import com.eressea.util.logging.Logger;

/**
 * This class is the root of all evil. It represents also the main entry point
 * into the application and also the basic frame the application creates.
 * 
 * @author $author$
 * @version $Revision: 470 $
 */
public class Client extends JFrame implements ShortcutListener, PreferencesFactory {
    private static final Logger log = Logger.getInstance(Client.class);

    // private GameData data = null;
    private List panels = null;

    private MapperPanel mapPanel = null;

    private EMapOverviewPanel overviewPanel = null;

    private EMapDetailsPanel detailsPanel = null;

    private MessagePanel messagePanel = null;

    /**
     * DOCUMENT ME!
     * 
     * @deprecated, use info from GameData
     */
    private File dataFile = null;

    protected boolean everLoadedReport = false; // indicates that the user
                                                // loaded a report at least once
                                                // in order to decide about
                                                // showing a save dialog when
                                                // quitting

    private FileHistory fileHistory;

    private JMenu factionOrdersMenu;

    private JMenu factionOrdersMenuNot;

    private JMenu invertAllOrdersConfirmation;

    private List nodeWrapperFactories;

    private List preferencesAdapterList;

    private MenuAction saveAction;

    private OptionAction optionAction;

    private MagellanDesktop desktop;

    private ReportObserver reportState;

    /** Manager for setting and activating bookmarks. */
    private BookmarkManager bookmarkManager;

    /** Central undo manager - specialized to deliver change events */
    private MagellanUndoManager undoMgr = null;

    // Directories
    private static File filesDirectory = null; // Magellan directory

    private static File settingsDirectory = null; // Directory of
                                                    // "magellan.ini"

    // show order status in title
    protected boolean showStatus = false;

    protected JMenuItem progressItem;

    // start window, disposed after first init
    protected static StartWindow startWindow;

    protected static ResourceBundle startBundle;

    /**
     * Creates a new Client object taking its data from <tt>gd</tt>.
     * 
     * <p>
     * Preferences are read from and stored in a file called <tt>client.ini</tt>.
     * This file is usually located in the user's home directed, which is the
     * Windows directory in a Microsoft Windows environment.
     * </p>
     * 
     * @param gd
     *            TODO: DOCUMENT ME!
     * @param fileDir
     *            TODO: DOCUMENT ME!
     * @param settingsDir
     *            TODO: DOCUMENT ME!
     */
    public Client(GameData gd, File fileDir, File settingsDir) {
        filesDirectory = fileDir;
        settingsDirectory = settingsDir;

        // get new dispatcher
        EventDispatcher dispatcher = new EventDispatcher();

        startWindow.progress(1, (startBundle != null) ? startBundle.getString("1")
				: "Loading settings...");
        Properties settings = loadSettings(settingsDirectory, "magellan.ini");
        if (settings == null) {
			log.info("Client.loadSettings: settings file " + "magellan.ini"
					+ " does not exist, using default values.");
			settings = new SelfCleaningProperties();
	        initLocales(settings, true);
		}else{
			initLocales(settings, false);
		}
        
        showStatus = PropertiesHelper.getboolean(settings,"Client.ShowOrderStatus", false);

        Properties completionSettings = loadSettings(settingsDirectory, "magellan_completions.ini");
        if (completionSettings==null)
        	completionSettings=new SelfCleaningProperties();

        // initialize the context, this has to be very early.
        context = new MagellanContext(this);
        context.setEventDispatcher(dispatcher);
        context.setProperties(settings);
        context.setCompletionProperties(completionSettings);
        context.init();

        context.setGameData(gd);
        // init icon, fonts, repaint shortcut, L&F, window things
        initUI();

        // create management and observer objects
        dispatcher.addSelectionListener(SelectionHistory.getEventHook());
        bookmarkManager = new BookmarkManager(dispatcher, settings);
        undoMgr = new MagellanUndoManager();
        reportState = new ReportObserver(dispatcher);

        // init components
        startWindow.progress(2, (startBundle != null) ? startBundle
                .getString("2") : "Initializing components...");
        panels = CollectionFactory.createLinkedList();
        nodeWrapperFactories = CollectionFactory.createLinkedList();

        List topLevelComponents = new LinkedList();
        Map components = initComponents(topLevelComponents);

        // init desktop
        startWindow.progress(3, (startBundle != null) ? startBundle
                .getString("3") : "Creating desktop environment...");
        Rectangle bounds = PropertiesHelper.loadRect(settings, null, "Client");
        if (bounds!=null)
        	setBounds(bounds);

        desktop = new MagellanDesktop(this, context, settings, components,
                settingsDirectory);

        setContentPane(desktop);

        // do it here because we need the desktop menu
        setJMenuBar(createMenuBar(topLevelComponents));

        // enable EventDisplayer
        // new
        // com.eressea.util.logging.EventDisplayDialog(this,false,dispatcher).setVisible(true);
    }

    // ////////////////////////
    // BASIC initialization //
    // ////////////////////////

    private MagellanContext context;

    /**
     * Load the file fileName in the given directory into the settings object.
     * 
     * @param directory
     *            TODO: DOCUMENT ME!
     * @param fileName
     *            TODO: DOCUMENT ME!
     */
    protected Properties loadSettings(File directory, String fileName) {
        Properties settings =  new SelfCleaningProperties();
        // settings = new OrderedOutputProperties();
        // settings = new AgingProperties();

        settings.clear();

        File settingsFile = new File(directory, fileName);

        // load settings from file
        if (settingsFile.exists()) {
            try {
                settings.load(new BufferedInputStream(new FileInputStream(
                        settingsFile)));
                log.info("Client.loadSettings: successfully loaded "+settingsFile);
            } catch (IOException e) {
                log.error("Client.loadSettings: Error while loading "
                        + settingsFile, e);
                return null;
            }
        } else {
        	return null;
        }
        return settings;
    }
    
    protected void initLocales(Properties settings, boolean ask) {
    	if (ask){
    		LanguageDialog ld = new LanguageDialog(settings, filesDirectory);

    		if (ld.languagesFound()) {
    			startWindow.toBack();
    			Locale locale = ld.showDialog(startWindow);
    			startWindow.toFront();

    			if ((locale != null) && !locale.equals(Locale.getDefault())) {
    				settings.setProperty("locales.gui", locale.getLanguage());
    				settings.setProperty("locales.orders", locale.getLanguage());
    			}
    		}
    	}
    	
        if (settings.getProperty("locales.gui") != null)
        	Locales.setGUILocale(new Locale(settings.getProperty("locales.gui")));
        else
        	Locales.setGUILocale(Locale.getDefault());
        if (settings.getProperty("locales.orders") != null)
        	Locales.setOrderLocale(new Locale(settings.getProperty("locales.orders")));
        else
        	Locales.setOrderLocale(Locale.GERMAN);
        log.info("GUI locale: "+Locales.getGUILocale()+settings.getProperty("locales.gui")+", orders locale: "+Locales.getOrderLocale()+settings.getProperty("locales.orders"));
    }

    // TODO (stm) this is used by exactly once in the whole project. Why do we
    // need context anyway?
    /**
     * Returns the MagellanContext
     */
    public MagellanContext getMagellanContext() {
        return context;
    }

    /**
     * Returns the application icon
     * 
     * @return the application icon
     */
    public static Image getApplicationIcon() {
        // set the application icon
        URL iconURL = ResourcePathClassLoader
                .getResourceStatically("images/about/appicon.gif");

        if (iconURL == null) {
            iconURL = ResourcePathClassLoader
                    .getResourceStatically("res/images/about/appicon.gif");
        }

        if (log.isDebugEnabled()) {
            log.debug("Client.getApplicationIcon() iconURL: " + iconURL);
        }

        return (iconURL == null) ? null : Toolkit.getDefaultToolkit()
                .createImage(iconURL);
    }

    /**
     * Inits base UI things: # frame icon # window event things # fonts #
     * repaint shortcut # L&F
     */
    protected void initUI() {
        Image iconImage = getApplicationIcon();

        // set the application icon
        if (iconImage != null) {
            setIconImage(iconImage);
        }

        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                quit(true);
            }
        });

        /* setup font size */
        try {
            float fScale = PropertiesHelper.getfloat(getProperties(),
                    "Client.FontScale", 1.0f);

            if (fScale != 1.0f) {
                // TODO(pavkovic): the following code bloats the fonts in an
                // undesired way, perhaps
                // we remove this configuration option?
                UIDefaults table = UIManager.getDefaults();
                Enumeration eKeys = table.keys();

                while (eKeys.hasMoreElements()) {
                    Object obj = eKeys.nextElement();
                    Font font = UIManager.getFont(obj);

                    if (font != null) {
                        font = new FontUIResource(font.deriveFont(font
                                .getSize2D()
                                * fScale));
                        UIManager.put(obj, font);
                    }
                }
            }
        } catch (Exception e) {
            log.error(e);
        }

        // initialize client shortcut - F5 to repaint
        DesktopEnvironment.registerShortcutListener(KeyStroke.getKeyStroke(
                KeyEvent.VK_F5, 0), this);

        // init L&F
        initLookAndFeels();
    }

    // ////////////////////////////
    // COMPONENT initialization //
    // ////////////////////////////

    /**
     * Initializes the Magellan components. The returned hashtable holds all
     * components with well-known desktop keywords.
     * 
     * @param topLevel
     *            TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    protected Map initComponents(List topLevel) {
        Map components = CollectionFactory.createHashtable();

        // configure and add map panel
        // get cell geometry
        CellGeometry geo = new CellGeometry("cellgeometry.txt");

        // load custom renderers
        // ForcedFileClassLoader.directory = filesDirectory;
        RendererLoader rl = new RendererLoader(filesDirectory, ".", geo,
                getProperties());
        Collection cR = rl.loadRenderers();

        // init mapper
        mapPanel = new MapperPanel(getMagellanContext(), cR, geo);
        mapPanel.setMinimumSize(new Dimension(100, 10));
        mapPanel.setScaleFactor(PropertiesHelper.getfloat(getProperties(),
                "Map.scaleFactor", 1.0f));
        panels.add(mapPanel);
        components.put("MAP", mapPanel);
        components.put("MINIMAP", mapPanel.getMinimap());
        topLevel.add(mapPanel);

        // configure and add message panel
        messagePanel = new MessagePanel(getDispatcher(), getData(),
                getProperties());
        messagePanel.setMinimumSize(new Dimension(100, 10));
        panels.add(messagePanel);
        nodeWrapperFactories.add(messagePanel.getNodeWrapperFactory());
        components.put("MESSAGES", messagePanel);
        topLevel.add(messagePanel);

        // configure and add details panel
        detailsPanel = new EMapDetailsPanel(getDispatcher(), getData(),
                getProperties(), undoMgr);
        detailsPanel.setMinimumSize(new Dimension(100, 10));
        panels.add(detailsPanel);
        nodeWrapperFactories.add(detailsPanel.getNodeWrapperFactory());

        Container c = (Container) detailsPanel.topSplitPane.getTopComponent();
        components.put("NAME&DESCRIPTION", c);
        components.put("NAME", c.getComponent(0));
        components.put("DESCRIPTION", c.getComponent(1));
        components.put("DETAILS", detailsPanel.topSplitPane
                .getBottomComponent());
        components.put("ORDERS", detailsPanel.bottomSplitPane
                .getBottomComponent());

        // this keyword is deprecated
        components.put("COMMANDS", detailsPanel.bottomSplitPane
                .getBottomComponent());
        topLevel.add(detailsPanel);

        // configure and add overview panel
        overviewPanel = new EMapOverviewPanel(getDispatcher(), getProperties());
        overviewPanel.setMinimumSize(new Dimension(100, 10));
        panels.add(overviewPanel);
        components.put("OVERVIEW", overviewPanel.getOverviewComponent());
        components.put("HISTORY", overviewPanel.getHistoryComponent());
        components.put("OVERVIEW&HISTORY", overviewPanel);
        nodeWrapperFactories.add(overviewPanel.getNodeWrapperFactory());
        topLevel.add(overviewPanel);

        return components;
    }

    // //////////////////////////
    // MENUBAR initialization //
    // //////////////////////////

    /**
     * Creates a menu bar to be added to this frame.
     * 
     * @param components
     *            TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    private JMenuBar createMenuBar(Collection components) {
        JMenuBar menuBar = new JMenuBar();

        // create static menus
        menuBar.add(createFileMenu());
        menuBar.add(createEditMenu());
        menuBar.add(createOrdersMenu());
        menuBar.add(createBookmarkMenu());
        menuBar.add(createMapMenu());

        // create dynamix menus
        Map topLevel = new HashMap();
        List direction = new LinkedList();
        Iterator it = components.iterator();
        log.info("Checking for menu-providers...");

        while (it.hasNext()) {
            Object o = it.next();

            if (o instanceof MenuProvider) {
                MenuProvider mp = (MenuProvider) o;

                if (mp.getSuperMenu() != null) {
                    if (!topLevel.containsKey(mp.getSuperMenu())) {
                        topLevel.put(mp.getSuperMenu(), new JMenu(mp
                                .getSuperMenuTitle()));
                        direction.add(topLevel.get(mp.getSuperMenu()));
                    }

                    JMenu top = (JMenu) topLevel.get(mp.getSuperMenu());
                    top.add(mp.getMenu());
                } else {
                    direction.add(mp.getMenu());
                }
            }
        }
        
        log.info("Checking for menu-providers...(Module3");
        // add external modules if some can be found
        Collection menuS = getExternalModules3();
        if (menuS!=null && menuS.size()>0) {
        	for (Iterator i3 = menuS.iterator();i3.hasNext();){
        		ExternalModule3 externalModule3 = (ExternalModule3)i3.next();
        		JMenu externalJMenu3 = externalModule3.getJMenu(this);
        		if (externalJMenu3!=null) {
        			menuBar.add(externalJMenu3);
        		}
        	}
        }
        

        // menuBar.add(createTreeMenu());
        // desktop and extras last
        menuBar.add(desktop.getDesktopMenu());
        menuBar.add(createExtrasMenu());

        return menuBar;
    }

    protected JMenu createFileMenu() {
        JMenu file = new JMenu(getString("menu.file.caption"));
        file.setMnemonic(getString("menu.file.mnemonic").charAt(0));
        addMenuItem(file, new OpenCRAction(this));
        addMenuItem(file, new AddCRAction(this));
        addMenuItem(file, new OpenOrdersAction(this));
        file.addSeparator();
        saveAction = new FileSaveAction(this);
        addMenuItem(file, saveAction);
        addMenuItem(file, new FileSaveAsAction(this));
        addMenuItem(file, new SaveOrdersAction(this));
        file.addSeparator();
        addMenuItem(file, new ExportCRAction(this));
        file.addSeparator();

        // now create the file history since we have all data
        fileHistory = new FileHistory(this, getProperties(), file, file
                .getItemCount());
        fileHistory.buildFileHistoryMenu();
        file.addSeparator();
        addMenuItem(file, new AbortAction(this));
        addMenuItem(file, new QuitAction(this));

        return file;
    }

    protected JMenu createEditMenu() {
        JMenu edit = new JMenu(getString("menu.edit.caption"));
        edit.setMnemonic(getString("menu.edit.mnemonic").charAt(0));
        addMenuItem(edit, new UndoAction(this, undoMgr));
        addMenuItem(edit, new RedoAction(this, undoMgr));
        edit.addSeparator();
        addMenuItem(edit, new FindAction(this));

        return edit;
    }

    protected JMenu createOrdersMenu() {
        JMenu ordersMenu = new JMenu(getString("menu.orders.caption"));
        ordersMenu.setMnemonic(getString("menu.orders.mnemonic").charAt(0));
        addMenuItem(ordersMenu, new UnconfirmAction(this, overviewPanel));
        addMenuItem(ordersMenu, new FindPreviousUnconfirmedAction(this,
                overviewPanel));

        addMenuItem(ordersMenu, new ConfirmAction(this, overviewPanel));

        // add factionordersmenu to ordersmenu
        factionOrdersMenu = new JMenu(getString("menu.orders.all.caption"));
        factionOrdersMenu.setMnemonic(getString("menu.orders.all.mnemonic")
                .charAt(0));
        ordersMenu.add(factionOrdersMenu);

        // add factionordersmenunot to ordersmenu
        factionOrdersMenuNot = new JMenu(
                getString("menu.orders.allnot.caption"));
        factionOrdersMenuNot.setMnemonic(getString(
                "menu.orders.allnot.mnemonic").charAt(0));
        ordersMenu.add(factionOrdersMenuNot);

        // add factionordersmenu to ordersmenu
        invertAllOrdersConfirmation = new JMenu(
                getString("menu.orders.invert.caption"));
        invertAllOrdersConfirmation.setMnemonic(getString(
                "menu.orders.invert.mnemonic").charAt(0));
        ordersMenu.add(invertAllOrdersConfirmation);

        updateConfirmMenu();

        return ordersMenu;
    }

    private void refillChangeFactionConfirmation(JMenu aMenu,
            int aConfirmationType) {
        if (aMenu.getItemCount() == 0) {
            // fill basic faction "all units"
            addMenuItem(aMenu, new ChangeFactionConfirmationAction(this, null,
                    aConfirmationType, false));
            addMenuItem(aMenu, new ChangeFactionConfirmationAction(this, null,
                    aConfirmationType, true));
        } else {
            JMenuItem one = aMenu.getItem(0);
            JMenuItem two = aMenu.getItem(1);
            aMenu.removeAll();
            aMenu.add(one);
            aMenu.add(two);
        }

        if (getData() != null) {
            // add all privileged factions
            for (Iterator iter = getData().factions().values().iterator(); iter
                    .hasNext();) {
                Faction f = (Faction) iter.next();

                if ((f.isPrivileged()) && !f.units().isEmpty()) {
                    aMenu.add(new ChangeFactionConfirmationAction(this, f,
                            aConfirmationType, false));
                    aMenu.add(new ChangeFactionConfirmationAction(this, f,
                            aConfirmationType, true));
                }
            }
        }
    }

    protected JMenu createMapMenu() {
        JMenu map = new JMenu(getString("menu.map.caption"));
        map.setMnemonic(getString("menu.map.mnemonic").charAt(0));
        addMenuItem(map, new SetOriginAction(this));
        addMenuItem(map, new IslandAction(this));
        addMenuItem(map, new MapSaveAction(this, mapPanel));
        map.addSeparator();
        addMenuItem(map, new SelectAllAction(this));
        addMenuItem(map, new SelectNothingAction(this));
        addMenuItem(map, new InvertSelectionAction(this));
        addMenuItem(map, new SelectIslandsAction(this));
        addMenuItem(map, new FillSelectionAction(this));
        addMenuItem(map, new ExpandSelectionAction(this));
        map.addSeparator();
        addMenuItem(map, new OpenSelectionAction(this));
        addMenuItem(map, new AddSelectionAction(this));
        addMenuItem(map, new SaveSelectionAction(this));

        return map;
    }

    protected JMenu createBookmarkMenu() {
        JMenu bookmarks = new JMenu(getString("menu.bookmarks.caption"));
        bookmarks.setMnemonic(getString("menu.bookmarks.mnemonic").charAt(0));

        JMenuItem toggle = new JMenuItem(
                getString("menu.bookmarks.toggle.caption"));
        toggle.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2,
                KeyEvent.CTRL_MASK));
        toggle.addActionListener(new ToggleBookmarkAction());
        bookmarks.add(toggle);

        JMenuItem forward = new JMenuItem(
                getString("menu.bookmarks.forward.caption"));
        forward.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0));
        forward.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                bookmarkManager.jumpForward();
            }
        });
        bookmarks.add(forward);

        JMenuItem backward = new JMenuItem(
                getString("menu.bookmarks.backward.caption"));
        backward.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2,
                KeyEvent.SHIFT_MASK));
        backward.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                bookmarkManager.jumpBackward();
            }
        });
        bookmarks.add(backward);

        JMenuItem show = new JMenuItem(getString("menu.bookmarks.show.caption"));
        show.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2,
                KeyEvent.ALT_MASK));
        show.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                bookmarkManager.showDialog(Client.this);
            }
        });
        bookmarks.add(show);

        JMenuItem clear = new JMenuItem(
                getString("menu.bookmarks.clear.caption"));
        clear.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                bookmarkManager.clearBookmarks();
            }
        });
        bookmarks.add(clear);

        return bookmarks;
    }

    protected JMenu createExtrasMenu() {
        JMenu extras = new JMenu(getString("menu.extras.caption"));
        extras.setMnemonic(getString("menu.extras.mnemonic").charAt(0));
        addMenuItem(extras, new FactionStatsAction(this));
        addMenuItem(extras, new ArmyStatsAction(this));
        addMenuItem(extras, new TradeOrganizerAction(this));

        // add external modules if some can be found
        JMenuItem menuItems[] = getExternalModuleItems();

        if (menuItems.length > 0) {
            extras.addSeparator();
        }

        for (int i = 0; i < menuItems.length; i++) {
            if (menuItems[i] != null) {
                extras.add(menuItems[i]);
            }
        }

        if (menuItems.length > 0) {
            extras.addSeparator();
        }

        addMenuItem(extras, new TaskTableAction(this));
        addMenuItem(extras, new ECheckAction(this));
        addMenuItem(extras, new VorlageAction(this));
        extras.addSeparator();
        addMenuItem(extras, new RepaintAction(this));
        addMenuItem(extras, new TileSetAction(this, mapPanel));
        extras.addSeparator();
        preferencesAdapterList = CollectionFactory.createArrayList(8);
        preferencesAdapterList.add(this);
        preferencesAdapterList.add(desktop);
        preferencesAdapterList.add(overviewPanel);
        preferencesAdapterList.add(detailsPanel);
        preferencesAdapterList.add(mapPanel);
        preferencesAdapterList.add(messagePanel);
        preferencesAdapterList
                .add(new IconAdapterFactory(nodeWrapperFactories));
        preferencesAdapterList
                .add(new ResourceSettingsFactory(getProperties()));
        optionAction = new OptionAction(this, preferencesAdapterList);
        addMenuItem(extras, optionAction);

        // TODO(pavkovic): currently EresseaOptionPanel is broken, I deactivated
        // it.
        extras.addSeparator();
        addMenuItem(extras, new HelpAction(this));
        addMenuItem(extras, new TipOfTheDayAction(this));
        extras.addSeparator();
        addMenuItem(extras, new InfoAction(this));

        return extras;
    }

    private Collection externalModules = null;

    /**
     * delivers all externalModules
     */
    public synchronized Collection getExternalModules() {
        if (externalModules == null) {
            externalModules = new ArrayList();

            Collection c = ExternalModuleLoader
                    .getExternalModuleClasses(getProperties());
            for (Iterator iter = c.iterator(); iter.hasNext();) {
                Class foundClass = (Class) iter.next();
                try {

                    // get it's constructor
                    Object externalModule = foundClass.getConstructor(
                            new Class[] {}).newInstance(new Object[] {});

                    if (externalModule instanceof ExternalModule
                            || externalModule instanceof ExternalModule2) {
                        // register as SelectionListener if applicable
                        if (externalModule instanceof SelectionListener) {
                            getDispatcher().addSelectionListener(
                                    (SelectionListener) externalModule);
                        }

                        externalModules.add(externalModule);
                    }
                } catch (IllegalAccessException e) {
                    log.error(e);
                } catch (NoSuchMethodException e) {
                    log.error(e);
                } catch (InstantiationException e) {
                    log.error(e);
                } catch (InvocationTargetException e) {
                    log.error(e);
                }
            }
        }
        return externalModules;

    }

    private Collection externalModules3 = null;
    
    /**
     * delivers all externalModules3
     */
    public synchronized Collection getExternalModules3() {
        if (externalModules3 == null) {
            externalModules3 = new ArrayList();

            Collection c = ExternalModule3Loader
                    .getExternalModuleClasses(getProperties());
            for (Iterator iter = c.iterator(); iter.hasNext();) {
                Class foundClass = (Class) iter.next();
                try {

                    // get it's constructor
                    Object externalModule3 = foundClass.getConstructor(
                            new Class[] {}).newInstance(new Object[] {});

                    if (externalModule3 instanceof ExternalModule3) {
                        // do not register as SelectionListener if applicable
                        externalModules3.add(externalModule3);
                    }
                } catch (IllegalAccessException e) {
                    log.error(e);
                } catch (NoSuchMethodException e) {
                    log.error(e);
                } catch (InstantiationException e) {
                    log.error(e);
                } catch (InvocationTargetException e) {
                    log.error(e);
                }
            }
        }
        return externalModules3;
    }
    
    /**
     * Retrieves the menu items for the external modules.
     * 
     * @see com.eressea.extern.ExternalModule2 and
     * @see com.eressea.extern.ExternalModuleLoader for documentation.
     * 
     * @return TODO: DOCUMENT ME!
     */
    private JMenuItem[] getExternalModuleItems() {
        List menuItems = CollectionFactory.createArrayList();

        for (Iterator iter = getExternalModules().iterator(); iter.hasNext();) {
            Object externalModule = iter.next();

            // get menuString
            String menuString = null;

            if (externalModule instanceof ExternalModule) {
                ExternalModule module = (ExternalModule) externalModule;
                menuString = module.getMenuItemName();
            }

            if (externalModule instanceof ExternalModule2) {
                ExternalModule2 module = (ExternalModule2) externalModule;
                menuString = module.getMenuItemName();
            }

            if (menuString != null) {
                // create menu item
                JMenuItem item = new JMenuItem(new ExternalModuleAction(this,
                        menuString, externalModule));
                menuItems.add(item);
            }
        }

        return (JMenuItem[]) menuItems.toArray(new JMenuItem[] {});
    }

    private class ToggleBookmarkAction implements ActionListener,
            SelectionListener {
        private Object activeObject;

        /**
         * Creates a new ToggleBookmarkAction object.
         */
        public ToggleBookmarkAction() {
            getDispatcher().addSelectionListener(this);
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @param se
         *            TODO: DOCUMENT ME!
         */
        public void selectionChanged(SelectionEvent se) {
            activeObject = se.getActiveObject();
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @param e
         *            TODO: DOCUMENT ME!
         */
        public void actionPerformed(ActionEvent e) {
            bookmarkManager.toggleBookmark(activeObject);
        }
    }

    /**
     * Adds a new menu item to the specifie menu associating it with the
     * specified action, setting its mnemonic and registers its accelerator if
     * it has one.
     * 
     * @param parentMenu
     *            TODO: DOCUMENT ME!
     * @param action
     *            TODO: DOCUMENT ME!
     * 
     * @return the menu item created.
     */
    private JMenuItem addMenuItem(JMenu parentMenu, MenuAction action) {
        JMenuItem item = parentMenu.add(action);
        item.setMnemonic(action.getMnemonic());

        if (action.getAccelerator() != null) {
            DesktopEnvironment.registerActionListener(action.getAccelerator(),
                    action);
            item.setAccelerator(action.getAccelerator());
        }

        new MenuActionObserver(item, action);

        return item;
    }

    // ////////////////////
    // START & END Code //
    // ////////////////////
    /**
     * START & END Code
     * 
     * @param args TODO: DOCUMENT ME!
     */
    public static void main(String args[]) {
        try {
            String report = null; // the report to be loaded on startup
            File fileDir = null; // the directory to store ini files and
                                    // stuff in

            /* set the stderr to stdout while there is no log attached */
            System.setErr(System.out);

            // Fiete 20061208
            // set finalizer prio to max
            com.eressea.util.MemoryManagment.setFinalizerPriority(Thread.MAX_PRIORITY);
            
            // initialize start window
            Icon startIcon = null;

            try {
                URL url = ClassLoader.getSystemClassLoader().getResource(
                        "res/images/about/magellan.gif");

                if (url != null) {
                    startIcon = new ImageIcon(url);
                }
            } catch (Exception exc) {
                log.info("Could not find start image.");
            }

            try {
                startBundle = ResourceBundle
                        .getBundle("res/lang/com-eressea-demo-clientstart");
            } catch (RuntimeException exc) {
                log.info(startBundle);
            }

            startWindow = new StartWindow(startIcon, 5);

            startWindow.setVisible(true);

            startWindow.progress(0, (startBundle != null) ? startBundle
                    .getString("0") : "Initializing Magellan...");

            /* determine default value for files directory */
            fileDir = MagellanFinder.findMagellanDirectory();

            File settFileDir = null;

            /* process command line parameters */
            int i = 0;

            while (i < args.length) {
                if (args[i].toLowerCase().startsWith("-log")) {
                    String level = null;

                    if (args[i].toLowerCase().startsWith("-log=")
                            && (args[i].length() > 5)) {
                        level = args[i].charAt(5) + "";
                    } else if (args[i].equals("-log")
                            && (args.length > (i + 1))) {
                        i++;
                        level = args[i];
                    }

                    if (level != null) {
                        level = level.toUpperCase();
                        log.info("Client.main: Set logging to " + level);
                        Logger.setLevel(level);

                        if ("A".equals(level)) {
                            log
                                    .awt("Start logging of awt events to awtdebug.txt.");
                        }
                    }
                } else if (args[i].equals("-d") && (args.length > (i + 1))) {
                    i++;

                    try {
                        File tmpFile = new File(args[i]).getCanonicalFile();

                        if (tmpFile.exists() && tmpFile.isDirectory()
                                && tmpFile.canWrite()) {
                            fileDir = tmpFile;
                        } else {
                            log
                                    .info("Client.main(): the specified files directory does not "
                                            + "exist, is not a directory or is not writeable.");
                        }
                    } catch (Exception e) {
                        log
                                .error(
                                        "Client.main(): the specified files directory is invalid.",
                                        e);
                    }
                } else {
                    if (args[i].toLowerCase().endsWith(".cr")
                            || args[i].toLowerCase().endsWith(".bz2")
                            || args[i].toLowerCase().endsWith(".zip")) {
                        report = args[i];
                    }
                }

                i++;
            }

            settFileDir = MagellanFinder.findSettingsDirectory(fileDir,
                    settFileDir);

            // tell the user where we expect ini files and errors.txt
            log.info("Client.main(): directory used for ini files: "
                    + settFileDir.toString());

            // now redirect stderr through our log
           
            Log LOG = new Log(fileDir);
            System.setErr(LOG.getPrintStream());

            log.error("Start writing error file with encoding "
                    + LOG.encoding);
            
            String version = VersionInfo.getVersion();
            if (version==null){
            	log.info("no magellan version available");
            } else {
            	log.info("This is Magellan Version " + version);
            }

            // can't call loadRules from here, so we initially work with an
            // empty ruleset.
            // This is not very nice, though...
            GameData data = new MissingData();

            // new CompleteData(new com.eressea.rules.Eressea(), "void");
            Client c = new Client(data, fileDir, settFileDir);

            if (report != null) {
                startWindow.progress(4, (startBundle != null) ? startBundle
                        .getString("4") : "Loading game data...");

                File crFile = new File(report);

                c.dataFile = crFile;

                // load new data
                c.setData(c.loadCR(crFile));
            }

            c.setReportChanged(false);

            startWindow.progress(5, (startBundle != null) ? startBundle
                    .getString("5") : "Opening work-space...");
            c.setAllVisible(true);
            startWindow.setVisible(false);
            startWindow.dispose();
            startWindow = null;

            // show tip of the day window
            if (c.getProperties().getProperty("TipOfTheDay.showTips", "true")
                    .equals("true")
                    || c.getProperties().getProperty("TipOfTheDay.firstTime",
                            "true").equals("true")) {
                TipOfTheDay totd = new TipOfTheDay(c, c.getProperties());

                if (totd.doShow()) {
                    totd.setVisible(true);
                    totd.showNextTip();
                }
            }
        } catch (Throwable exc) { // any fatal error
            log.error(exc); // print it so it can be written to errors.txt

            // try to create a nice output
            String out = null;

            if (startBundle != null) {
                try {
                    out = startBundle.getString("error") + " "
                            + startBundle.getString(exc.getClass().getName());

                    if (out == null) {
                        out = startBundle.getString("error") + " "
                                + startBundle.getString("error.other");
                    }

                    if (out == null) {
                        out = startBundle.getString("error") + " "
                                + exc.toString();
                    }
                } catch (Exception exc2) {
                }
            }

            if (out == null) {
                out = "A fatal error occured: " + exc.toString();
            }

            log.error(out, exc);
            JOptionPane.showMessageDialog(new JFrame(), out);
            System.exit(1);
        }
    }

    /**
     * Asks the user whether the current report should be saved and does so if
     * necessary. Called before open and close actions.
     * 
     * @return whether the action shall be continued (user did not press cancel
     *         button)
     */
    public boolean askToSave() {
        if (reportState.isStateChanged()) {
            String msg = null;

            if (dataFile != null) {
                Object msgArgs[] = { dataFile.getAbsolutePath() };
                msg = (new MessageFormat(
                        getString("msg.quit.confirmsavefile.text")))
                        .format(msgArgs);
            } else {
                msg = getString("msg.quit.confirmsavenofile.text");
            }

            switch (JOptionPane.showConfirmDialog(this, msg,
                    getString("msg.quit.confirmsave.title"),
                    JOptionPane.YES_NO_CANCEL_OPTION)) {
            case JOptionPane.YES_OPTION:

                try {
                    saveAction.actionPerformed(null);
                } catch (Exception e) {
                    log.error(e);

                    return false;
                }

                break;

            case JOptionPane.CANCEL_OPTION:
                return false;
            }
        }

        return true;
    }

    /**
     * This method should be called before the application is terminated in
     * order to store GUI settings etc.
     * 
     * @param storeSettings
     *            store the settings to magellan.ini if
     *            <code>storeSettings</code> is <code>true</code>.
     */
    public void quit(boolean storeSettings) {
        if (reportState.isStateChanged()) {
            if (!askToSave()) {
                return; // cancel or exception
            }
        }

        saveExtendedState();
        this.setVisible(false);

        for (Iterator iter = panels.iterator(); iter.hasNext();) {
            InternationalizedDataPanel p = (InternationalizedDataPanel) iter
                    .next();
            p.quit();
        }

        NameGenerator.quit();

        fileHistory.storeFileHistory();

        // store settings to file
        if (storeSettings) {
            // save the desktop
            desktop.save();

            try {
                // if necessary, use settings file in local directory
                File settingsFile = new File(settingsDirectory, "magellan.ini");

                if (settingsFile.exists()) {
                    try {
                        File backup = FileBackup.create(settingsFile);
                        log.info("Created backupfile " + backup);
                    } catch (IOException ie) {
                        log.warn("Could not create backupfile for file "
                                + settingsFile);
                    }
                }

                log.info("Storing Magellan configuration to " + settingsFile);

                getProperties().store(new FileOutputStream(settingsFile), "");
            } catch (IOException ioe) {
                log.error(ioe);
            }
        }

        System.exit(0);
    }

    // //////////////////
    // GAME DATA Code //
    // //////////////////

    /**
     * DOCUMENT ME!
     * 
     * @param fileName
     *            The file name to be loaded.
     * 
     * @return a new <tt>GameData</tt> object filled with the data from the
     *         CR.
     */
    public GameData loadCR(File fileName) {
        GameData d = null;

        try {
            d = new GameDataReader().readGameData(FileTypeFactory.singleton()
                    .createFileType(fileName, true,
                            new ClientFileTypeChooser(this)));
            everLoadedReport = true;
        } catch (FileTypeFactory.NoValidEntryException e) {
            JOptionPane.showMessageDialog(this,
                    getString("msg.loadcr.missingcr.text.1") + fileName
                            + getString("msg.loadcr.missingcr.text.2"),
                    getString("msg.loadcr.error.title"),
                    JOptionPane.ERROR_MESSAGE);
        } catch (Exception exc) {
            // here we also catch RuntimeExceptions on purpose!
            // } catch (IOException exc) {
            JOptionPane.showMessageDialog(this,
                    getString("msg.loadcr.error.text") + exc.toString(),
                    getString("msg.loadcr.error.title"),
                    JOptionPane.ERROR_MESSAGE);
            log.error(exc);
        }
       
        if (d.outOfMemory){
        	JOptionPane.showMessageDialog(this,
                    getString("msg.outofmemory.text"),
                    getString("msg.outofmemory.title"),
                    JOptionPane.ERROR_MESSAGE);
            log.error(getString("msg.outofmemory.text"));
        }
        return d;
    }

	/**
	 * Sets the origin of this client's data to newOrigin.
	 *
	 * 
	 * @param newOrigin The region in the GameData that is going to be the new origin
	 */
	public void setOrigin(CoordinateID newOrigin) {
		GameData newData=null;
		try {
			newData = (GameData) getData().clone(newOrigin);
		} catch (final CloneNotSupportedException e) {
			e.printStackTrace();
		}

		if(newData != null) {
			setData(newData);
			setReportChanged(false);
		}
	}
	

    /**
     * Callbacks of FileTypeFactory are handled by this object. Right now it
     * returns the first ZipEntry to mimic old cr loading behaviour for zip
     * files.
     */
    private static class ClientFileTypeChooser extends
            FileTypeFactory.FileTypeChooser {
        Client client;

        /**
         * Creates a new ClientFileTypeChooser object.
         * 
         * @param client
         *            the parent Client object
         */
        public ClientFileTypeChooser(Client client) {
            this.client = client;
        }

        /** 
         * open selection window to choose a zipentry
         * 
         * @see com.eressea.io.file.FileTypeFactory.FileTypeChooser#chooseZipEntry(java.util.zip.ZipEntry[])
         */
        public ZipEntry chooseZipEntry(ZipEntry entries[]) {
            String stringEntries[] = new String[entries.length];

            for (int i = 0; i < entries.length; i++) {
                stringEntries[i] = entries[i].toString();
            }

            Object selected = JOptionPane.showInputDialog(client.getRootPane(),
                    client.getString("msg.loadcr.multiplezipentries.text"),
                    client.getString("msg.loadcr.multiplezipentries.title"),
                    JOptionPane.QUESTION_MESSAGE, null, stringEntries,
                    stringEntries[0]);

            if (selected == null) {
                return null;
            }

            for (int i = 0; i < entries.length; i++) {
                if (selected.equals(entries[i].toString())) {
                    return entries[i];
                }
            }

            return null;
        }
    }

    /**
     * Do some additional checks after loading a report.
     * 
     * @param aData
     *            the currently loaded game data
     */
    private void postProcessLoadedCR(GameData aData) {
        // show a warning if no password is set for any of the privileged
        // factions
        boolean privFacsWoPwd = true;

        if ((aData != null) && (aData.factions() != null)) {
            for (Iterator factions = aData.factions().values().iterator(); factions
                    .hasNext();) {
                Faction f = (Faction) factions.next();

                if (f.password == null) {
                    // take password from settings but only if it is not an
                    // empty string
                    String pwd = getProperties().getProperty(
                            "Faction.password."
                                    + ((EntityID) f.getID()).intValue(), null);

                    if ((pwd != null) && !pwd.equals("")) {
                        f.password = pwd;
                    }
                }

                // now check whether this faction has a password and eventually
                // set Trustlevel
                if ((f.password != null) && !f.trustLevelSetByUser) {
                    f.trustLevel = Faction.TL_PRIVILEGED;
                }

                if (f.password != null) {
                    privFacsWoPwd = false;
                }

                // check messages whether the password was changed
                if (f.messages != null) {
                    for (Iterator iter = f.messages.iterator(); iter.hasNext();) {
                        Message m = (Message) iter.next();

                        // check message id (new and old)
                        if ((m.getMessageType() != null)
                                && ((((IntegerID) m.getMessageType().getID())
                                        .intValue() == 1784377885) || (((IntegerID) m
                                        .getMessageType().getID()).intValue() == 19735))) {
                            // this message indicates that the password has been
                            // changed
                            if (m.attributes != null) {
                                String value = (String) m.attributes
                                        .get("value");

                                // if the password in the message is valid and
                                // does not match
                                // the password already set anyway set it for
                                // the faction and in the settings
                                if (value != null) {
                                    String password = value;

                                    if (!password.equals("")
                                            && !password.equals(f.password)) {
                                        // ask user for confirmation to take new
                                        // password from message
                                        Object msgArgs[] = { f.toString() };

                                        if (JOptionPane
                                                .showConfirmDialog(
                                                        getRootPane(),
                                                        (new java.text.MessageFormat(
                                                                getString("msg.postprocessloadedcr.acceptnewpassword.text")))
                                                                .format(msgArgs),
                                                        getString("msg.postprocessloadedcr.acceptnewpassword.title"),
                                                        JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                                            f.password = password;

                                            if (!f.trustLevelSetByUser) { // password
                                                                            // set
                                                f.trustLevel = Faction.TL_PRIVILEGED;
                                            }

                                            privFacsWoPwd = false;

                                            if (getProperties() != null) {
                                                getProperties()
                                                        .setProperty(
                                                                "Faction.password."
                                                                        + ((EntityID) f
                                                                                .getID())
                                                                                .intValue(),
                                                                f.password);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // recalculate default-trustlevels after CR-Load
            TrustLevels.recalculateTrustLevels(aData);

            if (privFacsWoPwd) { // no password set for any faction
                JOptionPane
                        .showMessageDialog(
                                getRootPane(),
                                getString("msg.postprocessloadedcr.missingpassword.text"));
            }
        }
    }

    // ///////////////
    // UPDATE Code //
    // ///////////////
    private void updateTitleCaption() {
        String title = createTitle(getData(), showStatus, false);

        try {
            title = createTitle(getData(), showStatus, true);
        } catch (Exception e) {
            log.error("createTitle failed!", e);
        }

        setTitle(title);
    }

    private String createTitle(GameData data, boolean showStatusOverride,
            boolean longTitle) {
        // set frame title (date)
        String title = "Magellan";

        String version = VersionInfo.getVersion();

        if (version != null) {
            title += (" " + version);
        }

        // pavkovic 2002.05.7: data may be null in this situation
        if (data == null) {
            return title;
        }

        if (data.filetype != null) {
            String file;

            try {
                file = data.filetype.getFile().toString();
            } catch (IOException e) {
                file = data.filetype.toString();
            }

            file = file.substring(file.lastIndexOf(File.separator) + 1);
            title += (" [" + file + "]");
        }

        if (data.getDate() != null) {
            title = title
                    + " - "
                    + data.getDate().toString(
                            showStatusOverride ? EresseaDate.TYPE_SHORT
                                    : EresseaDate.TYPE_PHRASE_AND_SEASON)
                    + " (" + data.getDate().getDate() + ")";
        }

        if (!longTitle) {
            return title;
        }

        if (showStatusOverride) {
            int units = 0;
            int done = 0;

            for (Iterator iter = data.units().values().iterator(); iter
                    .hasNext();) {
                Unit u = (Unit) iter.next();

                if (u.getFaction().isPrivileged()) {
                    units++;

                    if (u.ordersConfirmed) {
                        done++;
                    }
                }

                // also count temp units
                for (Iterator iter2 = u.tempUnits().iterator(); iter2.hasNext();) {
                    Unit u2 = (Unit) iter2.next();

                    if (u2.getFaction().isPrivileged()) {
                        units++;

                        if (u2.ordersConfirmed) {
                            done++;
                        }
                    }
                }
            }

            if (units > 0) {
                BigDecimal percent = (new BigDecimal(( done * 100)
                        / ((float) units))).setScale(2, BigDecimal.ROUND_DOWN);
                title += (" (" + units + " " + getString("title.unit") + ", "
                        + done + " " + getString("title.done") + ", "
                        + getString("title.thatare") + " " + percent + " "
                        + getString("title.percent") + ")");
            }
        }

        return title;
    }

    /**
     * Updates the order confirmation menu after the game data changed.
     */
    private void updateConfirmMenu() {
        refillChangeFactionConfirmation(factionOrdersMenu,
                ChangeFactionConfirmationAction.SETCONFIRMATION);
        refillChangeFactionConfirmation(factionOrdersMenuNot,
                ChangeFactionConfirmationAction.REMOVECONFIRMATION);
        refillChangeFactionConfirmation(invertAllOrdersConfirmation,
                ChangeFactionConfirmationAction.INVERTCONFIRMATION);
    }

    /**
     * Called after GameData changes. Also called via EventDispatcher thread to ensure graphical changes do occur.
     */
    private void updatedGameData() {
        updateTitleCaption();
        updateConfirmMenu();

        if (getData().getCurTempID() == -1) {
            String s = getProperties().getProperty(
                    "ClientPreferences.TempIDsInitialValue", "");

            try {
                getData().setCurTempID(
                        "".equals(s) ? 0 : Integer.parseInt(s, getData().base));
            } catch (java.lang.NumberFormatException nfe) {
            }
        }

        // pavkovic 2004.01.04:
        // this method behaves at if the gamedata has been loaded by this
        // method.
        // this is not true at all but true enough for our needs.
        // dispatcher.fire(new GameDataEvent(this, data));
        getDispatcher().fire(new GameDataEvent(this, getData(), true));
        // also inform system about the new selection found in the GameData
        // object
        getDispatcher().fire(
                new SelectionEvent(this, getData()
                        .getSelectedRegionCoordinates().values(), null,
                        SelectionEvent.ST_REGIONS));

    }

    // ////////////
    // L&F Code //
    // ////////////
    /**
     * @param laf
     */
    public void setLookAndFeel(String laf) {
        boolean lafSet = true;

        if (MagellanLookAndFeel.equals(laf)
                && laf.equals(getProperties().getProperty("Client.lookAndFeel",
                        ""))) {
            lafSet = false;
        } else {
            lafSet = MagellanLookAndFeel.setLookAndFeel(laf);

            if (!lafSet) {
                laf = "Metal";
                lafSet = MagellanLookAndFeel.setLookAndFeel("Metal");
            }
        }

        if (laf.equals("Metal")) {
            MagellanLookAndFeel.loadBackground(getProperties());
        }

        if (!lafSet) {
            return;
        }

        updateLaF();

        getProperties().setProperty("Client.lookAndFeel", laf);
    }

    /**
     * TODO: DOCUMENT ME!
     */
    public void updateLaF() {
        // call updateUI in MagellanDesktop
        if (desktop != null) {
            desktop.updateLaF();
        }

        // call updateUI on preferences
        if (optionAction != null) {
            optionAction.updateLaF();
        }
    }

    private void initLookAndFeels() {
        setLookAndFeel(getProperties().getProperty("Client.lookAndFeel",
                "Metal"));
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    public String[] getLookAndFeels() {
        return (String[]) MagellanLookAndFeel.getLookAndFeelNames().toArray(
                new String[] {});
    }

    // ///////////////////
    // HISTORY methods //
    // ///////////////////

    /**
     * Adds a single file to the file history.
     * 
     * @param f
     *            TODO: DOCUMENT ME!
     */
    public void addFileToHistory(File f) {
        fileHistory.addFileToHistory(f);
    }

    /**
     * Returns the maximum number of entries in the history of loaded files.
     * 
     * @return TODO: DOCUMENT ME!
     */
    public int getMaxFileHistorySize() {
        return fileHistory.getMaxFileHistorySize();
    }

    /**
     * Allows to set the maximum number of files appearing in the file history.
     * 
     * @param size
     *            TODO: DOCUMENT ME!
     */
    public void setMaxFileHistorySize(int size) {
        fileHistory.setMaxFileHistorySize(size);
    }

    // ///////////////////
    // PROPERTY Access //
    // ///////////////////
    /**
     * Changes to the report state can be done here. Normally, a change
     * is recognized by the following events.
     *
     * @param changed
     */
    public void setReportChanged(boolean changed) {
        if (changed == false) {
            // for call from FileSaveAsAction
            updateTitleCaption();
        }

        reportState.setStateChanged(changed);
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    public boolean isReportChanged() {
        return reportState.isStateChanged();
    }

    /**
     * Get the selected Regions. The returned map can be empty but is never
     * null. This is a wrapper function so we dont need to give away
     * MapperPanel.
     * 
     * @return TODO: DOCUMENT ME!
     */
    public Map getSelectedRegions() {
        return mapPanel.getSelectedRegions();
    }

    /**
     * Get the Level on the mapper Panel. This is a wrapper function so we dont
     * need to give away MapperPanel.
     * 
     * @return TODO: DOCUMENT ME!
     */
    public int getLevel() {
        return mapPanel.getLevel();
    }

    // //////////////////////////
    // GENERAL ACCESS METHODS //
    // //////////////////////////

    /**
     * Returns the global settings used by Magellan.
     * 
     * @return TODO: DOCUMENT ME!
     */
    public Properties getProperties() {
        return context.getProperties();
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @param newData
     *            TODO: DOCUMENT ME!
     */
    public void setData(GameData newData) {
        context.setGameData(newData);
        postProcessLoadedCR(newData);
        
        if (newData!=null && PropertiesHelper.getboolean(getProperties(), "map.creating.void", false)) {
        	newData.postProcessTheVoid();
        }
        
        getDispatcher().fire(new GameDataEvent(this, newData));
        
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {        
                Client.this.updatedGameData();
            }});
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    public GameData getData() {
        return context.getGameData();
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    public MagellanDesktop getDesktop() {
        return desktop;
    }

    /**
     * 
     * @return TODO: DOCUMENT ME!
     */
    public EventDispatcher getDispatcher() {
        return context.getEventDispatcher();
    }

    /**
     * Returns the directory the local copy of Magellan is inside.
     * 
     * @return TODO: DOCUMENT ME!
     */
    public static File getMagellanDirectory() {
        return filesDirectory;
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    public static File getSettingsDirectory() {
        return settingsDirectory;
    }

    /**
     * DOCUMENT ME!
     * 
     * @return the BookmarkManager associated with this Client-Object
     */
    public BookmarkManager getBookmarkManager() {
        return bookmarkManager;
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    public boolean isShowingStatus() {
        return showStatus;
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @param bool
     *            TODO: DOCUMENT ME!
     */
    public void setShowStatus(boolean bool) {
        if (showStatus != bool) {
            showStatus = bool;
            getProperties().setProperty("Client.ShowOrderStatus",
                    showStatus ? "true" : "false");

            if (getData() != null) {
                updateTitleCaption();
            }
        }
    }

    // /////////////////////////////
    // INTERNATIONALIZATION Code //
    // /////////////////////////////

    /**
     * Returns a translation from the translation table for the specified key.
     * 
     * @param key
     *            TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    protected String getString(String key) {
        // this will indirectly evaluate getDefaultTranslation!
        return Translations.getTranslation(Client.class, key);
    }

    // pavkovic 2003.01.28: this is a Map of the default Translations mapped to
    // this class
    // it is called by reflection (we could force the implementation of an
    // interface,
    // this way it is more flexible.)
    // Pls use this mechanism, so the translation files can be created
    // automagically
    // by inspecting all classes.
    private static Map defaultTranslations;

    /**
     * TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    public static synchronized Map getDefaultTranslations() {
        if (defaultTranslations == null) {
            defaultTranslations = CollectionFactory.createHashtable();
            defaultTranslations.put("menu.file.caption", "File");
            defaultTranslations.put("menu.file.mnemonic", "f");
            defaultTranslations.put("menu.edit.caption", "Edit");
            defaultTranslations.put("menu.edit.mnemonic", "e");
            defaultTranslations.put("menu.orders.caption", "Orders");
            defaultTranslations.put("menu.orders.mnemonic", "r");
            defaultTranslations.put("menu.orders.all.caption", "All");
            defaultTranslations.put("menu.orders.all.mnemonic", "a");
            defaultTranslations.put("menu.orders.allnot.caption", "All not");
            defaultTranslations.put("menu.orders.allnot.mnemonic", "n");
            defaultTranslations.put("menu.orders.invert.caption", "Invert");
            defaultTranslations.put("menu.orders.invert.mnemonic", "i");
            defaultTranslations.put("shortcut.description", "Repaint all");
            defaultTranslations.put("shortcut.title", "Client");
            defaultTranslations.put("title.unit", "Units");
            defaultTranslations.put("menu.tree.caption", "Tree");
            defaultTranslations.put("menu.tree.mnemonic", "t");
            defaultTranslations.put("menu.map.caption", "Map");
            defaultTranslations.put("menu.map.mnemonic", "m");
            defaultTranslations.put("menu.extras.caption", "Extras");
            defaultTranslations.put("menu.extras.mnemonic", "x");
            defaultTranslations.put("msg.loadcr.multiplezipentries.text",
                    "Multiple computer reports have been\n"
                            + "found in the comppressed file.\n"
                            + "Please choose one.");
            defaultTranslations.put("msg.loadcr.multiplezipentries.title",
                    "Choose one");
            defaultTranslations.put("msg.loadcr.missingcr.text.1",
                    "No Computer Report was found in the compressed file \"");
            defaultTranslations.put("msg.loadcr.missingcr.text.2", "\".");
            defaultTranslations.put("msg.loadcr.error.text",
                    "While loading the report the following error occurred:\n");
            defaultTranslations.put("msg.loadcr.error.title", "Error");
            defaultTranslations
                    .put("msg.main.invalidcr.text",
                            "The file \"{0}\" cannot be opened because it does not exist.");
            defaultTranslations.put("msg.quit.confirmsavefile.text",
                    "Do you want to save changes in file \"{0}\"?");
            defaultTranslations.put("msg.quit.confirmsavenofile.text",
                    "Do you want to save changes?");
            defaultTranslations.put("title.percent", "%");
            defaultTranslations.put("title.thatare", "that are");
            defaultTranslations.put("title.done", "confirmed");
            defaultTranslations.put("msg.quit.confirmsave.title", "Save");
            defaultTranslations
                    .put(
                            "msg.postprocessloadedcr.acceptnewpassword.text",
                            "The loaded report contains a message indicating that\n"
                                    + "faction {0} has a new password set.\n"
                                    + "Do you want Magellan to use and save this new password for\n"
                                    + "exporting orders?");
            defaultTranslations.put(
                    "msg.postprocessloadedcr.acceptnewpassword.title",
                    "Accept new passwort?");
            defaultTranslations
                    .put(
                            "msg.postprocessloadedcr.missingpassword.text",
                            "None of the factions in the report has a password set.\n"
                                    + "Therefore, you cannot edit unit orders!\n"
                                    + "To edit them and avoid this message in the future open the\n"
                                    + "faction statistics dialog via the Extras menu and set a\n"
                                    + "password for your faction (The password will be stored on\n"
                                    + "your computer. It will not be included in any exported\n"
                                    + "computer report files).");
            defaultTranslations
                    .put(
                            "msg.startpatcher.text",
                            "A new version of Magellan is available. Do you want to exit\n"
                                    + "Magellan and launch the patcher to upgrade Magellan to that\n"
                                    + "latest version?");
            defaultTranslations
                    .put("msg.startpatcher.title", "Launch patcher?");
            defaultTranslations
                    .put("msg.patchererror.text",
                            "The patcher could not be started due to the following error:\n");
            defaultTranslations.put("msg.patchererror.title", "Error");
            defaultTranslations
                    .put(
                            "msg.newversion.text",
                            "A new version of Magellan is available. If the patcher was\n"
                                    + "installed (http://eressea.upb.de/magellan/download.shtml)\n"
                                    + "you could instantly upgrade Magellan to that latest version.");
            defaultTranslations.put("msg.newversion.title", "New version");
            defaultTranslations
                    .put(
                            "msg.versionunknown.text",
                            "Version information about Magellan could not be retrieved.\n"
                                    + "Make sure that you are online while Magellan tries to\n"
                                    + "determine the version information.");
            defaultTranslations.put("msg.versionunknown.title",
                    "Version unknown");
            defaultTranslations.put("menu.bookmarks.caption", "Bookmarks");
            defaultTranslations.put("menu.bookmarks.mnemonic", "b");
            defaultTranslations.put("menu.bookmarks.toggle.caption",
                    "Toggle bookmark");
            defaultTranslations.put("menu.bookmarks.forward.caption",
                    "Jump forward");
            defaultTranslations.put("menu.bookmarks.backward.caption",
                    "Jump backward");
            defaultTranslations.put("menu.bookmarks.show.caption", "Show list");
            defaultTranslations
                    .put("menu.bookmarks.clear.caption", "Clear all");
            defaultTranslations.put("msg.outofmemory.text","Java VM is running out of memory.");
            defaultTranslations.put("msg.outofmemory.title","Out Of Memory.");
        }

        return defaultTranslations;
    }

    // /////////////////////////////
    // REPAINT & VISIBILITY Code //
    // /////////////////////////////
    /**
     * @param v
     */
    public void setAllVisible(boolean v) {
        desktop.setAllVisible(v);
        resetExtendedState();
    }

    private void saveExtendedState() {
        getProperties().setProperty("Client.extendedState",
                String.valueOf(JVMUtilities.getExtendedState(this)));
    }

    private void resetExtendedState() {
        int state = new Integer(getProperties().getProperty(
                "Client.extendedState", "-1")).intValue();

        if (state != -1) {
            JVMUtilities.setExtendedState(this, state);
        }
    }

    // The repaint functions are overwritten to repaint the whole Magellan
    // Desktop. This is necessary because of the desktop mode FRAME.
    /**
     * @see java.awt.Component#repaint()
     */
    public void repaint() {
        super.repaint();

        if (desktop != null) {
            desktop.repaintAllComponents();
        }
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @param millis
     *            TODO: DOCUMENT ME!
     */
    public void repaint(int millis) {
        super.repaint(millis);

        if (desktop != null) {
            desktop.repaintAllComponents();
        }
    }

    // /////////////////
    // SHORTCUT Code //
    // /////////////////

    /**
     * Empty because registered directly.
     * 
     * @return TODO: DOCUMENT ME!
     */
    public Iterator getShortCuts() {
        return null; // not used - we register directly with a KeyStroke
    }

    /**
     * Repaints the client.
     * 
     * @param shortcut
     *            TODO: DOCUMENT ME!
     */
    public void shortCut(javax.swing.KeyStroke shortcut) {
        desktop.repaintAllComponents();
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @param stroke
     *            TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    public String getShortcutDescription(Object stroke) {
        return getString("shortcut.description");
    }

    /**
     * TODO: DOCUMENT ME!
     * 
     * @return TODO: DOCUMENT ME!
     */
    public String getListenerDescription() {
        return getString("shortcut.title");
    }

    /**
     * Returns an adapter for the preferences of this class.
     * 
     * @return TODO: DOCUMENT ME!
     */
    public PreferencesAdapter createPreferencesAdapter() {
        return new ClientPreferences(getProperties(), this);
    }

    // /////////////////
    // INNER Classes //
    // /////////////////
    private class MenuActionObserver implements PropertyChangeListener {
        protected JMenuItem item;

        /**
         * Creates a new MenuActionObserver object.
         * 
         * @param item
         *            TODO: DOCUMENT ME!
         * @param action
         *            TODO: DOCUMENT ME!
         */
        public MenuActionObserver(JMenuItem item, Action action) {
            this.item = item;
            action.addPropertyChangeListener(this);
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @param e
         *            TODO: DOCUMENT ME!
         */
        public void propertyChange(PropertyChangeEvent e) {
            if ((e.getPropertyName() != null)
                    && e.getPropertyName().equals("accelerator")) {
                item.setAccelerator((KeyStroke) e.getNewValue());
            }
        }
    }

    /**
     * Simple class to look for events changing the data.
     */
    protected class ReportObserver implements GameDataListener,
            OrderConfirmListener, TempUnitListener, UnitOrdersListener {
        protected boolean stateChanged = false;

        protected long lastClear;

        /**
         * Creates a new ReportObserver object.
         */
        public ReportObserver(EventDispatcher e) {

            e.addGameDataListener(this);
            e.addOrderConfirmListener(this);
            e.addTempUnitListener(this);
            e.addUnitOrdersListener(this);

            lastClear = -1;
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @return TODO: DOCUMENT ME!
         */
        public boolean isStateChanged() {
            return stateChanged;
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @param newState
         *            TODO: DOCUMENT ME!
         */
        public void setStateChanged(boolean newState) {
            stateChanged = newState;

            if (!newState) {
                lastClear = System.currentTimeMillis();
            }
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @param e
         *            TODO: DOCUMENT ME!
         */
        public void orderConfirmationChanged(OrderConfirmEvent e) {
            if ((getData() != null) && isShowingStatus()) {
                updateTitleCaption();
            }

            if (lastClear < e.getTimestamp()) {
                stateChanged = true;
            }
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @param e
         *            TODO: DOCUMENT ME!
         */
        public void tempUnitCreated(TempUnitEvent e) {
            if (lastClear < e.getTimestamp()) {
                stateChanged = true;
            }
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @param e
         *            TODO: DOCUMENT ME!
         */
        public void tempUnitDeleted(TempUnitEvent e) {
            if (lastClear < e.getTimestamp()) {
                stateChanged = true;
            }
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @param e
         *            TODO: DOCUMENT ME!
         */
        public void gameDataChanged(GameDataEvent e) {
            if ((lastClear < e.getTimestamp()) && (e.getGameData() != null)) {
                stateChanged = true;
            } else {
                stateChanged = false;
            }
        }

        /**
         * TODO: DOCUMENT ME!
         * 
         * @param e
         *            TODO: DOCUMENT ME!
         */
        public void unitOrdersChanged(UnitOrdersEvent e) {
            if (lastClear < e.getTimestamp()) {
                stateChanged = true;
            }
        }
    }
    
    public Collection getSelectedObjects(){
    	return overviewPanel.getSelectedObjects();
    }


}




See more files for this project here

Magellan-Client

The Magellan Client is basicly a GUI for the pbem game eressea but can be used for other pbems based on \"atlantis\" too.

Project homepage: http://sourceforge.net/projects/magellan-client
Programming language(s): Java
License: other

  actions/
    AbortAction.java
    AddCRAction.java
    AddSelectionAction.java
    ArmyStatsAction.java
    ChangeFactionConfirmationAction.java
    ConfirmAction.java
    ECheckAction.java
    EresseaOptionsAction.java
    ExpandSelectionAction.java
    ExportCRAction.java
    ExternalModuleAction.java
    FactionStatsAction.java
    FileHistoryAction.java
    FileSaveAction.java
    FileSaveAsAction.java
    FillSelectionAction.java
    FindAction.java
    FindPreviousUnconfirmedAction.java
    HelpAction.java
    InfoAction.java
    InvertSelectionAction.java
    IslandAction.java
    MapSaveAction.java
    MenuAction.java
    OpenCRAction.java
    OpenOrdersAction.java
    OpenSelectionAction.java
    OptionAction.java
    QuitAction.java
    RedoAction.java
    RepaintAction.java
    SaveOrdersAction.java
    SaveSelectionAction.java
    SelectAllAction.java
    SelectIslandsAction.java
    SelectNothingAction.java
    SetOriginAction.java
    TaskTableAction.java
    TileSetAction.java
    TipOfTheDayAction.java
    TradeOrganizerAction.java
    UnconfirmAction.java
    UndoAction.java
    VorlageAction.java
  desktop/
    DesktopEnvironment.java
    ExtendedShortcutListener.java
    FrameRectangle.java
    FrameTreeBuilder.java
    FrameTreeNode.java
    Initializable.java
    MagellanDesktop.java
    ShortcutListener.java
    SplitBuilder.java
  Client.java
  ClientPreferences.java
  EMapDetailsPanel.java
  EMapOverviewPanel.java
  FindDialog.java
  MagellanUndoManager.java
  SetOriginDialog.java