dw_fluffos_v2/
dw_fluffos_v2/fluffos-2.9-ds2.05/
dw_fluffos_v2/fluffos-2.9-ds2.05/ChangeLog.old/
dw_fluffos_v2/fluffos-2.9-ds2.05/Win32/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/simuls/
dw_fluffos_v2/fluffos-2.9-ds2.05/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/clone/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/command/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/data/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/etc/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/master/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/log/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/compiler/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/efuns/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/operators/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/u/
dw_fluffos_v2/fluffos-2.9-ds2.05/tmp/
dw_fluffos_v2/fluffos-2.9-ds2.05/windows/
dw_fluffos_v2/lib/
dw_fluffos_v2/lib/binaries/cmds/
dw_fluffos_v2/lib/binaries/cmds/creator/
dw_fluffos_v2/lib/binaries/cmds/living/
dw_fluffos_v2/lib/binaries/cmds/player/
dw_fluffos_v2/lib/binaries/d/admin/obj/
dw_fluffos_v2/lib/binaries/d/liaison/
dw_fluffos_v2/lib/binaries/global/virtual/
dw_fluffos_v2/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v2/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v2/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v2/lib/binaries/obj/misc/
dw_fluffos_v2/lib/binaries/obj/misc/buckets/
dw_fluffos_v2/lib/binaries/obj/monster/
dw_fluffos_v2/lib/binaries/obj/reactions/
dw_fluffos_v2/lib/binaries/obj/reagents/
dw_fluffos_v2/lib/binaries/secure/cmds/creator/
dw_fluffos_v2/lib/binaries/secure/master/
dw_fluffos_v2/lib/binaries/std/
dw_fluffos_v2/lib/binaries/std/dom/
dw_fluffos_v2/lib/binaries/std/effects/object/
dw_fluffos_v2/lib/binaries/std/guilds/
dw_fluffos_v2/lib/binaries/std/languages/
dw_fluffos_v2/lib/binaries/std/races/
dw_fluffos_v2/lib/binaries/std/room/
dw_fluffos_v2/lib/binaries/std/room/basic/
dw_fluffos_v2/lib/binaries/std/shops/
dw_fluffos_v2/lib/binaries/std/shops/inherit/
dw_fluffos_v2/lib/binaries/www/
dw_fluffos_v2/lib/cmds/guild-race/
dw_fluffos_v2/lib/cmds/guild-race/crafts/
dw_fluffos_v2/lib/cmds/guild-race/other/
dw_fluffos_v2/lib/cmds/playtester/
dw_fluffos_v2/lib/cmds/playtester/senior/
dw_fluffos_v2/lib/d/admin/
dw_fluffos_v2/lib/d/admin/log/
dw_fluffos_v2/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v2/lib/d/admin/meetings/
dw_fluffos_v2/lib/d/admin/obj/
dw_fluffos_v2/lib/d/admin/room/we_care/
dw_fluffos_v2/lib/d/admin/save/
dw_fluffos_v2/lib/d/dist/
dw_fluffos_v2/lib/d/dist/mtf/
dw_fluffos_v2/lib/d/dist/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/chars/
dw_fluffos_v2/lib/d/dist/pumpkin/desert/
dw_fluffos_v2/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v2/lib/d/dist/pumpkin/hospital/
dw_fluffos_v2/lib/d/dist/pumpkin/inherit/
dw_fluffos_v2/lib/d/dist/pumpkin/map/
dw_fluffos_v2/lib/d/dist/pumpkin/plain/
dw_fluffos_v2/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/save/
dw_fluffos_v2/lib/d/dist/pumpkin/squash/
dw_fluffos_v2/lib/d/dist/pumpkin/terrain/
dw_fluffos_v2/lib/d/dist/pumpkin/woods/
dw_fluffos_v2/lib/d/dist/start/
dw_fluffos_v2/lib/d/learning/TinyTown/buildings/
dw_fluffos_v2/lib/d/learning/TinyTown/map/
dw_fluffos_v2/lib/d/learning/TinyTown/roads/
dw_fluffos_v2/lib/d/learning/add_command/
dw_fluffos_v2/lib/d/learning/arms_and_weps/
dw_fluffos_v2/lib/d/learning/chars/
dw_fluffos_v2/lib/d/learning/cutnpaste/
dw_fluffos_v2/lib/d/learning/examples/npcs/
dw_fluffos_v2/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v2/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v2/lib/d/learning/functions/
dw_fluffos_v2/lib/d/learning/handlers/
dw_fluffos_v2/lib/d/learning/help_topics/npcs/
dw_fluffos_v2/lib/d/learning/help_topics/objects/
dw_fluffos_v2/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v2/lib/d/learning/items/
dw_fluffos_v2/lib/d/learning/save/
dw_fluffos_v2/lib/d/liaison/
dw_fluffos_v2/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v2/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v2/lib/db/
dw_fluffos_v2/lib/doc/
dw_fluffos_v2/lib/doc/creator/
dw_fluffos_v2/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v2/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v2/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v2/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v2/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v2/lib/doc/creator/autodoc/std/key/
dw_fluffos_v2/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v2/lib/doc/creator/autodoc/std/map/
dw_fluffos_v2/lib/doc/creator/autodoc/std/race/
dw_fluffos_v2/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v2/lib/doc/creator/files/
dw_fluffos_v2/lib/doc/creator/policy/
dw_fluffos_v2/lib/doc/creator/room/
dw_fluffos_v2/lib/doc/effects/
dw_fluffos_v2/lib/doc/ideas/
dw_fluffos_v2/lib/doc/known_command/
dw_fluffos_v2/lib/doc/lpc/basic_manual/
dw_fluffos_v2/lib/doc/lpc/intermediate/
dw_fluffos_v2/lib/doc/new/add_command/
dw_fluffos_v2/lib/doc/new/handlers/
dw_fluffos_v2/lib/doc/new/living/
dw_fluffos_v2/lib/doc/new/living/race/
dw_fluffos_v2/lib/doc/new/living/spells/
dw_fluffos_v2/lib/doc/new/player/
dw_fluffos_v2/lib/doc/new/room/guild/
dw_fluffos_v2/lib/doc/new/room/outside/
dw_fluffos_v2/lib/doc/new/room/storeroom/
dw_fluffos_v2/lib/doc/object/
dw_fluffos_v2/lib/doc/playtesters/
dw_fluffos_v2/lib/doc/policy/
dw_fluffos_v2/lib/doc/weapons/
dw_fluffos_v2/lib/global/handlers/
dw_fluffos_v2/lib/global/virtual/setup_compiler/
dw_fluffos_v2/lib/include/
dw_fluffos_v2/lib/include/cmds/
dw_fluffos_v2/lib/include/effects/
dw_fluffos_v2/lib/include/npc/
dw_fluffos_v2/lib/include/shops/
dw_fluffos_v2/lib/net/daemon/chars/
dw_fluffos_v2/lib/net/inherit/
dw_fluffos_v2/lib/net/intermud3/
dw_fluffos_v2/lib/net/intermud3/services/
dw_fluffos_v2/lib/net/obj/
dw_fluffos_v2/lib/net/save/
dw_fluffos_v2/lib/net/smnmp/
dw_fluffos_v2/lib/net/snmp/
dw_fluffos_v2/lib/obj/amulets/
dw_fluffos_v2/lib/obj/b_day/
dw_fluffos_v2/lib/obj/examples/
dw_fluffos_v2/lib/obj/food/alcohol/
dw_fluffos_v2/lib/obj/food/chocolates/
dw_fluffos_v2/lib/obj/food/fruits/
dw_fluffos_v2/lib/obj/food/meat/
dw_fluffos_v2/lib/obj/food/nuts/
dw_fluffos_v2/lib/obj/food/seafood/
dw_fluffos_v2/lib/obj/food/vegetables/
dw_fluffos_v2/lib/obj/fungi/
dw_fluffos_v2/lib/obj/furnitures/artwork/
dw_fluffos_v2/lib/obj/furnitures/bathroom/
dw_fluffos_v2/lib/obj/furnitures/beds/
dw_fluffos_v2/lib/obj/furnitures/cabinets/
dw_fluffos_v2/lib/obj/furnitures/chairs/
dw_fluffos_v2/lib/obj/furnitures/chests/
dw_fluffos_v2/lib/obj/furnitures/clocks/
dw_fluffos_v2/lib/obj/furnitures/crockery/
dw_fluffos_v2/lib/obj/furnitures/cupboards/
dw_fluffos_v2/lib/obj/furnitures/cushions/
dw_fluffos_v2/lib/obj/furnitures/fake_plants/
dw_fluffos_v2/lib/obj/furnitures/lamps/
dw_fluffos_v2/lib/obj/furnitures/mirrors/
dw_fluffos_v2/lib/obj/furnitures/outdoor/
dw_fluffos_v2/lib/obj/furnitures/safes/
dw_fluffos_v2/lib/obj/furnitures/shelves/
dw_fluffos_v2/lib/obj/furnitures/sideboards/
dw_fluffos_v2/lib/obj/furnitures/sofas/
dw_fluffos_v2/lib/obj/furnitures/stoves/
dw_fluffos_v2/lib/obj/furnitures/tables/
dw_fluffos_v2/lib/obj/furnitures/wardrobes/
dw_fluffos_v2/lib/obj/handlers/
dw_fluffos_v2/lib/obj/handlers/autodoc/
dw_fluffos_v2/lib/obj/jewellery/anklets/
dw_fluffos_v2/lib/obj/jewellery/bracelets/
dw_fluffos_v2/lib/obj/jewellery/earrings/
dw_fluffos_v2/lib/obj/jewellery/misc/
dw_fluffos_v2/lib/obj/jewellery/necklaces/
dw_fluffos_v2/lib/obj/jewellery/rings/
dw_fluffos_v2/lib/obj/media/
dw_fluffos_v2/lib/obj/misc/buckets/
dw_fluffos_v2/lib/obj/misc/jars/
dw_fluffos_v2/lib/obj/misc/papers/
dw_fluffos_v2/lib/obj/misc/player_shop/
dw_fluffos_v2/lib/obj/misc/shops/
dw_fluffos_v2/lib/obj/misc/traps/
dw_fluffos_v2/lib/obj/monster/
dw_fluffos_v2/lib/obj/monster/godmother/
dw_fluffos_v2/lib/obj/monster/transport/
dw_fluffos_v2/lib/obj/plants/inherit/
dw_fluffos_v2/lib/obj/potions/
dw_fluffos_v2/lib/open/boards/
dw_fluffos_v2/lib/save/autodoc/
dw_fluffos_v2/lib/save/bank_accounts/
dw_fluffos_v2/lib/save/boards/frog/
dw_fluffos_v2/lib/save/books/bed_catalog/
dw_fluffos_v2/lib/save/creators/
dw_fluffos_v2/lib/save/mail/
dw_fluffos_v2/lib/save/mail/p/
dw_fluffos_v2/lib/save/soul/data/
dw_fluffos_v2/lib/save/tasks/
dw_fluffos_v2/lib/save/vaults/
dw_fluffos_v2/lib/secure/cmds/lord/
dw_fluffos_v2/lib/secure/config/
dw_fluffos_v2/lib/secure/items/
dw_fluffos_v2/lib/secure/player/
dw_fluffos_v2/lib/soul/
dw_fluffos_v2/lib/soul/i/
dw_fluffos_v2/lib/soul/j/
dw_fluffos_v2/lib/soul/k/
dw_fluffos_v2/lib/soul/o/
dw_fluffos_v2/lib/soul/q/
dw_fluffos_v2/lib/soul/to_approve/
dw_fluffos_v2/lib/soul/u/
dw_fluffos_v2/lib/soul/v/
dw_fluffos_v2/lib/soul/wish_list/
dw_fluffos_v2/lib/soul/y/
dw_fluffos_v2/lib/soul/z/
dw_fluffos_v2/lib/std/creator/
dw_fluffos_v2/lib/std/effects/
dw_fluffos_v2/lib/std/effects/attached/
dw_fluffos_v2/lib/std/effects/external/
dw_fluffos_v2/lib/std/effects/fighting/
dw_fluffos_v2/lib/std/effects/other/
dw_fluffos_v2/lib/std/environ/
dw_fluffos_v2/lib/std/guilds/
dw_fluffos_v2/lib/std/hospital/
dw_fluffos_v2/lib/std/house/
dw_fluffos_v2/lib/std/house/onebedhouse/
dw_fluffos_v2/lib/std/house/onebedhut/
dw_fluffos_v2/lib/std/house/tworoomflat/
dw_fluffos_v2/lib/std/languages/
dw_fluffos_v2/lib/std/liquids/
dw_fluffos_v2/lib/std/nationality/
dw_fluffos_v2/lib/std/nationality/accents/
dw_fluffos_v2/lib/std/nationality/accents/national/
dw_fluffos_v2/lib/std/nationality/accents/regional/
dw_fluffos_v2/lib/std/npc/goals/
dw_fluffos_v2/lib/std/npc/goals/basic/
dw_fluffos_v2/lib/std/npc/goals/misc/
dw_fluffos_v2/lib/std/npc/inherit/
dw_fluffos_v2/lib/std/npc/plans/
dw_fluffos_v2/lib/std/npc/plans/basic/
dw_fluffos_v2/lib/std/outsides/
dw_fluffos_v2/lib/std/races/shadows/
dw_fluffos_v2/lib/std/room/basic/topography/
dw_fluffos_v2/lib/std/room/controller/
dw_fluffos_v2/lib/std/room/controller/topography/
dw_fluffos_v2/lib/std/room/furniture/games/
dw_fluffos_v2/lib/std/room/furniture/inherit/
dw_fluffos_v2/lib/std/room/inherit/carriage/
dw_fluffos_v2/lib/std/room/inherit/topography/
dw_fluffos_v2/lib/std/room/punishments/
dw_fluffos_v2/lib/std/room/topography/area/
dw_fluffos_v2/lib/std/room/topography/iroom/
dw_fluffos_v2/lib/std/room/topography/milestone/
dw_fluffos_v2/lib/std/shadows/
dw_fluffos_v2/lib/std/shadows/attached/
dw_fluffos_v2/lib/std/shadows/curses/
dw_fluffos_v2/lib/std/shadows/disease/
dw_fluffos_v2/lib/std/shadows/fighting/
dw_fluffos_v2/lib/std/shadows/room/
dw_fluffos_v2/lib/std/shops/controllers/
dw_fluffos_v2/lib/std/shops/objs/
dw_fluffos_v2/lib/std/shops/player_shop/
dw_fluffos_v2/lib/std/shops/player_shop/office_code/
dw_fluffos_v2/lib/std/socket/
dw_fluffos_v2/lib/www/
dw_fluffos_v2/lib/www/external/autodoc/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v2/lib/www/external/java/telnet/examples/
dw_fluffos_v2/lib/www/external/java/telnet/tools/
dw_fluffos_v2/lib/www/pics/
dw_fluffos_v2/lib/www/secure/creator/
dw_fluffos_v2/lib/www/secure/editors/
dw_fluffos_v2/lib/www/secure/survey_results/
dw_fluffos_v2/win32/
package mapmaker;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.undo.*;
import javax.swing.event.*;
import javax.swing.*;
import java.util.*;

import mapmaker.event.*;

import com.sun.glf.goodies.RadialGradientPaint;

public class MapView extends JPanel
{
    // The modes this view can be in.
    public final static int MODE_SELECT_AND_DRAG = 0;
    private SelectAdapter mSelectAdapter = new SelectAdapter(this);

    // The model we're wrapping.
    private MapModel mMapModel;

    // The rectangle dragged out for selecting locations.
    private Rectangle mSelectionRectangle;

    // Store possible link ups.
    private LinkUpStore mLinkUpStore = new LinkUpStore();

    // The currently selected link up group.
    private int mCurrentLinkUpGroup;

    // The selected map locations.
    private MapSelection mMapSelection = new MapSelection();

    // Probably shouldn't need these and do everything with listeners...
    public MainFrame mMainFrame;
    private InheritBean mInheritBean;
    private ExitBean mExitBean;

    // Undo/redo support.
    public UndoManager mUndoManager = new UndoManager()
    {
        public boolean addEdit(UndoableEdit aUndoableEdit)
        {
            boolean retValue = super.addEdit(aUndoableEdit);
            mMainFrame.refreshUndoRedo();
            return retValue;
        }

        public void undo()
        {
            super.undo();
            mMainFrame.refreshUndoRedo();
        }

        public void redo()
        {
            super.redo();
            mMainFrame.refreshUndoRedo();
        }
    };
    public CompoundEdit mCompoundEdit = new CompoundEdit();

    Zone mDisplayZone = null;
    Linker mDisplayLinker = null;

    public MapView(MapModel aMapModel, MainFrame aMainFrame,
        InheritBean aInheritBean, ExitBean aExitBean)
    {
        mMapModel = aMapModel;

        setMapSize(mMapModel.getMapSize());

        mInheritBean = aInheritBean;
        mMainFrame = aMainFrame;
        mExitBean = aExitBean;

        setBackground(Color.white);

        this.addMouseListener(mSelectAdapter);
        this.addMouseMotionListener(mSelectAdapter);
    }

    public void setMapSize(Dimension aMapSize)
    {
        setSize(aMapSize);
        setPreferredSize(aMapSize);
        mMapModel.setMapSize(aMapSize);
    }

    public MapSelection getMapSelection()
    {
        return mMapSelection;
    }

    public void setDisplayZone(Zone aZone)
    {
        mDisplayZone = aZone;
    }

    public void setDisplayLinker(Linker aLinker)
    {
        mDisplayLinker = aLinker;
    }

    public void setMode(int aMode)
    {
        this.removeMouseListener(mSelectAdapter);
        this.removeMouseMotionListener(mSelectAdapter);

        switch (aMode)
        {
            case MODE_SELECT_AND_DRAG :
                 this.addMouseListener(mSelectAdapter);
                 this.addMouseMotionListener(mSelectAdapter);
                 break;
        }
    }

    public MapModel getMapModel()
    {
        return mMapModel;
    }

    public void setSelectionRectangle(Rectangle aSelectionRectangle)
    {
        mSelectionRectangle = aSelectionRectangle;
    }

    /**
     * This method performs an edit and records it in the current compound edit.
     *
     * @param aEdit the edit to perform and record
     */
    public void doEdit(UndoableEdit aEdit)
    {
        aEdit.redo();
        mCompoundEdit.addEdit(aEdit);
    }

    public void addCompoundEdit()
    {
        mCompoundEdit.end();
        mUndoManager.addEdit(mCompoundEdit);
        mCompoundEdit = new CompoundEdit();
    }

    public void clearExits(MapLocation aLocation)
    {
        aLocation.clearExits();
    }

    public void clearExitsToUnselected(MapLocation aLocation)
    {
        Vector exits = aLocation.getExits();

        for (Enumeration e = exits.elements(); e.hasMoreElements();)
        {
            Exit exit = (Exit)e.nextElement();

            // Check if the exit has a destination
            if (exit.isDummy())
            {
                continue;
            }

            MapLocation to = exit.getTo();

            if (!mMapSelection.isSelected(to))
            {
                RemoveExitEdit fromEdit = new RemoveExitEdit(aLocation, exit);
                RemoveExitEdit toEdit = new RemoveExitEdit(to, exit.getOpposite());

                doEdit(fromEdit);
                doEdit(toEdit);
            }
        }
    }

    // Works out what link ups are available to a location.
    public Vector getLinkUpsForLocation(MapLocation aLocation)
    {
        Vector linkUps = new Vector();

        Rectangle linkUpRectangle = aLocation.getLinkUpRectangle();

        Vector mapLocations = mMapModel.getMapLocations();
        int sizeOfMapLocations = mapLocations.size();

        // Store the location's cardinal handles.
        Rectangle[] ourHandles = aLocation.getHandles();

        for (int i = 0; i < sizeOfMapLocations; i++)
        {
            MapLocation location = (MapLocation)mapLocations.elementAt(i);

            // First check if the other location is within linkup range.
            if (linkUpRectangle.intersects(location.getLinkUpRectangle()))
            {
                // Don't check with self.
                if (location == aLocation)
                {
                    continue;
                }

                // Don't check with other selected locations.
                if (mMapSelection.isSelected(location))
                {
                    continue;
                }

                // Now check if any opposing cardinal points are in range.
                Rectangle[] theirHandles = location.getHandles();
                for (int j = 0; j < 8; j++)
                {
                    if (ourHandles[j].intersects(theirHandles[Direction.getOppoisite(j)]))
                    {
                        // Add the link up.
                        linkUps.addElement(new LinkUp(aLocation, location, j));
                    }
                }
            }
        }

        return linkUps;
    }

    public void refreshLinkUpStore()
    {
        mLinkUpStore = this.getLinkUpGroupsForSelection();
    }

    // Works out what link up alternatives are available to a selection.
    public LinkUpStore getLinkUpGroupsForSelection()
    {
        Vector linkUps = new Vector();

        Vector selectedLocations = mMapSelection.getSelected();
        int sizeOfSelectedLocations = selectedLocations.size();

        // Gather together all the link ups.
        for (int i = 0; i < sizeOfSelectedLocations; i++)
        {
            MapLocation location = (MapLocation)selectedLocations.elementAt(i);

            linkUps.addAll(this.getLinkUpsForLocation(location));
        }

        // Sort the link ups into groups based on the translation they require
        // to be peformed.

        LinkUpStore linkUpStore = new LinkUpStore();
        int sizeOfLinkUps = linkUps.size();

        for (int i = 0; i < sizeOfLinkUps; i++)
        {
            LinkUp linkUp = (LinkUp)linkUps.elementAt(i);

            linkUpStore.addLinkUp(linkUp);
        }

        return linkUpStore;
    }

    public void doLinkUps()
    {
        Vector linkUps = mLinkUpStore.getGroupAt(mCurrentLinkUpGroup);

        if (linkUps == null)
        {
            return;
        }

        int sizeOfLinkUps = linkUps.size();

        // Locations which have been moved to link up.
        Vector done = new Vector();
        Point translation = ((LinkUp)linkUps.elementAt(0)).mTranslation;

        for (int i = 0; i < sizeOfLinkUps; i++)
        {
            LinkUp linkUp = (LinkUp)linkUps.elementAt(i);

            connectExits(linkUp);

            if (!done.contains(linkUp.mSelectedLocation))
            {
                performLinkUp(linkUp);
            }

            done.addElement(linkUp.mSelectedLocation);
        }

        // Move the selected locations with no link ups with the link ups.
        Vector selectedLocations = mMapSelection.getSelected();
        int sizeOfSelected = selectedLocations.size();

        for (int i = 0; i < sizeOfSelected; i++)
        {
            MapLocation mapLocation = (MapLocation)selectedLocations.elementAt(i);

            if (!done.contains(mapLocation))
            {
                setLocation(mapLocation, mapLocation.getX() - translation.x,
                    mapLocation.getY() - translation.y);
            }

            done.addElement(mapLocation);
        }

        mExitBean.readSelection();
    }

    public void performLinkUp(LinkUp aLinkUp)
    {
        int translateX = aLinkUp.mTranslation.x;
        int translateY = aLinkUp.mTranslation.y;

        setLocation(aLinkUp.mSelectedLocation, aLinkUp.mSelectedX - translateX,
            aLinkUp.mSelectedY - translateY);
    }

    public void setLocation(MapLocation aLocation, int aX, int aY)
    {
        mCompoundEdit.addEdit(new TranslateLocationEdit(aLocation,
            new Point(aLocation.mTempX, aLocation.mTempY),
            new Point(aX, aY)));

        aLocation.setX(aX);
        aLocation.setY(aY);
    }
/*
    public void setLocation(MapLocation aLocation, int aX, int aY)
    {
        TranslateLocationEdit edit = new TranslateLocationEdit(aLocation,
            new Point(aLocation.mTempX, aLocation.mTempY), new Point(aX, aY));

        doEdit(edit);
    }
*/
    public void connectExits(LinkUp aLinkUp)
    {
        Exit to = new Exit(aLinkUp.mDirection, aLinkUp.mSelectedLocation,
            aLinkUp.mUnselectedLocation, "path");
        Exit from = new Exit(Direction.getOppoisite(aLinkUp.mDirection),
            aLinkUp.mUnselectedLocation, aLinkUp.mSelectedLocation, "path");

        AddExitEdit toEdit = new AddExitEdit(aLinkUp.mSelectedLocation, to);
        AddExitEdit fromEdit = new AddExitEdit(aLinkUp.mUnselectedLocation, from);

        doEdit(toEdit);
        doEdit(fromEdit);
    }

    public void paintComponent(Graphics aGraphics)
    {
        super.paintComponent(aGraphics);

        Graphics2D g = (Graphics2D)aGraphics;

        Vector mapLocations = mMapModel.getMapLocations();
        int sizeOfMapLocations = mapLocations.size();

        // Paint zone
        if (mDisplayZone != null)
        {
            // Paint the zone of the map locations.
            for (int i = 0; i < sizeOfMapLocations; i++)
            {
                MapLocation currentLocation = (MapLocation)mapLocations.elementAt(i);

                if (currentLocation.isInZone(mDisplayZone))
                {
                    RadialGradientPaint paint = new RadialGradientPaint(
                        currentLocation.getZoneRectangle(), mDisplayZone.getColor(),
                        new Color(255, 255, 255, 0));
                    g.setPaint(paint);
                    g.fill(currentLocation.getZoneRectangle());
                }
            }
        }

        // Paint linker
        if (mDisplayLinker != null)
        {
            // Paint the zone of the map locations.
            for (int i = 0; i < sizeOfMapLocations; i++)
            {
                MapLocation currentLocation = (MapLocation)mapLocations.elementAt(i);

                if (currentLocation.isInLinker(mDisplayLinker))
                {
                    g.setPaint(mDisplayLinker.getColor());
                    g.fill(currentLocation.getLinkerRectangle());
                }
            }
        }

        // Paint the background of the map locations.
        for (int i = 0; i < sizeOfMapLocations; i++)
        {
            MapLocation currentLocation = (MapLocation)mapLocations.elementAt(i);

            currentLocation.renderBackground(g);
        }

        // Paint the foreground of the map locations.
        for (int i = 0; i < sizeOfMapLocations; i++)
        {
            MapLocation currentLocation = (MapLocation)mapLocations.elementAt(i);

            currentLocation.renderForeground(g);
/*
            g.setPaint(Color.blue);
            g.setStroke(new BasicStroke(1f));
            g.draw(currentLocation.getRectangle());
*/
        }

        mMapSelection.render(g);

        // Draw preview link ups.
        Vector linkUps = mLinkUpStore.getGroupAt(mCurrentLinkUpGroup);
        if (linkUps != null)
        {
            int sizeOfLinkUps = linkUps.size();

            for (int i = 0; i < sizeOfLinkUps; i++)
            {
                LinkUp linkUp = (LinkUp)linkUps.elementAt(i);

                Point dragPoint = linkUp.mSelectedLocation.getCardinals()
                      [linkUp.mDirection];
                Point linkPoint = (Point)linkUp.mUnselectedLocation.getCardinals()
                      [Direction.getOppoisite(linkUp.mDirection)].clone();

                linkPoint.translate(-2, -2);

                g.setPaint(Color.black);

                g.fill(new Rectangle(linkPoint, new Dimension(4, 4)));
            }
        }

        // Draw the selection rectangle.
        if (mSelectionRectangle != null)
        {
            g.setPaint(Color.gray);
            g.setStroke(new BasicStroke(1.f, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_BEVEL, 8.f, new float[]{ 2.f, 2.f }, 0.f));
            g.draw(mSelectionRectangle);
        }
    }

    //==========================================================================
    // Cycles through link up groups so link ups can be selected.
    // To be called from the key listener when locations are being dragged.
    //==========================================================================

    public void cycleLinkUpGroup()
    {
        mCurrentLinkUpGroup++;

        if (mCurrentLinkUpGroup >= mLinkUpStore.getNoOfGroups())
        {
            mCurrentLinkUpGroup = 0;
        }

        this.repaint();
    }

    public void deleteSelectedLocations()
    {
        Vector selectedLocations = mMapSelection.getSelected();
        int sizeOfSelectedLocations = selectedLocations.size();

        // Gather together all the link ups.
        for (int i = 0; i < sizeOfSelectedLocations; i++)
        {
            MapLocation location = (MapLocation)selectedLocations.elementAt(i);

            RemoveLocationEdit edit = new RemoveLocationEdit(location);
            doEdit(edit);
        }

        addCompoundEdit();

        mMapSelection.clearSelected();

        this.repaint();
    }

    //==========================================================================
    // Store selected map locations in here.
    //==========================================================================

    class MapSelection
    {
        private Color mSelectionColor = Color.red;
        private Stroke mSelectionStroke = new BasicStroke(1f);
        private Vector mSelectedMapLocations = new Vector();

        public void setSelected(Vector aSelectedMapLocations)
        {
            mSelectedMapLocations = aSelectedMapLocations;

            fireSelectionChanged();
        }

        public void addSelected(MapLocation aMapLocation)
        {
            if (!mSelectedMapLocations.contains(aMapLocation))
            {
                mSelectedMapLocations.addElement(aMapLocation);
                fireSelectionChanged();
            }
        }

        public void removeSelected(MapLocation aMapLocation)
        {
            mSelectedMapLocations.removeElement(aMapLocation);

            fireSelectionChanged();
        }

        public boolean isSelected(MapLocation aMapLocation)
        {
            return mSelectedMapLocations.contains(aMapLocation);
        }

        public Vector getSelected()
        {
            return mSelectedMapLocations;
        }

        public void clearSelected()
        {
            mSelectedMapLocations = new Vector();

            fireSelectionChanged();
        }

        public void addMapSelectionListener(MapSelectionListener listener)
        {
            listenerList.add(MapSelectionListener.class, listener);
        }

        public void removeMapSelectionListener(MapSelectionListener listener)
        {
            listenerList.remove(MapSelectionListener.class, listener);
        }

        protected void fireSelectionChanged()
        {
            Object[] listeners = listenerList.getListenerList();

            MapSelectionEvent mapSelectionEvent = new MapSelectionEvent(this,
                mSelectedMapLocations);

            for (int i = listeners.length - 2; i >= 0; i -= 2)
            {
                if (listeners[i] == MapSelectionListener.class)
                {
                    ((MapSelectionListener)listeners[i + 1]).mapSelectionChanged(mapSelectionEvent);
                }
            }
        }

        public void render(Graphics2D g)
        {
            int sizeOfSelectedMapLocations = mSelectedMapLocations.size();

            for (int i = 0; i < sizeOfSelectedMapLocations; i++)
            {
                MapLocation currentLocation = (MapLocation)mSelectedMapLocations.elementAt(i);
                g.setPaint(mSelectionColor);
                g.setStroke(mSelectionStroke);
                g.draw(currentLocation.getRectangle());
            }
        }
    }

    //==========================================================================
    // Class for storing groups of link ups. These link ups are grouped by the
    // transform which they need to go through to 'link up' with other
    // locations.
    //==========================================================================

    class LinkUpStore
    {
        Hashtable linkUpTable;

        public LinkUpStore()
        {
            linkUpTable = new Hashtable();
        }

        public void addLinkUp(LinkUp aLinkUp)
        {
            Point key = aLinkUp.mTranslation;

            if (!linkUpTable.containsKey(key))
            {
                linkUpTable.put(key, new Vector());
            }

            Vector group = (Vector)linkUpTable.get(key);

            if (!group.contains(aLinkUp))
            {
                group.addElement(aLinkUp);
            }
        }

        public int getNoOfGroups()
        {
            int count = 0;

            for (Enumeration e = linkUpTable.keys(); e.hasMoreElements(); count++)
            {
                e.nextElement();
            }

            return count;
        }

        public Vector getGroupAt(int aIndex)
        {
            int count = 0;

            for (Enumeration e = linkUpTable.keys(); e.hasMoreElements(); count++)
            {
                Point key = (Point)e.nextElement();

                if (count == aIndex)
                {
                    return (Vector)linkUpTable.get(key);
                }
            }

            return null;
        }
    }

    //==========================================================================
    //
    //==========================================================================

    class SelectAdapter extends MouseInputAdapter
    {
        private int mStartX;
        private int mStartY;
        private boolean mLocationPressed;
        private MapView mMapView;

        public SelectAdapter(MapView aMapView)
        {
            mMapView = aMapView;
        }

        public void mouseClicked(MouseEvent e)
        {
            MapLocation location = mMapModel.getLocationAt(e.getX(), e.getY());

            // If they didn't click a location, deselect all selected locations.
            if (location == null)
            {
                mMapSelection.clearSelected();

                if (e.getClickCount() == 2)
                {
                    // Get the current inherit room.
                    RoomProperties inherit = mInheritBean.getSelectedInherit();

                    // Make sure an inherit room is selected.
                    if (inherit == null)
                    {
                        JOptionPane.showMessageDialog(mMainFrame,
                            "Before you add a room to the map, you "
                            + "need to select an inheritable from the "
                            + "Inherit Window.\n",
                            "Warning", JOptionPane.WARNING_MESSAGE);
                        return;
                    }

                    MapLocation newLocation = new MapLocation(inherit,
                        e.getX(), e.getY());

                    doEdit(new AddLocationEdit(newLocation));

                    addCompoundEdit();
                }
            }
            else if (e.getClickCount() == 2)
            {
                RoomDialog.showEditDialog(mMainFrame, location);
            }

            repaint();
        }

        public void mousePressed(MouseEvent e)
        {
            // Needed for moving locations relatively when dragging.
            mStartX = e.getX();
            mStartY = e.getY();

            MapLocation location = mMapModel.getLocationAt(e.getX(), e.getY());

            // Needed for determining whether to start dragging.
            mLocationPressed = (location != null);

            if (location != null)
            {
                if (!mMapSelection.isSelected(location))
                {
                    if (!e.isShiftDown())
                    {
                        Vector selectedLocations = mMapSelection.getSelected();
                        int sizeOfSelectedLocations = selectedLocations.size();

                        mMapSelection.clearSelected();

                        mMapSelection.addSelected(location);
                    }
                    else
                    {
                        mMapSelection.addSelected(location);
                    }
                }
                else if (e.isShiftDown())
                {
                    mMapSelection.removeSelected(location);
                }
            }

            repaint();
        }

        public void mouseReleased(MouseEvent e)
        {
            Vector mapLocations = mMapModel.getMapLocations();
            int sizeOfMapLocations = mapLocations.size();

            for (int i = 0; i < sizeOfMapLocations; i++)
            {
                MapLocation location = (MapLocation)mapLocations.elementAt(i);

                location.mOldX = location.getX();
                location.mOldY = location.getY();
            }

            mMapView.setSelectionRectangle(null);
            mLocationPressed = false;

            // Move to currently selected link up group.
            mMapView.doLinkUps();

            // Clear link ups.
            mLinkUpStore = new LinkUpStore();

            addCompoundEdit();

            mMapView.repaint();
        }

        public void mouseDragged(MouseEvent e)
        {
            Vector mapLocations = mMapModel.getMapLocations();

            MapLocation location = mMapModel.getLocationAt(e.getX(), e.getY());

            if (!mLocationPressed)
            {
                Rectangle selection = new Rectangle(
                    Math.min(mStartX, e.getX()),
                    Math.min(mStartY, e.getY()),
                    Math.abs(mStartX - e.getX()),
                    Math.abs(mStartY - e.getY()));

                mMapView.setSelectionRectangle(selection);

                int sizeOfMapLocations = mapLocations.size();

                Vector selected = new Vector();

                for (int i = 0; i < sizeOfMapLocations; i++)
                {
                    MapLocation loc = (MapLocation)mapLocations.elementAt(i);

                    // Maybe this should be contains?
                    if (selection.intersects(loc.getRectangle()))
                    {
                        selected.addElement(loc);
                    }
                }
                if (e.isShiftDown())
                {
                    selected.addAll(mMapSelection.getSelected());
                }
                mMapSelection.setSelected(selected);
            }
            else
            {
                // Move selected locations with mouse pointer.
                int xShift = e.getX() - mStartX;
                int yShift = e.getY() - mStartY;
                mMapView.setSelectionRectangle(null);
                int sizeOfMapLocations = mapLocations.size();

                for (int i = 0; i < sizeOfMapLocations; i++)
                {
                    MapLocation loc = (MapLocation)mapLocations.elementAt(i);

                    if (mMapSelection.isSelected(loc))
                    {
                        loc.mTempX = loc.getX();
                        loc.mTempY = loc.getY();

                        setLocation(loc, loc.mOldX + xShift,
                            loc.mOldY + yShift);
                    }
                }

                refreshLinkUpStore();

                // Discard exits to unselected rooms.
                Vector selectedMapLocations = mMapSelection.getSelected();
                int sizeOfSelectedMapLocation = selectedMapLocations.size();

                for (int k = 0; k < sizeOfSelectedMapLocation; k++)
                {
                    MapLocation current = (MapLocation)selectedMapLocations.elementAt(k);
                    mMapView.clearExitsToUnselected(current);
                }
            }

            if (mMapView.mLinkUpStore.getNoOfGroups() > 1)
            {
                // Send message to status bar indicating choice of link ups.
            }

            mExitBean.readSelection();
            mMapView.repaint();
        }
    }

    //==========================================================================
    //
    //==========================================================================

    class RemoveLocationEdit extends AbstractUndoableEdit
    {
        private MapLocation mMapLocation;
        private Point mCoords;

        public RemoveLocationEdit(MapLocation aMapLocation)
        {
            mMapLocation = aMapLocation;
        }

        public void undo()
        {
            mMapModel.addMapLocation(mMapLocation);
        }

        public void redo()
        {
            mMapSelection.removeSelected(mMapLocation);
            mMapModel.removeMapLocation(mMapLocation);
        }

        public String getUndoPresentationName()
        {
            return "Undo Remove Location";
        }

        public String getRedoPresentationName()
        {
            return "Redo Remove Location";
        }
    }

    //==========================================================================
    //
    //==========================================================================

    class AddLocationEdit extends AbstractUndoableEdit
    {
        private MapLocation mMapLocation;
        private Point mCoords;

        public AddLocationEdit(MapLocation aMapLocation)
        {
            mMapLocation = aMapLocation;
        }

        public void undo()
        {
            mMapSelection.removeSelected(mMapLocation);
            mMapModel.removeMapLocation(mMapLocation);
        }

        public void redo()
        {
            mMapModel.addMapLocation(mMapLocation);
        }

        public String getUndoPresentationName()
        {
            return "Undo Add Location";
        }

        public String getRedoPresentationName()
        {
            return "Redo Add Location";
        }
    }

    //==========================================================================
    //
    //==========================================================================

    class TranslateLocationEdit extends AbstractUndoableEdit
    {
        private MapLocation mMapLocation;
        private Point mOldCoords;
        private Point mNewCoords;

        public TranslateLocationEdit(MapLocation aMapLocation,
            Point aOldCoords, Point aNewCoords)
        {
            super();

            mMapLocation = aMapLocation;
            mOldCoords = aOldCoords;
            mNewCoords = aNewCoords;
        }

        public void redo()
        {
            mMapLocation.setX(mNewCoords.x);
            mMapLocation.setY(mNewCoords.y);

            mMapLocation.mOldX = mNewCoords.x;
            mMapLocation.mOldY = mNewCoords.y;
        }

        public void undo()
        {
            mMapLocation.setX(mOldCoords.x);
            mMapLocation.setY(mOldCoords.y);

            mMapLocation.mOldX = mOldCoords.x;
            mMapLocation.mOldY = mOldCoords.y;
        }

        public String getUndoPresentationName()
        {
            return "Undo Move Location";
        }

        public String getRedoPresentationName()
        {
            return "Redo Move Location";
        }
    }
}