001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.vmd.game.model;
042:
043: import java.awt.Dialog;
044: import java.awt.Graphics2D;
045: import java.awt.Rectangle;
046: import java.awt.event.ActionEvent;
047: import java.util.ArrayList;
048: import java.util.HashSet;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Set;
052: import javax.swing.Action;
053: import org.netbeans.modules.vmd.game.editor.tiledlayer.TiledLayerEditor;
054: import javax.swing.AbstractAction;
055: import javax.swing.JComponent;
056: import javax.swing.event.EventListenerList;
057: import org.netbeans.modules.vmd.game.dialog.RenameTiledLayerDialog;
058: import org.netbeans.modules.vmd.game.editor.tiledlayer.TiledLayerNavigator;
059: import org.netbeans.modules.vmd.game.editor.tiledlayer.TiledLayerPreviewPanel;
060: import org.openide.DialogDescriptor;
061: import org.openide.DialogDisplayer;
062: import org.openide.util.NbBundle;
063:
064: public class TiledLayer extends Layer implements ImageResourceListener {
065:
066: private TiledLayerPreviewPanel preview;
067: private TiledLayerNavigator navigator;
068:
069: public static final boolean DEBUG = false;
070:
071: EventListenerList listenerList = new EventListenerList();
072:
073: private JComponent editor;
074: private int[][] grid;
075:
076: TiledLayer(GlobalRepository gameDesign, String name,
077: ImageResource imageResource, int rows, int columns,
078: int tileWidth, int tileHeight) {
079: super (gameDesign, name, imageResource, tileWidth, tileHeight);
080: getImageResource().addImageResourceListener(this );
081: this .grid = new int[rows][columns];
082: }
083:
084: TiledLayer(GlobalRepository gameDesign, String name,
085: ImageResource imageResource, int[][] grid, int tileWidth,
086: int tileHeight) {
087: super (gameDesign, name, imageResource, tileWidth, tileHeight);
088: getImageResource().addImageResourceListener(this );
089: this .grid = grid;
090: }
091:
092: TiledLayer(GlobalRepository gameDesign, String name,
093: TiledLayer tiledLayer) {
094: super (gameDesign, name, tiledLayer.getImageResource(),
095: tiledLayer.getTileWidth(), tiledLayer.getTileHeight());
096: getImageResource().addImageResourceListener(this );
097: this .grid = new int[tiledLayer.grid.length][];
098: for (int i = 0; i < this .grid.length; i++) {
099: int[] copyRow = new int[tiledLayer.grid[i].length];
100: System.arraycopy(tiledLayer.grid[i], 0, copyRow, 0,
101: tiledLayer.grid[i].length);
102: this .grid[i] = copyRow;
103: }
104: }
105:
106: public ImageResourceInfo getImageResourceInfo() {
107: return new ImageResourceInfo(this .getImageResource(), this
108: .getTileWidth(), this .getTileHeight(), false);
109: }
110:
111: public void addTiledLayerListener(TiledLayerListener l) {
112: this .listenerList.add(TiledLayerListener.class, l);
113: }
114:
115: public void removeTiledLayerListener(TiledLayerListener l) {
116: this .listenerList.remove(TiledLayerListener.class, l);
117: }
118:
119: public JComponent getEditor() {
120: return this .editor == null ? this .editor = new TiledLayerEditor(
121: this )
122: : this .editor;
123: }
124:
125: public int getRowCount() {
126: return this .grid.length;
127: }
128:
129: public int getColumnCount() {
130: return this .grid[0].length;
131: }
132:
133: public int getTileIndexAt(Position position) {
134: return this
135: .getTileIndexAt(position.getRow(), position.getCol());
136: }
137:
138: public int getTileIndexAt(int rowIndex, int columnIndex) {
139: if (rowIndex >= this .grid.length
140: || columnIndex >= this .grid[0].length)
141: return 0;
142: return grid[rowIndex][columnIndex];
143: }
144:
145: public Tile getTileAt(Position position) {
146: return getTileAt(position.getRow(), position.getCol());
147: }
148:
149: public Tile getTileAt(int rowIndex, int columnIndex) {
150: if (rowIndex < 0 || rowIndex >= this .getRowCount()
151: || columnIndex < 0
152: || columnIndex >= this .getColumnCount())
153: return this .getImageResource().getTile(
154: Tile.EMPTY_TILE_INDEX, this .getTileWidth(),
155: this .getTileHeight(), false);
156: Tile tile = null;
157: int tileIndex = this .grid[rowIndex][columnIndex];
158: tile = this .getImageResource().getTile(tileIndex,
159: this .getTileWidth(), this .getTileHeight(), false);
160: return tile;
161: }
162:
163: /**
164: * Assigns a tile index to multiple positions.
165: */
166: public void setTileAtPositions(int tileIndex, Set positions) {
167: for (Iterator it = positions.iterator(); it.hasNext();) {
168: Position pos = (Position) it.next();
169: this .updateIndexGrid(tileIndex, pos.getRow(), pos.getCol());
170: }
171: this .fireTilesChanged(positions);
172: }
173:
174: private void fireTilesChanged(Set positions) {
175: Object[] listeners = listenerList.getListenerList();
176: for (int i = listeners.length - 2; i >= 0; i -= 2) {
177: if (listeners[i] == TiledLayerListener.class) {
178: ((TiledLayerListener) listeners[i + 1]).tilesChanged(
179: this , positions);
180: }
181: }
182: }
183:
184: private void fireTilesChanged() {
185: Object[] listeners = listenerList.getListenerList();
186: for (int i = listeners.length - 2; i >= 0; i -= 2) {
187: if (listeners[i] == TiledLayerListener.class) {
188: ((TiledLayerListener) listeners[i + 1])
189: .tilesStructureChanged(this );
190: }
191: }
192: }
193:
194: /**
195: * Returns AnimatedTiles currently assigned to this TiledLayer.
196: */
197: private Set getAnimatedTiles() {
198: HashSet set = new HashSet();
199: for (int i = 0; i < this .grid.length; i++) {
200: for (int j = 0; j < this .grid[i].length; j++) {
201: Tile tile = this .getImageResource().getTile(
202: this .grid[i][j], this .getTileWidth(),
203: this .getTileHeight(), false);
204: if (tile instanceof AnimatedTile) {
205: set.add(tile);
206: }
207: }
208: }
209: return set;
210: }
211:
212: public void setTileAt(int tileIndex, Position position) {
213: this .setTileAt(tileIndex, position.getRow(), position.getCol());
214: }
215:
216: public void setTileAt(int tileIndex, int rowIndex, int columnIndex) {
217: if (updateIndexGrid(tileIndex, rowIndex, columnIndex)) {
218: this .fireTileChanged(rowIndex, columnIndex);
219: }
220: }
221:
222: private void fireTileChanged(int rowIndex, int columnIndex) {
223: Object[] listeners = listenerList.getListenerList();
224: for (int i = listeners.length - 2; i >= 0; i -= 2) {
225: if (listeners[i] == TiledLayerListener.class) {
226: ((TiledLayerListener) listeners[i + 1]).tileChanged(
227: this , rowIndex, columnIndex);
228: }
229: }
230: }
231:
232: private boolean updateIndexGrid(int tileIndex, int rowIndex,
233: int columnIndex) {
234: while (rowIndex < 0) {
235: this .insertRows(0, 1);
236: rowIndex++;
237: }
238: while (columnIndex < 0) {
239: this .insertColumns(0, 1);
240: columnIndex++;
241: }
242:
243: boolean changed = false;
244:
245: if (rowIndex >= this .getRowCount()
246: || columnIndex >= this .getColumnCount()) {
247: this .growLayerToSize(rowIndex, columnIndex);
248: changed = true;
249: }
250:
251: if (this .grid[rowIndex][columnIndex] == tileIndex && !changed)
252: return false;
253:
254: this .grid[rowIndex][columnIndex] = tileIndex;
255: return true;
256: }
257:
258: public void growLayerToSize(int rows, int cols) {
259: int origRows = this .getRowCount();
260: int origCols = this .getColumnCount();
261: if (rows >= this .getRowCount()) {
262: int newRowCount = rows + 1;
263: int[][] newRows = new int[newRowCount][];
264: System
265: .arraycopy(this .grid, 0, newRows, 0,
266: this .grid.length);
267: for (int row = this .grid.length; row < newRowCount; row++) {
268: newRows[row] = new int[this .getColumnCount()];
269: }
270: this .grid = newRows;
271: }
272: if (cols >= this .getColumnCount()) {
273: int newColCount = cols + 1;
274: for (int row = 0; row < this .getRowCount(); row++) {
275: int[] oldRow = this .grid[row];
276: int[] newRow = new int[newColCount];
277: System.arraycopy(oldRow, 0, newRow, 0, oldRow.length);
278: this .grid[row] = newRow;
279: }
280: }
281:
282: int newRowCount = this .getRowCount();
283: int newColCount = this .getColumnCount();
284: if (origRows < newRowCount) {
285: this .fireRowsInserted(origRows - 1, newRowCount - origRows);
286: }
287: if (origCols < newColCount) {
288: this .fireColumnsInserted(origCols - 1, newColCount
289: - origCols);
290: }
291: }
292:
293: /**
294: * Removes all outer rows and columns that contain only empty tiles. Thus shrinking
295: * the layer to minimal grid size without affecting the visible content.
296: */
297: public void trimToSize() {
298: int left = Integer.MAX_VALUE;
299: int right = Integer.MIN_VALUE;
300: int top = Integer.MAX_VALUE;
301: int bottom = Integer.MIN_VALUE;
302: synchronized (this .grid) {
303: for (int r = 0; r < this .grid.length; r++) {
304: for (int c = 0; c < this .grid[r].length; c++) {
305: if (this .grid[r][c] != Tile.EMPTY_TILE_INDEX) {
306: //find left boundry
307: left = Math.min(left, c);
308: //find right boundry
309: right = Math.max(right, c);
310: //find top boundry
311: top = Math.min(top, r);
312: //find bottom boundry
313: bottom = Math.max(bottom, r);
314: }
315: }
316: }
317: //then remove all the rows and cols outside the boundries
318: //remove right
319: this .deleteColumns(right + 1, this .grid[0].length - right
320: - 1);
321: //remove bottom
322: this .deleteRows(bottom + 1, this .grid.length - bottom - 1);
323: //remove left
324: this .deleteColumns(0, left);
325: //remove top
326: this .deleteRows(0, top);
327: }
328: }
329:
330: private void fireRowsInserted(int index, int count) {
331: Object[] listeners = listenerList.getListenerList();
332: for (int i = listeners.length - 2; i >= 0; i -= 2) {
333: if (listeners[i] == TiledLayerListener.class) {
334: ((TiledLayerListener) listeners[i + 1]).rowsInserted(
335: this , index, count);
336: }
337: }
338: }
339:
340: private void fireRowsRemoved(int index, int count) {
341: Object[] listeners = listenerList.getListenerList();
342: for (int i = listeners.length - 2; i >= 0; i -= 2) {
343: if (listeners[i] == TiledLayerListener.class) {
344: ((TiledLayerListener) listeners[i + 1]).rowsRemoved(
345: this , index, count);
346: }
347: }
348: }
349:
350: private void fireColumnsInserted(int index, int count) {
351: Object[] listeners = listenerList.getListenerList();
352: for (int i = listeners.length - 2; i >= 0; i -= 2) {
353: if (listeners[i] == TiledLayerListener.class) {
354: ((TiledLayerListener) listeners[i + 1])
355: .columnsInserted(this , index, count);
356: }
357: }
358: }
359:
360: private void fireColumnsRemoved(int index, int count) {
361: Object[] listeners = listenerList.getListenerList();
362: for (int i = listeners.length - 2; i >= 0; i -= 2) {
363: if (listeners[i] == TiledLayerListener.class) {
364: ((TiledLayerListener) listeners[i + 1]).columnsRemoved(
365: this , index, count);
366: }
367: }
368: }
369:
370: public void insertRows(int insRowIndex, int count) {
371: int[][] newGrid = new int[this .grid.length + count][];
372:
373: for (int rowIndex = 0, newIndex = 0; newIndex < newGrid.length; newIndex++, rowIndex++) {
374:
375: //copy first existing rows
376: if (newIndex < insRowIndex) {
377: newGrid[newIndex] = this .grid[rowIndex];
378: }
379: //insert new empty rows
380: else if (newIndex == insRowIndex) {
381: for (int i = 0; i < count; i++) {
382: newGrid[newIndex] = new int[this .grid[0].length];
383: newIndex++;
384: }
385: if (rowIndex < this .grid.length) {
386: newGrid[newIndex] = this .grid[rowIndex];
387: }
388: }
389: //copy last existing rows
390: else {
391: newGrid[newIndex] = this .grid[rowIndex];
392: }
393:
394: }
395: this .grid = newGrid;
396:
397: this .fireRowsInserted(insRowIndex, count);
398: }
399:
400: public void deleteRows(int rowIndex, int count) {
401: int endIndex = rowIndex + count - 1;
402: int[][] newGrid = new int[this .grid.length - count][];
403: for (int r = 0, index = 0; r < this .grid.length; r++) {
404: if (r < rowIndex || r > endIndex) {
405: newGrid[index] = this .grid[r];
406: index++;
407: }
408: }
409: this .grid = newGrid;
410: this .fireRowsRemoved(rowIndex, count);
411: }
412:
413: public void insertColumns(int colIndex, int count) {
414: int[] insert = new int[count];
415: for (int r = 0; r < this .grid.length; r++) {
416: int[] row = this .grid[r];
417: int[] newRow = new int[row.length + count];
418: //copy the part before insert
419: System.arraycopy(row, 0, newRow, 0, colIndex);
420: //copy the insert
421: System.arraycopy(insert, 0, newRow, colIndex, count);
422: //copy the part after insert
423: System.arraycopy(row, colIndex, newRow, colIndex + count,
424: row.length - colIndex);
425:
426: this .grid[r] = newRow;
427: }
428: this .fireColumnsInserted(colIndex, count);
429: }
430:
431: public void deleteColumns(int colIndex, int count) {
432: int endIndex = colIndex + count;
433: synchronized (this .grid) {
434: for (int r = 0; r < this .grid.length; r++) {
435: int[] newRow = new int[this .grid[r].length - count];
436: System.arraycopy(this .grid[r], 0, newRow, 0, colIndex);
437: System.arraycopy(this .grid[r], endIndex, newRow,
438: colIndex, this .grid[r].length - endIndex);
439: this .grid[r] = newRow;
440: }
441: }
442: this .fireColumnsRemoved(colIndex, count);
443: }
444:
445: public int getHeight() {
446: return this .grid.length * this .getTileHeight();
447: }
448:
449: public int getWidth() {
450: return this .grid[0].length * this .getTileWidth();
451: }
452:
453: public List<Action> getActions() {
454: List<Action> super Actions = super .getActions();
455: List<Action> actions = new ArrayList<Action>();
456: actions.addAll(super Actions);
457: actions.add(new RenameAction());
458: return actions;
459: }
460:
461: public class RenameAction extends AbstractAction {
462: {
463: this .putValue(NAME, NbBundle.getMessage(TiledLayer.class,
464: "TiledLayer.RenameAction.text"));
465: }
466:
467: public void actionPerformed(ActionEvent e) {
468: RenameTiledLayerDialog dialog = new RenameTiledLayerDialog(
469: TiledLayer.this );
470: DialogDescriptor dd = new DialogDescriptor(dialog, NbBundle
471: .getMessage(TiledLayer.class,
472: "TiledLayer.RenameAction.text"));
473: dd.setButtonListener(dialog);
474: dd.setValid(false);
475: dialog.setDialogDescriptor(dd);
476: Dialog d = DialogDisplayer.getDefault().createDialog(dd);
477: d.setVisible(true);
478: }
479: }
480:
481: public String getDisplayableTypeName() {
482: return NbBundle.getMessage(TiledLayer.class, "TiledLayer.text");
483: }
484:
485: public int[][] getTiles() {
486: return cloneTiles(this .grid);
487: }
488:
489: public void setTiles(int[][] newGrid) {
490: this .grid = cloneTiles(newGrid);
491: this .fireTilesChanged();
492: }
493:
494: public static int[][] cloneTiles(int[][] grid) {
495: int[][] clone = new int[grid.length][];
496: for (int r = 0; r < grid.length; r++) {
497: clone[r] = new int[grid[r].length];
498: System.arraycopy(grid[r], 0, clone[r], 0, grid[r].length);
499: }
500: return clone;
501: }
502:
503: // Previewable
504: public void paint(Graphics2D g, int x, int y) {
505: // TODO
506: }
507:
508: public JComponent getPreview() {
509: if (this .preview == null) {
510: return this .preview = new TiledLayerPreviewPanel(this , true);
511: }
512: return this .preview;
513: }
514:
515: public JComponent getNavigator() {
516: if (this .navigator == null) {
517: return this .navigator = new TiledLayerNavigator(this );
518: }
519: return this .navigator;
520: }
521:
522: public void paint(Graphics2D g) {
523:
524: //only paint the tiles inside the clip
525: Rectangle rect = g.getClipBounds();
526: int tileWidth = super .getTileWidth();
527: int tileHeight = super .getTileHeight();
528:
529: int minRow = rect.y / tileHeight;
530: int maxRow = ((rect.y + rect.height) / tileHeight) + 1;
531: int minCol = rect.x / tileWidth;
532: int maxCol = ((rect.x + rect.width) / tileWidth) + 1;
533:
534: for (int r = minRow; r < this .getRowCount() && r < maxRow; r++) {
535: for (int c = minCol; c < this .getColumnCount()
536: && c < maxCol; c++) {
537: this .getImageResource().paint(this .grid[r][c], g,
538: c * tileWidth, r * tileHeight, tileWidth,
539: tileHeight, false);
540: }
541: }
542: }
543:
544: public void animatedTileAdded(ImageResource source,
545: AnimatedTile tile) {
546: }
547:
548: public void animatedTileRemoved(ImageResource source,
549: AnimatedTile tile) {
550: Set<Position> changes = new HashSet<Position>();
551: for (int i = 0; i < this .grid.length; i++) {
552: for (int j = 0; j < this .grid[i].length; j++) {
553: if (tile.getIndex() == this .grid[i][j]) {
554: this .grid[i][j] = Tile.EMPTY_TILE_INDEX;
555: changes.add(new Position(i, j));
556: }
557: }
558: }
559: if (!changes.isEmpty()) {
560: this .fireTilesChanged(changes);
561: }
562: }
563:
564: public void sequenceAdded(ImageResource source, Sequence sequence) {
565: }
566:
567: public void sequenceRemoved(ImageResource source, Sequence sequence) {
568: }
569: }
|