Migrated the bulk of the scene editor into Stage. It's just lovely.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3442 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2005-03-30 02:39:20 +00:00
parent 80609dbe81
commit 8135af3e4c
28 changed files with 5196 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,192 @@
//
// $Id: DirectionButton.java 5784 2003-01-08 04:17:40Z mdb $
package com.threerings.stage.tools.editor;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractButton;
import javax.swing.DefaultButtonModel;
import com.threerings.media.image.ColorUtil;
import com.threerings.util.DirectionCodes;
/**
* A button that allows for selection from the compass directions.
*/
public class DirectionButton extends AbstractButton
{
/**
* Construct a 41x41 DirectionButton.
*/
public DirectionButton ()
{
this(41);
}
/**
* Construct a DirectionButton with the specified preferred diameter.
*/
public DirectionButton (int preferredDiameter)
{
_prefdia = preferredDiameter;
configSize(_prefdia);
setModel(new DefaultButtonModel());
addMouseListener(new MouseAdapter() {
public void mousePressed (MouseEvent event)
{
if (isEnabled()) {
_armed = getDirection(event.getX(), event.getY());
repaint();
}
}
public void mouseReleased (MouseEvent event)
{
if ((_armed != -1) &&
isEnabled() &&
(_armed == getDirection(event.getX(), event.getY())) &&
(_direction != _armed)) {
_direction = _armed;
fireStateChanged();
}
_armed = -1;
repaint();
}
});
}
/**
* Paint this component and the selected direction,
* dimmed if we're inactive.
*/
public void paintComponent (Graphics g)
{
super.paintComponent(g);
// draw the selected direction circle
g.setColor(isEnabled() ? Color.red
: ColorUtil.blend(Color.red, getBackground()));
g.fillOval(_coords[_direction][0], _coords[_direction][1],
_cdia, _cdia);
if (_armed != -1) {
g.setColor(ColorUtil.blend(Color.black, getBackground()));
g.fillOval(_coords[_armed][0], _coords[_armed][1],
_cdia, _cdia);
}
// draw a circle around the various direction circles
g.setColor(isEnabled() ? Color.black
: ColorUtil.blend(Color.black, getBackground()));
g.drawOval(0, 0, _dia, _dia);
// draw each direction circle
for (int ii=DirectionCodes.SOUTHWEST;
ii < DirectionCodes.DIRECTION_COUNT;
ii++) {
g.drawOval(_coords[ii][0], _coords[ii][1], _cdia, _cdia);
}
}
/**
* Get the direction that is currently selected.
*/
public int getDirection ()
{
return _direction;
}
/**
* Set the currently displayed direction
*/
public void setDirection (int direction)
{
if (direction != _direction) {
_direction = direction;
fireStateChanged();
repaint();
}
}
/**
* Get the direction specified by the mouse coordinates
*/
protected int getDirection (int x, int y)
{
for (int ii=0; ii < DirectionCodes.DIRECTION_COUNT; ii++) {
if ((x > _coords[ii][0]) && (x < (_coords[ii][0] + _cdia)) &&
(y > _coords[ii][1]) && (y < (_coords[ii][1] + _cdia))) {
return ii;
}
}
return -1;
}
// documentation inherited
public void setSize (Dimension d)
{
super.setSize(d);
configSize(Math.min(d.width, d.height));
}
/**
* Reconfigure the way we look for a new size
*/
protected void configSize (int diameter)
{
// recompute our sizes
_dia = diameter - 1;
_cdia = _dia / 4;
int mid = (_dia - _cdia) / 2;
int num = DirectionCodes.DIRECTION_COUNT; // oh we all know it's 8.
for (int ii=0; ii < num; ii++) {
double rads = Math.PI * 2 * ii / num;
// 0 radians specifies EAST, so we offset
int dir = (ii + DirectionCodes.EAST) % num;
_coords[dir][0] = mid + ((int) (mid * Math.cos(rads)));
_coords[dir][1] = mid + ((int) (mid * Math.sin(rads)));
}
}
// documentation inherited
public Dimension getPreferredSize ()
{
return new Dimension(_prefdia, _prefdia);
}
/** The current direction displayed by the button. */
protected int _direction = DirectionCodes.SOUTH;
// if we're "armed" for a possible state change, which direction is armed?
// (-1 for unarmed)
protected int _armed = -1;
/** Diameter of the drawn enclosing circle. */
protected int _dia;
/** Diameter of selection circles. */
protected int _cdia;
/** Coordinates of each of the selection circles. */
protected int[][] _coords = new int[DirectionCodes.DIRECTION_COUNT][2];
/** Our preferred diameter. */
protected int _prefdia;
}
@@ -0,0 +1,356 @@
//
// $Id: EditorApp.java 19661 2005-03-09 02:40:29Z andrzej $
package com.threerings.stage.tools.editor;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.EventQueue;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
import com.samskivert.swing.util.SwingUtil;
import com.samskivert.util.DebugChords;
import com.samskivert.util.RuntimeAdjust;
import com.samskivert.util.StringUtil;
import com.threerings.util.KeyDispatcher;
import com.threerings.util.KeyboardManager;
import com.threerings.util.MessageBundle;
import com.threerings.util.MessageManager;
import com.threerings.resource.ResourceManager;
import com.threerings.media.FrameManager;
import com.threerings.media.IconManager;
import com.threerings.media.image.ColorPository;
import com.threerings.media.image.ImageManager;
import com.threerings.media.sound.SoundManager;
import com.threerings.media.util.ModeUtil;
import com.threerings.media.tile.TileSetRepository;
import com.threerings.media.tile.bundle.BundledTileSetRepository;
import com.threerings.cast.ComponentRepository;
import com.threerings.cast.bundle.BundledComponentRepository;
import com.threerings.miso.tile.MisoTileManager;
import com.threerings.stage.data.StageSceneModel;
import com.threerings.stage.tools.editor.util.EditorContext;
/**
* A scene editor application that provides facilities for viewing,
* editing, and saving the scene templates that comprise a game.
*/
public class EditorApp implements Runnable
{
/**
* Construct and initialize the EditorApp object.
*/
public EditorApp (final String target)
throws IOException
{
// we need to use heavy-weight popup menus so that they can overly
// our non-lightweight Miso view
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
// create and size the main application frame
_frame = new EditorFrame();
// create our frame manager
_framemgr = FrameManager.newInstance(_frame);
// create our myriad managers, repositories, etc.
_rmgr = new ResourceManager("rsrc");
// build up a simple ui for displaying progress
JPanel progressPanel = new JPanel(new BorderLayout());
final JLabel progressLabel = new JLabel();
final JProgressBar progress = new JProgressBar(0,100);
final JPanel spacer = new JPanel();
progressPanel.add(progressLabel, BorderLayout.CENTER);
progressPanel.add(progress, BorderLayout.SOUTH);
progressPanel.setPreferredSize(new Dimension(300,80));
spacer.add(progressPanel);
_frame.getContentPane().add(spacer);
final EditorFrame frameRef = _frame;
ResourceManager.InitObserver obs = new ResourceManager.InitObserver() {
public void progress (int percent, long remaining) {
String msg = "Unpacking...";
if (remaining >= 0) {
msg += " " + remaining + " seconds remaining.";
}
progressLabel.setText(msg);
progress.setValue(percent);
if (percent >= 100) {
frameRef.getContentPane().remove(spacer);
EditorApp.this.finishInit(target);
}
}
public void initializationFailed (Exception e) {
Log.warning("Failed unpacking bundles [e=" + e + "].");
Log.logStackTrace(e);
}
};
// we want our methods called on the AWT thread
obs = new ResourceManager.AWTInitObserver(obs);
_rmgr.initBundles(null, "config/resource/editor.properties", obs);
}
public void finishInit (String target)
{
_msgmgr = new MessageManager("rsrc.i18n");
_imgr = new ImageManager(_rmgr, _frame);
_tilemgr = new MisoTileManager(_rmgr, _imgr);
try {
_tsrepo = new BundledTileSetRepository(_rmgr, _imgr, "tilesets");
_tilemgr.setTileSetRepository(_tsrepo);
_iconmgr = new IconManager(_tilemgr, ICON_MANAGER_CONFIG);
_crepo = new BundledComponentRepository(
_rmgr, _imgr, "components");
} catch (IOException e) {
Log.warning("Exception loading tilesets and and icon manager " +
"[Exception=" + e + "].");
return;
}
_iconmgr.setSource("general");
_colpos = ColorPository.loadColorPository(_rmgr);
_soundmgr = new SoundManager(_rmgr, null, null);
_kbdmgr = new KeyboardManager();
_keydisp = new KeyDispatcher(_frame);
_ctx = new EditorContextImpl();
// initialize the frame with the now-prepared context
_frame.init(_ctx, target);
// wire up our runtime adjustment editor
DebugChords.activate();
// if we have a target file, load it up
if (target != null) {
_frame.openScene(target);
}
}
/**
* Given a subdirectory name (that should correspond to the calling
* service), returns a file path that can be used to store local data.
*/
public static String localDataDir (String subdir)
{
String appdir = System.getProperty("appdir");
if (StringUtil.blank(appdir)) {
appdir = ".narya-editor";
String home = System.getProperty("user.home");
if (!StringUtil.blank(home)) {
appdir = home + File.separator + appdir;
}
}
return appdir + File.separator + subdir;
}
/**
* Run the application.
*/
public void run ()
{
// enter full-screen exclusive mode if available and if we have
// the right display mode
GraphicsEnvironment env =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = env.getDefaultScreenDevice();
DisplayMode pmode = null;
try {
DisplayMode cmode = gd.getDisplayMode();
pmode = ModeUtil.getDisplayMode(
gd, cmode.getWidth(), cmode.getHeight(), 16, 15);
} catch (Throwable t) {
// Win98 seems to choke on it's own vomit when we attempt to
// enumerate the available display modes; yay!
Log.warning("Failed to probe display mode.");
Log.logStackTrace(t);
}
if (_viewFullScreen.getValue() && gd.isFullScreenSupported() &&
pmode != null) {
Log.info("Switching to screen mode " +
"[mode=" + ModeUtil.toString(pmode) + "].");
// set the frame to undecorated, full-screen
_frame.setUndecorated(true);
gd.setFullScreenWindow(_frame);
// switch to our happy custom display mode
gd.setDisplayMode(pmode);
// lay the frame out properly (we can't do this before making
// it full screen because packing causes the window to become
// displayable which apparently prevents the window from
// subsequently being made a full-screen window)
_frame.pack();
} else {
_frame.setSize(1024, 768);
SwingUtil.centerWindow(_frame);
_frame.setVisible(true);
}
_framemgr.start();
}
/**
* Instantiate the application object and start it running.
*/
public static void main (String[] args)
{
String target = (args.length > 0) ? args[0] : null;
if (System.getProperty("no_log_redir") != null) {
Log.info("Logging to console only.");
} else {
String dlog = localDataDir("editor.log");
try {
PrintStream logOut = new PrintStream(
new BufferedOutputStream(new FileOutputStream(dlog)));
System.setOut(logOut);
System.setErr(logOut);
Log.info("Opened debug log '" + dlog + "'.");
} catch (IOException ioe) {
Log.warning("Failed to open debug log [path=" + dlog +
", error=" + ioe + "].");
}
}
try {
EditorApp app = new EditorApp(target);
// start up the UI on the AWT thread to avoid deadlocks
EventQueue.invokeLater(app);
} catch (IOException ioe) {
Log.warning("Unable to initialize editor.");
Log.logStackTrace(ioe);
}
}
/**
* The implementation of the EditorContext interface that provides
* handles to the config and manager objects that offer commonly
* used services.
*/
protected class EditorContextImpl implements EditorContext
{
public MisoTileManager getTileManager () {
return _tilemgr;
}
public FrameManager getFrameManager () {
return _framemgr;
}
public ResourceManager getResourceManager () {
return _rmgr;
}
public ImageManager getImageManager () {
return _imgr;
}
public MessageManager getMessageManager () {
return _msgmgr;
}
public IconManager getIconManager () {
return _iconmgr;
}
public SoundManager getSoundManager() {
return _soundmgr;
}
public KeyboardManager getKeyboardManager() {
return _kbdmgr;
}
public ComponentRepository getComponentRepository () {
return _crepo;
}
public KeyDispatcher getKeyDispatcher () {
return _keydisp;
}
public String xlate (String message) {
return xlate("stage.editor", message);
}
public String xlate (String bundle, String message) {
MessageBundle mbundle = _msgmgr.getBundle(bundle);
if (mbundle == null) {
Log.warning("Requested to translate message with " +
"non-existent bundle [bundle=" + bundle +
", message=" + message + "].");
return message;
} else {
return mbundle.xlate(message);
}
}
public TileSetRepository getTileSetRepository () {
return _tsrepo;
}
public ColorPository getColorPository () {
return _colpos;
}
public void enumerateSceneTypes (List types) {
types.add(StageSceneModel.WORLD);
}
}
protected EditorContext _ctx;
protected EditorFrame _frame;
protected FrameManager _framemgr;
protected ResourceManager _rmgr;
protected ImageManager _imgr;
protected MisoTileManager _tilemgr;
protected TileSetRepository _tsrepo;
protected ColorPository _colpos;
protected MessageManager _msgmgr;
protected IconManager _iconmgr;
protected SoundManager _soundmgr;
protected KeyboardManager _kbdmgr;
protected BundledComponentRepository _crepo;
protected KeyDispatcher _keydisp;
/** The path to the config file for the icon manager. */
protected static final String ICON_MANAGER_CONFIG =
"rsrc/config/stage/iconmgr.properties";
/** A debug hook that toggles debug rendering of traversable tiles. */
protected static RuntimeAdjust.BooleanAdjust _viewFullScreen =
new RuntimeAdjust.BooleanAdjust(
"Toggles whether or not the scene editor uses full screen mode.",
"stage.editor.full_screen", EditorConfig.config, false);
}
@@ -0,0 +1,34 @@
//
// $Id: EditorConfig.java 1352 2002-03-28 23:55:21Z ray $
package com.threerings.stage.tools.editor;
import com.samskivert.util.Config;
/**
* Provides access to configuration data for the editor.
*/
public class EditorConfig
{
/** Provides access to config data for this package. */
public static Config config = new Config("rsrc/config/stage/tools/editor");
/**
* Accessor method for getting the test tile directory.
*/
public static String getTestTileDirectory ()
{
return config.getValue(TESTTILE_KEY, TESTTILE_DEF);
}
/**
* Accessor method for setting the test tile directory.
*/
public static void setTestTileDirectory (String newvalue)
{
config.setValue(TESTTILE_KEY, newvalue);
}
private static final String TESTTILE_KEY = "testtiledir";
private static final String TESTTILE_DEF = ".";
}
@@ -0,0 +1,552 @@
//
// $Id: EditorFrame.java 19411 2005-02-19 00:28:49Z mdb $
package com.threerings.stage.tools.editor;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.KeyStroke;
import javax.swing.filechooser.FileFilter;
import com.samskivert.swing.GroupLayout;
import com.samskivert.swing.HGroupLayout;
import com.samskivert.swing.util.MenuUtil;
import com.samskivert.swing.util.SwingUtil;
import com.threerings.media.ManagedJFrame;
import com.threerings.miso.tile.BaseTileSet;
import com.threerings.miso.util.MisoSceneMetrics;
import com.threerings.stage.data.StageScene;
import com.threerings.stage.data.StageSceneModel;
import com.threerings.stage.tools.editor.util.EditorContext;
import com.threerings.stage.tools.editor.util.EditorDialogUtil;
import com.threerings.stage.tools.xml.StageSceneParser;
import com.threerings.stage.tools.xml.StageSceneWriter;
public class EditorFrame extends ManagedJFrame
{
public EditorFrame ()
{
// treat a closing window as a request to quit
addWindowListener(new WindowAdapter () {
public void windowClosing (WindowEvent e) {
handleQuit(null);
}
});
// set our initial window title
setFilePath(null);
}
public void init (EditorContext ctx, String target)
{
_ctx = ctx;
// create the editor data model for reference by the panels
_model = new EditorModel(_ctx.getTileManager());
// create the file chooser used for saving and loading scenes
if (target == null) {
target = EditorConfig.config.getValue(
"editor.last_dir", System.getProperty("user.dir"));
}
_chooser = (target == null) ?
new JFileChooser() : new JFileChooser(target);
_chooser.setFileFilter(new FileFilter () {
public boolean accept (File f) {
return (f.isDirectory() || f.getName().endsWith(".xml"));
}
public String getDescription () {
return "XML Files";
}
});
// instead of using a popup, we slip the file chooser into the
// main interface (so the editor can run full-screen); thus we
// can't use the standard business and have to add an action
// listen to our file chooser
_chooser.addActionListener(_openListener);
// set up the menu bar
createMenuBar();
// create a top-level panel to manage everything
JPanel top = new JPanel(new BorderLayout());
// create a sub-panel to contain the toolbar and scene view
_main = new JPanel(new BorderLayout());
// set up the scene view panel with a default scene
_svpanel = new EditorScenePanel(_ctx, this, _model);
_main.add(_svpanel, BorderLayout.CENTER);
// create a toolbar for action selection and other options
JPanel upper = new JPanel(
new HGroupLayout(GroupLayout.NONE, GroupLayout.LEFT));
upper.add(new EditorToolBarPanel(_ctx.getTileManager(), _model),
GroupLayout.FIXED);
_sceneInfoPanel = new SceneInfoPanel(_ctx, _model, _svpanel);
upper.add(_sceneInfoPanel, GroupLayout.FIXED);
_main.add(upper, BorderLayout.NORTH);
// create a couple of scroll bars
JScrollBar horiz = new JScrollBar(JScrollBar.HORIZONTAL);
horiz.setBlockIncrement(500);
horiz.setModel(_svpanel.getHorizModel());
_main.add(horiz, BorderLayout.SOUTH);
JScrollBar vert = new JScrollBar(JScrollBar.VERTICAL);
vert.setBlockIncrement(500);
vert.setModel(_svpanel.getVertModel());
_main.add(vert, BorderLayout.EAST);
// add the scene view and toolbar to the top-level panel
top.add(_main, BorderLayout.CENTER);
// set up our left sidebar panel
JPanel west = GroupLayout.makeVStretchBox(5);
_tpanel = new TileInfoPanel(_ctx, _model);
west.add(_tpanel);
JPanel boxPane = GroupLayout.makeHBox();
boxPane.add(_scrollBox = new EditorScrollBox(_svpanel));
west.add(boxPane, GroupLayout.FIXED);
// add the sub-panel to the top panel
top.add(west, BorderLayout.WEST);
// now add our top-level panel
getContentPane().add(top, BorderLayout.CENTER);
// observe mouse-wheel events in the scene view panel to allow
// scrolling the wheel to select the tile to be placed
_svpanel.addMouseWheelListener(new MouseWheelListener() {
public void mouseWheelMoved (MouseWheelEvent e) {
if (e.getWheelRotation() < 0) {
_tpanel.selectPreviousTile();
} else {
_tpanel.selectNextTile();
}
}
});
// finally set a default scene
newScene();
}
/**
* Create the menu bar and menu items and add them to the frame.
*/
public void createMenuBar ()
{
KeyStroke accel = null;
// create the "File" menu
JMenu menuFile = new JMenu("File");
menuFile.setMnemonic(KeyEvent.VK_F);
accel = KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK);
MenuUtil.addMenuItem(menuFile, "New", KeyEvent.VK_N, accel,
this, "handleNew");
accel = KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.CTRL_MASK);
MenuUtil.addMenuItem(menuFile, "Open", KeyEvent.VK_O, accel,
this, "handleOpen");
//addMenuItem(menuFile, "Close", KeyEvent.VK_C);
accel = KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK);
MenuUtil.addMenuItem(menuFile, "Save", KeyEvent.VK_S, accel,
this, "handleSave");
MenuUtil.addMenuItem(menuFile, "Save As", KeyEvent.VK_A, null,
this, "handleSaveAs");
accel = KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK);
MenuUtil.addMenuItem(menuFile, "Quit", KeyEvent.VK_Q, accel,
this, "handleQuit");
// create the "Edit" menu
// JMenu menuEdit = new JMenu("Edit");
// MenuUtil.addMenuItem(menuEdit, "Undo", this, "handleUndo");
// MenuUtil.addMenuItem(menuEdit, "Cut", this, "handleCut");
// MenuUtil.addMenuItem(menuEdit, "Copy", this, "handleCopy");
// MenuUtil.addMenuItem(menuEdit, "Paste", this, "handlePaste");
// MenuUtil.addMenuItem(menuEdit, "Clear", this, "handleClear");
// MenuUtil.addMenuItem(menuEdit, "Select All", this, "handleSelectAll");
// menuEdit.setMnemonic(KeyEvent.VK_E);
// create the "Actions" menu
JMenu menuActions = new JMenu("Actions");
MenuUtil.addMenuItem(menuActions, "Load (reload) test tiles", this,
"handleTestTiles");
menuActions.setMnemonic(KeyEvent.VK_A);
accel = KeyStroke.getKeyStroke(KeyEvent.VK_D, ActionEvent.CTRL_MASK);
MenuUtil.addMenuItem(menuActions, "Make default base tile",
KeyEvent.VK_M, accel, this, "handleSetDefBase");
accel = KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.CTRL_MASK);
MenuUtil.addMenuItem(menuActions, "Update mini view",
KeyEvent.VK_M, accel, this, "updateMiniView");
// create the "Settings" menu
JMenu menuSettings = new JMenu("Settings");
menuSettings.setMnemonic(KeyEvent.VK_S);
accel = KeyStroke.getKeyStroke(
KeyEvent.VK_SEMICOLON, ActionEvent.CTRL_MASK);
MenuUtil.addMenuItem(menuSettings, "Preferences...", KeyEvent.VK_P,
accel, this, "handlePreferences");
// create the menu bar
JMenuBar bar = new JMenuBar();
bar.add(menuFile);
// bar.add(menuEdit);
bar.add(menuActions);
bar.add(menuSettings);
// add the menu bar to the frame
setJMenuBar(bar);
}
protected void setScene (StageScene scene)
{
// save off the scene objects
_scene = scene;
// display the name of the scene
_sceneInfoPanel.setScene(_scene);
// set up the scene view panel with the new scene
_svpanel.setScene(_scene);
_svpanel.repaint();
}
protected void newScene ()
{
try {
MisoSceneMetrics metrics = _svpanel.getSceneMetrics();
StageSceneModel model = StageSceneModel.blankStageSceneModel();
model.type = StageSceneModel.WORLD;
setScene(new StageScene(model, null));
} catch (Exception e) {
Log.warning("Unable to set blank scene.");
Log.logStackTrace(e);
}
}
/**
* Creates a blank scene and configures the editor to begin editing
* it. Eventually this should make sure the user has a chance to save
* any scene for which editing is currently in progress.
*/
public void handleNew (ActionEvent evt)
{
// clear out the fiel path so that "Save" pops up "Save as" rather
// than overwriting whatever you were editing before
setFilePath(null);
newScene();
}
/**
* Presents the user with an open file dialog and loads the scenes
* from the selected file.
*/
public void handleOpen (ActionEvent evt)
{
_chooser.setDialogType(JFileChooser.OPEN_DIALOG);
_main.remove(_svpanel);
_main.add(_chooser, BorderLayout.CENTER);
SwingUtil.refresh(_main);
}
/**
* Loads the scene from the specified path.
*/
public void openScene (String path)
{
String errmsg = null;
// attempt loading and installation of the scene
try {
StageSceneModel model = (StageSceneModel)_parser.parseScene(path);
if (model == null) {
errmsg = "No scene data found";
} else {
setScene(new StageScene(model, null));
// keep track of the path for later saving
setFilePath(path);
}
} catch (Exception e) {
errmsg = "Parse error: " + e;
Log.logStackTrace(e);
}
if (errmsg != null) {
errmsg = "Unable to load scene from " + path + ":\n" + errmsg;
JOptionPane.showMessageDialog(
this, errmsg, "Load error", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Save the scenes to the file they were last associated with.
*/
public void handleSave (ActionEvent evt)
{
if (_filepath == null) {
handleSaveAs(evt);
return;
}
if (!checkSaveOk()) {
return;
}
try {
// we don't write directly to the file in question, in case
// something craps out during the writing process
File tmpfile = new File(_filepath + ".tmp");
_writer.writeScene(tmpfile, _scene.getSceneModel());
// now that we've successfully written the new file, delete
// the old file
File sfile = new File(_filepath);
if (!sfile.delete()) {
Log.warning("Aiya! Not able to remove " + _filepath +
" so that we can replace it with " +
tmpfile.getPath() + ".");
}
// now rename the new save file into place
if (!tmpfile.renameTo(sfile)) {
Log.warning("Fork! Not able to rename " + tmpfile.getPath() +
" to " + _filepath + ".");
}
} catch (Exception e) {
String errmsg = "Unable to save scene to " + _filepath + ":\n" + e;
JOptionPane.showMessageDialog(
this, errmsg, "Save error", JOptionPane.ERROR_MESSAGE);
Log.warning("Error writing scene " +
"[fname=" + _filepath + ", e=" + e + "].");
Log.logStackTrace(e);
}
}
/**
* Check to see if the save can proceed, and pop up errors if it
* can't.
*/
protected boolean checkSaveOk ()
{
String name = _scene.getName();
String type = _scene.getType();
String err = "";
if (name.equals("") || name.indexOf("<") != -1 ||
name.indexOf(">") != -1) {
err += "Invalid scene name.\n";
}
if (type.equals("")) {
err += "No scene type specified.\n";
}
if (_svpanel.getEntrance() == null) {
err += "No default entrance portal.\n";
}
if ("".equals(err)) {
return true;
} else {
JOptionPane.showMessageDialog(
this, err + "\nPlease fix and try again.",
"Won't save: scene is broken!",
JOptionPane.ERROR_MESSAGE);
return false;
}
}
/**
* Present the user with a save file dialog and save the scenes to
* the selected file.
*/
public void handleSaveAs (ActionEvent evt)
{
_chooser.setDialogType(JFileChooser.SAVE_DIALOG);
_main.remove(_svpanel);
_main.add(_chooser, BorderLayout.CENTER);
SwingUtil.refresh(_main);
}
/**
* Keeps our file path around and conveys that information in the
* window title.
*/
protected void setFilePath (String filepath)
{
_filepath = filepath;
setTitle("Narya Scene Editor: " +
(_filepath == null ? "<new>" : _filepath));
}
/**
* Handles a request to quit. Presently this just quits, but
* eventually it should give the user the chance to save any edits in
* progress.
*/
public void handleQuit (ActionEvent evt)
{
System.exit(0);
}
/**
* Handles a request to reload the test tiles.
*/
public void handleTestTiles (ActionEvent evt)
{
// don't instantiate a testLoader until we actually need one
if (_testLoader == null) {
_testLoader = new TestTileLoader();
}
_tpanel.insertTestTiles(_testLoader.loadTestTiles());
}
/**
* Update the mini view in the scrollbox.
*/
public void updateMiniView (ActionEvent evt)
{
_scrollBox.updateView();
}
/**
* Handles a request to open the preferences dialog.
*/
public void handlePreferences (ActionEvent evt)
{
PreferencesDialog pd = new PreferencesDialog();
EditorDialogUtil.display(this, pd);
}
/**
* Make the currently selected base tile into the scene's default
* tile.
*/
public void handleSetDefBase (ActionEvent evt)
{
if (_model.getTileSet() instanceof BaseTileSet) {
_svpanel.updateDefaultTileSet(_model.getTileSetId());
} else {
Log.warning("Not making non-base tileset into default " +
_model.getTileSet() + ".");
}
}
/** Handles JFileChooser responses when opening files. */
protected ActionListener _openListener = new ActionListener() {
public void actionPerformed (ActionEvent event) {
String cmd = event.getActionCommand();
// restore the scene view
_main.remove(_chooser);
_main.add(_svpanel);
SwingUtil.refresh(_main);
// load up the scene if they selected one
if (cmd.equals(JFileChooser.APPROVE_SELECTION)) {
File filescene = _chooser.getSelectedFile();
switch (_chooser.getDialogType()) {
case JFileChooser.OPEN_DIALOG:
openScene(filescene.getPath());
EditorConfig.config.setValue(
"editor.last_dir", filescene.getParent());
break;
case JFileChooser.SAVE_DIALOG:
setFilePath(filescene.getPath());
handleSave(null);
break;
default:
Log.warning("Wha? Weird dialog type " +
_chooser.getDialogType() + ".");
break;
}
}
// oh god the hackery; on linux at least, the focus seems
// to be hosed after we hide the chooser and we can't just
// request to move it somewhere useful because it just
// ignores us; so instead we have to wait for the current
// event queue to flush before we can transfer focus
EventQueue.invokeLater(new Runnable() {
public void run () {
_sceneInfoPanel.requestFocus();
}
});
}
};
/** The scene currently undergoing edit. */
protected StageScene _scene;
/** The file last associated with the current scene. */
protected String _filepath;
/** Contains the scene view panel or other fun stuff. */
protected JPanel _main;
/** Used for displaying dialogs. */
protected JInternalFrame _dialog;
/** The file chooser used for loading and saving scenes. */
protected JFileChooser _chooser;
/** The panel that displays the scene view. */
protected EditorScenePanel _svpanel;
/** The panel that displays tile info. */
protected TileInfoPanel _tpanel;
/** The panel that displays scene info. */
protected SceneInfoPanel _sceneInfoPanel;
/** The scrollbox used to display the view position within the scene. */
protected EditorScrollBox _scrollBox;
/** The editor data model. */
protected EditorModel _model;
/** The editor context. */
protected EditorContext _ctx;
/** We use this to load scenes. */
protected StageSceneParser _parser = new StageSceneParser();
/** We use this to save scenes. */
protected StageSceneWriter _writer = new StageSceneWriter();
/** The test tileset loader. */
protected TestTileLoader _testLoader;
}
@@ -0,0 +1,262 @@
//
// $Id: EditorModel.java 17780 2004-11-10 23:00:07Z ray $
package com.threerings.stage.tools.editor;
import java.util.ArrayList;
import com.threerings.media.tile.Tile;
import com.threerings.media.tile.TileManager;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.TileUtil;
import com.threerings.util.DirectionCodes;
/**
* The EditorModel class provides a holding place for storing,
* modifying and retrieving data that is shared across the Editor
* application and its myriad UI components.
*/
public class EditorModel
{
/** Action mode constants. */
public static final int ACTION_PLACE_TILE = 0;
public static final int ACTION_EDIT_TILE = 1;
public static final int ACTION_PLACE_PORTAL = 2;
/** The number of actions. */
public static final int NUM_ACTIONS = 3;
/** Miso layer constants. */
public static final int BASE_LAYER = 0;
public static final int OBJECT_LAYER = 1;
public static final String[] LAYER_NAMES = { "Base", "Object" };
/** Prose descriptions of actions suitable for tool tip display. */
public static final String[] TIP_ACTIONS = {
"Place/Delete tiles.", "Edit object tiles.",
"Create/Edit/Delete portal."
};
/** String translations for action identifiers. */
public static final String[] CMD_ACTIONS = {
"place_tile", "edit_tile", "place_portal"
};
public EditorModel (TileManager tilemgr)
{
_tilemgr = tilemgr;
_tileSetId = _tileIndex = -1;
_lnum = _mode = 0;
}
/**
* Add an editor model listener.
*
* @param l the listener.
*/
public void addListener (EditorModelListener l)
{
if (!_listeners.contains(l)) {
_listeners.add(l);
}
}
/**
* Notify all model listeners that the editor model has changed.
*/
protected void notifyListeners (int event)
{
int size = _listeners.size();
for (int ii = 0; ii < size; ii++) {
((EditorModelListener)_listeners.get(ii)).modelChanged(event);
}
}
/**
* Returns whether the currently selected tile is valid.
*/
public boolean isTileValid ()
{
return (_tile != null);
}
/**
* Returns the current editor action mode.
*/
public int getActionMode ()
{
return _mode;
}
/**
* Returns the current scene layer index undergoing edit.
*/
public int getLayerIndex ()
{
return _lnum;
}
/**
* Returns the currently selected tile set.
*/
public TileSet getTileSet ()
{
return _tileSet;
}
/**
* Returns the currently selected tile set id.
*/
public int getTileSetId ()
{
return _tileSetId;
}
/**
* Returns the currently selected tile id within the selected tile
* set.
*/
public int getTileId ()
{
return _tileIndex;
}
/**
* Returns the currently selected tile for placement within the
* scene.
*/
public Tile getTile ()
{
return _tile;
}
/**
* Returns the fully qualified tile id of the currently selected tile.
*/
public int getFQTileId ()
{
return _fqTileId;
}
/**
* Marks the currently selected tile as invalid such that editing
* when no tiles are available can be properly effected.
*/
public void clearTile ()
{
_tileSet = null;
_tileSetId = -1;
_tileIndex = -1;
_tile = null;
}
/**
* Sets the editor action mode. The specified mode should be one
* of <code>CMD_ACTIONS</code>.
*/
public void setActionMode (String cmd)
{
for (int ii = 0; ii < CMD_ACTIONS.length; ii++) {
if (CMD_ACTIONS[ii].equals(cmd)) {
_mode = ii;
notifyListeners(EditorModelListener.ACTION_MODE_CHANGED);
return;
}
}
Log.warning("Attempt to set to unknown mode [cmd=" + cmd + "].");
}
/**
* Sets the scene layer index undergoing edit.
*/
public void setLayerIndex (int lnum)
{
if (lnum != _lnum) {
_lnum = lnum;
notifyListeners(EditorModelListener.LAYER_INDEX_CHANGED);
}
}
/**
* Sets the selected tile for placement within the scene.
*/
public void setTile (TileSet set, int tileSetId, int tileIndex)
{
_tile = set.getTile(tileIndex);
_tileSet = set;
_tileSetId = tileSetId;
_tileIndex = tileIndex;
_fqTileId = TileUtil.getFQTileId(tileSetId, tileIndex);
notifyListeners(EditorModelListener.TILE_CHANGED);
}
/**
* Sets the tile id of the tile selected for placement within the
* scene.
*/
public void setTileId (int tileIndex)
{
setTile(_tileSet, _tileSetId, tileIndex);
}
/**
* Sets the direction in which we should grip objects.
*/
public void setObjectGripDirection (int direction)
{
_objectGrip = direction;
}
/**
* Gets the direction in which we should grip objects.
*/
public int getObjectGripDirection ()
{
return _objectGrip;
}
/**
* Returns a string representation of the editor model.
*/
public String toString ()
{
StringBuffer buf = new StringBuffer();
buf.append("[set=").append(_tileSet);
buf.append(", tid=").append(_tileIndex);
buf.append(", lnum=").append(_lnum);
buf.append(", tile=").append(_tile);
return buf.append("]").toString();
}
/** The currently selected action mode. */
protected int _mode;
/** The currently selected tileset. */
protected TileSet _tileSet;
/** The currently selected tileset id. */
protected int _tileSetId;
/** The currently selected tile id. */
protected int _tileIndex;
/** The fully qualified tile id of the currently selected tile. */
protected int _fqTileId;
/** The currently selected layer number. */
protected int _lnum;
/** The currently selected tile for placement in the scene. */
protected Tile _tile;
/** The model listeners. */
protected ArrayList _listeners = new ArrayList();
/** The tile manager. */
protected TileManager _tilemgr;
/** Direction (which corner) we grip an object by. */
protected int _objectGrip = DirectionCodes.SOUTH;
}
@@ -0,0 +1,24 @@
//
// $Id: EditorModelListener.java 217 2001-11-29 00:29:31Z mdb $
package com.threerings.stage.tools.editor;
/**
* The editor model listener interface should be implemented by
* classes that would like to be notified when the editor model is
* changed.
*
* @see EditorModel
*/
public interface EditorModelListener
{
/**
* Called by the {@link EditorModel} when the model is changed.
*/
public void modelChanged (int event);
/** Notification event constants. */
public static final int ACTION_MODE_CHANGED = 0;
public static final int LAYER_INDEX_CHANGED = 1;
public static final int TILE_CHANGED = 2;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,174 @@
//
// $Id: EditorScrollBox.java 19363 2005-02-17 21:36:41Z ray $
package com.threerings.stage.tools.editor;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import com.samskivert.swing.ScrollBox;
import com.samskivert.swing.util.SwingUtil;
/**
* A scrollbox that shows our position in the editor.
*/
public class EditorScrollBox extends ScrollBox
{
/**
* Create the tightly-coupled EditorScrollBox.
*/
public EditorScrollBox (EditorScenePanel panel)
{
super(panel.getHorizModel(), panel.getVertModel());
_panel = panel;
_panel.setEditorScrollBox(this);
_panel.addComponentListener(new ComponentAdapter() {
public void componentResized (ComponentEvent e)
{
SwingUtil.refresh(EditorScrollBox.this);
}
});
createMiniMap(1, 1);
}
/**
* Update the view of the editor map.
*/
public void updateView ()
{
_updatingView = true;
_horz.setValue(_horz.getMinimum());
_vert.setValue(_vert.getMinimum());
}
/**
* Get the graphics to draw into when rendering onto the minimap.
*/
public Graphics2D getMiniGraphics ()
{
Graphics2D gfx = (Graphics2D) _miniMap.getGraphics();
gfx.transform(AffineTransform.getTranslateInstance(_box.x, _box.y));
gfx.transform(AffineTransform.getScaleInstance(
_hFactor, _vFactor));
if (_updatingView) {
EventQueue.invokeLater(new Runnable() {
public void run ()
{
doNextPieceOfUpdate();
}
});
}
return gfx;
}
/**
* Do the next piece of the update of the view.
*/
protected void doNextPieceOfUpdate ()
{
int x = _horz.getValue();
int y = _vert.getValue();
int lastX = _horz.getMaximum() - _horz.getExtent();
if (x != lastX) {
x = Math.min(x + _horz.getExtent(), lastX);
} else {
x = _horz.getMinimum();
int lastY = _vert.getMaximum() - _vert.getExtent();
if (y != lastY) {
y = Math.min(y + _vert.getExtent(), lastY);
} else {
// we're done
_updatingView = false;
return;
}
}
_horz.setValue(x);
_vert.setValue(y);
}
// documentation inherited
public Dimension getPreferredSize ()
{
int horz = _horz.getMaximum() - _horz.getMinimum();
int vert = _vert.getMaximum() - _vert.getMinimum();
int height = MAX_HEIGHT;
int width = (int) Math.round(horz * (((float) height) / vert));
int maxwidth = getParent().getWidth();
if (maxwidth > 0 && width > maxwidth) {
height = (int) Math.round(height * (((float) maxwidth) / width));
width = maxwidth;
}
return new Dimension(width, height);
}
// documentation inherited
public void setBounds (int x, int y, int w, int h)
{
super.setBounds(x, y, w, h);
if ((w != _miniMap.getWidth()) || (h != _miniMap.getHeight())) {
createMiniMap(w, h);
}
}
// documentation inherited
protected void paintBackground (Graphics g)
{
g.drawImage(_miniMap, 0, 0, null);
}
/**
* Create a mini map image of the correct dimensions.
*/
protected void createMiniMap (int w, int h)
{
BufferedImage newMini = new BufferedImage(
w, h, BufferedImage.TYPE_INT_ARGB);
// TODO: copy stuff over?
_miniMap = newMini;
Graphics g = _miniMap.getGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, w, h);
g.dispose();
}
// documentation inherited
protected boolean isActiveButton (MouseEvent e)
{
// all buttons are ok.
return true;
}
/** The minimap image. */
protected BufferedImage _miniMap;
/** The panel we box for. */
protected EditorScenePanel _panel;
/** Whether or not we're updating our little view. */
protected boolean _updatingView;
/** Our maximum height. */
protected static final int MAX_HEIGHT = 200;
}
@@ -0,0 +1,110 @@
//
// $Id: EditorToolBarPanel.java 17780 2004-11-10 23:00:07Z ray $
package com.threerings.stage.tools.editor;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
import com.samskivert.swing.*;
import com.threerings.media.tile.Tile;
import com.threerings.media.tile.TileIcon;
import com.threerings.media.tile.TileManager;
import com.threerings.media.tile.UniformTileSet;
public class EditorToolBarPanel extends JPanel implements ActionListener
{
public EditorToolBarPanel (TileManager tilemgr, EditorModel model)
{
_model = model;
// use of flowlayout positions the toolbar and floats properly
setLayout(new FlowLayout(FlowLayout.LEFT));
// get our toolbar icons
UniformTileSet tbset = tilemgr.loadTileSet(ICONS_PATH, 40, 40);
// create the toolbar
JToolBar toolbar = new JToolBar();
// add all of the toolbar buttons
_buttons = new ArrayList();
for (int ii = 0; ii < EditorModel.NUM_ACTIONS; ii++) {
// get the button icon images
Tile tile = tbset.getTile(ii);
if (tile != null) {
String cmd = EditorModel.CMD_ACTIONS[ii];
String tip = EditorModel.TIP_ACTIONS[ii];
// create the button
JButton b = addButton(toolbar, cmd, tip, new TileIcon(tile));
// add it to the set of buttons we're managing
_buttons.add(b);
} else {
Log.warning("Unable to load toolbar icon " +
"[index=" + ii + "].");
}
}
// default to the first button
setSelectedButton((JButton)_buttons.get(0));
// add the toolbar
add(toolbar);
}
protected JButton addButton (JToolBar toolbar, String cmd, String tip,
TileIcon icon)
{
// create the button and configure accordingly
JButton button = new JButton(new DimmedIcon(icon));
button.setSelectedIcon(icon);
button.addActionListener(this);
button.setActionCommand("tbar_" + cmd);
button.setToolTipText(tip);
// add the button to the toolbar
toolbar.add(button);
return button;
}
protected void setSelectedButton (JButton button)
{
for (int ii = 0; ii < _buttons.size(); ii++) {
JButton tb = (JButton)_buttons.get(ii);
tb.setSelected(tb == button);
}
}
public void actionPerformed (ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd.startsWith("tbar")) {
// select the chosen mode in the toolbar
setSelectedButton((JButton)e.getSource());
// update the active mode in the model, stripping the
// "tbar_" prefix from the command string
_model.setActionMode(cmd.substring(5));
} else {
Log.warning("Unknown action command [cmd=" + cmd + "].");
}
}
/** The buttons in the tool bar. */
protected ArrayList _buttons;
/** The editor data model. */
protected EditorModel _model;
protected static final String ICONS_PATH =
"media/stage/tools/editor/toolbar_icons.png";
}
@@ -0,0 +1,38 @@
//
// $Id: Log.java 8859 2003-05-16 20:05:45Z mdb $
package com.threerings.stage.tools.editor;
/**
* A placeholder class that contains a reference to the log object used by
* this package.
*/
public class Log
{
public static com.samskivert.util.Log log =
new com.samskivert.util.Log("stage.tools.editor");
/** Convenience function. */
public static void debug (String message)
{
log.debug(message);
}
/** Convenience function. */
public static void info (String message)
{
log.info(message);
}
/** Convenience function. */
public static void warning (String message)
{
log.warning(message);
}
/** Convenience function. */
public static void logStackTrace (Throwable t)
{
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
}
}
@@ -0,0 +1,245 @@
// $Id: ObjectEditorDialog.java 16959 2004-08-30 22:09:53Z ray $
package com.threerings.stage.tools.editor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import com.samskivert.swing.HGroupLayout;
import com.samskivert.swing.VGroupLayout;
import com.samskivert.util.StringUtil;
import com.threerings.media.image.ColorPository.ColorRecord;
import com.threerings.media.image.ColorPository;
import com.threerings.media.tile.NoSuchTileSetException;
import com.threerings.media.tile.RecolorableTileSet;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.TileUtil;
import com.threerings.miso.client.SceneObject;
import com.threerings.stage.tools.editor.util.EditorContext;
import com.threerings.stage.tools.editor.util.EditorDialogUtil;
/**
* Used to edit object attributes.
*/
public class ObjectEditorDialog extends JInternalFrame
implements ActionListener
{
public ObjectEditorDialog (EditorContext ctx, EditorScenePanel panel)
{
super("Edit object attributes", true);
_ctx = ctx;
_panel = panel;
// get a handle on the top-level panel
JPanel top = (JPanel)getContentPane();
// set up a layout manager for the panel
VGroupLayout gl = new VGroupLayout(
VGroupLayout.STRETCH, VGroupLayout.STRETCH, 5, VGroupLayout.CENTER);
top.setLayout(gl);
top.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// object action editor elements
JPanel sub = new JPanel(new HGroupLayout(HGroupLayout.STRETCH));
sub.add(new JLabel("Object action command:"), HGroupLayout.FIXED);
sub.add(_action = new JTextField());
_action.addActionListener(this);
_action.setActionCommand("ok");
top.add(sub);
// create the priority slider
sub = new JPanel(new HGroupLayout(HGroupLayout.STRETCH));
sub.add(new JLabel("Render priority:"), HGroupLayout.FIXED);
sub.add(_priority = new JSlider(-5, 5));
_priority.setMajorTickSpacing(5);
_priority.setMinorTickSpacing(1);
_priority.setPaintTicks(true);
top.add(sub);
// create colorization selectors
JPanel zations = HGroupLayout.makeButtonBox(HGroupLayout.LEFT);
zations.add(new JLabel("Colorizations:"));
zations.add(_primary = new JComboBox(NO_CHOICES));
zations.add(_secondary = new JComboBox(NO_CHOICES));
top.add(zations);
// create our OK/Cancel buttons
sub = HGroupLayout.makeButtonBox(HGroupLayout.CENTER);
EditorDialogUtil.addButton(this, sub, "OK", "ok");
EditorDialogUtil.addButton(this, sub, "Cancel", "cancel");
top.add(sub);
pack();
}
/**
* Prepare the dialog for display. This method should be called
* before <code>display()</code> is called.
*
* @param scobj the object to edit.
*/
public void prepare (SceneObject scobj)
{
_scobj = scobj;
// set our title to the name of the tileset and the tile index
String title;
int tsid = TileUtil.getTileSetId(scobj.info.tileId);
int tidx = TileUtil.getTileIndex(scobj.info.tileId);
TileSet tset = null;
try {
tset = _ctx.getTileManager().getTileSet(tsid);
title = tset.getName() + ": " + tidx;
} catch (NoSuchTileSetException nstse) {
title = "Error(" + tsid + "): " + tidx;
}
title += " (" + StringUtil.coordsToString(
_scobj.info.x, _scobj.info.y) + ")";
setTitle(title);
// configure our elements
String atext = (scobj.info.action == null ? "" : scobj.info.action);
_action.setText(atext);
_priority.setValue(scobj.getPriority());
// if the object supports colorizations, configure those
boolean haveZations = false;
Object[] pzations = null;
Object[] szations = null;
if (tset != null) {
String[] zations = null;
if (tset instanceof RecolorableTileSet) {
zations = ((RecolorableTileSet)tset).getColorizations();
}
if (zations != null) {
pzations = computeZations(zations, 0);
szations = computeZations(zations, 1);
}
}
configureZations(_primary, pzations, _scobj.info.getPrimaryZation());
configureZations(_secondary, szations,
_scobj.info.getSecondaryZation());
// select the text edit field and focus it
_action.setCaretPosition(0);
_action.moveCaretPosition(atext.length());
_action.requestFocus();
}
protected Object[] computeZations (String[] zations, int index)
{
if (zations.length <= index || StringUtil.blank(zations[index])) {
return null;
}
ColorPository cpos = _ctx.getColorPository();
ColorRecord[] crecs = cpos.enumerateColors(zations[index]);
if (crecs == null) {
return null;
}
Object[] czations = new Object[crecs.length+1];
czations[0] = new ZationChoice(0, "none");
for (int ii = 0; ii < crecs.length; ii++) {
czations[ii+1] =
new ZationChoice(crecs[ii].colorId, crecs[ii].name);
}
Arrays.sort(czations);
return czations;
}
protected void configureZations (
JComboBox combo, Object[] zations, int colorId)
{
int selidx = 0;
combo.setEnabled(zations != null);
if (zations != null) {
combo.removeAllItems();
for (int ii = 0; ii < zations.length; ii++) {
combo.addItem(zations[ii]);
if (((ZationChoice)zations[ii]).colorId == colorId) {
selidx = ii;
}
}
}
combo.setSelectedIndex(selidx);
}
// documentation inherited from interface
public void actionPerformed (ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd.equals("ok")) {
_scobj.info.action = _action.getText();
byte prio = (byte)_priority.getValue();
if (prio != _scobj.getPriority()) {
_scobj.setPriority(prio);
}
int ozations = _scobj.info.zations;
ZationChoice pchoice = (ZationChoice)_primary.getSelectedItem();
ZationChoice schoice = (ZationChoice)_secondary.getSelectedItem();
_scobj.info.setZations(pchoice.colorId, schoice.colorId);
if (ozations != _scobj.info.zations) {
_scobj.refreshObjectTile(_panel);
}
_panel.objectEditorDismissed();
} else if (cmd.equals("cancel")) {
// do nothing except hide the dialog
_panel.objectEditorDismissed();
} else {
System.err.println("Received unknown action: " + e);
return;
}
// hide the dialog
EditorDialogUtil.dismiss(this);
}
/** Used to display colorization choices. */
protected static class ZationChoice
implements Comparable
{
public short colorId;
public String name;
public ZationChoice (int colorId, String name) {
this.colorId = (short)colorId;
this.name = name;
}
public int compareTo (Object other) {
return colorId - ((ZationChoice)other).colorId;
}
public String toString () {
return name;
}
}
protected EditorContext _ctx;
protected EditorScenePanel _panel;
protected JTextField _action;
protected JSlider _priority;
protected SceneObject _scobj;
protected JComboBox _primary, _secondary;
protected static final ZationChoice[] NO_CHOICES = new ZationChoice[] {
new ZationChoice(0, "none") };
}
@@ -0,0 +1,164 @@
//
// $Id: PortalDialog.java 9371 2003-06-02 20:28:21Z mdb $
package com.threerings.stage.tools.editor;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.samskivert.swing.*;
import com.threerings.whirled.spot.data.Portal;
import com.threerings.whirled.spot.tools.EditablePortal;
import com.threerings.stage.data.StageScene;
import com.threerings.stage.tools.editor.util.EditorDialogUtil;
/**
* The <code>PortalDialog</code> is used to present the user with a dialog
* allowing them to enter the information associated with an
* <code>EditablePortal</code>. The dialog is used both to set up a new
* portal and to edit an existing portal.
*/
public class PortalDialog extends JInternalFrame
implements ActionListener
{
/**
* Constructs the portal dialog.
*/
public PortalDialog ()
{
super("Edit Portal", true);
// get a handle on the top-level panel
JPanel top = (JPanel)getContentPane();
// set up a layout manager for the panel
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
gl.setOffAxisPolicy(GroupLayout.STRETCH);
top.setLayout(gl);
top.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// add the dialog instruction text
top.add(new JLabel("Enter settings for this portal:"));
// create a panel to contain the portal name info
JPanel sub = new JPanel(new HGroupLayout(GroupLayout.STRETCH));
sub.add(new JLabel("Portal name:", SwingConstants.RIGHT));
// create and add the portal name text entry field
sub.add(_portalText = new JTextField());
// add the portal name info to the top-level panel
top.add(sub);
// create a check box to allow making this the default
// entrance portal
_entrance = new JCheckBox("Default Entrance");
_entrance.addActionListener(this);
_entrance.setActionCommand("entrance");
top.add(_entrance);
// create a panel to contain the OK/Cancel buttons
sub = new JPanel(new HGroupLayout());
EditorDialogUtil.addButton(this, sub, "OK", "ok");
// add the buttons to the top-level panel
top.add(sub);
pack();
}
/**
* Prepare the dialog for display. This method should be called
* before <code>display()</code> is called.
*
* @param port the portal to edit.
*/
public void prepare (StageScene scene, EditablePortal port)
{
_port = port;
_scene = scene;
// if the location is already a portal, fill the text entry field
// with the current scene name, else clear it
String text = port.name;
_portalText.setText(text);
// select the text edit field
_portalText.setCaretPosition(0);
_portalText.moveCaretPosition(text.length());
// select the default entrance check box appropriately
Portal entry = _scene.getDefaultEntrance();
_entrance.setSelected(entry == null ||
entry.portalId == _port.portalId);
// request the keyboard focus so that the destination scene
// name can be typed immediately
_portalText.requestFocus();
}
/**
* Handle action events on the dialog user interface elements.
*/
public void actionPerformed (ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd.equals("entrance")) {
_entrance.setSelected(_entrance.isSelected());
} else if (cmd.equals("ok")) {
handleSubmit();
} else {
Log.warning("Unknown action command [cmd=" + cmd + "].");
}
}
/**
* Handles the user submitting the dialog via the "OK" button.
*/
protected void handleSubmit ()
{
// get the destination scene name
_port.name = _portalText.getText();
// update the scene's default entrance
if (_entrance.isSelected()) {
_scene.setDefaultEntrance(_port);
} else if (_scene.getDefaultEntrance() == _port) {
_scene.setDefaultEntrance(null);
}
// hide the dialog
EditorDialogUtil.dismiss(this);
}
// documentation inherited
protected void processKeyEvent (KeyEvent e)
{
switch (e.getKeyCode()) {
case KeyEvent.VK_ENTER: handleSubmit(); break;
case KeyEvent.VK_ESCAPE: setVisible(false); break;
}
}
/** The scene. */
protected StageScene _scene;
/** The portal name text entry field. */
protected JTextField _portalText;
/** The portal default entrance check box. */
protected JCheckBox _entrance;
/** The location object denoting the portal location. */
protected EditablePortal _port;
/** The combo box listing the direction orientations. */
protected JComboBox _orientcombo;
}
@@ -0,0 +1,161 @@
//
// $Id: PortalTool.java 20143 2005-03-30 01:12:48Z mdb $
package com.threerings.stage.tools.editor;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import javax.swing.event.MouseInputAdapter;
import java.util.Iterator;
import com.threerings.miso.util.MisoUtil;
import com.threerings.util.DirectionCodes;
import com.threerings.util.DirectionUtil;
import com.threerings.whirled.spot.tools.EditablePortal;
import com.threerings.stage.data.StageScene;
import com.threerings.stage.tools.editor.util.ExtrasPainter;
/**
* A tool for setting a portal.
*/
public class PortalTool extends MouseInputAdapter
implements ExtrasPainter
{
public void init (EditorScenePanel panel, int centerx, int centery)
{
_panel = panel;
_scene = (StageScene)_panel.getScene();
// adjust the center screen coordinate so that it is positioned at
// the closest full coordinate where a portal may be placed
Point p = _panel.getFullCoords(centerx, centery);
// configure the portal with these full coordinates
_portal = new EditablePortal();
_portal.x = p.x;
_portal.y = p.y;
// we want to listen to drags and clicks
_panel.addMouseListener(this);
_panel.addMouseMotionListener(this);
_panel.addExtrasPainter(this);
// now map that back to a screen coordinate
_center = _panel.getScreenCoords(p.x, p.y);
calculateOrientation(centerx, centery);
}
/**
* When the mouse is dragged we update the current portal.
*/
public void mouseDragged (MouseEvent event)
{
calculateOrientation(event.getX(), event.getY());
}
/**
* If button1 is released, we store the new portal, if button3: we
* cancel.
*/
public void mouseReleased (MouseEvent event)
{
switch (event.getButton()) {
case MouseEvent.BUTTON1:
savePortal();
// fall through to BUTTON3
case MouseEvent.BUTTON3:
dispose();
break;
}
}
/**
* Paint what the clusters would look like if we were to add them to
* the scene.
*/
public void paintExtras (Graphics2D gfx)
{
_panel.paintPortal(gfx, _portal);
}
/**
* Updates the orientation of the portal based on the current mouse
* position and potentially repaints.
*/
protected void calculateOrientation (int x, int y)
{
// get the orientation towards the center from the current x/y
int norient = MisoUtil.getProjectedIsoDirection(
x, y, _center.x, _center.y);
norient = DirectionUtil.getOpposite(norient);
if (norient == DirectionCodes.NONE) {
norient = DirectionCodes.NORTH;
}
// if our orientation changed, repaint
if (norient != _portal.orient) {
_portal.orient = (byte)norient;
_panel.repaint();
}
}
/**
* Adds our portal to the scene.
*/
protected void savePortal ()
{
// try to come up with a reasonable name
String dirname = DirectionUtil.toString(_portal.orient).toLowerCase();
String name = dirname;
for (int ii=1; portalNameExists(name); ii++) {
name = dirname + ii;
}
_portal.name = name;
// add the portal to the scene and pop up the editor dialog
_scene.addPortal(_portal);
_panel.editPortal(_portal);
}
/**
* See if the portal name already exists.
*/
protected boolean portalNameExists (String name)
{
Iterator iter = _scene.getPortals();
while (iter.hasNext()) {
if (((EditablePortal)iter.next()).name.equals(name)) {
return true;
}
}
return false;
}
/**
* Get rid of ourselves.
*/
protected void dispose ()
{
_panel.removeMouseListener(this);
_panel.removeMouseMotionListener(this);
_panel.removeExtrasPainter(this);
}
/** The panel. */
protected EditorScenePanel _panel;
/** The scene. */
protected StageScene _scene;
/** Center coordinates. */
protected Point _center;
/** Our newly created portal. */
protected EditablePortal _portal;
}
@@ -0,0 +1,103 @@
//
// $Id: PreferencesDialog.java 9938 2003-06-20 03:55:58Z mdb $
package com.threerings.stage.tools.editor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.samskivert.swing.GroupLayout;
import com.samskivert.swing.HGroupLayout;
import com.samskivert.swing.VGroupLayout;
import com.threerings.stage.tools.editor.util.EditorDialogUtil;
/**
* A dialog for editing preferences.
*/
public class PreferencesDialog extends JInternalFrame
implements ActionListener
{
/**
* Creates a preferences dialog.
*/
public PreferencesDialog ()
{
super("Editor Preferences", true);
// set up a layout manager for the panel
JPanel top = (JPanel)getContentPane();
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
gl.setOffAxisPolicy(GroupLayout.STRETCH);
top.setLayout(gl);
top.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// create the display for the test tile directory pref
JPanel sub = new JPanel(new HGroupLayout(GroupLayout.STRETCH));
sub.add(new JLabel("Directory to search for test tiles:"),
GroupLayout.FIXED);
EditorDialogUtil.addButton(this, sub,
EditorConfig.getTestTileDirectory(), "testtiledir");
top.add(sub);
sub = new JPanel(new HGroupLayout());
EditorDialogUtil.addButton(this, sub, "OK", "ok");
top.add(sub);
pack();
}
/**
* Handle action events.
*/
public void actionPerformed (ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd.equals("testtiledir")) {
changeTestTileDir((JButton) e.getSource());
} else if (cmd.equals("ok")) {
EditorDialogUtil.dispose(this);
} else {
Log.warning("Unknown action command [cmd=" + cmd + "].");
}
}
/**
* Pop up a file selection box for specifying the directory to look
* in for test tiles.
*/
protected void changeTestTileDir (JButton button)
{
JFileChooser chooser;
// figure out which
File f = new File(button.getText());
if (!f.exists()) {
chooser = new JFileChooser();
} else {
chooser = new JFileChooser(f);
}
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setSelectedFile(f);
int result = chooser.showDialog(this, "Select");
if (JFileChooser.APPROVE_OPTION == result) {
f = chooser.getSelectedFile();
String newdir = f.getPath();
button.setText(newdir);
EditorConfig.setTestTileDirectory(newdir);
}
}
}
@@ -0,0 +1,288 @@
//
// $Id: SceneInfoPanel.java 20143 2005-03-30 01:12:48Z mdb $
package com.threerings.stage.tools.editor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.util.HashSet;
import java.util.Iterator;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import com.samskivert.swing.GroupLayout;
import com.samskivert.swing.HGroupLayout;
import com.samskivert.util.CollectionUtil;
import com.samskivert.util.SortableArrayList;
import com.samskivert.util.SortedIterator;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.miso.data.SparseMisoSceneModel.ObjectVisitor;
import com.threerings.media.image.ColorPository;
import com.threerings.media.tile.NoSuchTileSetException;
import com.threerings.media.tile.RecolorableTileSet;
import com.threerings.media.tile.TileManager;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.TileUtil;
import com.threerings.stage.data.StageMisoSceneModel;
import com.threerings.stage.data.StageScene;
import com.threerings.stage.tools.editor.util.EditorContext;
/**
* The scene info panel presents the user with options to select the
* scene layer to edit, and whether to display tile coordinates and
* locations in the scene view.
*/
public class SceneInfoPanel extends JPanel
implements ActionListener
{
/**
* Constructs the scene info panel.
*/
public SceneInfoPanel (EditorContext ctx, EditorModel model,
EditorScenePanel svpanel)
{
_ctx = ctx;
_svpanel = svpanel;
// configure the panel
GroupLayout gl = new HGroupLayout();
gl.setGap(12);
setLayout(gl);
JPanel vert = GroupLayout.makeVStretchBox(5);
JPanel hbox = GroupLayout.makeHBox();
// create a panel for the name label
hbox.add(createLabel("Scene name:", _scenename = new JTextField(10)));
_scenename.addActionListener(this);
_scenename.addFocusListener(new FocusAdapter() {
public void focusLost (FocusEvent e) {
_scenename.postActionEvent();
}
});
// create a drop-down for selecting the scene type
SortableArrayList types = new SortableArrayList();
ctx.enumerateSceneTypes(types);
types.sort();
types.add(0, "");
hbox.add(createLabel("Scene type:",
_scenetype = new JComboBox(types.toArray())));
_scenetype.addActionListener(this);
vert.add(hbox);
// set up the scene colorization stuff
hbox = GroupLayout.makeButtonBox(GroupLayout.LEFT);
hbox.add(_colorClasses = new JComboBox());
hbox.add(_colorIds = new JComboBox());
vert.add(createLabel("Colorizations:", hbox), GroupLayout.FIXED);
_colorClasses.addActionListener(this);
_colorIds.addActionListener(this);
_colorClasses.addPopupMenuListener(new PopupMenuListener() {
public void popupMenuCanceled (PopupMenuEvent e) { }
public void popupMenuWillBecomeInvisible (PopupMenuEvent e) { }
public void popupMenuWillBecomeVisible (PopupMenuEvent e) {
recomputeColorClasses();
}
});
add(vert, GroupLayout.FIXED);
}
protected JPanel createLabel (String label, JComponent comp)
{
JPanel panel = GroupLayout.makeButtonBox(GroupLayout.CENTER);
panel.add(new JLabel(label), GroupLayout.FIXED);
panel.add(comp);
return panel;
}
/**
* Called when the scene in the editor changes.
*/
public void setScene (StageScene scene)
{
_scene = scene;
_scenename.setText(scene.getName());
_scenetype.setSelectedItem(scene.getType());
recomputeColorClasses();
}
// documentation inherited from interface ActionListener
public void actionPerformed (ActionEvent e)
{
Object src = e.getSource();
if (src == _scenename) {
_scene.setName(_scenename.getText().trim());
} else if (src == _scenetype) {
_scene.setType((String) _scenetype.getSelectedItem());
} else if (src == _colorClasses) {
String cclass = (String) _colorClasses.getSelectedItem();
configureColorIds(cclass);
} else if (src == _colorIds) {
setNewDefaultColor();
}
}
/**
* Prior to the color classes popup popping up, recompute the possible
* values.
*/
protected void recomputeColorClasses ()
{
// add all possible colorization names to the list
final TileManager tilemgr = _ctx.getTileManager();
final HashSet set = new HashSet();
StageMisoSceneModel msmodel = StageMisoSceneModel.getSceneModel(
_scene.getSceneModel());
msmodel.visitObjects(new ObjectVisitor() {
public void visit (ObjectInfo info) {
int tsid = TileUtil.getTileSetId(info.tileId);
TileSet tset;
try {
tset = tilemgr.getTileSet(tsid);
} catch (NoSuchTileSetException nstse) {
return;
}
String[] zations;
if (tset instanceof RecolorableTileSet) {
zations = ((RecolorableTileSet) tset).getColorizations();
} else {
return;
}
if (zations != null) {
for (int ii=0; ii < zations.length; ii++) {
set.add(zations[ii]);
}
}
}
});
Object selected = _colorClasses.getSelectedItem();
DefaultComboBoxModel model =
(DefaultComboBoxModel) _colorClasses.getModel();
model.removeAllElements();
for (Iterator itr = new SortedIterator(set); itr.hasNext(); ) {
model.addElement(itr.next());
}
if (selected != null) {
_colorClasses.setSelectedItem(selected);
}
}
/**
* Show which color Ids are available for the currently selected
* colorization class, and select the selected one.
*/
protected void configureColorIds (String cclass)
{
_colorIds.removeActionListener(this);
try {
DefaultComboBoxModel model =
(DefaultComboBoxModel) _colorIds.getModel();
model.removeAllElements();
if (cclass == null) {
return;
}
ColorPository cpos = _ctx.getColorPository();
String noChoice = "<none>";
String choice = noChoice;
ColorPository.ClassRecord classRec = cpos.getClassRecord(cclass);
int pick = _scene.getDefaultColor(classRec.classId);
ColorPository.ColorRecord[] colors = cpos.enumerateColors(cclass);
SortableArrayList list = new SortableArrayList();
for (int ii=0; ii < colors.length; ii++) {
list.insertSorted(colors[ii].name);
if (colors[ii].colorId == pick) {
choice = colors[ii].name;
}
}
model.addElement(noChoice);
for (int ii=0; ii < list.size(); ii++) {
model.addElement(list.get(ii));
}
_colorIds.setSelectedItem(choice);
} finally {
_colorIds.addActionListener(this);
}
}
/**
* Called when a _colorIds color is selected.
*/
protected void setNewDefaultColor ()
{
String cclass = (String) _colorClasses.getSelectedItem();
if (cclass == null) {
return;
}
ColorPository cpos = _ctx.getColorPository();
ColorPository.ClassRecord classRec = cpos.getClassRecord(cclass);
ColorPository.ColorRecord[] colors = cpos.enumerateColors(cclass);
Object selected = _colorIds.getSelectedItem();
int pick = -1;
for (int ii=0; ii < colors.length; ii++) {
if (colors[ii].name.equals(selected)) {
pick = colors[ii].colorId;
break;
}
}
// only update the scene if our selection has actually changed
if (_scene.getDefaultColor(classRec.classId) != pick) {
_scene.setDefaultColor(classRec.classId, pick);
_svpanel.setScene(_scene);
_svpanel.repaint();
}
}
/** The giver of life. */
protected EditorContext _ctx;
/** The scene we're controlling. */
protected StageScene _scene;
/** The scene name entry field. */
protected JTextField _scenename;
/** The scene type selector. */
protected JComboBox _scenetype, _colorClasses, _colorIds;
/** The scene panel, which we hackily repaint when default colors change. */
protected EditorScenePanel _svpanel;
/** The object grip direction button. */
protected DirectionButton _dirbutton;
}
@@ -0,0 +1,173 @@
//
// $Id: TestTileLoader.java 9938 2003-06-20 03:55:58Z mdb $
package com.threerings.stage.tools.editor;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import javax.imageio.ImageIO;
import com.samskivert.util.HashIntMap;
import com.threerings.media.tile.ImageProvider;
import com.threerings.media.tile.SimpleCachingImageProvider;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.TileSetIDBroker;
import com.threerings.miso.tile.tools.xml.BaseTileSetRuleSet;
import com.threerings.media.tile.tools.xml.ObjectTileSetRuleSet;
import com.threerings.media.tile.tools.xml.SwissArmyTileSetRuleSet;
import com.threerings.media.tile.tools.xml.XMLTileSetParser;
/**
* The TestTileLoader handles test tiles. Test tiles are tiles that an
* artist can load in on-the-fly to see how things look in the scene editor.
*/
public class TestTileLoader implements TileSetIDBroker
{
/**
* Construct the TestTileLoader.
*/
public TestTileLoader ()
{
// our xml parser
_parser = new XMLTileSetParser();
// add some rulesets
_parser.addRuleSet("bundle/base", new BaseTileSetRuleSet());
_parser.addRuleSet("bundle/object", new ObjectTileSetRuleSet());
// we used to parse fringes, but we don't anymore
//_parser.addRuleSet("bundle/fringe", new SwissArmyTileSetRuleSet());
}
/**
* Check the specified directory and all its subdirectories for xml files.
* Each directory should contain at most one xml file, each xml file
* should specify at most one tileset. That tileset specification
* will be used to create tilesets for all the .png files in the same
* directory.
*
* @return a HashIntMap containing a TileSetId -> TileSet mapping for
* all the tilesets we create.
*/
public HashIntMap loadTestTiles ()
{
String directory = EditorConfig.getTestTileDirectory();
HashIntMap map = new HashIntMap();
// recurse test directory, making a tileset from the xml file inside
// and cloning it for each image we find in there.
File testdir = new File(directory);
// make sure it's a directory
if (!testdir.isDirectory()) {
Log.warning("Test tileset directory is not actually a directory: " +
directory);
return map;
}
// recursively load all the test tiles
loadTestTilesFromDir(testdir, map);
return map;
}
/**
* Load xml tile sets from a directory.
*/
protected void loadTestTilesFromDir (File directory,
HashIntMap sets)
{
// first recurse
File[] subdirs = directory.listFiles(new FileFilter() {
public boolean accept (File f) {
return f.isDirectory();
}
});
for (int ii=0; ii < subdirs.length; ii++) {
loadTestTilesFromDir(subdirs[ii], sets);
}
// now look for the xml file
String[] xml = directory.list(new FilenameFilter() {
public boolean accept (File dir, String name) {
return name.endsWith(".xml");
}
});
for (int ii=0; ii < xml.length; ii++) {
File xmlfile = new File(directory, xml[ii]);
HashMap tiles = new HashMap();
try {
_parser.loadTileSets(xmlfile, tiles);
} catch (IOException ioe) {
Log.warning("Error while parsing " + xmlfile.getPath());
Log.logStackTrace(ioe);
continue;
}
Iterator iter = tiles.values().iterator();
while (iter.hasNext()) {
TileSet ts = (TileSet) iter.next();
String path = new File(directory, ts.getImagePath()).getPath();
// before we insert, make sure we can load the image
if (null != _improv.getTileSetImage(path, null)) {
ts.setImageProvider(_improv);
ts.setImagePath(path);
sets.put(getTileSetID(path), ts);
}
}
}
}
/**
* Generate unique and completely fake tileset IDs that will be stable
* even after a reload of test tiles.
*/
public int getTileSetID (String tileSetPath)
{
Integer id = (Integer) _idmap.get(tileSetPath);
if (null == id) {
id = new Integer(_fakeID--);
_idmap.put(tileSetPath, id);
}
return id.intValue();
}
// documentation inherited
public boolean tileSetMapped (String tilesetPath)
{
return _idmap.containsKey(tilesetPath);
}
/**
* Since we're just testing, we don't save these crazy IDs.
*/
public void commit ()
{
// this method does nothing. perhaps it should be called "committee".
}
/** The value of the next fakeID we'll hand out. */
protected int _fakeID = -1;
/** A mapping of pathname -> tileset id. */
protected HashMap _idmap = new HashMap();
/** Our xml parser. */
protected XMLTileSetParser _parser;
/** Our image provider. */
protected ImageProvider _improv = new SimpleCachingImageProvider() {
protected BufferedImage loadImage (String path)
throws IOException {
return ImageIO.read(new File(path));
}
};
}
@@ -0,0 +1,707 @@
//
// $Id: TileInfoPanel.java 17625 2004-10-28 17:50:03Z mdb $
package com.threerings.stage.tools.editor;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JList;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.QuickSort;
import com.samskivert.util.StringUtil;
import com.threerings.media.SafeScrollPane;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.TileSetRepository;
import com.threerings.stage.tools.editor.util.EditorContext;
import com.threerings.stage.tools.editor.util.TileSetUtil;
/**
* The tile info panel presents the user with options to select the
* tile to be applied to the scene.
*/
public class TileInfoPanel extends JSplitPane
implements ListSelectionListener, TreeSelectionListener
{
/**
* Constructs the tile info panel.
*/
public TileInfoPanel (EditorContext ctx, EditorModel model)
{
TileSetRepository tsrepo = ctx.getTileSetRepository();
// set up our key observers
registerKeyListener(ctx);
_model = model;
// we're going to sort all of the available tilesets into those
// which are applicable to each layer
try {
_layerSets = new ArrayList[2];
_layerLengths = new int[2];
for (int ii=0; ii < 2; ii++) {
_layerSets[ii] = new ArrayList();
}
Iterator tsids = tsrepo.enumerateTileSetIds();
while (tsids.hasNext()) {
Integer tsid = (Integer)tsids.next();
TileSet set = tsrepo.getTileSet(tsid.intValue());
// determine which layer to which this tileset applies
int lidx = TileSetUtil.getLayerIndex(set);
if (lidx != -1) {
_layerSets[lidx].add(
new TileSetRecord(lidx, tsid.intValue(), set));
}
}
for (int ii=0; ii < 2; ii++) {
_layerLengths[ii] = _layerSets[ii].size();
}
} catch (Exception e) {
Log.warning("Error enumerating tilesets.");
Log.logStackTrace(e);
}
// set up a border denoting our contents
Border border = BorderFactory.createEtchedBorder();
setBorder(BorderFactory.createTitledBorder(border, "Tile Info"));
// create a tree for selecting tileset
DefaultMutableTreeNode root = new DefaultMutableTreeNode("");
_tsettree = new JTree(root);
// don't draw any funny little icons in the tree
DefaultTreeCellRenderer cellrend =
(DefaultTreeCellRenderer) _tsettree.getCellRenderer();
cellrend.setLeafIcon(null);
cellrend.setOpenIcon(null);
cellrend.setClosedIcon(null);
// tree- only let one thing be selected, and let us know when it haps
_tsettree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
_tsettree.addTreeSelectionListener(this);
// create a scrollpane to hold the tree
SafeScrollPane scrolly = new SafeScrollPane(_tsettree);
scrolly.setVerticalScrollBarPolicy(
SafeScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
DefaultListModel qmodel = new DefaultListModel();
for (int ii=0; ii < 10; ii++) {
qmodel.addElement("");
}
_quickList = new JList(qmodel);
_quickList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
_quickList.addListSelectionListener(this);
_quickList.setCellRenderer(new DefaultListCellRenderer() {
public Component getListCellRendererComponent (
JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus)
{
// put the key number in front of each element
Component result = super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
setText("" + ((index + 11) % 10) + ". " + getText());
return result;
}
});
JSplitPane leftSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
leftSplit.setTopComponent(new SafeScrollPane(_quickList));
leftSplit.setBottomComponent(scrolly);
// add the west side
setLeftComponent(leftSplit);
// create a table to display the tiles in the selected tileset
_tiletable = new JTable(_tablemodel = new TileTableModel());
_tiletable.getSelectionModel().addListSelectionListener(this);
_tiletable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
updateTileTable();
// wrap the table in a scrollpane for lengthy tilesets
_scroller = new SafeScrollPane(_tiletable);
_scroller.setVerticalScrollBarPolicy(
SafeScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
// add the tile table as the entire east side
setRightComponent(_scroller);
// add the tilesets and select the starting tile set
updateTileSetTree();
// The damn splitpane freaks out unless we do this
setDividerLocation(230);
}
/**
* Register key listeners to do things with the quick list.
*/
protected void registerKeyListener (EditorContext ctx)
{
ctx.getKeyDispatcher().addGlobalKeyListener(new KeyAdapter() {
public void keyTyped (KeyEvent e)
{
char keychar = e.getKeyChar();
if ((keychar < '0') || (keychar > '9')) {
return;
}
// turn 1 into 0, and 0 into 9
int index = (keychar - '0' + 9) % 10;
if (e.isControlDown() || e.isAltDown()) {
// add
if (_curTrec == null) {
return;
}
_quickList.clearSelection();
DefaultListModel model =
(DefaultListModel) _quickList.getModel();
int olddex = model.indexOf(_curTrec);
if (olddex != -1) {
model.set(olddex, "");
}
model.set(index, _curTrec);
_quickList.setSelectedIndex(index);
} else {
// select
_quickList.setSelectedIndex(index);
}
}
});
}
/**
* Selects the previous tile in the list of available tiles.
*/
public void selectPreviousTile ()
{
int row = _tiletable.getSelectedRow();
if (--row >= 0) {
_tiletable.setRowSelectionInterval(row, row);
}
}
/**
* Selects the next tile in the list of available tiles.
*/
public void selectNextTile ()
{
int row = _tiletable.getSelectedRow();
if (++row < _tiletable.getRowCount()) {
_tiletable.setRowSelectionInterval(row, row);
}
}
// documentation inherited
public Dimension getPreferredSize ()
{
return new Dimension(WIDTH, HEIGHT);
}
/**
* Display the tiles in a tileset when it is selected.
*/
public void valueChanged (TreeSelectionEvent e)
{
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) _tsettree.getLastSelectedPathComponent();
// we only care when a leaf is selected
if ((node != null) && (node.isLeaf())) {
_selected = node;
Object uobj = node.getUserObject();
if (!(uobj instanceof TileSetRecord)) {
Log.info("Eh? Non-TileSetRecord leaf [obj=" + uobj +
", class=" + StringUtil.shortClassName(uobj) + "].");
return;
}
tileSetSelected((TileSetRecord) uobj);
_quickList.clearSelection();
}
}
/**
* Called when a tileset is selected, either via the tree
* or the recent list.
*/
protected void tileSetSelected (TileSetRecord trec)
{
// if they've selected something new, update our tile display
if (_model.getTileSet() != trec.tileSet) {
_curTrec = trec;
_model.setLayerIndex(trec.layer);
// update the model to reflect new tile set and select tile
// zero by default
_model.setTile(trec.tileSet, trec.tileSetId, 0);
// update the tile table to reflect the new tileset
updateTileTable();
// _quickList.removeListSelectionListener(this);
// // add it to the recent list
// DefaultListModel recentModel = (DefaultListModel)
// _quickList.getModel();
// recentModel.removeElement(trec);
// recentModel.add(0, trec);
// _quickList.setSelectedIndex(0);
// _quickList.addListSelectionListener(this);
}
}
/**
* Remove previous test tiles and insert the new batch.
*/
protected void insertTestTiles (HashIntMap tests)
{
// trim the tilesets back to remove any previous test tiles
for (int ii=0; ii < 2; ii++) {
for (int jj=_layerSets[ii].size() - 1; jj >= _layerLengths[ii];
jj--) {
_layerSets[ii].remove(jj);
}
}
// insert the new test tiles
Iterator iter = tests.keys();
while (iter.hasNext()) {
Integer tsid = (Integer) iter.next();
TileSet set = (TileSet) tests.get(tsid);
// determine which layer to which this tileset applies
int lidx = TileSetUtil.getLayerIndex(set);
if (lidx != -1) {
// make up a negative number to refer to this temporary tileset
_layerSets[lidx].add(
new TileSetRecord(lidx, tsid.intValue(), set));
}
}
updateTileSetTree();
}
/**
* The layer has changed, update the tree to reflect the tilesets
* now available.
*/
public void updateTileSetTree ()
{
// first clear out the tree
DefaultTreeModel model = (DefaultTreeModel) _tsettree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
root.removeAllChildren();
ArrayList expand = new ArrayList();
// add all the elements in the base layer
DefaultMutableTreeNode base = new DefaultMutableTreeNode("Base Layer");
root.add(base);
addNodes(base, getSortedTileSets(EditorModel.BASE_LAYER),
"", 0, expand);
// add all the elements in the object layer
DefaultMutableTreeNode obj = new DefaultMutableTreeNode("Object Layer");
root.add(obj);
addNodes(obj, getSortedTileSets(EditorModel.OBJECT_LAYER),
"", 0, expand);
// notify the JTree that we've put some brand new branches on it.
model.reload();
// expand our container categories
for (Iterator iter = expand.iterator(); iter.hasNext(); ) {
_tsettree.expandPath((TreePath)iter.next());
}
// now select the previously selected item, or the first...
if (_selected == null) {
_selected = (DefaultMutableTreeNode) root.getFirstLeaf();
}
_tsettree.setSelectionPath(new TreePath(_selected.getPath()));
}
/**
* Populate the tree with the available tilesets for the selected layer.
*/
protected TileSetRecord[] getSortedTileSets (int layer)
{
// get the list of tilesets we now want to show
ArrayList sets = _layerSets[layer];
// we don't want to sort the actual array since we have
// kept the test tiles at the end
TileSetRecord[] sorted = new TileSetRecord[sets.size()];
sets.toArray(sorted);
QuickSort.sort(sorted);
return sorted;
}
/**
* Recursively add tilesets to the tree.
*
* @param prefix The portion of the full tileset name that we've
* already parsed, it corresponds to the node we're adding to.
* @param position The position in the array from whence to start adding.
* @return the number of elements added to 'node' from 'list'.
*/
protected int addNodes (DefaultMutableTreeNode node,
TileSetRecord[] list, String prefix, int position,
ArrayList expand)
{
int prefixlen = prefix.length();
for (int ii = position; ii < list.length; ) {
String name = list[ii].fullname();
// if the next name on the list doesn't start with the prefix,
// we have no business adding it to this node.
if (!name.startsWith(prefix)) {
return ii - position;
}
// is there another category name?
int dex = name.indexOf('/', prefixlen);
if (dex == -1) {
// nope, just add this item to the node.
DefaultMutableTreeNode item = new DefaultMutableTreeNode(
list[ii]);
node.add(item);
// oh, we're so sneaky!
// if the item we're adding has the same TileSetRecord
// as the previously selected item, we're going to want to
// select it..
if ((_selected != null) &&
(list[ii].equals(_selected.getUserObject()))) {
_selected = item;
}
ii++;
} else {
// new category!
String catname = name.substring(prefixlen, dex);
DefaultMutableTreeNode category =
new DefaultMutableTreeNode(catname);
node.add(category);
// if we have further categories below, start expanded
if (name.indexOf('/', dex+1) != -1) {
expand.add(new TreePath(category.getPath()));
}
// recurse..
ii += addNodes(category, list, name.substring(0, dex + 1),
ii, expand);
}
}
return list.length - position;
}
/**
* Update the tile table to reflect the currently selected tile set.
*/
protected void updateTileTable ()
{
// get the table width before we update the table model since
// updating the model seems to reset the table width to an
// incorrect default
TableColumn tcol = _tiletable.getColumnModel().getColumn(0);
_tablewid = tcol.getWidth() - (2 * EDGE_TILE_H);
// clear out the old selection because we're going to change
// tilesets
_tiletable.clearSelection();
// update the table model with the new tile set tiles
_tablemodel.updateTileSet();
// if there are no tiles in the current tile set, we're done
if (!_model.isTileValid()) {
return;
}
// set row heights to match the scaled tile image heights
int numTiles = getTileCount();
TileSet set = _model.getTileSet();
for (int ii = 0; ii < numTiles; ii++) {
Image img = set.getRawTileImage(ii);
int hei = getScaledTileImageHeight(img);
_tiletable.setRowHeight(ii, hei + (2 * EDGE_TILE_V));
}
// select the selected tile
int tid = _model.getTileId();
_tiletable.setRowSelectionInterval(tid, tid);
if (_scroller != null) {
// scroll to the selected tile
Rectangle r = _tiletable.getCellRect(tid, 0, true);
_scroller.getViewport().setViewPosition(new Point(r.x, r.y));
}
}
/**
* Handle tile table selections.
*/
public void valueChanged (ListSelectionEvent e)
{
// ignore extra messages
if (e.getValueIsAdjusting()) {
return;
}
Object src = e.getSource();
if (src == _quickList) {
Object o = _quickList.getSelectedValue();
if (o instanceof TileSetRecord) {
tileSetSelected((TileSetRecord) o);
if (o != _selected.getUserObject()) {
_tsettree.clearSelection();
}
}
} else {
// otherwise they clicked on the tile table.
ListSelectionModel lsm = (ListSelectionModel) src;
if (!lsm.isSelectionEmpty()) {
_model.setTileId(lsm.getMinSelectionIndex());
}
}
}
/**
* Returns the number of tiles in the currently selected tileset.
*/
protected int getTileCount ()
{
if (!_model.isTileValid()) {
return 0;
} else {
TileSet set = _model.getTileSet();
return (set == null) ? 0 : set.getTileCount();
}
}
/**
* Returns the height of the given tile image after scaling to fit
* within the width of the tile table.
*/
protected int getScaledTileImageHeight (Image img)
{
int wid = img.getWidth(null), hei = img.getHeight(null);
if (wid > _tablewid) {
float frac = (float)wid / (float)_tablewid;
return (int)(hei / frac);
}
return hei;
}
/**
* Extends the {@link AbstractTableModel} to encapsulate the table
* layout and display options required when displaying the tiles in
* the currently selected tileset.
*/
protected class TileTableModel extends AbstractTableModel
{
/**
* Called when the tile set associated with the table has been
* changed. Clears the cached image icons used to display each
* cell and updates the number of rows in the table to properly
* deal with tile sets of varying sizes.
*/
public synchronized void updateTileSet ()
{
int numTiles = getTileCount();
_icons = new ImageIcon[numTiles];
fireTableRowsInserted(0, numTiles);
}
// documentation inherited
public int getColumnCount ()
{
return 1;
}
// documentation inherited
public String getColumnName (int columnIndex)
{
return null;
}
// documentation inherited
public int getRowCount ()
{
return getTileCount();
}
// documentation inherited
public Object getValueAt (int row, int col)
{
// return the icon immediately if it's already cached
if (_icons[row] != null) {
return _icons[row];
}
// generate and save off the tile image scaled to fit the table
TileSet set = _model.getTileSet();
Image img = set.getRawTileImage(row);
int hei = getScaledTileImageHeight(img);
if (hei != img.getHeight(null)) {
img = img.getScaledInstance(
_tablewid, hei, Image.SCALE_SMOOTH);
}
return (_icons[row] = new ImageIcon(img));
}
// documentation inherited
public Class getColumnClass (int c)
{
// return the object associated with the column to force
// rendering of our icon images rather than straight text
return getValueAt(0, c).getClass();
}
/** The image icons used to display the table cell contents. */
protected ImageIcon _icons[];
}
/**
* Used to manage tilesets in the tileset selection combobox.
*/
protected static class TileSetRecord implements Comparable
{
public int layer;
public int tileSetId;
public TileSet tileSet;
public String shortname;
public TileSetRecord (int layer, int tileSetId, TileSet tileSet)
{
this.layer = layer;
this.tileSetId = tileSetId;
this.tileSet = tileSet;
shortname = fullname();
// cut everything before the last slash for our shortname
int lastdex = shortname.lastIndexOf('/');
if (lastdex != -1) {
shortname = shortname.substring(lastdex + 1);
}
}
public String fullname ()
{
return tileSet.getName();
}
public String toString ()
{
return shortname;
}
public int compareTo (Object o)
{
return fullname().compareToIgnoreCase(
((TileSetRecord) o).fullname());
}
public boolean equals (Object o)
{
if (o instanceof TileSetRecord) {
TileSetRecord tsr = (TileSetRecord) o;
return ((tsr.layer == layer) && (tsr.tileSetId == tileSetId));
}
return false;
}
}
/** Default desired panel dimensions. */
protected static final int WIDTH = 400;
protected static final int HEIGHT = 300;
/** Buffer space surrounding each tile in the tile table. */
protected static final int EDGE_TILE_H = 4;
protected static final int EDGE_TILE_V = 4;
/** An ArrayList of TileSetRecords for each layer. */
protected ArrayList[] _layerSets;
/** The original number of TileSetRecords for each layer. */
protected int[] _layerLengths;
/** The tree listing available tilesets. */
protected JTree _tsettree;
/** The selected tree node. */
protected DefaultMutableTreeNode _selected;
/** The table listing all tiles in the selected tileset. */
protected JTable _tiletable;
/** The list of quickly-selectable tilesets. */
protected JList _quickList;
/** The currently selected tileset record. */
protected TileSetRecord _curTrec;
/** The width of the tile table column in pixels. */
protected int _tablewid;
/** The scroll pane containing the tile table. */
protected SafeScrollPane _scroller;
/** The editor model. */
protected EditorModel _model;
/** The tile table data model. */
protected TileTableModel _tablemodel;
}
@@ -0,0 +1,31 @@
//
// $Id: EditorContext.java 7429 2003-04-01 02:19:34Z mdb $
package com.threerings.stage.tools.editor.util;
import java.util.List;
import com.threerings.media.image.ColorPository;
import com.threerings.media.tile.TileSetRepository;
import com.threerings.stage.util.StageContext;
public interface EditorContext extends StageContext
{
/**
* Return a reference to the tile set repository in use by the tile
* manager. This reference is valid for the lifetime of the
* application.
*/
public TileSetRepository getTileSetRepository ();
/**
* Returns a colorization repository for use by the editor.
*/
public ColorPository getColorPository ();
/**
* Inserts all known scene types into the supplied list.
*/
public void enumerateSceneTypes (List types);
}
@@ -0,0 +1,100 @@
//
// $Id: EditorDialogUtil.java 9223 2003-05-27 02:35:00Z mdb $
package com.threerings.stage.tools.editor.util;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane;
import com.threerings.util.DirectionUtil;
public class EditorDialogUtil
{
/**
* Add a button to a container with the given parameters and
* action listener.
*
* @param l the listener.
* @param container the container.
* @param name the button name.
* @param cmd the action command.
*/
public static void addButton (ActionListener l, Container container,
String name, String cmd)
{
JButton button = new JButton(name);
button.addActionListener(l);
button.setActionCommand(cmd);
container.add(button);
}
/**
* Create and return a combo box seeded with the various possible
* orientation direction names.
*
* @param l the listener.
*
* @return the combo box.
*/
public static JComboBox getOrientationComboBox (ActionListener l)
{
JComboBox box = new JComboBox(DirectionUtil.getDirectionNames());
box.addActionListener(l);
box.setActionCommand("orient");
return box;
}
/**
* Centers the supplied dialog in its parent's bounds.
*/
public static void center (JFrame parent, JInternalFrame dialog)
{
Dimension psize = parent.getSize();
Dimension dsize = dialog.getSize();
dialog.setLocation((psize.width-dsize.width)/2,
(psize.height-dsize.height)/2);
}
/**
* Display a dialog centered within the given frame.
*
* @param parent the parent frame.
* @param dialog the dialog.
*/
public static void display (JFrame parent, JInternalFrame dialog)
{
center(parent, dialog);
parent.getLayeredPane().add(dialog, JLayeredPane.POPUP_LAYER);
dialog.setVisible(true);
}
/**
* Removes the supplied dialog from its parent container, but does not
* dispose it.
*/
public static void dismiss (JInternalFrame dialog)
{
Container parent = dialog.getParent();
if (parent != null) {
parent.remove(dialog);
parent.repaint();
}
dialog.setVisible(false);
}
/**
* Handles safely dismissing and disposing of the supplied dialog.
*/
public static void dispose (JInternalFrame dialog)
{
dismiss(dialog);
dialog.dispose();
}
}
@@ -0,0 +1,14 @@
//
// $Id: ExtrasPainter.java 7998 2003-04-18 18:34:41Z mdb $
package com.threerings.stage.tools.editor.util;
import java.awt.Graphics2D;
/**
* An interface for painting extra stuff.
*/
public interface ExtrasPainter
{
public void paintExtras (Graphics2D gfx);
}
@@ -0,0 +1,30 @@
//
// $Id: TileSetUtil.java 9371 2003-06-02 20:28:21Z mdb $
package com.threerings.stage.tools.editor.util;
import com.threerings.media.tile.TileSet;
import com.threerings.miso.tile.BaseTileSet;
import com.threerings.stage.tools.editor.Log;
import com.threerings.stage.tools.editor.EditorModel;
/**
* Miscellaneous useful routines for working with lists of {@link TileSet}
* and {@link BaseTileSet} objects.
*/
public class TileSetUtil
{
/**
* Returns the layer index of the layer for which this tileset
* provides tiles.
*/
public static int getLayerIndex (TileSet set)
{
if (set instanceof BaseTileSet) {
return EditorModel.BASE_LAYER;
} else {
return EditorModel.OBJECT_LAYER;
}
}
}
@@ -0,0 +1,21 @@
//
// $Id: StageMisoSceneRuleSet.java 20143 2005-03-30 01:12:48Z mdb $
package com.threerings.stage.tools.xml;
import com.threerings.miso.data.SparseMisoSceneModel;
import com.threerings.miso.tools.xml.SparseMisoSceneRuleSet;
import com.threerings.stage.data.StageMisoSceneModel;
/**
* Customizes the miso scene rule set such that it creates {@link
* StageMisoSceneModel} instances.
*/
public class StageMisoSceneRuleSet extends SparseMisoSceneRuleSet
{
protected SparseMisoSceneModel createMisoSceneModel ()
{
return new StageMisoSceneModel();
}
}
@@ -0,0 +1,67 @@
//
// $Id: StageSceneParser.java 16546 2004-07-27 20:49:56Z ray $
package com.threerings.stage.tools.xml;
import org.apache.commons.digester.Rule;
import org.xml.sax.Attributes;
import com.threerings.whirled.spot.tools.xml.SpotSceneRuleSet;
import com.threerings.whirled.tools.xml.SceneParser;
import com.threerings.whirled.tools.xml.SceneRuleSet;
import com.threerings.stage.data.StageSceneModel;
/**
* Parses {@link StageSceneModel} instances from an XML description file.
*/
public class StageSceneParser extends SceneParser
{
/**
* Constructs a parser that can be used to parse Stage scene models.
*/
public StageSceneParser ()
{
super("");
// add a rule to parse scene colorizations
_digester.addRule("scene/zations/zation", new Rule() {
public void begin (String namespace, String name,
Attributes attrs) throws Exception {
StageSceneModel yoscene = (StageSceneModel) digester.peek();
int classId = Integer.parseInt(attrs.getValue("classId"));
int colorId = Integer.parseInt(attrs.getValue("colorId"));
yoscene.setDefaultColor(classId, colorId);
}
});
// add rule sets for our aux scene models
registerAuxRuleSet(new SpotSceneRuleSet());
registerAuxRuleSet(new StageMisoSceneRuleSet());
}
// documentation inherited from interface
protected SceneRuleSet createSceneRuleSet ()
{
return new StageSceneRuleSet();
}
/**
* A simple hook for parsing a single scene from the command line.
*/
public static void main (String[] args)
{
if (args.length < 1) {
System.err.println("Usage: StageSceneParser scene.xml");
System.exit(-1);
}
try {
System.out.println(
"Parsed " + new StageSceneParser().parseScene(args[0]));
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
@@ -0,0 +1,21 @@
//
// $Id: StageSceneRuleSet.java 6370 2003-02-12 07:32:00Z mdb $
package com.threerings.stage.tools.xml;
import com.threerings.whirled.tools.xml.SceneRuleSet;
import com.threerings.stage.data.StageScene;
import com.threerings.stage.data.StageSceneModel;
/**
* Used to parse an {@link StageScene} from XML.
*/
public class StageSceneRuleSet extends SceneRuleSet
{
// documentation inherited
protected Class getSceneClass ()
{
return StageSceneModel.class;
}
}
@@ -0,0 +1,67 @@
//
// $Id: StageSceneWriter.java 20143 2005-03-30 01:12:48Z mdb $
package com.threerings.stage.tools.xml;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import com.megginson.sax.DataWriter;
import com.threerings.miso.tools.xml.SparseMisoSceneWriter;
import com.threerings.whirled.spot.data.SpotSceneModel;
import com.threerings.whirled.spot.tools.xml.SpotSceneWriter;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.tools.xml.SceneWriter;
import com.threerings.stage.data.StageMisoSceneModel;
import com.threerings.stage.data.StageSceneModel;
/**
* Generates an XML representation of a {@link StageSceneModel}.
*/
public class StageSceneWriter extends SceneWriter
{
public StageSceneWriter ()
{
// register our auxiliary model writers
registerAuxWriter(SpotSceneModel.class, new SpotSceneWriter());
registerAuxWriter(StageMisoSceneModel.class,
new SparseMisoSceneWriter());
}
// documentation inherited
protected void addSceneAttributes (SceneModel scene, AttributesImpl attrs)
{
super.addSceneAttributes(scene, attrs);
StageSceneModel sscene = (StageSceneModel)scene;
attrs.addAttribute("", "type", "", "", sscene.type);
}
// documentation inherited
protected void writeSceneData (SceneModel scene, DataWriter writer)
throws SAXException
{
// write out any default colorizations
StageSceneModel sscene = (StageSceneModel)scene;
if (sscene.defaultColors != null) {
writer.startElement("zations");
int[] keys = sscene.defaultColors.getKeys();
for (int ii=0, nn=keys.length; ii < nn; ii++) {
int value = sscene.defaultColors.get(keys[ii]);
AttributesImpl attrs = new AttributesImpl();
attrs.addAttribute("", "classId", "", "",
String.valueOf(keys[ii]));
attrs.addAttribute("", "colorId", "", "",
String.valueOf(value));
writer.emptyElement("", "zation", "", attrs);
}
writer.endElement("zations");
}
super.writeSceneData(scene, writer);
}
}
@@ -0,0 +1,16 @@
#
# $Id: manager.properties 2121 2003-01-13 22:55:28Z mdb $
#
# Test resource manager configuration
# configure the BundledTileSetRepository
resource.set.tilesets = bundles/tiles/ground/bundle.jar:\
bundles/tiles/objects/bundle.jar
# configure the BundledComponentRepository
resource.set.components = bundles/components/metadata.jar:\
bundles/components/pirate/components.jar: \
bundles/components/vessel/components.jar
resource.set.general = \
bundles/components/pirate/components.jar
+103
View File
@@ -0,0 +1,103 @@
#
# $Id: iconmgr.properties 20012 2005-03-24 23:19:41Z ray $
#
# Configuration for the icon manager
# simple arrow images in the order: down, up, right, left
arrows.path = media/yohoho/icons/arrows.png
arrows.metrics = 16, 16
# simpler arrows used to change values
valuearrows.path = media/yohoho/icons/value_arrows.png
valuearrows.metrics = 16, 15
# up and down arrows with "+" and "-" in them
zoomarrows.path = media/yohoho/icons/zoom_arrows.png
zoomarrows.metrics = 24, 14
# collapsible entry plus and minus icons
collapse.path = media/yohoho/icons/collapse.png
collapse.metrics = 8, 8
# icons for the tabbed panes
tabtitles.path = media/yohoho/icons/tabtitles.png
tabtitles.metrics = 16, 16
# icons for crew ranks
ranks.path = media/yohoho/icons/crew_ranks.png
ranks.metrics = 25, 25
# icon for mateys
matey.path = media/yohoho/icons/matey_icon.png
matey.metrics = 25, 25
# icons for radial menus
radmenu.path = media/yohoho/icons/radial.png
radmenu.metrics = 32, 32
# icons for portal traversal
portal.path = media/yohoho/icons/portal_arrows.png
portal.metrics = 48, 48
# icons for youser activities
activity.path = media/yohoho/icons/activity.png
activity.metrics = 27, 27
# icons for youser activities
puzzles.path = media/yohoho/icons/puzzles.png
puzzles.metrics = 24, 24
# icons for pirate info icons
pirateinfo.path = media/yohoho/icons/pirate_info.png
pirateinfo.metrics = 28, 28
# icons for scrolly history
historyscroll.path = media/yohoho/icons/history-scroll.png
historyscroll.metrics = 14, 11
# icons for full-screen history
historyfull.path = media/yohoho/icons/history-full.png
historyfull.metrics = 14, 11
# icons for the duty status button
dutystatus.path = media/yohoho/icons/duty-status.png
dutystatus.metrics = 16, 12
hiring.path = media/yohoho/icons/hiring.png
hiring.metrics = 17, 17
# icons for the wee lock images
lock.path = media/yohoho/icons/lock.png
lock.metrics = 23, 18
# icon for the ferry
ferry.path = media/yohoho/icons/ferry.png
ferry.metrics = 58, 48
# icon for whisking
whisk.path = media/yohoho/icons/whisk.png
whisk.metrics = 12, 11
# icon to indicate that a player was present for a sea battle
sea_battle.path = media/yohoho/icons/sea_battle.png
sea_battle.metrics = 13, 12
# item menu icons
item_action.path = media/yohoho/icons/item_actions.png
item_action.metrics = 27, 27
# portrait eye menu icons
portrait_eyes_male.path = media/yohoho/icons/portrait_eyes_male.png
portrait_eyes_male.metrics = 70, 25
portrait_eyes_female.path = media/yohoho/icons/portrait_eyes_female.png
portrait_eyes_female.metrics = 70, 25
# portrait mouth menu icons
portrait_mouths_male.path = media/yohoho/icons/portrait_mouths_male.png
portrait_mouths_male.metrics = 70, 25
portrait_mouths_female.path = media/yohoho/icons/portrait_mouths_female.png
portrait_mouths_female.metrics = 70, 25
# dnd icon
dnd.path = media/yohoho/icons/dnd.png
dnd.metrics = 32, 32