0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.modules.vmd.game.editor.sequece;
0042:
0043: import java.awt.Color;
0044: import java.awt.Dimension;
0045: import java.awt.Graphics;
0046: import java.awt.Graphics2D;
0047: import java.awt.Point;
0048: import java.awt.Rectangle;
0049: import java.awt.datatransfer.Transferable;
0050: import java.awt.datatransfer.UnsupportedFlavorException;
0051: import java.awt.dnd.DnDConstants;
0052: import java.awt.dnd.DragGestureEvent;
0053: import java.awt.dnd.DragGestureListener;
0054: import java.awt.dnd.DragGestureRecognizer;
0055: import java.awt.dnd.DragSource;
0056: import java.awt.dnd.DragSourceAdapter;
0057: import java.awt.dnd.DragSourceDropEvent;
0058: import java.awt.dnd.DropTarget;
0059: import java.awt.dnd.DropTargetDragEvent;
0060: import java.awt.dnd.DropTargetDropEvent;
0061: import java.awt.dnd.DropTargetEvent;
0062: import java.awt.dnd.DropTargetListener;
0063: import java.awt.event.ActionEvent;
0064: import java.awt.event.ComponentAdapter;
0065: import java.awt.event.ComponentEvent;
0066: import java.awt.event.MouseEvent;
0067: import java.awt.event.MouseListener;
0068: import java.awt.event.MouseMotionListener;
0069: import java.io.IOException;
0070: import java.util.ArrayList;
0071: import java.util.HashSet;
0072: import java.util.Iterator;
0073: import java.util.List;
0074: import java.util.Set;
0075: import javax.swing.AbstractAction;
0076: import javax.swing.Action;
0077: import javax.swing.JComponent;
0078: import javax.swing.JPopupMenu;
0079: import javax.swing.JViewport;
0080: import javax.swing.Scrollable;
0081: import javax.swing.ToolTipManager;
0082: import javax.swing.event.EventListenerList;
0083: import org.netbeans.modules.vmd.game.model.ImageResource;
0084: import org.netbeans.modules.vmd.game.model.Sequence;
0085: import org.netbeans.modules.vmd.game.model.SequenceContainer;
0086: import org.netbeans.modules.vmd.game.model.SequenceListener;
0087: import org.netbeans.modules.vmd.game.model.StaticTile;
0088: import org.netbeans.modules.vmd.game.model.Tile;
0089: import org.netbeans.modules.vmd.game.model.TileDataFlavor;
0090: import org.openide.util.NbBundle;
0091:
0092: /*
0093: Implement DnD of multiple frames between sequences (give option to either insert of overwrite)
0094: Implement DnD of multiple frames from image resource
0095:
0096: Repaint only individual frames as needed instead of always the whole sequence - performance sucks!!!!
0097: When dropping a frame at the end of sequence scroll to make it visible
0098: When having more than one frame selected only play the selected frames in the preview
0099: */
0100:
0101: /**
0102: * @author kherink
0103: */
0104: public class SequenceEditingPanel extends JComponent implements
0105: Scrollable, MouseMotionListener, MouseListener,
0106: SequenceListener {
0107:
0108: public static final boolean DEBUG = false;
0109:
0110: private static final int BOUNDARY_MIN = 10;
0111: private static final int SEPARATOR_WIDTH_MIN = 15;
0112:
0113: private static final Color COLOR_SEPARATOR_VERTICAL = new Color(
0114: 204, 218, 228);
0115: private static final Color COLOR_SEPARATOR_HORIZONTAL = COLOR_SEPARATOR_VERTICAL; //new Color(162, 162, 178);
0116: private Color backgroundColor = Color.WHITE;
0117:
0118: private static final int NONE = -1;
0119:
0120: private Sequence sequence;
0121: private SequenceContainer sequenceContainer;
0122:
0123: private JViewport viewPort;
0124:
0125: private int frameWidth;
0126: private int frameHeight;
0127:
0128: private int unitWidth;
0129: private int unitHeight;
0130:
0131: private int separatorWidth;
0132: private int separatorHeight;
0133:
0134: private int outlineWidth;
0135: private int outlineHeight;
0136:
0137: private boolean centerVertically;
0138: private boolean centerHorizontally;
0139:
0140: private Point start = new Point(0, 0);
0141:
0142: private Point transOutlineToFrame;
0143: private Point transFrameToSeparator;
0144: private Point transOutlineToSeparator;
0145: private Point transSeparatorToOutline;
0146:
0147: private int filmUnitWidth;
0148:
0149: private FrameSelectionManager selection;
0150:
0151: private int hilitedColumn = NONE;
0152:
0153: EventListenerList listenerList = new EventListenerList();
0154:
0155: public SequenceEditingPanel(Sequence sequence) {
0156: this .setDoubleBuffered(true);
0157: this .sequence = sequence;
0158: this .selection = new FrameSelectionManager();
0159: this .sequence.addSequenceListener(this );
0160: this .sequence.addSequenceListener(this .selection);
0161:
0162: this .frameWidth = this .sequence.getFrameWidth();
0163: this .frameHeight = this .sequence.getFrameHeight();
0164:
0165: this .unitWidth = Math.max(this .frameWidth / 5, 10);
0166: this .unitHeight = Math.max(this .frameHeight / 5, 10);
0167:
0168: this .outlineWidth = this .frameWidth + unitWidth * 2;
0169: this .outlineHeight = this .frameHeight + unitHeight * 2;
0170:
0171: this .separatorWidth = Math.max(unitWidth, SEPARATOR_WIDTH_MIN);
0172:
0173: this .transSeparatorToOutline = new Point(separatorWidth
0174: + unitWidth, 0);
0175: this .transOutlineToFrame = new Point(unitWidth, unitHeight);
0176: this .transFrameToSeparator = new Point(this .frameWidth
0177: + unitWidth * 2, -unitHeight);
0178: this .transOutlineToSeparator = new Point(unitWidth, 0);
0179:
0180: this .filmUnitWidth = this .transSeparatorToOutline.x
0181: + this .transOutlineToFrame.x
0182: + this .transFrameToSeparator.x;
0183:
0184: this .addMouseMotionListener(this );
0185: this .addMouseListener(this );
0186:
0187: //DnD
0188: DropTarget dropTarget = new DropTarget(this ,
0189: new SequenceDropTargetListener());
0190: dropTarget.setActive(true);
0191: this .setDropTarget(dropTarget);
0192:
0193: DragSource dragSource = new DragSource();
0194: DragGestureRecognizer dgr = dragSource
0195: .createDefaultDragGestureRecognizer(this ,
0196: DnDConstants.ACTION_COPY, new DGL());
0197:
0198: ToolTipManager.sharedInstance().registerComponent(this );
0199: }
0200:
0201: public void setBackground(Color color) {
0202: this .backgroundColor = color;
0203: }
0204:
0205: private class DGL extends DragSourceAdapter implements
0206: DragGestureListener {
0207:
0208: public void dragGestureRecognized(DragGestureEvent dge) {
0209: Point dragOrigin = dge.getDragOrigin();
0210: int col = SequenceEditingPanel.this
0211: .getColumnForPoint(dragOrigin);
0212: int frame = SequenceEditingPanel.this
0213: .getFrameForColumn(col);
0214: }
0215:
0216: public void dragDropEnd(DragSourceDropEvent dsde) {
0217: super .dragDropEnd(dsde);
0218: if (dsde.getDropSuccess()) {
0219: if (DEBUG)
0220: System.out.println("Drop successful"); // NOI18N
0221: } else {
0222: if (DEBUG)
0223: System.out.println("Drop unsuccessful"); // NOI18N
0224: }
0225: }
0226:
0227: }
0228:
0229: public void setSequenceContainer(SequenceContainer sequenceContainer) {
0230: this .sequenceContainer = sequenceContainer;
0231: }
0232:
0233: public Sequence getSequence() {
0234: return this .sequence;
0235: }
0236:
0237: public String getToolTipText(MouseEvent event) {
0238: int col = this .getColumnForPoint(event.getPoint());
0239: int frame = this .getFrameForColumn(col);
0240: if (frame == -1)
0241: return null;
0242: return NbBundle.getMessage(SequenceEditingPanel.class,
0243: "SequenceEditingPanel.frame.tooltip",
0244: new Object[] { frame,
0245: this .sequence.getFrame(frame).getIndex() });
0246: // return "Index: " + frame + ", tile: " + this.sequence.getFrame(frame).getIndex();
0247: }
0248:
0249: //DnD implementation
0250: private class SequenceDropTargetListener implements
0251: DropTargetListener {
0252: public void dragEnter(DropTargetDragEvent dtde) {
0253: if (DEBUG)
0254: System.out.println("dragEnter"); // NOI18N
0255: }
0256:
0257: public void dragOver(DropTargetDragEvent dtde) {
0258: SequenceEditingPanel.this .updateHiLite(dtde.getLocation());
0259: }
0260:
0261: public void dropActionChanged(DropTargetDragEvent dtde) {
0262: if (DEBUG)
0263: System.out.println("dropActionChanged"); // NOI18N
0264: }
0265:
0266: public void dragExit(DropTargetEvent dte) {
0267: if (DEBUG)
0268: System.out.println("dragExit"); // NOI18N
0269: SequenceEditingPanel.this .hilitedColumn = SequenceEditingPanel.NONE;
0270: SequenceEditingPanel.this .repaint();
0271: }
0272:
0273: public void drop(DropTargetDropEvent dtde) {
0274: Point dropPoint = dtde.getLocation();
0275: if (DEBUG)
0276: System.out.println("Start drop @: " + dropPoint); // NOI18N
0277: Transferable transferable = dtde.getTransferable();
0278: try {
0279: TileDataFlavor tileFlavor = new TileDataFlavor();
0280: if (transferable.isDataFlavorSupported(tileFlavor)) {
0281: dtde.acceptDrop(DnDConstants.ACTION_COPY);
0282: List<Tile> tiles = (List<Tile>) transferable
0283: .getTransferData(tileFlavor);
0284: int col = SequenceEditingPanel.this
0285: .getColumnForPoint(dropPoint);
0286: //if dropping on separator i want to insert
0287: if (col % 2 == 0) {
0288: for (int i = 0; i < tiles.size(); i++) {
0289: SequenceEditingPanel.this .sequence
0290: .insertFrame((StaticTile) tiles
0291: .get(i),
0292: (col + (i * 2)) / 2);
0293: }
0294: }
0295: //if dropping on frame i want to overwrite it
0296: else {
0297: for (Tile tile : tiles) {
0298: SequenceEditingPanel.this .sequence
0299: .setFrame(
0300: (StaticTile) tile,
0301: SequenceEditingPanel.this
0302: .getFrameForColumn(col));
0303: }
0304: }
0305: dtde.dropComplete(true);
0306: } else {
0307: if (DEBUG)
0308: System.out.println("NOT a Tile :("); // NOI18N
0309: dtde.dropComplete(false);
0310: }
0311: } catch (ClassNotFoundException e) {
0312: e.printStackTrace();
0313: dtde.dropComplete(false);
0314: } catch (UnsupportedFlavorException e) {
0315: e.printStackTrace();
0316: dtde.dropComplete(false);
0317: } catch (IOException e) {
0318: e.printStackTrace();
0319: dtde.dropComplete(false);
0320: }
0321: }
0322: }
0323:
0324: private void updateHiLite(Point p) {
0325: if (DEBUG)
0326: System.out.println("SET Hi-Lite on col "
0327: + this .getColumnForPoint(p)); // NOI18N
0328: //TODO : set a clip when optimizing preformance
0329: this .handleMouseMoveOver(p);
0330: }
0331:
0332: private class ResizeListener extends ComponentAdapter {
0333: public void componentResized(ComponentEvent e) {
0334: if (DEBUG && SequenceEditingPanel.this .viewPort != null)
0335: System.out.println(">> componentResized: " // NOI18N
0336: + SequenceEditingPanel.this .viewPort.getWidth()
0337: + ", "
0338: + SequenceEditingPanel.this .viewPort
0339: .getHeight()); // NOI18N
0340: SequenceEditingPanel.this .validate();
0341: }
0342: }
0343:
0344: public void setViewPort(JViewport viewPort) {
0345: this .viewPort = viewPort;
0346: }
0347:
0348: public Dimension getPreferredSize() {
0349: if (DEBUG)
0350: System.out.println("getPreferredSize"); // NOI18N
0351:
0352: int filmRollWidth = (this .filmUnitWidth * this .sequence
0353: .getFrameCount())
0354: + this .transOutlineToSeparator.x
0355: + this .transSeparatorToOutline.x;
0356: int filmRollHeight = BOUNDARY_MIN + this .separatorHeight;
0357:
0358: int prefWidth = 0;
0359: int prefHeight = 0;
0360:
0361: if (this .viewPort != null
0362: && this .viewPort.getWidth() > filmRollWidth) {
0363: prefWidth += this .viewPort.getWidth();
0364: } else {
0365: prefWidth += filmRollWidth + 20;
0366: this .start.x = BOUNDARY_MIN;
0367: }
0368:
0369: prefHeight += filmRollHeight;
0370: this .start.y = BOUNDARY_MIN;
0371:
0372: return new Dimension(prefWidth, prefHeight);
0373: }
0374:
0375: //Always repaints the whole sequence :(
0376: public void paintComponent(Graphics graphics) {
0377: Graphics2D g = (Graphics2D) graphics;
0378: g.setColor(this .backgroundColor);
0379: //g.fillRect(0, 0, this.getWidth(), this.getHeight());
0380: g.setColor(COLOR_SEPARATOR_HORIZONTAL);
0381: g.drawLine(0, this .getHeight() - 1, this .getWidth(), this
0382: .getHeight() - 1);
0383:
0384: this .start = new Point(this .separatorWidth,
0385: (this .getHeight() - this .outlineHeight) / 2);
0386: Point current = new Point(this .start);
0387: int col = 0;
0388: for (int i = 0; i < this .sequence.getFrameCount(); i++) {
0389: this .drawSeparator(g, col, current);
0390: col++;
0391:
0392: current.translate(transSeparatorToOutline.x,
0393: transSeparatorToOutline.y);
0394:
0395: this .drawOutline(g, i, current);
0396:
0397: current.translate(transOutlineToFrame.x,
0398: transOutlineToFrame.y);
0399: StaticTile frame = this .sequence.getFrame(i);
0400: this .drawFrame(g, frame, current);
0401: col++;
0402:
0403: current.translate(transFrameToSeparator.x,
0404: transFrameToSeparator.y);
0405: }
0406: this .drawSeparator(g, col, current);
0407: }
0408:
0409: private void drawSeparator(Graphics2D g, int col, Point p) {
0410: Color c = (this .hilitedColumn == col) ? new Color(255, 235, 140)
0411: : this .backgroundColor;
0412: g.setColor(c);
0413: g.fillRect(p.x, 0, this .separatorWidth, this .getHeight() - 1);
0414: g.setColor(COLOR_SEPARATOR_VERTICAL);
0415: int x = p.x + this .separatorWidth / 2;
0416: g.drawLine(x, 0, x, this .getHeight() - 1);
0417: }
0418:
0419: private void drawOutline(Graphics2D g, int frameIndex, Point p) {
0420: int col = this .getColumnForFrame(frameIndex);
0421: int halfW = this .unitWidth / 2;
0422: int halfH = this .unitHeight / 2;
0423: if (this .selection.isFrameSelected(frameIndex)) {
0424: g.setColor(new Color(175, 195, 255));
0425: if (DEBUG)
0426: System.out.println("COL: " + frameIndex); // NOI18N
0427: g.fillRect(p.x - this .unitWidth, p.y - this .unitHeight,
0428: this .outlineWidth + 2 * this .unitWidth,
0429: this .outlineHeight + 2 * this .unitHeight);
0430: }
0431: if (this .hilitedColumn == col) {
0432: g.setColor(new Color(255, 235, 140));
0433: g.fillRect(p.x - halfW, p.y - halfH, this .outlineWidth
0434: + this .unitWidth, this .outlineHeight
0435: + this .unitHeight);
0436: }
0437: g.setColor(Color.WHITE);
0438: g.fillRect(p.x, p.y, this .outlineWidth, this .outlineHeight);
0439: }
0440:
0441: private void drawFrame(Graphics2D g, StaticTile frame, Point p) {
0442: //g.fillRect(p.x, p.y, this.frameWidth, this.frameHeight);
0443: frame.paint(g, p.x, p.y);
0444: }
0445:
0446: private int getColumnForPoint(Point p) {
0447: if (p.x < this .start.x + this .transSeparatorToOutline.x)
0448: return 0;
0449: if (p.x > this .start.x + this .filmUnitWidth
0450: * (this .sequence.getFrameCount()))
0451: return this .getLastColIndex();
0452: //first determine in which film unit the point is
0453: int filmUnit = (p.x - this .start.x) / this .filmUnitWidth;
0454: //then offsetColumn
0455: int offsetCol = 0;
0456: int offset = (p.x - this .start.x) % this .filmUnitWidth;
0457: if (offset < this .transSeparatorToOutline.x)
0458: offsetCol = 0;
0459: else if (offset < this .transSeparatorToOutline.x
0460: + this .outlineWidth)
0461: offsetCol = 1;
0462: else
0463: offsetCol = 2;
0464:
0465: return 2 * filmUnit + offsetCol;
0466: }
0467:
0468: private Rectangle getAreaForColumn(int col) {
0469: int x = 0;
0470: int y = 0;
0471: int width = 0;
0472: int height = this .getHeight();
0473: //even columns are separators
0474: if (col % 2 == 0) {
0475: //first col is special case
0476: if (col == 0) {
0477: width = this .start.x + transSeparatorToOutline.x;
0478: } else {
0479: x = this .start.x
0480: + (this .sequence.getFrameCount() * this .filmUnitWidth)
0481: - this .transOutlineToSeparator.x;
0482: //last col is special case
0483: if (col == this .getLastColIndex()) {
0484: width = this .getWidth() - x;
0485: }
0486: //separators in the middle
0487: else {
0488: width = this .transSeparatorToOutline.x
0489: + this .transOutlineToSeparator.x;
0490: }
0491: }
0492: }
0493: //odd columns are frames
0494: else {
0495: x = this .start.x + this .transSeparatorToOutline.x
0496: + (this .filmUnitWidth * (col / 2));
0497: width = this .outlineWidth;
0498: }
0499: return new Rectangle(x, y, width, height);
0500: }
0501:
0502: private int getLastColIndex() {
0503: return this .sequence.getFrameCount() * 2;
0504: }
0505:
0506: private int getColumnForFrame(int frameIndex) {
0507: return frameIndex * 2 + 1;
0508: }
0509:
0510: private int getFrameForColumn(int col) {
0511: if (col % 2 == 0)
0512: return -1;
0513: return (col - 1) / 2;
0514: }
0515:
0516: private int getColumnCount() {
0517: return this .getLastColIndex() + 1;
0518: }
0519:
0520: //---------- Scrollable ------------
0521: public Dimension getPreferredScrollableViewportSize() {
0522: return this .getPreferredSize();
0523: }
0524:
0525: public int getScrollableUnitIncrement(Rectangle visibleRect,
0526: int orientation, int direction) {
0527: return 10;
0528: }
0529:
0530: public int getScrollableBlockIncrement(Rectangle visibleRect,
0531: int orientation, int direction) {
0532: return 10;
0533: }
0534:
0535: public boolean getScrollableTracksViewportWidth() {
0536: return false;
0537: }
0538:
0539: public boolean getScrollableTracksViewportHeight() {
0540: return false;
0541: }
0542:
0543: //---------- MouseMotionListener ------------
0544: public void mouseDragged(MouseEvent e) {
0545: this .handleMouseMoveOver(e.getPoint());
0546: }
0547:
0548: public void mouseMoved(MouseEvent e) {
0549: this .handleMouseMoveOver(e.getPoint());
0550: }
0551:
0552: private void handleMouseMoveOver(Point p) {
0553: int old = this .hilitedColumn;
0554: this .hilitedColumn = this .getColumnForPoint(p);
0555: if (old != this .hilitedColumn) {
0556: if (DEBUG)
0557: System.out
0558: .println("HilitedCol = " + this .hilitedColumn); // NOI18N
0559: int frame = this .getFrameForColumn(this .hilitedColumn);
0560: if (frame != -1) {
0561: this .fireFrameHilited(frame);
0562: }
0563: this .repaint();
0564: }
0565: }
0566:
0567: //------------ SequenceListener ---------------
0568: public void frameAdded(Sequence sequence, int index) {
0569: this .revalidate();
0570: this .repaint();
0571: if (DEBUG)
0572: System.out
0573: .println("SequenceEditingPanel.frameAdded index: "
0574: + index); // NOI18N
0575: Rectangle r;
0576: //if last frame then scroll all the way to the end
0577: if (index == this .sequence.getFrameCount() - 1) {
0578: if (DEBUG)
0579: System.out
0580: .println("SequenceEditingPanel last frame Added"); // NOI18N
0581: r = new Rectangle(new Point(this .getWidth()
0582: + this .filmUnitWidth, 0));
0583: } else {
0584: if (DEBUG)
0585: System.out
0586: .println("SequenceEditingPanel NON-last frame Added"); // NOI18N
0587: r = this .getAreaForColumn(this .getColumnForFrame(index));
0588: }
0589: this .scrollRectToVisible(r);
0590: }
0591:
0592: public void frameRemoved(Sequence sequence, int index) {
0593: this .revalidate();
0594: this .repaint();
0595: }
0596:
0597: public void frameModified(Sequence sequence, int index) {
0598: if (DEBUG)
0599: System.out.println("SequenceEditingPanel.frameModified"); // NOI18N
0600: this .repaint(this .getAreaForColumn(getColumnForFrame(index)));
0601: }
0602:
0603: public void framesChanged(Sequence sequence) {
0604: if (DEBUG)
0605: System.out.println("SequenceEditingPanel.framesChanged"); // NOI18N
0606: this .revalidate();
0607: this .repaint();
0608: }
0609:
0610: //------------ MouseListener -----------
0611: //on separator right-click offer tween :)
0612:
0613: public void mouseClicked(MouseEvent e) {
0614: }
0615:
0616: public void mousePressed(MouseEvent e) {
0617: if (e.isPopupTrigger()) {
0618: this .handlePopUp(e);
0619: } else {
0620: int col = this .getColumnForPoint(e.getPoint());
0621: int f = this .getFrameForColumn(col);
0622: if (f == -1)
0623: return;
0624: if (isContinuousSelect(e)) {
0625: int anchor = this .selection.getAnchorSelectionIndex();
0626: if (anchor == FrameSelectionManager.NONE) {
0627: this .selection.setSelected(f, true);
0628: } else {
0629: this .selection
0630: .setIntervalSelection(anchor, f, true);
0631: }
0632: } else if (isMultiSelect(e)) {
0633: this .selection.flipSelection(f);
0634: } else {
0635: boolean alreadySelected = this .selection
0636: .isFrameSelected(f);
0637: this .selection.clearSelections();
0638: this .selection.setSelected(f, !alreadySelected);
0639: }
0640: this .repaint();
0641: this .sequence.getGameDesign().getMainView().requestPreview(
0642: this .sequence.getFrame(f));
0643: }
0644: }
0645:
0646: public void mouseReleased(MouseEvent e) {
0647: if (e.isPopupTrigger()) {
0648: this .handlePopUp(e);
0649: }
0650: }
0651:
0652: public void mouseEntered(MouseEvent e) {
0653: }
0654:
0655: public void mouseExited(MouseEvent e) {
0656: this .hilitedColumn = NONE;
0657: this .repaint();
0658: this .fireHiliteLost();
0659: }
0660:
0661: private boolean isMultiSelect(MouseEvent e) {
0662: return e.isControlDown();
0663: }
0664:
0665: private boolean isContinuousSelect(MouseEvent e) {
0666: return e.isShiftDown();
0667: }
0668:
0669: private void handlePopUp(MouseEvent e) {
0670: JPopupMenu menu = new JPopupMenu();
0671:
0672: //common menu items
0673: if (this .sequenceContainer != null) {
0674: for (Iterator it = this .sequenceContainer
0675: .getActionsForSequence(this .sequence).iterator(); it
0676: .hasNext();) {
0677: Action a = (Action) it.next();
0678: menu.add(a);
0679: }
0680:
0681: menu.addSeparator();
0682: }
0683:
0684: int col = this .getColumnForPoint(e.getPoint());
0685: //menu for separator
0686: if (this .getFrameForColumn(col) == -1) {
0687: for (Iterator it = this .getSeparatorActions(col).iterator(); it
0688: .hasNext();) {
0689: Action a = (Action) it.next();
0690: menu.add(a);
0691: }
0692: }
0693: //menu for frame
0694: else {
0695: for (Iterator it = this .getFrameActions(col).iterator(); it
0696: .hasNext();) {
0697: Action a = (Action) it.next();
0698: menu.add(a);
0699: }
0700: }
0701: menu.show(this , e.getX(), e.getY());
0702: }
0703:
0704: //----------- Frame actions
0705: private List getFrameActions(int col) {
0706: ArrayList actions = new ArrayList();
0707:
0708: SelectAllAction saa = new SelectAllAction();
0709: actions.add(saa);
0710:
0711: RemoveAction ra = new RemoveAction();
0712: if (this .sequence.getFrameCount() == 1
0713: || this .selection.getSelectedIndexes().size() > 1)
0714: ra.setEnabled(false);
0715: ra.putValue(RemoveAction.PROP_COL, new Integer(col));
0716: actions.add(ra);
0717:
0718: RemoveSelectedAction rsa = new RemoveSelectedAction();
0719: if (this .selection.isSelectionEmpty())
0720: rsa.setEnabled(false);
0721:
0722: //I cannot erase all frames from sequence || no selection at all
0723: if (this .sequence.getFrameCount() <= this .selection
0724: .getSelectedIndexes().size()
0725: || this .selection.getSelectedIndexes().size() == 0)
0726: rsa.setEnabled(false);
0727:
0728: actions.add(rsa);
0729:
0730: return actions;
0731: }
0732:
0733: private class SelectAllAction extends AbstractAction {
0734: {
0735: this .putValue(NAME, NbBundle.getMessage(
0736: SequenceEditingPanel.class,
0737: "SequenceEditingPanel.menuSelectAll.txt"));
0738: }
0739:
0740: public void actionPerformed(ActionEvent e) {
0741: Sequence seq = SequenceEditingPanel.this .sequence;
0742: SequenceEditingPanel.this .selection.setIntervalSelection(0,
0743: seq.getFrameCount() - 1, true);
0744: SequenceEditingPanel.this .repaint();
0745: }
0746: }
0747:
0748: private class RemoveAction extends AbstractAction {
0749: public static final String PROP_COL = "PROP_COL"; // NOI18N
0750:
0751: {
0752: this .putValue(NAME, NbBundle.getMessage(
0753: SequenceEditingPanel.class,
0754: "SequenceEditingPanel.menuRemoveFrame.txt"));
0755: }
0756:
0757: public void actionPerformed(ActionEvent e) {
0758: int col = ((Integer) this .getValue(PROP_COL)).intValue();
0759: Sequence seq = SequenceEditingPanel.this .sequence;
0760: seq.removeFrame(SequenceEditingPanel.this
0761: .getFrameForColumn(col));
0762: }
0763: }
0764:
0765: private class RemoveSelectedAction extends AbstractAction {
0766:
0767: {
0768: this
0769: .putValue(
0770: NAME,
0771: NbBundle
0772: .getMessage(
0773: SequenceEditingPanel.class,
0774: "SequenceEditingPanel.menuRemoveSelectedFrames.txt"));
0775: }
0776:
0777: public void actionPerformed(ActionEvent e) {
0778: Set<Integer> indexes = SequenceEditingPanel.this .selection
0779: .getSelectedIndexes();
0780: SequenceEditingPanel.this .sequence.removeFrames(indexes);
0781: }
0782: }
0783:
0784: //----------- Separator actions
0785: private List getSeparatorActions(int col) {
0786: ArrayList actions = new ArrayList();
0787:
0788: TweenAction ta = new TweenAction();
0789: if (col == 0 || col == this .sequence.getFrameCount() * 2)
0790: ta.setEnabled(false);
0791: ta.putValue(TweenAction.PROP_COL, new Integer(col));
0792: actions.add(ta);
0793:
0794: InsertEmptyFrameAction iefa = new InsertEmptyFrameAction();
0795: iefa.putValue(InsertEmptyFrameAction.PROP_COL, col);
0796: actions.add(iefa);
0797:
0798: return actions;
0799: }
0800:
0801: private class TweenAction extends AbstractAction {
0802: public static final String PROP_COL = "PROP_COL"; // NOI18N
0803:
0804: {
0805: this .putValue(NAME, NbBundle.getMessage(
0806: SequenceEditingPanel.class,
0807: "SequenceEditingPanel.menuTweenFrames.txt"));
0808: }
0809:
0810: public void actionPerformed(ActionEvent e) {
0811: int separatorCol = ((Integer) this .getValue(PROP_COL))
0812: .intValue();
0813: int beforeFrameIndex = SequenceEditingPanel.this
0814: .getFrameForColumn(separatorCol - 1);
0815: int afterFrameIndex = SequenceEditingPanel.this
0816: .getFrameForColumn(separatorCol + 1);
0817: Sequence seq = SequenceEditingPanel.this .sequence;
0818: StaticTile before = seq.getFrame(beforeFrameIndex);
0819: StaticTile after = seq.getFrame(afterFrameIndex);
0820: int indexBefore = before.getIndex();
0821: int indexAfter = after.getIndex();
0822: //can't tween two same frames or consecutive frames
0823: if (indexBefore == indexAfter
0824: || indexBefore == (indexAfter + 1)
0825: || indexBefore == (indexAfter - 1))
0826: return;
0827: int incr = 1;
0828: if (indexBefore > indexAfter) {
0829: incr = -1;
0830: }
0831: int insertIndex = afterFrameIndex;
0832: while ((indexBefore += incr) != indexAfter) {
0833: ImageResource imgRes = seq.getImageResource();
0834: StaticTile tile = (StaticTile) imgRes.getTile(
0835: indexBefore, frameWidth, frameHeight, seq
0836: .isZeroBasedIndex());
0837: seq.insertFrame(tile, insertIndex);
0838: if (DEBUG)
0839: System.out.println("insert tile: "
0840: + tile.getIndex()
0841: + " at index "
0842: + insertIndex
0843: + " at Column "
0844: + SequenceEditingPanel.this
0845: .getColumnForFrame(insertIndex)); // NOI18N
0846: SequenceEditingPanel.this .selection.setSelected(
0847: insertIndex, true);
0848: insertIndex++;
0849: }
0850: }
0851: }
0852:
0853: private class InsertEmptyFrameAction extends AbstractAction {
0854: public static final String PROP_COL = "PROP_COL"; // NOI18N
0855:
0856: {
0857: this .putValue(NAME, NbBundle.getMessage(
0858: SequenceEditingPanel.class,
0859: "SequenceEditingPanel.menuInsertFrame.txt"));
0860: }
0861:
0862: public void actionPerformed(ActionEvent e) {
0863: int separatorCol = ((Integer) this .getValue(PROP_COL))
0864: .intValue();
0865: int frameIndex = SequenceEditingPanel.this
0866: .getFrameForColumn(separatorCol + 1);
0867: Sequence seq = SequenceEditingPanel.this .sequence;
0868: seq.insertFrame(null, frameIndex);
0869: }
0870: }
0871:
0872: //----------- End actions
0873:
0874: public synchronized void addSequenceEditingPanelListener(
0875: SequenceEditingPanelListener l) {
0876: this .listenerList.add(SequenceEditingPanelListener.class, l);
0877: }
0878:
0879: public synchronized void removeSequenceEditingPanelListener(
0880: SequenceEditingPanelListener l) {
0881: this .listenerList.remove(SequenceEditingPanelListener.class, l);
0882: }
0883:
0884: private void fireFrameHilited(int index) {
0885: Object[] listeners = listenerList.getListenerList();
0886: for (int i = listeners.length - 2; i >= 0; i -= 2) {
0887: if (listeners[i] == SequenceEditingPanelListener.class) {
0888: ((SequenceEditingPanelListener) listeners[i + 1])
0889: .frameHilited(this , index);
0890: }
0891: }
0892: }
0893:
0894: private void fireHiliteLost() {
0895: Object[] listeners = listenerList.getListenerList();
0896: for (int i = listeners.length - 2; i >= 0; i -= 2) {
0897: if (listeners[i] == SequenceEditingPanelListener.class) {
0898: ((SequenceEditingPanelListener) listeners[i + 1])
0899: .hiliteLost(this );
0900: }
0901: }
0902: }
0903:
0904: private void fireSelectioChange(int[] selectedFrameIdices) {
0905: Object[] listeners = listenerList.getListenerList();
0906: for (int i = listeners.length - 2; i >= 0; i -= 2) {
0907: if (listeners[i] == SequenceEditingPanelListener.class) {
0908: ((SequenceEditingPanelListener) listeners[i + 1])
0909: .frameSelectionChange(this , selectedFrameIdices);
0910: }
0911: }
0912: }
0913:
0914: private class FrameSelectionManager implements SequenceListener {
0915: public static final int NONE = -1;
0916:
0917: private ArrayList<Boolean> frameSelections;
0918:
0919: private int anchorSelectionIndex = NONE;
0920: private int leadSelectionIndex = NONE;
0921:
0922: public FrameSelectionManager() {
0923: this .clearSelections();
0924: }
0925:
0926: public int getAnchorSelectionIndex() {
0927: return this .anchorSelectionIndex;
0928: }
0929:
0930: public int getLeadSelectionIndex() {
0931: return this .leadSelectionIndex;
0932: }
0933:
0934: public void clearSelections() {
0935: int size = SequenceEditingPanel.this .sequence
0936: .getFrameCount();
0937: this .frameSelections = new ArrayList<Boolean>();
0938: this .frameSelections.ensureCapacity(size);
0939: for (int i = 0; i < size; i++) {
0940: this .frameSelections.add(Boolean.FALSE);
0941: }
0942: this .anchorSelectionIndex = NONE;
0943: this .leadSelectionIndex = NONE;
0944: }
0945:
0946: public boolean flipSelection(int frame) {
0947: Boolean x = (Boolean) this .frameSelections.get(frame);
0948: if (x == null)
0949: x = Boolean.FALSE;
0950: x = !x;
0951: this .frameSelections.set(frame, x);
0952: return x;
0953: }
0954:
0955: public void setSelected(int frame, boolean selected) {
0956: this .frameSelections.set(frame, selected);
0957: if (selected) {
0958: this .anchorSelectionIndex = frame;
0959: } else {
0960: this .anchorSelectionIndex = NONE;
0961: }
0962: this .leadSelectionIndex = NONE;
0963: }
0964:
0965: public void setIntervalSelection(int startFrameInclusive,
0966: int endFrameInclusive, boolean selected) {
0967: if (startFrameInclusive <= endFrameInclusive) {
0968: for (int i = startFrameInclusive; i <= endFrameInclusive; i++) {
0969: this .frameSelections.set(i, selected);
0970: }
0971: } else {
0972: for (int i = startFrameInclusive; i >= endFrameInclusive; i--) {
0973: this .frameSelections.set(i, selected);
0974: }
0975: }
0976: if (selected) {
0977: this .anchorSelectionIndex = startFrameInclusive;
0978: this .leadSelectionIndex = endFrameInclusive;
0979: }
0980: }
0981:
0982: public boolean isFrameSelected(int index) {
0983: Boolean selection = (Boolean) this .frameSelections
0984: .get(index);
0985: assert selection != null;
0986: return selection;
0987: }
0988:
0989: public Set<Integer> getSelectedIndexes() {
0990: Set<Integer> indexes = new HashSet();
0991: for (int i = 0; i < this .frameSelections.size(); i++) {
0992: if (this .frameSelections.get(i).equals(Boolean.TRUE)) {
0993: indexes.add(i);
0994: }
0995: }
0996: return indexes;
0997: }
0998:
0999: public boolean isSelectionEmpty() {
1000: return this .frameSelections.isEmpty();
1001: }
1002:
1003: public void frameAdded(Sequence sequence, int index) {
1004: this .clearSelections();
1005: this .frameSelections.add(index, true);
1006: }
1007:
1008: public void frameRemoved(Sequence sequence, int index) {
1009: this .frameSelections.remove(index);
1010: this .clearSelections();
1011: }
1012:
1013: public void frameModified(Sequence sequence, int index) {
1014: }
1015:
1016: public void framesChanged(Sequence sequence) {
1017: this.clearSelections();
1018: }
1019: }
1020:
1021: }
|