/* * Copyright (C) 2007, 2008, 2010, 2011 IsmAvatar <IsmAvatar@gmail.com> * Copyright (C) 2007, 2008, 2009 Quadduc <quadduc@gmail.com> * Copyright (C) 2014, egofree * * This file is part of LateralGM. * LateralGM is free software and comes with ABSOLUTELY NO WARRANTY. * See LICENSE for details. */ package org.lateralgm.components.visual; import static org.lateralgm.main.Util.deRef; import static org.lateralgm.main.Util.gcd; import static org.lateralgm.main.Util.negDiv; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.List; import javax.swing.JCheckBoxMenuItem; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.undo.CompoundEdit; import javax.swing.undo.UndoableEdit; import org.lateralgm.main.LGM; import org.lateralgm.messages.Messages; import org.lateralgm.resources.Background; import org.lateralgm.resources.Background.PBackground; import org.lateralgm.resources.GmObject; import org.lateralgm.resources.ResourceReference; import org.lateralgm.resources.Room; import org.lateralgm.resources.Room.PRoom; import org.lateralgm.resources.Room.Piece; import org.lateralgm.resources.sub.Instance; import org.lateralgm.resources.sub.Instance.PInstance; import org.lateralgm.resources.sub.Tile; import org.lateralgm.resources.sub.Tile.PTile; import org.lateralgm.resources.sub.View; import org.lateralgm.resources.sub.View.PView; import org.lateralgm.subframes.CodeFrame; import org.lateralgm.subframes.RoomFrame; import org.lateralgm.ui.swing.visuals.RoomVisual; import org.lateralgm.util.ActiveArrayList; import org.lateralgm.util.AddPieceInstance; import org.lateralgm.util.ModifyPieceInstance; import org.lateralgm.util.PropertyMap; import org.lateralgm.util.PropertyMap.PropertyUpdateEvent; import org.lateralgm.util.PropertyMap.PropertyUpdateListener; import org.lateralgm.util.PropertyMap.PropertyValidator; import org.lateralgm.util.RemovePieceInstance; public class RoomEditor extends VisualPanel { private static final long serialVersionUID = 1L; public static final int ZOOM_MIN = -1; public static final int ZOOM_MAX = 2; private final Room room; protected final RoomFrame frame; private Piece cursor; // The selected piece has a white border private Piece selectedPiece; public final PropertyMap<PRoomEditor> properties; public final RoomVisual roomVisual; private final RoomPropertyListener rpl = new RoomPropertyListener(); private final RoomEditorPropertyValidator repv = new RoomEditorPropertyValidator(); // Save the original position of a selected piece (Used when moving an object for the undo) private Point objectFirstPosition = null; // Option which set if the tiles editing is available for all layers or only for the selected one private boolean editOtherLayers = false; // Rectangle which stores the user's selection public Rectangle selection = null; // Original position when drawing a selection private Point selectionOrigin = null; // Save the original position of the selected instances/tiles private Point selectedPiecesOrigin = null; // The instances selected by the user private List<Instance> selectedInstances = new ArrayList<Instance>(); private List<Tile> selectedTiles = new ArrayList<Tile>(); // Show if the user has pasted a region private boolean pasteMode = false; // Show if the alt key has been pressed private boolean altKeyHasBeenPressed = false; // Save if the ctrl key has been pressed private boolean ctrlKeyHasBeenPressed = false; // Show if the shift key has been pressed private boolean shiftKeyHasBeenPressed = false; public enum PRoomEditor { SHOW_GRID,SHOW_OBJECTS(RoomVisual.Show.INSTANCES),SHOW_TILES,SHOW_BACKGROUNDS,SHOW_FOREGROUNDS, SHOW_VIEWS,DELETE_UNDERLYING_OBJECTS,DELETE_UNDERLYING_TILES,GRID_OFFSET_X,GRID_OFFSET_Y,ZOOM, SINGLE_SELECTION,MULTI_SELECTION,SNAP_TO_GRID,ADD_ON_TOP,ADD_MULTIPLE; final RoomVisual.Show rvBinding; private PRoomEditor() { String n = name(); if (n.startsWith("SHOW_")) rvBinding = RoomVisual.Show.valueOf(n.substring(5)); else rvBinding = null; } private PRoomEditor(RoomVisual.Show b) { rvBinding = b; } } private static final EnumMap<PRoomEditor,Object> DEFS = PropertyMap.makeDefaultMap( PRoomEditor.class,true,true,true,true,true,false,true,true,0,0,1,true,false,true,false,false); public RoomEditor(Room r, RoomFrame frame) { if (r.get(PRoom.REMEMBER_WINDOW_SIZE)) { EnumMap<PRoomEditor,Object> m = new EnumMap<PRoomEditor,Object>(PRoomEditor.class); for (PRoomEditor pre : PRoomEditor.values()) try { m.put(pre,r.get(PRoom.valueOf(pre.toString()))); } catch (IllegalArgumentException e) { m.put(pre,DEFS.get(pre)); } properties = new PropertyMap<PRoomEditor>(PRoomEditor.class,repv,m); } else properties = new PropertyMap<PRoomEditor>(PRoomEditor.class,repv,DEFS); room = r; this.frame = frame; setFocusable(true); zoomOrigin = ORIGIN_MOUSE; r.properties.updateSource.addListener(rpl); enableEvents(MouseEvent.MOUSE_EVENT_MASK | MouseEvent.MOUSE_MOTION_EVENT_MASK); EnumSet<RoomVisual.Show> s = EnumSet.noneOf(RoomVisual.Show.class); for (PRoomEditor p : PRoomEditor.values()) if (p.rvBinding != null && (Boolean) properties.get(p)) s.add(p.rvBinding); lockBounds(); roomVisual = new RoomVisual(container,r,s); unlockBounds(); put(0,roomVisual); setOpaque(false); // so the EditorScrollPane's transparency pattern shows through setZoom((Integer) properties.get(PRoomEditor.ZOOM)); refresh(); } // Set if the tiles editing is available for all layers or only for the selected one public void editOtherLayers(boolean editOtherLayers) { this.editOtherLayers = editOtherLayers; } public Room getRoom() { return room; } public void refresh() { revalidate(); repaint(); } public Piece getSelectedPiece() { return selectedPiece; } public void setSelectedPiece(Piece selectedPiece) { this.selectedPiece = selectedPiece; } // Save the selected tiles and make a buffer image public void copySelectionTiles() { if (selection == null) return; selectedTiles.clear(); selectedInstances.clear(); Room currentRoom = getRoom(); Point tilePosition; // Get the selected layer Integer depth = (Integer) frame.tileLayer.getSelectedItem(); // Save all tiles in the selected region for (Tile tile : currentRoom.tiles) { tilePosition = tile.getPosition(); // If the instance is in the selected region if (tilePosition.x >= selection.x && tilePosition.x < (selection.x + selection.width) && tilePosition.y >= selection.y && tilePosition.y < (selection.y + selection.height)) { // If the were editing only the current layer, and if the tile is not in the current layer if (!frame.tEditOtherLayers.isSelected() && tile.getDepth() != depth) continue; selectedTiles.add(tile); } } // Save the origin of the selected tiles selectedPiecesOrigin = new Point(selection.x,selection.y); // Make an image of the region made by the user roomVisual.setSelectionImage(null,selectedTiles); } // Save the selected instances and make a buffer image public void copySelectionInstances() { if (selection == null) return; selectedInstances.clear(); selectedTiles.clear(); Room currentRoom = getRoom(); Point instancePosition; // Save all instances in the selected region for (Instance instance : currentRoom.instances) { instancePosition = instance.getPosition(); // If the instance is in the selected region if (instancePosition.x >= selection.x && instancePosition.x < (selection.x + selection.width) && instancePosition.y >= selection.y && instancePosition.y < (selection.y + selection.height)) selectedInstances.add(instance); } // Save the origin of the selected instances; selectedPiecesOrigin = new Point(selection.x,selection.y); // Make an image of the region made by the user roomVisual.setSelectionImage(selectedInstances,null); } // Activate the object selection mode public void activateSelectObjectMode() { properties.put(PRoomEditor.SINGLE_SELECTION,true); } // Deactivate the object selection mode public void deactivateSelectObjectMode() { properties.put(PRoomEditor.SINGLE_SELECTION,false); } // Activate the rectangular selection mode public void activateSelectRegionMode() { properties.put(PRoomEditor.MULTI_SELECTION,true); } // Deactivate the rectangular selection mode public void deactivateSelectRegionMode() { properties.put(PRoomEditor.MULTI_SELECTION,false); roomVisual.setSelection(null); selection = null; } // Deactivate the paste mode public void deactivatePasteMode() { pasteMode = false; roomVisual.deactivatePasteMode(); } // Activate the paste mode public void activatePasteMode() { pasteMode = true; roomVisual.activatePasteMode(); // Disable the selection tool properties.put(PRoomEditor.MULTI_SELECTION,false); } // Paste the selected instances on the given mouse position private void pasteInstances(Point mousePosition) { boolean deleteUnderlyingInstances = properties.get(PRoomEditor.DELETE_UNDERLYING_OBJECTS); // Stores several actions in one compound action for the undo CompoundEdit compoundEdit = new CompoundEdit(); // If the 'Delete underlying' option is checked, delete all instances for the selected region if (deleteUnderlyingInstances) frame.deleteInstancesInSelection( new Rectangle(mousePosition.x,mousePosition.y,roomVisual.getSelectionImageWidth(), roomVisual.getSelectionImageHeight()),compoundEdit); for (Instance instance : selectedInstances) { Point position = instance.getPosition(); // Get the relative position of the instance inside the selected region Point newPosition = new Point(position.x - selectedPiecesOrigin.x + mousePosition.x, position.y - selectedPiecesOrigin.y + mousePosition.y); Instance newInstance = room.addInstance(); newInstance.properties.put(PInstance.OBJECT,instance.properties.get(PInstance.OBJECT)); newInstance.setRotation(instance.getRotation()); newInstance.setScale(instance.getScale()); newInstance.setColor(instance.getColor()); newInstance.setAlpha(instance.getAlpha()); newInstance.setCode(instance.getCode()); newInstance.setCreationCode(instance.getCreationCode()); newInstance.setPosition(newPosition); // Record the effect of adding a new instance for the undo UndoableEdit edit = new AddPieceInstance(frame,newInstance,room.instances.size() - 1); compoundEdit.addEdit(edit); } // Save the action for the undo compoundEdit.end(); frame.undoSupport.postEdit(compoundEdit); } // Paste the selected tiles on the given mouse position private void pasteTiles(Point mousePosition) { boolean deleteUnderlyingTiles = properties.get(PRoomEditor.DELETE_UNDERLYING_TILES); // Stores several actions in one compound action for the undo CompoundEdit compoundEdit = new CompoundEdit(); // If the 'Delete underlying' option is checked, delete all tiles for the selected region if (deleteUnderlyingTiles) frame.deleteTilesInSelection( new Rectangle(mousePosition.x,mousePosition.y,roomVisual.getSelectionImageWidth(), roomVisual.getSelectionImageHeight()),compoundEdit); for (Tile tile : selectedTiles) { Point position = tile.getPosition(); // Get the relative position of the tile inside the selected region Point newPosition = new Point(position.x - selectedPiecesOrigin.x + mousePosition.x, position.y - selectedPiecesOrigin.y + mousePosition.y); Tile newTile = new Tile(room,LGM.currentFile); newTile.properties.put(PTile.BACKGROUND,tile.properties.get(PTile.BACKGROUND)); newTile.setBackgroundPosition(tile.getBackgroundPosition()); newTile.setPosition(newPosition); newTile.setSize(tile.getSize()); newTile.setDepth(tile.getDepth()); room.tiles.add(newTile); // Record the effect of adding a new tile for the undo UndoableEdit edit = new AddPieceInstance(frame,newTile,room.tiles.size() - 1); compoundEdit.addEdit(edit); } // Save the action for the undo compoundEdit.end(); frame.undoSupport.postEdit(compoundEdit); } @Override protected void processMouseEvent(MouseEvent e) { super.processMouseEvent(e); mouseEdit(e); } @Override protected void processMouseMotionEvent(MouseEvent e) { super.processMouseMotionEvent(e); mouseEdit(e); } public void releaseCursor(Point lastPosition) { // Stores several actions in one compound action for the undo CompoundEdit compoundEdit = new CompoundEdit(); UndoableEdit edit = null; // If the piece was moved if (objectFirstPosition != null) // For the undo, record that the object was moved edit = new ModifyPieceInstance(frame,cursor,objectFirstPosition,new Point(lastPosition)); else // A new piece has been added { if (cursor instanceof Instance) edit = new AddPieceInstance(frame,cursor,room.instances.size() - 1); else edit = new AddPieceInstance(frame,cursor,room.tiles.size() - 1); } compoundEdit.addEdit(edit); objectFirstPosition = null; //it must be guaranteed that cursor != null boolean deleteUnderlyingObjects = properties.get(PRoomEditor.DELETE_UNDERLYING_OBJECTS); boolean deleteUnderlyingTiles = properties.get(PRoomEditor.DELETE_UNDERLYING_TILES); if (deleteUnderlyingObjects && cursor instanceof Instance) deleteUnderlying( roomVisual.intersectInstances(new Rectangle(lastPosition.x,lastPosition.y,1,1)), room.instances,compoundEdit); else if (deleteUnderlyingTiles && cursor instanceof Tile) deleteUnderlying( roomVisual.intersectTiles(new Rectangle(lastPosition.x,lastPosition.y,1,1),getTileDepth()), room.tiles,compoundEdit); // Save the action for the undo compoundEdit.end(); frame.undoSupport.postEdit(compoundEdit); unlockBounds(); cursor = null; } private <T>void deleteUnderlying(Iterator<T> i, ActiveArrayList<T> l, CompoundEdit compoundEdit) { HashSet<T> s = new HashSet<T>(); while (i.hasNext()) { T t = i.next(); if (t != cursor) { UndoableEdit edit; // Record the effect of removing an piece for the undo if (cursor instanceof Instance) edit = new RemovePieceInstance(frame,(Piece) t,room.instances.indexOf(t)); else edit = new RemovePieceInstance(frame,(Piece) t,room.tiles.indexOf(t)); compoundEdit.addEdit(edit); s.add(t); } } l.removeAll(s); } /** Do not call with null */ public void setCursor(Piece ds) { boolean addMultipleMode = properties.get(PRoomEditor.ADD_MULTIPLE); // If there was a selected piece, deselect it if (selectedPiece != null) selectedPiece.setSelected(false); // Save the selected piece if (ds != null) selectedPiece = ds; cursor = ds; // If we are not in 'Add multiple' mode, select the piece if (addMultipleMode == false) cursor.setSelected(true); if (ds instanceof Instance) { frame.oList.setSelectedValue(ds,true); frame.fireObjUpdate(); } else if (ds instanceof Tile) { frame.tList.setSelectedValue(ds,true); frame.fireTileUpdate(); } lockBounds(); } // Distance from the piece's origin int offsetX = 0; int offsetY = 0; private void processLeftButton(int modifiers, boolean pressed, Piece pieceUnderCursor, Point position) { // If we are modifying the position of a piece with the text fields, save the position for the undo if (frame.selectedPiece != null) { frame.processFocusLost(); this.requestFocusInWindow(); } boolean addMultipleMode = properties.get(PRoomEditor.ADD_MULTIPLE); boolean addOnTopMode = properties.get(PRoomEditor.ADD_ON_TOP); // If the 'add multiple' mode and 'add on top' modes are disabled if (addMultipleMode == false && addOnTopMode == false) { // If left button has been clicked and if there is an object under the cursor, move the object if (pressed && pieceUnderCursor != null && !pieceUnderCursor.isLocked()) { // Record the original position of the object (without snapping) for the undo objectFirstPosition = pieceUnderCursor.getPosition(); // Get distance from the piece's origin to prevent shifting when selecting a piece offsetX = position.x - pieceUnderCursor.getPosition().x; offsetY = position.y - pieceUnderCursor.getPosition().y; setCursor(pieceUnderCursor); } // If there is no objects under the cursor, add a new object if (pressed && pieceUnderCursor == null) { offsetX = 0; offsetY = 0; addNewPieceInstance(position); addMultipleMode = true; //prevents unnecessary coordinate update below } } else { offsetX = 0; offsetY = 0; // If the shift key is pressed, add objects under the cursor if (addMultipleMode && cursor != null) if (!roomVisual.intersects(new Rectangle(position.x,position.y,1,1),cursor)) { releaseCursor(position); pressed = true; //ensures that a new instance is created below } // If we have pressed the ctrl key if (pressed && cursor == null) { addNewPieceInstance(position); addMultipleMode = true; //prevents unnecessary coordinate update below } } if (cursor != null && !addMultipleMode) cursor.setPosition(new Point(position.x - offsetX,position.y - offsetY)); } private void addNewPieceInstance(Point position) { if (frame.tabs.getSelectedIndex() == Room.TAB_TILES) { ResourceReference<Background> bkg = frame.taSource.getSelected(); if (bkg == null) return; //I'd rather just break out of this IF, but this works Background b = bkg.get(); Tile t = new Tile(room,LGM.currentFile); t.properties.put(PTile.BACKGROUND,bkg); t.setBackgroundPosition(new Point(frame.tSelect.tx,frame.tSelect.ty)); t.setPosition(position); if (!(Boolean) b.get(PBackground.USE_AS_TILESET)) t.setSize(new Dimension(b.getWidth(),b.getHeight())); else t.setSize(new Dimension((Integer) b.get(PBackground.TILE_WIDTH), (Integer) b.get(PBackground.TILE_HEIGHT))); t.setDepth((Integer) frame.tileLayer.getSelectedItem()); room.tiles.add(t); setCursor(t); } else if (frame.tabs.getSelectedIndex() == Room.TAB_OBJECTS) { ResourceReference<GmObject> obj = frame.oNew.getSelected(); if (obj == null) return; //I'd rather just break out of this IF, but this works Instance instance = room.addInstance(); instance.properties.put(PInstance.OBJECT,obj); instance.setPosition(position); setCursor(instance); } } private void processRightButton(int modifiers, boolean pressed, final Piece mc, Point p) { // If we are modifying the position of a piece with the text fields, save the position for the undo if (frame.selectedPiece != null) { frame.processFocusLost(); this.requestFocusInWindow(); } // If there is a selected piece, deselect it if (selectedPiece != null) selectedPiece.setSelected(false); boolean addOnTopMode = properties.get(PRoomEditor.ADD_ON_TOP); if (addOnTopMode == true) { if (!pressed) return; JPopupMenu jp = new JPopupMenu(); JCheckBoxMenuItem cb = new JCheckBoxMenuItem( Messages.getString("RoomEditor.LOCKED"),mc.isLocked()); //$NON-NLS-1$ cb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { mc.setLocked(((JCheckBoxMenuItem) e.getSource()).isSelected()); } }); jp.add(cb); if (mc instanceof Instance) { final Instance i = (Instance) mc; JMenuItem mi = new JMenuItem(Messages.getString("RoomEditor.CREATION_CODE")); //$NON-NLS-1$ mi.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { frame.openInstanceCodeFrame(i); } }); jp.add(mi); } Point cp = p.getLocation(); visualToComponent(cp); jp.show(this,cp.x,cp.y); } else if (!mc.isLocked()) { ArrayList<?> alist = null; int pieceIndex = -1; JList<?> jlist = null; if (mc instanceof Instance) { pieceIndex = room.instances.indexOf(mc); if (pieceIndex == -1) return; alist = room.instances; jlist = frame.oList; CodeFrame fr = frame.codeFrames.get(mc); if (fr != null) fr.dispose(); } else if (mc instanceof Tile) { pieceIndex = room.tiles.indexOf(mc); if (pieceIndex == -1) return; alist = room.tiles; jlist = frame.tList; } else return; //unknown component with unknown lists // Record the effect of removing an object for the undo UndoableEdit edit = new RemovePieceInstance(frame,mc,pieceIndex); // notify the listeners frame.undoSupport.postEdit(edit); int i2 = jlist.getSelectedIndex(); alist.remove(pieceIndex); jlist.setSelectedIndex(Math.min(alist.size() - 1,i2)); } } // If the alt key was pressed, disable the snap to grid mode, if needed public void altKeyPressed() { boolean snapToGridMode = properties.get(PRoomEditor.SNAP_TO_GRID); // Save that the alt key has been pressed if (snapToGridMode) { altKeyHasBeenPressed = true; properties.put(PRoomEditor.SNAP_TO_GRID,false); } } // If the alt key was released, activate the snap to grid mode, if needed public void altKeyReleased() { if (altKeyHasBeenPressed) { altKeyHasBeenPressed = false; properties.put(PRoomEditor.SNAP_TO_GRID,true); } } // If the ctrl key was pressed, enable add on top mode, if needed public void ctrlKeyPressed() { boolean addOnTopMode = properties.get(PRoomEditor.ADD_ON_TOP); // Save that the ctrl key has been pressed if (addOnTopMode == false) { ctrlKeyHasBeenPressed = true; properties.put(PRoomEditor.ADD_ON_TOP,true); } } // If the ctrl key was released, disable add on top mode, if needed public void ctrlKeyReleased() { if (ctrlKeyHasBeenPressed) { ctrlKeyHasBeenPressed = false; properties.put(PRoomEditor.ADD_ON_TOP,false); } } // If the shift key was pressed, enable add multiple mode, if needed public void shiftKeyPressed() { boolean addMultipleMode = properties.get(PRoomEditor.ADD_MULTIPLE); // Save that the shift key has been pressed if (addMultipleMode == false) { shiftKeyHasBeenPressed = true; properties.put(PRoomEditor.ADD_MULTIPLE,true); } } // If the shift key was released, disable add multiple mode, if needed public void shiftKeyReleased() { if (shiftKeyHasBeenPressed) { shiftKeyHasBeenPressed = false; properties.put(PRoomEditor.ADD_MULTIPLE,false); } } protected void mouseEdit(MouseEvent e) { int modifiers = e.getModifiersEx(); int type = e.getID(); Point currentPosition = e.getPoint().getLocation(); componentToVisual(currentPosition); int x = currentPosition.x; int y = currentPosition.y; boolean leftButtonPressed = ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0); boolean rightButtonPressed = ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0); boolean selectionMode = properties.get(PRoomEditor.MULTI_SELECTION); boolean snapToGridMode = properties.get(PRoomEditor.SNAP_TO_GRID); // If the 'snap to grid' mode is activated if (snapToGridMode == true) { int sx = room.get(PRoom.SNAP_X); int sy = room.get(PRoom.SNAP_Y); int ox = properties.get(PRoomEditor.GRID_OFFSET_X); int oy = properties.get(PRoomEditor.GRID_OFFSET_Y); if (room.get(PRoom.ISOMETRIC)) { int gx = ox + negDiv(x - ox,sx) * sx; int gy = oy + negDiv(y - oy,sy) * sy; boolean d = (Math.abs(x - gx - sx / 2) * sy + Math.abs(y - gy - sy / 2) * sx) < sx * sy / 2; x = gx + (d ? sx / 2 : x > gx + sx / 2 ? sx : 0); y = gy + (d ? sy / 2 : y > gy + sy / 2 ? sy : 0); } else { x = ox + negDiv(x - ox,sx) * sx; y = oy + negDiv(y - oy,sy) * sy; } } // If the selection button is pressed if (selectionMode) { // If the user has pressed the left button if (leftButtonPressed) { // Ensure the selection is inside the room if (x < 0) x = 0; if (y < 0) y = 0; if (x > room.getWidth()) x = room.getWidth(); if (y > room.getHeight()) y = room.getHeight(); // If the drag process starts, save the position if (selectionOrigin == null) { // If there was a selected piece, deselect it if (selectedPiece != null) selectedPiece.setSelected(false); selectionOrigin = new Point(x,y); return; } else { // Calculate the origin and the dimension of the selection int newSelectionOriginX = Math.min(selectionOrigin.x,x); int newSelectionOriginY = Math.min(selectionOrigin.y,y); int width = Math.abs(x - selectionOrigin.x); int height = Math.abs(y - selectionOrigin.y); // Save the selection and display it selection = new Rectangle(newSelectionOriginX,newSelectionOriginY,width,height); roomVisual.setSelection(selection); return; } } else { // if the drag process ends, reset the selection if (selectionOrigin != null) { selectionOrigin = null; return; } } } frame.statX.setText(Messages.getString("RoomFrame.STAT_X") + x); //$NON-NLS-1$ frame.statY.setText(Messages.getString("RoomFrame.STAT_Y") + y); //$NON-NLS-1$ frame.statId.setText(""); //$NON-NLS-1$ frame.statSrc.setText(""); //$NON-NLS-1$ // Update the mouse position in room visual roomVisual.setMousePosition(new Point(x,y)); // If the user is doing a paste if (pasteMode && leftButtonPressed) { // If the user has selected instances, paste them if (selectedInstances.size() > 0) pasteInstances(new Point(x,y)); if (selectedTiles.size() > 0) pasteTiles(new Point(x,y)); return; } // If we are in paste mode and the user has clicked the right button, deactivate the paste mode if (pasteMode && rightButtonPressed) deactivatePasteMode(); if (selectionMode && rightButtonPressed) return; Piece mc = null; if (frame.tabs.getSelectedIndex() == Room.TAB_TILES) { Tile tile = getTopPiece(currentPosition,Tile.class,getTileDepth()); // If we can edit all layers and no tiles have been found, look for a tile, regardless of its layer if (tile == null && editOtherLayers == true) tile = getTopPiece(currentPosition,Tile.class); mc = tile; if (mc != null) { String idt = Messages.getString("RoomFrame.STAT_ID") //$NON-NLS-1$ + tile.properties.get(PTile.ID); if (mc.isLocked()) idt += " X"; //$NON-NLS-1$ frame.statId.setText(idt); ResourceReference<Background> rb = tile.properties.get(PTile.BACKGROUND); Background b = deRef(rb); String name = b == null ? Messages.getString("RoomFrame.NO_BACKGROUND") : b.getName(); idt = Messages.getString("RoomFrame.STAT_TILESET") + name; //$NON-NLS-1$ frame.statSrc.setText(idt); } } else { Instance instance = getTopPiece(currentPosition,Instance.class); mc = instance; if (instance != null) { String idt = Messages.getString("RoomFrame.STAT_ID") //$NON-NLS-1$ + instance.properties.get(PInstance.ID); if (mc.isLocked()) idt += " X"; //$NON-NLS-1$ frame.statId.setText(idt); ResourceReference<GmObject> or = instance.properties.get(PInstance.OBJECT); GmObject o = deRef(or); String name = o == null ? Messages.getString("RoomFrame.NO_OBJECT") : o.getName(); idt = Messages.getString("RoomFrame.STAT_OBJECT") + name; //$NON-NLS-1$ frame.statSrc.setText(idt); } if (frame.tabs.getSelectedIndex() != Room.TAB_OBJECTS) return; } if (leftButtonPressed) processLeftButton(modifiers,type == MouseEvent.MOUSE_PRESSED,mc,new Point(x,y)); else if (cursor != null) releaseCursor(new Point(x - offsetX,y - offsetY)); if (rightButtonPressed && mc != null) processRightButton(modifiers,type == MouseEvent.MOUSE_PRESSED,mc,currentPosition); //use mouse point } private <P extends Piece>P getTopPiece(Point p, Class<P> c) { Iterator<P> pi = roomVisual.intersect(new Rectangle(p.x,p.y,1,1),c); P piece = null; while (pi.hasNext()) piece = pi.next(); return piece; } private <P extends Piece>P getTopPiece(Point p, Class<P> c, int depth) { Iterator<P> pi = roomVisual.intersect(new Rectangle(p.x,p.y,1,1),c,depth); P piece = null; while (pi.hasNext()) piece = pi.next(); return piece; } protected int getTileDepth() { return (Integer) frame.tileLayer.getSelectedItem(); } public static interface CommandHandler { CodeFrame openInstanceCodeFrame(Instance i); } private class RoomPropertyListener extends PropertyUpdateListener<PRoom> { @Override public void updated(PropertyUpdateEvent<PRoom> e) { switch (e.key) { case SNAP_X: case SNAP_Y: setZoom((Integer) properties.get(PRoomEditor.ZOOM)); break; case DELETE_UNDERLYING_OBJECTS: case DELETE_UNDERLYING_TILES: case SHOW_BACKGROUNDS: case SHOW_FOREGROUNDS: case SHOW_GRID: case SHOW_OBJECTS: case SHOW_TILES: case SHOW_VIEWS: if (!validating) properties.put(PRoomEditor.valueOf(e.key.name()),room.get(e.key)); break; case REMEMBER_WINDOW_SIZE: if (room.get(PRoom.REMEMBER_WINDOW_SIZE)) for (PRoomEditor pre : PRoomEditor.values()) try { room.put(PRoom.valueOf(pre.name()),properties.get(pre)); } catch (IllegalArgumentException iae) { //Some of these settings aren't reflected in the //PRoom structure, so we just discard them for now. } default: break; } } } @Override public void setZoom(int z) { super.setZoom(z); if (z >= 1) roomVisual.setGridFactor(1); else { int sx = room.get(PRoom.SNAP_X); int sy = room.get(PRoom.SNAP_Y); roomVisual.setGridFactor((2 - z) / gcd(2 - z,gcd(sx < 2 ? 0 : sx,sy < 2 ? 0 : sy))); } } private boolean validating; private class RoomEditorPropertyValidator implements PropertyValidator<PRoomEditor> { public Object validate(PRoomEditor k, Object v) { switch (k) { case GRID_OFFSET_X: roomVisual.setGridXOffset((Integer) v); break; case GRID_OFFSET_Y: roomVisual.setGridYOffset((Integer) v); break; case MULTI_SELECTION: // If the multi selection mode is set to off, reset the selection if (((Boolean) v) == false) { roomVisual.setSelection(null); selection = null; } break; case ZOOM: int i = Math.max(ZOOM_MIN,Math.min(ZOOM_MAX,(Integer) v)); setZoom(i); return i; case ADD_MULTIPLE: case ADD_ON_TOP: case SNAP_TO_GRID: case SINGLE_SELECTION: break; case SHOW_BACKGROUNDS: case SHOW_FOREGROUNDS: case SHOW_OBJECTS: case SHOW_TILES: case SHOW_GRID: roomVisual.setVisible(k.rvBinding,(Boolean) v); break; case SHOW_VIEWS: updateViewsObjectFollowingProperty(); roomVisual.setVisible(k.rvBinding,(Boolean) v); break; case DELETE_UNDERLYING_OBJECTS: case DELETE_UNDERLYING_TILES: if (room.get(PRoom.REMEMBER_WINDOW_SIZE)) { PRoom prk = PRoom.valueOf(k.name()); validating = true; try { room.put(prk,v); } finally { validating = false; } return room.get(prk); } } return v; } // Set the 'object to follow' coordinates for each view in the room private void updateViewsObjectFollowingProperty() { // If the views are not enabled if ((Boolean) room.get(PRoom.VIEWS_ENABLED) == false) return; for (View view : room.views) { // If the view is not visible, don't show it if ((Boolean) view.properties.get(PView.VISIBLE) == false) return; // Get the reference to the 'Object following' object ResourceReference<GmObject> objectToFollowReference = null; // If there is 'Object following' object for the selected view if (view.properties.get(PView.OBJECT) != null) objectToFollowReference = view.properties.get(PView.OBJECT); // If there is no object to follow, reset the corresponding view properties if (objectToFollowReference == null) { view.properties.put(PView.OBJECT_FOLLOWING_X,-1); view.properties.put(PView.OBJECT_FOLLOWING_Y,-1); continue; } Instance instanceToFollow = null; // get the first instance in the room for (Instance instance : room.instances) { ResourceReference<GmObject> instanceObject = instance.properties.get(PInstance.OBJECT); if (instanceObject == objectToFollowReference) { instanceToFollow = instance; break; } } // If there is an instance to follow if (instanceToFollow != null) { // Properties of the view Point viewPosition = new Point(0,0); int viewWidth = (Integer) view.properties.get(PView.VIEW_W); int viewHeight = (Integer) view.properties.get(PView.VIEW_H); // Get the instance position Point instancePosition = new Point(0,0); instancePosition.x = (Integer) instanceToFollow.properties.get(PInstance.X); instancePosition.y = (Integer) instanceToFollow.properties.get(PInstance.Y); viewPosition.x = instancePosition.x - viewWidth / 2; viewPosition.y = instancePosition.y - viewHeight / 2; // Set this new location into the view properties view.properties.put(PView.OBJECT_FOLLOWING_X,viewPosition.x); view.properties.put(PView.OBJECT_FOLLOWING_Y,viewPosition.y); } else { view.properties.put(PView.OBJECT_FOLLOWING_X,-1); view.properties.put(PView.OBJECT_FOLLOWING_Y,-1); } } } } }