From: <aki...@us...> - 2008-08-07 18:14:43
|
Revision: 4783 http://gridarta.svn.sourceforge.net/gridarta/?rev=4783&view=rev Author: akirschbaum Date: 2008-08-07 18:14:47 +0000 (Thu, 07 Aug 2008) Log Message: ----------- Fix #1816940 ('Game object text editor' should warn of unapplied changes): changes are now automatically applied. Modified Paths: -------------- trunk/crossfire/ChangeLog trunk/daimonin/ChangeLog trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/AbstractGameObjectAttributesTab.java trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/ArchTab.java trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/GameObjectAttributesControl.java trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/MsgTextTab.java trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/TextEditorTab.java trunk/src/app/net/sf/gridarta/gui/gameobjecttexteditor/GameObjectTextEditor.java trunk/src/app/net/sf/gridarta/gui/undo/UndoControl.java trunk/src/app/net/sf/gridarta/map/DefaultMapModel.java trunk/src/app/net/sf/gridarta/map/MapTransactionListener.java Modified: trunk/crossfire/ChangeLog =================================================================== --- trunk/crossfire/ChangeLog 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/crossfire/ChangeLog 2008-08-07 18:14:47 UTC (rev 4783) @@ -1,3 +1,8 @@ +2008-08-07 Andreas Kirschbaum + + * Fix #1816940 ('Game object text editor' should warn of unapplied + changes): changes are now automatically applied. + 2008-08-05 Andreas Kirschbaum * Implement new view for archetype chooser: "Display icons only". Modified: trunk/daimonin/ChangeLog =================================================================== --- trunk/daimonin/ChangeLog 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/daimonin/ChangeLog 2008-08-07 18:14:47 UTC (rev 4783) @@ -1,3 +1,8 @@ +2008-08-07 Andreas Kirschbaum + + * Fix #1816940 ('Game object text editor' should warn of unapplied + changes): changes are now automatically applied. + 2008-08-05 Andreas Kirschbaum * Implement new view for archetype chooser: "Display icons only". Modified: trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/AbstractGameObjectAttributesTab.java =================================================================== --- trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/AbstractGameObjectAttributesTab.java 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/AbstractGameObjectAttributesTab.java 2008-08-07 18:14:47 UTC (rev 4783) @@ -20,6 +20,9 @@ package net.sf.gridarta.gui.gameobjectattributespanel; import java.awt.Color; +import java.awt.Component; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; import java.util.ArrayList; import java.util.List; import net.sf.gridarta.gameobject.Archetype; @@ -80,6 +83,24 @@ }; /** + * The focus listener to implement auto-applying. See {@link + * #addAutoApply(Component)}. + */ + private final FocusListener focusListener = new FocusListener() { + + /** {@inheritDoc} */ + public void focusGained(final FocusEvent e) { + // ignore + } + + /** {@inheritDoc} */ + public void focusLost(final FocusEvent e) { + fireApply(); + } + + }; + + /** * Creates a new instance. * @param gameObjectAttributesModel the model to track */ @@ -144,4 +165,12 @@ } } + /** + * Registers a component that auto-applies when the focus is lost. + * @param component the component + */ + protected void addAutoApply(@NotNull final Component component) { + component.addFocusListener(focusListener); + } + } // class AbstractGameObjectAttributesTab Modified: trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/ArchTab.java =================================================================== --- trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/ArchTab.java 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/ArchTab.java 2008-08-07 18:14:47 UTC (rev 4783) @@ -105,6 +105,7 @@ this.archetypeTypeSet = archetypeTypeSet; this.gameObjectAttributesModel = gameObjectAttributesModel; this.gameObjectTextEditor = gameObjectTextEditor; + addAutoApply(archNameField); refresh(gameObjectAttributesModel.getSelectedGameObject()); } Modified: trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/GameObjectAttributesControl.java =================================================================== --- trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/GameObjectAttributesControl.java 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/GameObjectAttributesControl.java 2008-08-07 18:14:47 UTC (rev 4783) @@ -53,6 +53,7 @@ import net.sf.gridarta.map.MapModelEvent; import net.sf.gridarta.map.MapModelListener; import net.sf.gridarta.map.MapSquare; +import net.sf.gridarta.map.MapTransactionListener; import net.sf.japi.swing.ActionFactory; import net.sf.japi.swing.ActionMethod; import org.jetbrains.annotations.NotNull; @@ -98,6 +99,11 @@ @Nullable private MapControl<G, A, R, V> currentMap; + /** + * Whether {@link #autoApplyArchPanelChanges()} is currently runnings. + */ + private boolean isInAutoApplyArchPanelChanges = false; + /** Preferences. */ private static final Preferences prefs = Preferences.userNodeForPackage(MainControl.class); @@ -125,6 +131,19 @@ */ private final Map<GameObjectAttributesTab<G, A, R>, Integer> tabIndex = new IdentityHashMap<GameObjectAttributesTab<G, A, R>, Integer>(); + /** + * The currently selected game object. + */ + @Nullable + private G selectedGameObject = null; + + /** + * Records whether a map transaction is active. This is used to prevent + * start recursive map transactions in {@link + * #autoApplyArchPanelChanges()}. + */ + private boolean isInMapTransaction = false; + /** The map manager listener which is attached to {@link #mainControl}. */ private final MapManagerListener<G, A, R, V> mapManagerListener = new MapManagerListener<G, A, R, V>() { @@ -132,10 +151,12 @@ public void currentMapChanged(@Nullable final MapControl<G, A, R, V> mapControl) { if (currentMap != null) { currentMap.getMapModel().removeMapModelListener(mapModelListener); + currentMap.getMapModel().removeMapTransactionListener(mapTransactionListener); } currentMap = mapControl; if (currentMap != null) { currentMap.getMapModel().addMapModelListener(mapModelListener); + currentMap.getMapModel().addMapTransactionListener(mapTransactionListener); } } @@ -181,7 +202,6 @@ /** {@inheritDoc} */ public void mapSquaresChanged(final MapModelEvent<G, A, R> e) { - final G selectedGameObject = gameObjectAttributesModel.getSelectedGameObject(); if (selectedGameObject == null) { return; } @@ -198,7 +218,6 @@ /** {@inheritDoc} */ public void mapObjectsChanged(final MapModelEvent<G, A, R> e) { - final G selectedGameObject = gameObjectAttributesModel.getSelectedGameObject(); for (final G gameObject : e.getGameObjects()) { if (selectedGameObject == gameObject) { refreshDisplay(); @@ -213,6 +232,35 @@ }; + /** + * The map transaction listener which is attached to {@link #currentMap}. + * It triggers auto-apply whenever a map transaction is about to start. + */ + private final MapTransactionListener<G, A, R> mapTransactionListener = new MapTransactionListener<G, A, R>() { + + /** {@inheritDoc} */ + public void preBeginTransaction(@NotNull final MapModel<G, A, R> mapModel) { + autoApplyArchPanelChanges(); + isInMapTransaction = true; + } + + /** {@inheritDoc} */ + public void beginTransaction(@NotNull final MapModel<G, A, R> mapModel, @NotNull final String name) { + // ignore + } + + /** {@inheritDoc} */ + public void endTransaction(@NotNull final MapModel<G, A, R> mapModel) { + // ignore + } + + /** {@inheritDoc} */ + public void postEndTransaction(@NotNull final MapModel<G, A, R> mapModel) { + isInMapTransaction = false; + } + + }; + /** The listener to detect changes of the selected game object. */ private final SelectedSquareListener<G, A, R> selectedSquareListener = new SelectedSquareListener<G, A, R>() { @@ -230,6 +278,8 @@ /** {@inheritDoc} */ public void selectedGameObjectChanged(@Nullable final G selectedGameObject) { + autoApplyArchPanelChanges(); + GameObjectAttributesControl.this.selectedGameObject = selectedGameObject; refreshDisplay(); } @@ -247,8 +297,7 @@ /** {@inheritDoc} */ public void apply() { - final G selectedGameObject = gameObjectAttributesModel.getSelectedGameObject(); - applyArchPanelChanges(selectedGameObject); + autoApplyArchPanelChanges(); } }; @@ -268,8 +317,6 @@ this.mainControl = mainControl; this.objectChooser = objectChooser; - addTabInt(new TextEditorTab<G, A, R>(gameObjectAttributesModel, gameObjectTextEditor)); - mapArchPanel.setLayout(new BorderLayout()); splitPane = new GSplitPane(JSplitPane.HORIZONTAL_SPLIT, mapArchPanel, gameObjectTextEditor); splitPane.setContinuousLayout(true); @@ -283,14 +330,18 @@ mapArchPanel.add(panelDesktop, BorderLayout.CENTER); selectedSquareControl.addSelectedSquareListener(selectedSquareListener); - gameObjectAttributesModel.addGameObjectAttributesModelListener(gameObjectAttributesModelListener); + gameObjectAttributesModel.addGameObjectAttributesModelListener(gameObjectAttributesModelListener); // this listener must be registered before any tab, including TextEditorTab + selectedGameObject = gameObjectAttributesModel.getSelectedGameObject(); refreshDisplay(); currentMap = mapManager.getCurrentMap(); if (currentMap != null) { currentMap.getMapModel().addMapModelListener(mapModelListener); + currentMap.getMapModel().addMapTransactionListener(mapTransactionListener); } mapManager.addMapManagerListener(mapManagerListener); + + addTabInt(new TextEditorTab<G, A, R>(gameObjectAttributesModel, gameObjectTextEditor)); } /** @@ -327,19 +378,15 @@ /** Update the displayed information for the selected game object. */ private void refreshDisplay() { - final G selectedGameObject = gameObjectAttributesModel.getSelectedGameObject(); - final G gameObject = selectedGameObject == null ? null : selectedGameObject.getHead(); - - aMapArchApply.setEnabled(gameObject != null); - aMapArchAddInv.setEnabled(gameObject != null); - aMapArchAttrib.setEnabled(gameObject != null); + aMapArchApply.setEnabled(selectedGameObject != null); + aMapArchAddInv.setEnabled(selectedGameObject != null); + aMapArchAttrib.setEnabled(selectedGameObject != null); } /** Action method for applying the changes made in the arch panel. */ @ActionMethod public void mapArchApply() { - final G selectedGameObject = gameObjectAttributesModel.getSelectedGameObject(); - applyArchPanelChanges(selectedGameObject); + applyArchPanelChanges(); } /** @@ -348,8 +395,9 @@ */ @ActionMethod public void mapArchAttrib() { - final G selectedGameObject = gameObjectAttributesModel.getSelectedGameObject(); - mainControl.openAttrDialog(selectedGameObject); + if (selectedGameObject != null) { + mainControl.openAttrDialog(selectedGameObject); + } } /** @@ -363,7 +411,6 @@ return; } - final G selectedGameObject = gameObjectAttributesModel.getSelectedGameObject(); if (selectedGameObject == null) { return; } @@ -391,23 +438,42 @@ } /** + * Same as {@link #applyArchPanelChanges()} but does protect against + * recursive calls. + */ + private void autoApplyArchPanelChanges() { + if (selectedGameObject == null || isInAutoApplyArchPanelChanges || isInMapTransaction) { + return; + } + + isInAutoApplyArchPanelChanges = true; + try { + applyArchPanelChanges(); + } finally { + isInAutoApplyArchPanelChanges = false; + } + } + + /** * When the "apply"-button on the ArchPanel (at the bottom of the window) is * pressed, this function updates the active arch object. - * @param activeArch the currently selected arch */ - public void applyArchPanelChanges(final G activeArch) { - if (activeArch == null) { + private void applyArchPanelChanges() { + if (selectedGameObject == null) { return; } // If the active gameObject is part of a multi, the multi-head's stats // are taken instead: - final G gameObject = activeArch.getHead(); + final G gameObject = selectedGameObject.getHead(); final R archetype = gameObject.getArchetype(); final MapSquare<G, A, R> mapSquare = gameObject.getMapSquare(); - assert mapSquare != null; + if (mapSquare == null) { + // auto-apply of deleted game object + return; + } mapSquare.getMapModel().beginTransaction("Change object attributes"); for (final GameObjectAttributesTab<G, A, R> tab : tabs) { Modified: trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/MsgTextTab.java =================================================================== --- trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/MsgTextTab.java 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/MsgTextTab.java 2008-08-07 18:14:47 UTC (rev 4783) @@ -52,6 +52,7 @@ public MsgTextTab(@NotNull final GameObjectAttributesModel<G, A, R> gameObjectAttributesModel) { super(gameObjectAttributesModel); archTextArea.setLineWrap(true); + addAutoApply(archTextArea); refresh(gameObjectAttributesModel.getSelectedGameObject()); } Modified: trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/TextEditorTab.java =================================================================== --- trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/TextEditorTab.java 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/src/app/net/sf/gridarta/gui/gameobjectattributespanel/TextEditorTab.java 2008-08-07 18:14:47 UTC (rev 4783) @@ -47,6 +47,7 @@ protected TextEditorTab(@NotNull final GameObjectAttributesModel<G, A, R> gameObjectAttributesModel, @NotNull final GameObjectTextEditor gameObjectTextEditor) { super(gameObjectAttributesModel); this.gameObjectTextEditor = gameObjectTextEditor; + addAutoApply(gameObjectTextEditor.getTextPane()); } /** {@inheritDoc} */ @@ -72,7 +73,9 @@ /** {@inheritDoc} */ @Override protected void apply(@Nullable final G gameObject) { - gameObjectTextEditor.applyChanges(gameObject); + if (gameObject != null) { + gameObjectTextEditor.applyChanges(gameObject); + } } } // class TextEditorTab Modified: trunk/src/app/net/sf/gridarta/gui/gameobjecttexteditor/GameObjectTextEditor.java =================================================================== --- trunk/src/app/net/sf/gridarta/gui/gameobjecttexteditor/GameObjectTextEditor.java 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/src/app/net/sf/gridarta/gui/gameobjecttexteditor/GameObjectTextEditor.java 2008-08-07 18:14:47 UTC (rev 4783) @@ -100,4 +100,12 @@ } } + /** + * Returns the text input pane. + * @return the text input pane + */ + public JTextPane getTextPane() { + return archEdit; + } + } // class GameObjectTextEditor Modified: trunk/src/app/net/sf/gridarta/gui/undo/UndoControl.java =================================================================== --- trunk/src/app/net/sf/gridarta/gui/undo/UndoControl.java 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/src/app/net/sf/gridarta/gui/undo/UndoControl.java 2008-08-07 18:14:47 UTC (rev 4783) @@ -85,6 +85,11 @@ private UndoModel<G, A, R> undoModel = null; /** {@inheritDoc} */ + public void preBeginTransaction(@NotNull final MapModel<G, A, R> mapModel) { + // ignore + } + + /** {@inheritDoc} */ public void beginTransaction(@NotNull final MapModel<G, A, R> mapModel, @NotNull final String name) { undoModel = undoModels.get(mapModel); undoState = undoModel.isEnabled() ? new UndoState<G, A, R>(name, mapModel) : null; @@ -114,6 +119,11 @@ refreshMenus(); } + /** {@inheritDoc} */ + public void postEndTransaction(@NotNull final MapModel<G, A, R> mapModel) { + // ignore + } + }; /** Listener to be notified for created/deleted maps. */ Modified: trunk/src/app/net/sf/gridarta/map/DefaultMapModel.java =================================================================== --- trunk/src/app/net/sf/gridarta/map/DefaultMapModel.java 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/src/app/net/sf/gridarta/map/DefaultMapModel.java 2008-08-07 18:14:47 UTC (rev 4783) @@ -388,9 +388,10 @@ /** {@inheritDoc} */ public void beginTransaction(@NotNull final String name) { if (transactionDepth == 0) { + firePreBeginTransaction(); transactionName = name; transactionThread = Thread.currentThread(); - + transactionDepth++; fireBeginTransaction(name); } else { // == is okay for threads. @@ -398,8 +399,8 @@ if (transactionThread != Thread.currentThread()) { throw new IllegalStateException("A transaction must only be used by one thread."); } + transactionDepth++; } - transactionDepth++; } /** {@inheritDoc} */ @@ -451,6 +452,7 @@ transactionThread = null; fireEvents(); fireEndTransaction(); + firePostEndTransaction(); } /** {@inheritDoc} */ @@ -500,6 +502,15 @@ } /** + * Fire a pre-begin transaction event. + */ + private void firePreBeginTransaction() { + for (final MapTransactionListener<G, A, R> listener : listenerList.getListeners(MapTransactionListener.class)) { + listener.preBeginTransaction(this); + } + } + + /** * Fire a begin transaction event. * @param name the transaction name */ @@ -517,6 +528,15 @@ } /** + * Fire a post-end transaction event. + */ + private void firePostEndTransaction() { + for (final MapTransactionListener<G, A, R> listener : listenerList.getListeners(MapTransactionListener.class)) { + listener.postEndTransaction(this); + } + } + + /** * This method checks whether the map is rectangular. A map always MUST be * rectangular, this method can be used for assertions regarding this * assumption. The check for rectangularity is a check whether the array Modified: trunk/src/app/net/sf/gridarta/map/MapTransactionListener.java =================================================================== --- trunk/src/app/net/sf/gridarta/map/MapTransactionListener.java 2008-08-06 21:11:04 UTC (rev 4782) +++ trunk/src/app/net/sf/gridarta/map/MapTransactionListener.java 2008-08-07 18:14:47 UTC (rev 4783) @@ -31,6 +31,12 @@ public interface MapTransactionListener<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> extends EventListener { /** + * A map transaction is about to start. + * @param mapModel the map model + */ + void preBeginTransaction(@NotNull final MapModel<G, A, R> mapModel); + + /** * A map transaction has started. * @param mapModel the map model * @param name the transaction name @@ -43,4 +49,10 @@ */ void endTransaction(@NotNull MapModel<G, A, R> mapModel); + /** + * A map transaction has been finished. + * @param mapModel the map model + */ + void postEndTransaction(@NotNull final MapModel<G, A, R> mapModel); + } // interface MapTransactionListener This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |