Gridarta Editor
GameObjectAttributesControl.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.gameobjectattributes;
21 
22 import java.awt.BorderLayout;
23 import java.awt.Component;
24 import java.awt.Container;
25 import java.awt.GridLayout;
26 import java.util.Collection;
27 import java.util.HashSet;
28 import java.util.IdentityHashMap;
29 import java.util.Map;
30 import java.util.Set;
31 import javax.swing.Action;
32 import javax.swing.JButton;
33 import javax.swing.JLabel;
34 import javax.swing.JPanel;
35 import javax.swing.JTabbedPane;
36 import javax.swing.SwingConstants;
60 import net.sf.gridarta.utils.Size2D;
61 import net.sf.japi.swing.action.ActionBuilder;
62 import net.sf.japi.swing.action.ActionBuilderFactory;
63 import net.sf.japi.swing.action.ActionMethod;
64 import org.jetbrains.annotations.NotNull;
65 import org.jetbrains.annotations.Nullable;
66 
72 public class GameObjectAttributesControl<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> extends JPanel {
73 
77  private static final long serialVersionUID = 1L;
78 
82  @NotNull
83  private static final ActionBuilder ACTION_BUILDER = ActionBuilderFactory.getInstance().getActionBuilder("net.sf.gridarta");
84 
88  @NotNull
90 
94  @NotNull
96 
100  @NotNull
102 
107  @NotNull
109 
113  @NotNull
115 
119  @NotNull
120  private final JTabbedPane panelDesktop = new JTabbedPane(SwingConstants.TOP);
121 
125  @NotNull
127 
132  @NotNull
134 
138  @Nullable
140 
145 
149  @NotNull
151 
155  @NotNull
156  private final Action aMapArchAddInv = ACTION_BUILDER.createAction(false, "mapArchAddInv", this);
157 
161  @NotNull
162  private final Action aMapArchAddEnv = ACTION_BUILDER.createAction(false, "mapArchAddEnv", this);
163 
167  @NotNull
168  private final Action aMapArchAttribute = ACTION_BUILDER.createAction(false, "mapArchAttrib", this);
169 
173  @NotNull
174  private final Collection<GameObjectAttributesTab<G, A, R>> tabs = new HashSet<>();
175 
179  @NotNull
180  private final Map<GameObjectAttributesTab<G, A, R>, Integer> tabIndex = new IdentityHashMap<>();
181 
185  @NotNull
186  private final Map<Component, GameObjectAttributesTab<G, A, R>> componentTabs = new IdentityHashMap<>();
187 
191  @Nullable
193 
198  private boolean isInMapTransaction;
199 
203  @NotNull
205 
206  @Override
207  public void currentMapChanged(@Nullable final MapControl<G, A, R> mapControl) {
208  if (currentMapControl != null) {
209  final MapModel<G, A, R> mapModel = currentMapControl.getMapModel();
212  }
213  currentMapControl = mapControl;
214  if (currentMapControl != null) {
215  final MapModel<G, A, R> mapModel = currentMapControl.getMapModel();
218  }
219  }
220 
221  @Override
222  public void mapCreated(@NotNull final MapControl<G, A, R> mapControl, final boolean interactive) {
223  // ignore
224  }
225 
226  @Override
227  public void mapClosing(@NotNull final MapControl<G, A, R> mapControl) {
228  // ignore
229  }
230 
231  @Override
232  public void mapClosed(@NotNull final MapControl<G, A, R> mapControl) {
233  // ignore
234  }
235 
236  };
237 
241  @NotNull
243 
244  @Override
245  public void mapSizeChanged(@NotNull final Size2D newSize) {
246  // ignore
247  }
248 
249  @Override
250  public void mapSquaresChanged(@NotNull final Set<MapSquare<G, A, R>> mapSquares) {
251  if (selectedGameObject == null) {
252  return;
253  }
254 
255  final G topGameObject = selectedGameObject.getTopContainer();
256  for (final Iterable<G> mapSquare : mapSquares) {
257  for (final G gameObject : mapSquare) {
258  if (gameObject == topGameObject) {
259  refreshDisplay();
260  }
261  }
262  }
263  }
264 
265  @Override
266  public void mapObjectsChanged(@NotNull final Set<G> gameObjects, @NotNull final Set<G> transientGameObjects) {
267  for (final GameObject<G, A, R> gameObject : gameObjects) {
268  if (selectedGameObject == gameObject.getHead()) {
269  refreshDisplay();
270  }
271  }
272  }
273 
274  @Override
275  public void errorsChanged(@NotNull final ErrorCollector<G, A, R> errors) {
276  // ignore
277  }
278 
279  @Override
280  public void mapFileChanged(@Nullable final MapFile oldMapFile) {
281  // ignore
282  }
283 
284  @Override
285  public void modifiedChanged() {
286  // ignore
287  }
288 
289  };
290 
296  @NotNull
298 
299  @Override
300  public void preBeginTransaction() {
302  isInMapTransaction = true;
303  }
304 
305  @Override
306  public void beginTransaction(@NotNull final String name) {
307  // ignore
308  }
309 
310  @Override
311  public void endTransaction(@NotNull final SavedSquares<G, A, R> savedSquares) {
312  // ignore
313  }
314 
315  @Override
316  public void postEndTransaction() {
317  isInMapTransaction = false;
318  }
319 
320  };
321 
325  @NotNull
327 
328  @Override
329  public void selectionChanged(@Nullable final MapSquare<G, A, R> mapSquare, @Nullable final G gameObject) {
330  gameObjectAttributesModel.setSelectedGameObject(gameObject);
331  }
332 
333  };
334 
338  @NotNull
340 
341  @Override
342  public void selectedGameObjectChanged(@Nullable final G selectedGameObject) {
345  refreshDisplay();
346  }
347 
348  @Override
349  public void refreshSelectedGameObject() {
350  // ignore
351  }
352 
353  };
354 
358  @NotNull
360 
361  @Override
362  public void tabSeverityChanged(@NotNull final GameObjectAttributesTab<G, A, R> tab, @NotNull final Severity tabSeverity) {
363  setTabSeverity(tab, tabSeverity);
364  }
365 
366  @Override
367  public void apply() {
369  }
370 
371  };
372 
385  public GameObjectAttributesControl(@NotNull final GameObjectAttributesModel<G, A, R> gameObjectAttributesModel, @NotNull final GameObjectAttributesDialogFactory<G, A, R> gameObjectAttributesDialogFactory, @NotNull final ObjectChooser<G, A, R> objectChooser, @NotNull final MapManager<G, A, R> mapManager, @NotNull final SelectedSquareModel<G, A, R> selectedSquareModel, @NotNull final GameObjectFactory<G, A, R> gameObjectFactory, @NotNull final MapViewSettings mapViewSettings) {
386  super(new BorderLayout());
387  this.gameObjectAttributesModel = gameObjectAttributesModel;
388  this.gameObjectAttributesDialogFactory = gameObjectAttributesDialogFactory;
389  this.objectChooser = objectChooser;
390  this.selectedSquareModel = selectedSquareModel;
391  this.gameObjectFactory = gameObjectFactory;
392  this.mapViewSettings = mapViewSettings;
393 
394  final Container mapArchPanel = new JPanel();
395  mapArchPanel.setLayout(new BorderLayout());
396  add(mapArchPanel, BorderLayout.CENTER);
397 
398  final Container buttonPanel = createButtonPanel();
399  buttonPanel.setLayout(new GridLayout(0, 1));
400  mapArchPanel.add(buttonPanel, BorderLayout.WEST);
401  mapArchPanel.add(panelDesktop, BorderLayout.CENTER);
402 
403  selectedSquareModel.addSelectedSquareListener(selectedSquareModelListener);
404  gameObjectAttributesModel.addGameObjectAttributesModelListener(gameObjectAttributesModelListener); // this listener must be registered before any tab, including TextEditorTab
405 
406  selectedGameObject = gameObjectAttributesModel.getSelectedGameObject();
407  refreshDisplay();
408  currentMapControl = mapManager.getCurrentMap();
409  if (currentMapControl != null) {
410  final MapModel<G, A, R> mapModel = currentMapControl.getMapModel();
411  mapModel.addMapModelListener(mapModelListener);
412  mapModel.addMapTransactionListener(mapTransactionListener);
413  }
414  mapManager.addMapManagerListener(mapManagerListener);
415  }
416 
422  listeners.add(listener);
423  }
424 
430  listeners.remove(listener);
431  }
432 
437  public void addTab(@NotNull final GameObjectAttributesTab<G, A, R> tab) {
438  tabIndex.put(tab, panelDesktop.getTabCount());
439  addTabInt(tab);
440  panelDesktop.add(tab.getPanel(), ActionBuilderUtils.getString(ACTION_BUILDER, "mapArchTab" + tab.getClass().getSimpleName() + ".text"));
441  setTabSeverity(tab, tab.getTabSeverity());
442  }
443 
448  private void addTabInt(final GameObjectAttributesTab<G, A, R> tab) {
449  tabs.add(tab);
450  componentTabs.put(tab.getPanel(), tab);
451  tab.addGameObjectAttributesTabListener(gameObjectAttributesTabListener);
452  }
453 
458  public void selectTab(@NotNull final GameObjectAttributesTab<G, A, R> tab) {
459  if (!tabs.contains(tab)) {
460  throw new IllegalArgumentException();
461  }
462 
463  panelDesktop.setSelectedComponent(tab.getPanel());
464  }
465 
470  @NotNull
472  final Component component = panelDesktop.getSelectedComponent();
473  final GameObjectAttributesTab<G, A, R> tab = componentTabs.get(component);
474  assert tab != null;
475  return tab;
476  }
477 
483  private void setTabSeverity(@NotNull final GameObjectAttributesTab<G, A, R> tab, @NotNull final Severity tabSeverity) {
484  final Integer index = tabIndex.get(tab);
485  if (index == null) {
486  return;
487  }
488 
489  /* panelDesktop.setForegroundAt() doesn't work with Nimbus, so use a (dirty) workaround. */
490  final JLabel title = new JLabel(panelDesktop.getTitleAt(index));
491  title.setForeground(tabSeverity.getColor());
492  panelDesktop.setTabComponentAt(index, title);
493 
494  Severity mainSeverity = tabSeverity;
495  for (final Map.Entry<GameObjectAttributesTab<G, A, R>, Integer> pair2 : tabIndex.entrySet()) {
496  final Severity tmpSeverity = pair2.getKey().getTabSeverity();
497  if (tmpSeverity.getLevel() > mainSeverity.getLevel()) {
498  mainSeverity = tmpSeverity;
499  }
500  }
501  if (severity == mainSeverity) {
502  return;
503  }
504  severity = mainSeverity;
505  for (final GameObjectAttributesControlListener listener : listeners.getListeners()) {
506  listener.severityChanged(mainSeverity);
507  }
508  }
509 
514  @NotNull
516  return severity;
517  }
518 
522  private void refreshDisplay() {
523  aMapArchAddInv.setEnabled(selectedGameObject != null);
524  aMapArchAddEnv.setEnabled(selectedGameObject != null);
525  aMapArchAttribute.setEnabled(selectedGameObject != null);
526  gameObjectAttributesModel.fireRefreshSelectedGameObject();
527  }
528 
533  @ActionMethod
534  public void mapArchAttrib() {
535  if (selectedGameObject != null) {
536  gameObjectAttributesDialogFactory.showAttributeDialog(selectedGameObject);
537  }
538  }
539 
544  @ActionMethod
545  public void mapArchAddInv() {
546  final BaseObject<G, A, R, ?> arch = objectChooser.getSelection();
547  if (arch == null) { // nothing selected?
548  return;
549  }
550 
551  final GameObject<G, A, R> gameObject = selectedGameObject;
552  if (gameObject == null) {
553  return;
554  }
555 
556  final G invNew = arch.newInstance(gameObjectFactory);
557 
558  final MapSquare<G, A, R> mapSquare = gameObject.getMapSquare();
559  assert mapSquare != null;
560  final MapModel<G, A, R> mapModel = mapSquare.getMapModel();
561  mapModel.beginTransaction("Add to inventory");
562  try {
563  gameObject.addLast(invNew);
564  if (arch instanceof Archetype) {
565  gameObjectFactory.createInventory(invNew, arch);
566  }
567  } finally {
568  mapModel.endTransaction();
569  }
570  }
571 
576  @ActionMethod
577  public void mapArchAddEnv() {
578  final BaseObject<G, A, R, ?> baseObject = objectChooser.getSelection();
579  if (baseObject == null) {
580  return;
581  }
582 
583  final G prevGameObject = selectedGameObject;
584  if (prevGameObject == null) {
585  return;
586  }
587 
588  final MapSquare<G, A, R> mapSquare = prevGameObject.getMapSquare();
589  assert mapSquare != null;
590  final MapModel<G, A, R> mapModel = mapSquare.getMapModel();
591  mapModel.beginTransaction("Add to environment"); // TODO; I18N/L10N
592  try {
593  final G insertedGameObject = mapModel.insertArchToMap(baseObject, prevGameObject, mapSquare.getMapLocation(), mapViewSettings.isAutojoin());
594  if (insertedGameObject != null) {
595  mapModel.removeGameObject(prevGameObject, mapViewSettings.isAutojoin());
596  insertedGameObject.addLast(prevGameObject);
597  selectedSquareModel.setSelectedGameObject(insertedGameObject);
598  }
599  } finally {
600  mapModel.endTransaction();
601  }
602  }
603 
608  private void autoApplyArchPanelChanges() {
609  if (selectedGameObject == null || isInAutoApplyArchPanelChanges || isInMapTransaction) {
610  return;
611  }
612 
613  isInAutoApplyArchPanelChanges = true;
614  try {
616  } finally {
617  isInAutoApplyArchPanelChanges = false;
618  }
619  }
620 
625  private void applyArchPanelChanges() {
626  final GameObject<G, A, R> gameObject = selectedGameObject;
627  if (gameObject == null) {
628  return;
629  }
630 
631  final MapSquare<G, A, R> mapSquare = gameObject.getMapSquare();
632  if (mapSquare == null) {
633  // auto-apply of deleted game object
634  return;
635  }
636 
637  // suppress unwanted map transactions if nothing has changed
638  boolean canApply = false;
639  for (final GameObjectAttributesTab<G, A, R> tab : tabs) {
640  if (tab.canApply()) {
641  canApply = true;
642  break;
643  }
644  }
645  if (!canApply) {
646  return;
647  }
648 
649  final MapModel<G, A, R> mapModel = mapSquare.getMapModel();
650  mapModel.beginTransaction("Change object attributes");
651  try {
652  for (final GameObjectAttributesTab<G, A, R> tab : tabs) {
653  if (tab.canApply()) {
654  tab.apply();
655  }
656  }
657  } finally {
658  mapModel.endTransaction();
659  }
660  }
661 
666  @NotNull
667  private Container createButtonPanel() {
668  final Container buttonPanel = new JPanel();
669  buttonPanel.add(new JButton(aMapArchAddInv));
670  buttonPanel.add(new JButton(aMapArchAddEnv));
671  buttonPanel.add(new JButton(aMapArchAttribute));
672  return buttonPanel;
673  }
674 
675 }
void removeGameObjectAttributesControlListener(@NotNull final GameObjectAttributesControlListener listener)
Removes a GameObjectAttributesControlListener to notify.
final MapTransactionListener< G, A, R > mapTransactionListener
The map transaction listener which is attached to currentMapControl.
void removeMapTransactionListener(@NotNull MapTransactionListener< G, A, R > listener)
Unregisters a map transaction listener.
A MapModel reflects the data of a map.
Definition: MapModel.java:75
A MapManager manages all opened maps.
Definition: MapManager.java:37
void autoApplyArchPanelChanges()
Same as applyArchPanelChanges() but does protect against recursive calls.
boolean isAutojoin()
Returns whether "autojoin" is enabled.
Graphical User Interface of Gridarta.
T [] getListeners()
Returns an array of all the listeners.
MapModel< G, A, R > getMapModel()
Returns the MapModel this map square is part of.
Definition: MapSquare.java:99
GameObjectAttributesControl(@NotNull final GameObjectAttributesModel< G, A, R > gameObjectAttributesModel, @NotNull final GameObjectAttributesDialogFactory< G, A, R > gameObjectAttributesDialogFactory, @NotNull final ObjectChooser< G, A, R > objectChooser, @NotNull final MapManager< G, A, R > mapManager, @NotNull final SelectedSquareModel< G, A, R > selectedSquareModel, @NotNull final GameObjectFactory< G, A, R > gameObjectFactory, @NotNull final MapViewSettings mapViewSettings)
Create the GameObjectAttributesPanel.
This package contains the framework for validating maps.
void refreshDisplay()
Update the displayed information for the selected game object.
void endTransaction()
End a transaction.
final Collection< GameObjectAttributesTab< G, A, R > > tabs
All active tabs.
void removeGameObject(@NotNull G gameObject, boolean join)
Delete an existing GameObject from the map.
final MapModelListener< G, A, R > mapModelListener
The map model listener which is attached to currentMapControl.
Interface for listeners listening on MapModel events.
BaseObject< G, A, R, ?> getSelection()
Returns the active arch in the left-side panel.
Interface for listeners interested in events of SelectedSquareModel instances.
void createInventory(@NotNull GameObject< G, A, R > gameObject, @NotNull Iterable< G > archetype)
Copies inventory objects from an archetype into a game object.
void addMapModelListener(@NotNull MapModelListener< G, A, R > listener)
Register a map listener.
final GameObjectAttributesDialogFactory< G, A, R > gameObjectAttributesDialogFactory
The MainControl to use, e.g.
MapModel< G, A, R > getMapModel()
Returns the map model.
boolean isInAutoApplyArchPanelChanges
Whether autoApplyArchPanelChanges() is currently running.
static String getString(@NotNull final ActionBuilder actionBuilder, @NotNull final String key, @NotNull final String defaultValue)
Returns the value of a key.
Base package of all Gridarta classes.
final GameObjectAttributesTabListener< G, A, R > gameObjectAttributesTabListener
The listener attached to all tabs.
Interface for listeners listening on map transactions of MapModels.
Container createButtonPanel()
Creates the button panel containing the actions.
final GameObjectAttributesModel< G, A, R > gameObjectAttributesModel
The model used by this controller.
Reflects a game object (object on a map).
Definition: GameObject.java:36
void addTab(@NotNull final GameObjectAttributesTab< G, A, R > tab)
Adds a tab.
void mapArchAttrib()
Action method for displaying the attributes of the currently selected object.
void addMapTransactionListener(@NotNull MapTransactionListener< G, A, R > listener)
Registers a map transaction listener.
Abstract factory for creating GameObject instances.
void addGameObjectAttributesTabListener(@NotNull GameObjectAttributesTabListener< G, A, R > listener)
Adds a listener.
void fireRefreshSelectedGameObject()
Notifies all listeners that the selected game object has changed.
Interface for listeners listening to MapManager changes.
Interface for listeners interested in GameObjectAttributesControl related events. ...
void remove(@NotNull final T listener)
Removes a listener.
final Map< Component, GameObjectAttributesTab< G, A, R > > componentTabs
Maps tab&#39;s component to tab.
Container for settings that affect the rendering of maps.
void setTabSeverity(@NotNull final GameObjectAttributesTab< G, A, R > tab, @NotNull final Severity tabSeverity)
Sets the tab color of a tab.
GameObjects are the objects based on Archetypes found on maps.
void add(@NotNull final T listener)
Adds a listener.
final MapManagerListener< G, A, R > mapManagerListener
The map manager 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.
Point getMapLocation()
Returns the coordinate on the map.
Definition: MapSquare.java:124
final SelectedSquareModelListener< G, A, R > selectedSquareModelListener
The listener to detect changes of the selected game object.
final GameObjectAttributesModelListener< G, A, R > gameObjectAttributesModelListener
The listener attached to gameObjectAttributesModel.
Displays the contents of the currently selected map square.
int getLevel()
Returns the severity level.
Definition: Severity.java:80
GameObjectAttributesTab< G, A, R > getSelectedTab()
Returns the selected tab.
G newInstance(@NotNull GameObjectFactory< G, A, R > gameObjectFactory)
Creates a new GameObject instance: an Archetype is instantiated, a GameObject is cloned.
void applyArchPanelChanges()
When the "apply"-button on the ArchPanel (at the bottom of the window) is pressed, this function updates the active arch object.
void removeMapModelListener(@NotNull MapModelListener< G, A, R > listener)
Unregister a map listener.
final ObjectChooser< G, A, R > objectChooser
The object chooser instance.
Severity levels for colors of tabs.
Definition: Severity.java:29
Utility class for ActionBuilder related functions.
Common base class for the panel that allows users to edit a GameObject&#39;s attributes.
Type-safe version of EventListenerList.
void mapArchAddInv()
Action method for adding an object to the inventory of the currently selected object.
Currently nothing more than a marker interface for unification.
Definition: MapControl.java:35
Records a set of changed map squares.
An interface for classes that collect errors.
DEFAULT
The tab contents are unchanged from defaults.
Definition: Severity.java:34
void mapArchAddEnv()
Action method for adding the currently selected object to the inventory of a new object.
void addGameObjectAttributesControlListener(@NotNull final GameObjectAttributesControlListener listener)
Adds a GameObjectAttributesControlListener to notify.
void setSelectedGameObject(@Nullable final G gameObject)
Sets the currently selected GameObject.
void setSelectedGameObject(@Nullable final G selectedGameObject)
If a game object is selected, the MapArchPanels (bottom right windows) get updated.
Common base interface for ObjectChoosers.
void beginTransaction(@NotNull String name)
Starts a new transaction.
The model component of the selected square control.
final EventListenerList2< GameObjectAttributesControlListener > listeners
The registered GameObjectAttributesControlListeners to notify.
final SelectedSquareModel< G, A, R > selectedSquareModel
The SelectedSquareModel to update.
void addLast(@NotNull G gameObject)
Add the given GameObject at the end of this Container.
void showAttributeDialog(@NotNull final G gameObject)
Shows the game object attributes dialog for a given GameObject instance.
The location of a map file with a map directory.
Definition: MapFile.java:31
void selectTab(@NotNull final GameObjectAttributesTab< G, A, R > tab)
Selects a tab.
final Map< GameObjectAttributesTab< G, A, R >, Integer > tabIndex
Maps tab to tab index.
final GameObjectFactory< G, A, R > gameObjectFactory
The GameObjectFactory for creating new GameObjects.
void addTabInt(final GameObjectAttributesTab< G, A, R > tab)
Adds a tab which is not shown in the tab panel.
The class Size2D represents a 2d rectangular area.
Definition: Size2D.java:30
MapSquare< G, A, R > getMapSquare()
Get the MapSquare of this GameObjectContainer.