Gridarta Editor
SelectedSquareView.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.gui.panel.selectedsquare;
21 
22 import java.awt.BorderLayout;
23 import java.awt.Color;
24 import java.awt.Container;
25 import java.awt.FlowLayout;
26 import java.awt.GridLayout;
27 import java.awt.Insets;
28 import java.awt.Point;
29 import java.awt.event.InputEvent;
30 import java.awt.event.MouseAdapter;
31 import java.awt.event.MouseEvent;
32 import java.awt.event.MouseListener;
33 import java.awt.geom.RectangularShape;
34 import java.util.Set;
35 import javax.swing.AbstractButton;
36 import javax.swing.Action;
37 import javax.swing.DefaultListModel;
38 import javax.swing.Icon;
39 import javax.swing.ImageIcon;
40 import javax.swing.JButton;
41 import javax.swing.JLabel;
42 import javax.swing.JList;
43 import javax.swing.JPanel;
44 import javax.swing.JScrollPane;
45 import javax.swing.JViewport;
46 import javax.swing.ListSelectionModel;
47 import javax.swing.ScrollPaneConstants;
48 import javax.swing.event.ListSelectionEvent;
49 import javax.swing.event.ListSelectionListener;
68 import net.sf.gridarta.utils.Size2D;
69 import org.jetbrains.annotations.NotNull;
70 import org.jetbrains.annotations.Nullable;
71 
81 public class SelectedSquareView<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> extends JPanel {
82 
86  private static final long serialVersionUID = 1L;
87 
91  @NotNull
93 
97  @NotNull
99 
103  @NotNull
105 
109  @NotNull
111 
115  @NotNull
117 
121  @NotNull
122  private final DefaultListModel<G> model = new DefaultListModel<>();
123 
127  @NotNull
128  private final JList<G> list = new JList<>(model);
129 
133  @NotNull
134  private final Container arrows = new JPanel();
135 
139  @Nullable
141 
146  @Nullable
148 
152  @NotNull
154 
155  @Override
156  public void mapSizeChanged(@NotNull final Size2D newSize) {
157  refresh();
158  }
159 
160  @Override
161  public void mapSquaresChanged(@NotNull final Set<MapSquare<G, A, R>> mapSquares) {
162  if (selectedSquareModel.isSelectedMapSquares(mapSquares)) {
163  refresh();
164  }
165  }
166 
167  @Override
168  public void mapObjectsChanged(@NotNull final Set<G> gameObjects, @NotNull final Set<G> transientGameObjects) {
169  if (selectedSquareModel.isSelectedGameObjects(gameObjects) || selectedSquareModel.isSelectedGameObjects(transientGameObjects)) {
170  refresh();
171  }
172  }
173 
174  @Override
175  public void errorsChanged(@NotNull final ErrorCollector<G, A, R> errors) {
176  // ignore
177  }
178 
179  @Override
180  public void mapFileChanged(@Nullable final MapFile oldMapFile) {
181  // ignore
182  }
183 
184  @Override
185  public void modifiedChanged() {
186  // ignore
187  }
188 
189  };
190 
194  @NotNull
196 
197  @Override
198  public void activeMapViewChanged(@Nullable final MapView<G, A, R> mapView) {
199  @Nullable final MapModel<G, A, R> mapModel;
200  @Nullable final MapCursor<G, A, R> mapCursor;
201  if (mapView == null) {
202  mapModel = null;
203  mapCursor = null;
204  } else {
205  mapModel = mapView.getMapControl().getMapModel();
206  mapCursor = mapView.getMapCursor();
207  }
208  setMapCursor(mapModel, mapCursor);
209  }
210 
211  @Override
212  public void mapViewCreated(@NotNull final MapView<G, A, R> mapView) {
213  // ignore
214  }
215 
216  @Override
217  public void mapViewClosing(@NotNull final MapView<G, A, R> mapView) {
218  // ignore
219  }
220 
221  };
222 
227  @NotNull
229 
230  @Override
231  public void selectionChanged(@Nullable final MapSquare<G, A, R> mapSquare, @Nullable final G gameObject) {
232  refresh();
233  }
234 
235  };
236 
241  @NotNull
242  private final ListSelectionListener listSelectionListener = new ListSelectionListener() {
243 
244  @Override
245  public void valueChanged(final ListSelectionEvent e) {
246  setSelectedIndex(list.getSelectedIndex());
247  }
248 
249  };
250 
254  @NotNull
256 
257  @Override
258  public void mapCursorChangedPos(@NotNull final Point location) {
259  // ignore
260  }
261 
262  @Override
263  public void mapCursorChangedMode() {
264  // ignore
265  }
266 
267  @Override
268  public void mapCursorChangedGameObject(@Nullable final MapSquare<G, A, R> mapSquare, @Nullable final G gameObject) {
269  if (selectedSquareModel.setSelectedMapSquare(mapSquare, gameObject)) {
270  refresh();
271  }
272  }
273 
274  @Override
275  public void mapCursorChangedSize() {
276  // ignore
277  }
278 
279  };
280 
285  @NotNull
286  private final MouseListener mouseListener = new MouseAdapter() {
287 
288  @Override
289  public void mousePressed(final MouseEvent e) {
290  if (isSelect(e)) {
291  if (e.getClickCount() > 1) { // LMB Double click
292  final G gameObject = selectedSquareModel.getSelectedGameObject();
293  if (gameObject != null) {
294  gameObjectAttributesDialogFactory.showAttributeDialog(gameObject);
295  }
296  }
297  } else if (isInsert(e)) {
299  } else if (isDelete(e)) {
301  }
302  }
303 
304  };
305 
321  public SelectedSquareView(@NotNull final SelectedSquareModel<G, A, R> selectedSquareModel, @NotNull final GameObjectAttributesDialogFactory<G, A, R> gameObjectAttributesDialogFactory, @NotNull final ObjectChooser<G, A, R> objectChooser, @NotNull final MapViewManager<G, A, R> mapViewManager, @NotNull final MapViewSettings mapViewSettings, @Nullable final ImageIcon compassIcon, @NotNull final FaceObjectProviders faceObjectProviders, @NotNull final Icon unknownSquareIcon, @NotNull final Action... actions) {
322  this.selectedSquareModel = selectedSquareModel;
323  this.gameObjectAttributesDialogFactory = gameObjectAttributesDialogFactory;
324  this.objectChooser = objectChooser;
325  this.mapViewSettings = mapViewSettings;
326 
327  setLayout(new BorderLayout());
328 
329  modelUpdater = new ModelUpdater<>(model, mapViewSettings);
330  list.setCellRenderer(new CellRenderer<G, A, R>(faceObjectProviders, unknownSquareIcon));
331  list.setBackground(Color.lightGray);
332  list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
333  final JScrollPane scrollPane = new JScrollPane(list);
334  scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
335  scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
336  add(scrollPane, BorderLayout.CENTER);
337 
338  for (final Action tmpAction : actions) {
339  final AbstractButton button = new JButton(tmpAction);
340  button.setMargin(new Insets(0, 0, 0, 0));
341  arrows.add(button);
342  }
343 
344  updateArrows(false);
345  if (compassIcon != null) {
346  final Container compass = new JPanel();
347  compass.add(new JLabel(compassIcon));
348  add(compass, BorderLayout.NORTH);
349  }
350  list.addListSelectionListener(listSelectionListener);
351  list.addMouseListener(mouseListener);
352  list.setFocusable(false); // XXX Workaround for Mantis #0000154 This is not clean and should be removed as soon as cut/copy/paste of arches is possible
353 
354  mapViewManager.addMapViewManagerListener(mapViewManagerListener);
355  selectedSquareModel.addSelectedSquareListener(selectedSquareModelListener);
356 
357  refresh();
358  }
359 
365  private void updateArrows(final boolean mapSquareListBottom) {
366  if (mapSquareListBottom) {
367  arrows.setLayout(new GridLayout(4, 1));
368  add(arrows, BorderLayout.WEST); // put up/down buttons west
369  } else {
370  arrows.setLayout(new FlowLayout());
371  add(arrows, BorderLayout.SOUTH); // put up/down buttons south
372  }
373  }
374 
383  public int getListIndex(@NotNull final MouseEvent e) {
384  final int lastIndex = model.getSize() - 1;
385  if (lastIndex < 0) {
386  return 0;
387  }
388 
389  final RectangularShape bounds = list.getCellBounds(lastIndex, lastIndex);
390  final int lowestY = (int) (bounds.getY() + bounds.getHeight());
391  if ((int) e.getPoint().getY() >= lowestY) {
392  return lastIndex + 1;
393  }
394 
395  final int listIndex = list.locationToIndex(e.getPoint());
396  assert listIndex >= 0; // -1 is returned only if the list is empty, but this was already checked
397  return listIndex;
398  }
399 
404  private void refresh() {
405  list.setEnabled(false);
406  try {
407  setSelectedIndex(modelUpdater.update(selectedSquareModel.getSelectedMapSquare(), selectedSquareModel.getSelectedGameObject()));
408  } finally {
409  list.setEnabled(true);
410  }
411  }
412 
418  @Nullable
419  @SuppressWarnings("TypeMayBeWeakened")
420  private G getListGameObject(final int index) {
421  final int actualIndex = getValidIndex(index);
422  if (actualIndex >= model.getSize()) {
423  return null;
424  }
425 
426  return model.getElementAt(actualIndex);
427  }
428 
434  private void setSelectedIndex(final int index) {
435  final int actualIndex = getValidIndex(index);
436  if (list.getSelectedIndex() != actualIndex) {
437  list.setSelectedIndex(actualIndex);
438  }
439 
440  if (mapCursor != null) {
441  mapCursor.setGameObject(list.getSelectedValue());
442  }
443  }
444 
450  private int getValidIndex(final int index) {
451  if (index < 0) {
452  return 0;
453  }
454  final int size = model.getSize();
455  if (index >= size) {
456  return Math.max(0, size - 1);
457  }
458  return index;
459  }
460 
465  private void deleteGameObject(final int index) {
467  if (mapCursor != null && index < model.getSize()) {
468  setSelectedIndex(index);
470  }
471  }
472 
477  private void insertGameObject(final int index) {
479  if (gameObject != null) {
481  if (mapCursor != null) {
482  mapCursor.insertGameObject(true, gameObject, index >= model.getSize(), mapViewSettings.isAutojoin());
483  }
484  }
485  setSelectedIndex(index);
486  }
487 
493  private static boolean isSelect(@NotNull final InputEvent e) {
494  return (e.getModifiers() & InputEvent.BUTTON1_MASK) != 0;
495  }
496 
502  private static boolean isInsert(@NotNull final InputEvent e) {
503  return (e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.CTRL_MASK)) == InputEvent.BUTTON3_MASK;
504  }
505 
511  private static boolean isDelete(@NotNull final InputEvent e) {
512  return (e.getModifiers() & InputEvent.BUTTON2_MASK) != 0 || (e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.CTRL_MASK)) == (InputEvent.BUTTON3_MASK | InputEvent.CTRL_MASK);
513  }
514 
515  private void setMapCursor(@Nullable final MapModel<G, A, R> mapModel, @Nullable final MapCursor<G, A, R> mapCursor) {
516  if (this.mapModel != null) {
517  this.mapModel.removeMapModelListener(mapModelListener);
518  }
519  this.mapModel = mapModel;
520  if (this.mapModel != null) {
521  this.mapModel.addMapModelListener(mapModelListener);
522  }
523 
524  if (this.mapCursor != null) {
525  this.mapCursor.removeMapCursorListener(mapCursorListener);
526  }
527  this.mapCursor = mapCursor;
528  if (this.mapCursor != null) {
529  this.mapCursor.addMapCursorListener(mapCursorListener);
530  }
531 
532  final G gameObject = mapCursor == null ? null : mapCursor.getGameObject();
533  final MapSquare<G, A, R> mapSquare = gameObject == null ? null : gameObject.getMapSquare();
534  if (selectedSquareModel.setSelectedMapSquare(mapSquare, gameObject)) {
535  refresh();
536  }
537  }
538 
539 }
MapModel< G, A, R > mapModel
The currently active MapModel.
final SelectedSquareModelListener< G, A, R > selectedSquareModelListener
The SelectedSquareModelListener attached to selectedSquareModel.
G getSelectedGameObject()
Returns the currently selected GameObject within this list (currently selected MapSquare).
final ObjectChooser< G, A, R > objectChooser
The object chooser.
final ListSelectionListener listSelectionListener
The listener attached to list to be informed about selection changes.
boolean isSelectedMapSquares(@NotNull final Iterable< MapSquare< G, A, R >> mapSquares)
A MapModel reflects the data of a map.
Definition: MapModel.java:75
boolean isAutojoin()
Returns whether "autojoin" is enabled.
void refresh()
Re-display the map square panel for SelectedSquareModel#selectedMapSquare.
Graphical User Interface of Gridarta.
final MapCursorListener< G, A, R > mapCursorListener
The MapCursorListener attached to mapCursor.
G getListGameObject(final int index)
Return a game object from the list.
This package contains the framework for validating maps.
static boolean isSelect(@NotNull final InputEvent e)
Determines if "select" was selected.
void deleteGameObject(final int index)
Deletes a GameObject with a specific list index.
void setMapCursor(@Nullable final MapModel< G, A, R > mapModel, @Nullable final MapCursor< G, A, R > mapCursor)
void updateArrows(final boolean mapSquareListBottom)
Updates the parameters of the arrows buttons.
Interface for listeners listening on MapModel events.
final SelectedSquareModel< G, A, R > selectedSquareModel
The model for this view.
static boolean isDelete(@NotNull final InputEvent e)
Determines if "delete" was selected.
MapSquare< G, A, R > getSelectedMapSquare()
Returns the currently selected map square.
BaseObject< G, A, R, ?> getSelection()
Returns the active arch in the left-side panel.
MapCursor provides methods to move and drag on map.
Definition: MapCursor.java:57
void addMapModelListener(@NotNull MapModelListener< G, A, R > listener)
Register a map listener.
final MapModelListener< G, A, R > mapModelListener
The MapModelListener attached to mapModel.
CellRenderer for rendering ArchObjects on a certain map square in a list.
void insertGameObject(final int index)
Inserts a new game object.
int update(@Nullable final GameObjectContainer< G, A, R > mapSquare, @Nullable final G gameObject)
Updates the model to reflect a net.sf.gridarta.model.mapmodel.MapSquare.
Base package of all Gridarta classes.
SelectedSquareView(@NotNull final SelectedSquareModel< G, A, R > selectedSquareModel, @NotNull final GameObjectAttributesDialogFactory< G, A, R > gameObjectAttributesDialogFactory, @NotNull final ObjectChooser< G, A, R > objectChooser, @NotNull final MapViewManager< G, A, R > mapViewManager, @NotNull final MapViewSettings mapViewSettings, @Nullable final ImageIcon compassIcon, @NotNull final FaceObjectProviders faceObjectProviders, @NotNull final Icon unknownSquareIcon, @NotNull final Action... actions)
Create a new instance.
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
int getListIndex(@NotNull final MouseEvent e)
Determine the list index for a given mouse event.
final MapViewManagerListener< G, A, R > mapViewManagerListener
The map view manager listener.
final ModelUpdater< G, A, R > modelUpdater
The ModelUpdater used for updating model.
final MouseListener mouseListener
The MouseListener attached to the view to process mouse actions.
Container for settings that affect the rendering of maps.
GameObjects are the objects based on Archetypes found on maps.
final MapViewSettings mapViewSettings
The MapViewSettings instance.
Interface for listeners interested in events related to MapViewManager instances. ...
final DefaultListModel< G > model
The DefaultListModel of list.
boolean setSelectedMapSquare(@Nullable final MapSquare< G, A, R > mapSquare, @Nullable final G gameObject)
Sets the currently selected map square.
Base classes for rendering maps.
void setGameObject(@Nullable final G gameObject)
Sets the selected GameObject.
Definition: MapCursor.java:446
MapCursor< G, A, R > mapCursor
The currently tracked MapCursor.
void setSelectedIndex(final int index)
Set the currently selected list index.
Provider for faces of GameObjects and Archetypes.
The face is the appearance of an object.
boolean deleteSelectedGameObject(final boolean performAction, final boolean autoJoin)
Deletes the selected game object.
Definition: MapCursor.java:658
The panel that displays the game objects of the currently selected map square.
int getValidIndex(final int index)
Determine a valid list index near a given index.
A map view consists of a map grid and a map cursor, and is attached to a map control.
Definition: MapView.java:43
An interface for classes that collect errors.
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
Common base interface for ObjectChoosers.
final GameObjectAttributesDialogFactory< G, A, R > gameObjectAttributesDialogFactory
The factory for creating game object attributes dialog instances.
Interface for listeners listening to MapCursor related events.
void showAttributeDialog(@NotNull final G gameObject)
Shows the game object attributes dialog for a given GameObject instance.
boolean isSelectedGameObjects(@NotNull final Iterable< G > gameObjects)
The location of a map file with a map directory.
Definition: MapFile.java:31
static boolean isInsert(@NotNull final InputEvent e)
Determines if "insert" was selected.
The class Size2D represents a 2d rectangular area.
Definition: Size2D.java:30