Gridarta Editor
MapCursor.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2015 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.model.mapcursor;
21 
22 import java.awt.Dimension;
23 import java.awt.Point;
24 import java.awt.Rectangle;
25 import java.awt.geom.Rectangle2D;
38 import net.sf.gridarta.utils.Size2D;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41 
57 public class MapCursor<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> {
58 
63  @NotNull
64  private final Point pos;
65 
69  @NotNull
70  private final Point dragStart = new Point();
71 
75  @NotNull
76  private final Dimension dragOffset = new Dimension();
77 
81  private boolean dragging;
82 
86  @NotNull
87  private final MapGrid mapGrid;
88 
92  @NotNull
93  private final MapModel<G, A, R> mapModel;
94 
98  @NotNull
99  private final Rectangle mapRec;
100 
105  @NotNull
106  private final Point tmpPoint = new Point();
107 
111  @Nullable
113 
117  @Nullable
118  private G gameObject;
119 
124  private int transactionDepth;
125 
130  @NotNull
131  private final Point transactionPos = new Point();
132 
137  private boolean transactionDragging;
138 
143  @Nullable
145 
150  @Nullable
152 
157  @NotNull
158  private final Rectangle2D transactionMapRec = new Rectangle();
159 
163  @NotNull
165 
172  public MapCursor(@NotNull final MapGrid mapGrid, @NotNull final MapModel<G, A, R> mapModel) {
173  this.mapGrid = mapGrid;
174  this.mapModel = mapModel;
175  final Size2D gridSize = mapGrid.getGridSize();
176  mapRec = new Rectangle(0, 0, gridSize.getWidth(), gridSize.getHeight());
177  final A mapArchObject = mapModel.getMapArchObject();
178  pos = fixPoint(new Point(mapArchObject.getEnterX(), mapArchObject.getEnterY()));
179  final MapGridListener mapGridListener = new MapGridListener() {
180 
181  @Override
182  public void mapGridChanged(@NotNull final MapGridEvent e) {
183  // We can ignore this event. Normally this MapCursor has caused it.
184  }
185 
186  @Override
187  public void mapGridResized(@NotNull final MapGridEvent e) {
188  final Size2D newSize = mapGrid.getGridSize();
189  final int newWidth = newSize.getWidth();
190  final int newHeight = newSize.getHeight();
192  try {
193  mapRec.setSize(newWidth, newHeight);
194  // Test if drag start point is outside map -> move inside
195  if (dragging && !mapRec.contains(dragStart)) {
196  dragStart.x = Math.min(dragStart.x, mapRec.width - 1);
197  dragStart.y = Math.min(dragStart.y, mapRec.height - 1);
198  }
199  // Test if cursor position is outside map -> move inside
200  if (!mapRec.contains(pos)) {
201  pos.x = Math.min(pos.x, mapRec.width - 1);
202  pos.y = Math.min(pos.y, mapRec.height - 1);
203  selectMapSquare();
204  }
205  } finally {
206  endTransaction();
207  }
208  }
209 
210  };
211  mapGrid.addMapGridListener(mapGridListener);
212  mapGrid.setCursor(pos);
214  try {
215  selectMapSquare();
216  } finally {
217  endTransaction();
218  }
219  }
220 
225  @NotNull
226  public Point getLocation() {
227  return new Point(pos);
228  }
229 
235  public void setLocation(@NotNull final Point p) {
237  try {
238  final Point effectivePoint = fixPoint(p);
239  if (!pos.equals(effectivePoint)) {
240  pos.setLocation(effectivePoint);
241  selectMapSquare();
242  }
243  } finally {
244  endTransaction();
245  }
246  }
247 
254  public boolean setLocationSafe(@Nullable final Point p) {
256  final boolean hasChanged;
257  try {
258  if (p != null && mapRec.contains(p)) {
259  if (pos.equals(p)) {
260  hasChanged = false;
261  } else {
262  pos.setLocation(p);
263  selectMapSquare();
264  hasChanged = true;
265  }
266  } else {
267  hasChanged = false;
268  }
269  } finally {
270  endTransaction();
271  }
272  return hasChanged;
273  }
274 
278  public void dragStart() {
279  if (!dragging) {
281  try {
282  dragStart.setLocation(pos);
283  mapGrid.preSelect(dragStart, pos);
284  dragging = true;
285  dragOffset.setSize(0, 0);
286  } finally {
287  endTransaction();
288  }
289  }
290  }
291 
298  public boolean dragTo(@Nullable final Point p) {
299  if (p != null && mapRec.contains(p) && dragging && !pos.equals(p)) {
301  try {
302  final Point oldPos = new Point(pos);
303  pos.setLocation(p);
304  selectMapSquare();
305  mapGrid.updatePreSelect(dragStart, oldPos, pos);
306  dragOffset.setSize(pos.x - dragStart.x, pos.y - dragStart.y);
307  } finally {
308  endTransaction();
309  }
310  return true;
311  }
312  return false;
313  }
314 
318  public void dragRelease() {
319  if (dragging) {
321  try {
322  mapGrid.unPreSelect(dragStart, pos);
323  dragging = false;
324  } finally {
325  endTransaction();
326  }
327  }
328  }
329 
337  public void dragSelect(@NotNull final SelectionMode selectionMode, final boolean forceSelect) {
338  if (dragging) {
340  try {
341  dragRelease();
342  if (forceSelect || !dragStart.equals(pos)) {
343  mapGrid.selectArea(dragStart, pos, selectionMode);
344  }
345  } finally {
346  endTransaction();
347  }
348  }
349  }
350 
354  public final void deactivate() {
356  try {
357  dragging = false;
358  mapGrid.unSelect();
359  } finally {
360  endTransaction();
361  }
362  }
363 
369  @Nullable
370  public Dimension getDragOffset() {
371  return dragging ? dragOffset : null;
372  }
373 
379  public boolean isOnGrid(@Nullable final Point p) {
380  return p != null && mapRec.contains(p);
381  }
382 
389  public boolean goTo(final boolean performAction, @NotNull final Direction dir) {
390  if (dir.getDx() == 0 && dir.getDy() == 0) {
391  return false;
392  }
393  tmpPoint.setLocation(pos.x + dir.getDx(), pos.y + dir.getDy());
394  if (!mapRec.contains(tmpPoint)) {
395  return false;
396  }
397  if (performAction) {
398  if (dragging) {
399  dragTo(tmpPoint);
400  } else {
401  setLocationSafe(tmpPoint);
402  }
403  }
404  return true;
405  }
406 
411  public boolean isDragging() {
412  return dragging;
413  }
414 
419  public void addMapCursorListener(@NotNull final MapCursorListener<G, A, R> listener) {
420  listenerList.add(listener);
421  }
422 
427  public void removeMapCursorListener(@NotNull final MapCursorListener<G, A, R> listener) {
428  listenerList.remove(listener);
429  }
430 
436  @Nullable
437  public G getGameObject() {
438  return gameObject;
439  }
440 
446  public void setGameObject(@Nullable final G gameObject) {
448  try {
449  if (gameObject == null) {
450  this.gameObject = null;
451  } else {
452  final MapSquare<G, A, R> mapSquare = gameObject.getMapSquare();
453  if (mapSquare != null && mapSquare.getMapModel() == mapModel) {
454  pos.setLocation(mapSquare.getMapX(), mapSquare.getMapY());
455  this.mapSquare = mapSquare;
456  this.gameObject = gameObject;
457  }
458  }
459  } finally {
460  endTransaction();
461  }
462  }
463 
469  public void setMapSquare(@Nullable final MapSquare<G, A, R> mapSquare) {
471  try {
472  if (mapSquare != null && mapSquare.getMapModel() == mapModel) {
473  pos.setLocation(mapSquare.getMapX(), mapSquare.getMapY());
474  this.mapSquare = mapSquare;
476  }
477  } finally {
478  endTransaction();
479  }
480  }
481 
488  public final void beginTransaction() {
489  if (transactionDepth == 0) {
490  transactionPos.setLocation(pos);
491  transactionDragging = dragging;
492  transactionMapSquare = mapSquare;
493  transactionGameObject = gameObject;
494  transactionMapRec.setRect(mapRec);
495  }
496  transactionDepth++;
497  mapGrid.beginTransaction();
498  }
499 
506  public final void endTransaction() {
507  assert transactionDepth > 0;
508  transactionDepth--;
509 
510  if (transactionDepth == 0) {
511  final boolean changedPos = !pos.equals(transactionPos) || mapSquare != transactionMapSquare;
512  final boolean changedMode = dragging != transactionDragging;
513  final boolean changedGameObject = mapSquare != transactionMapSquare || gameObject != transactionGameObject;
514  final boolean changedSize = !mapRec.equals(transactionMapRec);
515  if (!pos.equals(transactionPos)) {
516  mapGrid.unSetCursor(transactionPos);
517  mapGrid.setCursor(pos);
518  }
519  if (changedMode) {
520  for (final MapCursorListener<G, A, R> listener : listenerList.getListeners()) {
521  listener.mapCursorChangedMode();
522  }
523  }
524  if (changedPos) {
525  for (final MapCursorListener<G, A, R> listener : listenerList.getListeners()) {
526  listener.mapCursorChangedPos(getLocation());
527  }
528  }
529  if (changedGameObject) {
530  for (final MapCursorListener<G, A, R> listener : listenerList.getListeners()) {
531  listener.mapCursorChangedGameObject(mapSquare, gameObject);
532  }
533  }
534  if (changedSize) {
535  for (final MapCursorListener<G, A, R> listener : listenerList.getListeners()) {
536  listener.mapCursorChangedSize();
537  }
538  }
539  }
540 
541  mapGrid.endTransaction();
542  }
543 
548  private void selectMapSquare() {
549  assert transactionDepth > 0;
550  try {
551  mapSquare = mapModel.getMapSquare(pos);
552  } catch (final IndexOutOfBoundsException ignored) {
553  mapSquare = null;
554  }
556  }
557 
562  private void selectTopmostGameObject() {
563  assert transactionDepth > 0;
564  gameObject = mapSquare == null ? null : mapSquare.getLast();
565  }
566 
573  public boolean selectAbove(final boolean performAction) {
574  if (mapSquare == null || gameObject == null) {
575  return false;
576  }
577 
578  final G newGameObject = mapSquare.getPrev(gameObject);
579  if (newGameObject == null) {
580  return false;
581  }
582 
583  if (performAction) {
585  try {
586  gameObject = newGameObject;
587  } finally {
588  endTransaction();
589  }
590  }
591  return true;
592  }
593 
600  public boolean selectBelow(final boolean performAction) {
601  if (mapSquare == null || gameObject == null) {
602  return false;
603  }
604 
605  final G newGameObject = mapSquare.getNext(gameObject);
606  if (newGameObject == null) {
607  return false;
608  }
609 
610  if (performAction) {
612  try {
613  gameObject = newGameObject;
614  } finally {
615  endTransaction();
616  }
617  }
618  return true;
619  }
620 
631  public boolean insertGameObject(final boolean performAction, @NotNull final BaseObject<G, A, R, ?> gameObject, final boolean insertAtEnd, final boolean join) {
632  if (performAction) {
633  mapModel.beginTransaction("Insert"); // TODO; I18N/L10N
634  try {
635  final G insertedGameObject = mapModel.insertArchToMap(gameObject, insertAtEnd ? null : this.gameObject, pos, join);
636  if (insertedGameObject != null) {
638  try {
639  this.gameObject = insertedGameObject;
640  } finally {
641  endTransaction();
642  }
643  }
644  } finally {
645  mapModel.endTransaction();
646  }
647  }
648  return true;
649  }
650 
658  public boolean deleteSelectedGameObject(final boolean performAction, final boolean autoJoin) {
659  final G gameObject = this.gameObject;
660  if (gameObject == null) {
661  return false;
662  }
663 
664  final MapSquare<G, A, R> mapSquare = this.mapSquare;
665  if (mapSquare == null) {
666  return false;
667  }
668 
669  if (performAction) {
670  mapModel.beginTransaction("Delete"); // TODO; I18N/L10N
671  try {
672  G nextGameObject = mapSquare.getNext(gameObject);
673  mapModel.removeGameObject(gameObject, autoJoin);
674  if (nextGameObject == null) {
675  nextGameObject = mapSquare.getFirst();
676  }
677  if (nextGameObject != null) {
678  while (true) {
679  final G invGameObject = nextGameObject.getFirst();
680  if (invGameObject == null) {
681  break;
682  }
683  nextGameObject = invGameObject;
684  }
685  }
687  try {
688  this.gameObject = nextGameObject;
689  } finally {
690  endTransaction();
691  }
692  } finally {
693  mapModel.endTransaction();
694  }
695  }
696 
697  return true;
698  }
699 
705  @NotNull
706  private Point fixPoint(@NotNull final Point p) {
707  if (mapRec.contains(p)) {
708  return p;
709  }
710 
711  return new Point(Math.max(Math.min(p.x, mapRec.width - 1), 0), Math.max(Math.min(p.y, mapRec.height - 1), 0));
712  }
713 
714 }
Dimension getDragOffset()
Get offset from start position of dragging.
Definition: MapCursor.java:370
int getMapX()
Returns the x coordinate on the map.
Definition: MapSquare.java:107
void preSelect(@NotNull final Point start, @NotNull final Point end)
Rectangle defined by two points gets preselected.
Definition: MapGrid.java:313
void dragSelect(@NotNull final SelectionMode selectionMode, final boolean forceSelect)
Leave drag mode and select pre-selection using selectionMode.
Definition: MapCursor.java:337
A MapModel reflects the data of a map.
Definition: MapModel.java:75
G getNext(@NotNull final G gameObject)
Return the GameObject succeeding a given game object.
T [] getListeners()
Returns an array of all the listeners.
final Point transactionPos
The value of pos at the start of the outermost map cursor transaction.
Definition: MapCursor.java:131
MapModel< G, A, R > getMapModel()
Returns the MapModel this map square is part of.
Definition: MapSquare.java:99
void selectMapSquare()
Selects the map square on the current map location.
Definition: MapCursor.java:548
void endTransaction()
End a transaction.
boolean selectBelow(final boolean performAction)
Moves the selected GameObject.
Definition: MapCursor.java:600
G transactionGameObject
The value of gameObject at the start of the outermost map cursor transaction.
Definition: MapCursor.java:151
final Rectangle mapRec
Used to test if coordinates are on the map.
Definition: MapCursor.java:99
void selectArea(@NotNull final Point pos1, @NotNull final Point pos2, @NotNull final SelectionMode selectionMode)
Selects or deselects all squares in an area.
Definition: MapGrid.java:418
void removeGameObject(@NotNull G gameObject, boolean join)
Delete an existing GameObject from the map.
Point fixPoint(@NotNull final Point p)
Returns a valid location that is on the map.
Definition: MapCursor.java:706
G getGameObject()
Returns the selected GameObject.
Definition: MapCursor.java:437
MapCursor provides methods to move and drag on map.
Definition: MapCursor.java:57
final Dimension dragOffset
Offset of dragging.
Definition: MapCursor.java:76
final void beginTransaction()
Start a new transaction.
Definition: MapCursor.java:488
void unSelect()
Clears all selection and pre-selection flags from the grid.
Definition: MapGrid.java:263
boolean selectAbove(final boolean performAction)
Moves the selected GameObject.
Definition: MapCursor.java:573
final void endTransaction()
End a transaction.
Definition: MapCursor.java:506
boolean isOnGrid(@Nullable final Point p)
Check if point is on grid.
Definition: MapCursor.java:379
boolean setLocationSafe(@Nullable final Point p)
Move cursor to a new location.
Definition: MapCursor.java:254
void unPreSelect(@NotNull final Point start, @NotNull final Point end)
Pre-selection of rectangle defined by points gets deleted.
Definition: MapGrid.java:393
Point getLocation()
Get position of cursor.
Definition: MapCursor.java:226
boolean dragTo(@Nullable final Point p)
When in drag mode and the point is on the map cursor is moved to this position.
Definition: MapCursor.java:298
void setLocation(@NotNull final Point p)
Move cursor to a new location.
Definition: MapCursor.java:235
Base package of all Gridarta classes.
void addMapCursorListener(@NotNull final MapCursorListener< G, A, R > listener)
Register a MapCursorListener.
Definition: MapCursor.java:419
Reflects a game object (object on a map).
Definition: GameObject.java:36
Interface for listeners listening to MapGridEvents.
int transactionDepth
The nesting level of map cursor transactions.
Definition: MapCursor.java:124
void setCursor(@NotNull final Point pos)
Highlights the given cursor position.
Definition: MapGrid.java:296
void remove(@NotNull final T listener)
Removes a listener.
MapSquare< G, A, R > getMapSquare(@NotNull Point pos)
Get the square at a specified location.
GameObjects are the objects based on Archetypes found on maps.
void add(@NotNull final T listener)
Adds a listener.
G insertArchToMap(@NotNull BaseObject< G, A, R, ?> templateBaseObject, @Nullable G nextGameObject, @NotNull Point pos, boolean join)
Insert a game object to the map at a specified position.
int getWidth()
Returns the width of the area.
Definition: Size2D.java:96
2D-Grid containing flags for selection, pre-selection, cursor, warnings and errors.
Definition: MapGrid.java:45
G getFirst(@NotNull final GameObjectMatcher gameObjectMatcher)
Returns the first occurrence of a matching game object.
Definition: MapSquare.java:226
final MapGrid mapGrid
Grid where cursor is bound to.
Definition: MapCursor.java:87
void unSetCursor(@NotNull final Point pos)
Un-highlights the given cursor position.
Definition: MapGrid.java:276
void dragRelease()
Leave drag mode and undo pre-selection.
Definition: MapCursor.java:318
void setGameObject(@Nullable final G gameObject)
Sets the selected GameObject.
Definition: MapCursor.java:446
void beginTransaction()
Starts a new transaction.
Definition: MapGrid.java:756
void selectTopmostGameObject()
Selects the last (top-most) GameObject on the current map square.
Definition: MapCursor.java:562
final EventListenerList2< MapCursorListener< G, A, R > > listenerList
The MapCursorListeners to inform of changes.
Definition: MapCursor.java:164
final MapModel< G, A, R > mapModel
The MapModel of this cursor.
Definition: MapCursor.java:93
MapSquare< G, A, R > transactionMapSquare
The value of mapSquare at the start of the outermost map cursor transaction.
Definition: MapCursor.java:144
Type-safe version of EventListenerList.
final Rectangle2D transactionMapRec
The value of mapRec at the start of the outermost map cursor transaction.
Definition: MapCursor.java:158
G getPrev(@NotNull final G gameObject)
Return the GameObject preceding a given game object.
boolean deleteSelectedGameObject(final boolean performAction, final boolean autoJoin)
Deletes the selected game object.
Definition: MapCursor.java:658
void endTransaction()
Ends a transaction.
Definition: MapGrid.java:776
MapSquare< G, A, R > mapSquare
The selected MapSquare.
Definition: MapCursor.java:112
boolean goTo(final boolean performAction, @NotNull final Direction dir)
Moves the cursor one square relative to current position.
Definition: MapCursor.java:389
final Point pos
Current cursor position.
Definition: MapCursor.java:64
void updatePreSelect(@NotNull final Point start, @NotNull final Point oldEnd, @NotNull final Point newEnd)
Update the pre-selection rectangle.
Definition: MapGrid.java:334
boolean insertGameObject(final boolean performAction, @NotNull final BaseObject< G, A, R, ?> gameObject, final boolean insertAtEnd, final boolean join)
Inserts a GameObject before the selected game object.
Definition: MapCursor.java:631
boolean transactionDragging
The value of dragging at the start of the outermost map cursor transaction.
Definition: MapCursor.java:137
void dragStart()
Set cursor to drag mode when it is active.
Definition: MapCursor.java:278
Modes that describe how squares get selected.
void beginTransaction(@NotNull String name)
Starts a new transaction.
Interface for listeners listening to MapCursor related events.
int getHeight()
Returns the height of the area.
Definition: Size2D.java:104
boolean isDragging()
Returns whether the cursor is currently being dragged.
Definition: MapCursor.java:411
G getLast(@NotNull final GameObjectMatcher gameObjectMatcher)
Returns the last occurrence of a matching game object.
Definition: MapSquare.java:190
MapCursor(@NotNull final MapGrid mapGrid, @NotNull final MapModel< G, A, R > mapModel)
Construct a MapCursor.
Definition: MapCursor.java:172
final void deactivate()
Cursor gets deactivated.
Definition: MapCursor.java:354
void setMapSquare(@Nullable final MapSquare< G, A, R > mapSquare)
Sets the selected MapSquare.
Definition: MapCursor.java:469
void removeMapCursorListener(@NotNull final MapCursorListener< G, A, R > listener)
Remove a MapCursorListener.
Definition: MapCursor.java:427
final Point tmpPoint
Temporary point used in some methods.
Definition: MapCursor.java:106
int getMapY()
Returns the y coordinate on the map.
Definition: MapSquare.java:115
G gameObject
The selected GameObject.
Definition: MapCursor.java:118
The class Size2D represents a 2d rectangular area.
Definition: Size2D.java:30
This event is created by MapGrid.