Gridarta Editor
MapMenu.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2023 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.mapmenu;
21 
22 import java.io.IOException;
23 import java.util.ArrayDeque;
24 import java.util.Deque;
25 import javax.swing.JTree;
26 import javax.swing.event.TreeModelEvent;
27 import javax.swing.event.TreeModelListener;
28 import javax.swing.tree.DefaultMutableTreeNode;
29 import javax.swing.tree.DefaultTreeModel;
30 import javax.swing.tree.MutableTreeNode;
31 import javax.swing.tree.TreeNode;
32 import javax.swing.tree.TreePath;
37 import org.apache.log4j.Category;
38 import org.apache.log4j.Logger;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41 
46 public class MapMenu {
47 
51  @NotNull
52  private static final Category LOG = Logger.getLogger(MapMenu.class);
53 
57  @NotNull
59 
63  @NotNull
64  private final DefaultMutableTreeNode root = new DefaultMutableTreeNode(new MapMenuEntryDir("Bookmarks"), true);
65 
69  @NotNull
70  private final DefaultTreeModel treeModel = new DefaultTreeModel(root);
71 
75  @NotNull
76  private final Deque<DeletedNode> deletedNodes = new ArrayDeque<>();
77 
81  private boolean treeModelModified;
82 
87  @NotNull
88  private final TreeModelListener treeModelListener = new TreeModelListener() {
89 
90  @Override
91  public void treeNodesChanged(final TreeModelEvent e) {
92  treeModelModified = true;
93  }
94 
95  @Override
96  public void treeNodesInserted(final TreeModelEvent e) {
97  treeModelModified = true;
98  }
99 
100  @Override
101  public void treeNodesRemoved(final TreeModelEvent e) {
102  treeModelModified = true;
103  }
104 
105  @Override
106  public void treeStructureChanged(final TreeModelEvent e) {
107  treeModelModified = true;
108  }
109 
110  };
111 
117  public MapMenu(@NotNull final String key, @NotNull final PathManager pathManager) {
118  if (LOG.isDebugEnabled()) {
119  //noinspection ThisEscapedInObjectConstruction
120  LOG.debug(System.identityHashCode(this) + " new " + key);
121  }
122  mapMenuLoader = new MapMenuLoader(key, pathManager);
123  treeModel.addTreeModelListener(treeModelListener);
124  }
125 
129  public void load() {
130  final int num = mapMenuLoader.loadNumEntries();
131  if (LOG.isDebugEnabled()) {
132  LOG.debug(System.identityHashCode(this) + " load: root.removeAllChildren");
133  }
134  root.removeAllChildren();
135  for (int i = 0; i < num; i++) {
136  final Result result;
137  try {
138  result = mapMenuLoader.loadEntry(i);
139  } catch (final IOException ex) {
140  LOG.warn("dropping invalid bookmark: " + ex.getMessage());
141  continue;
142  }
143  addMapMenuEntry(result.getDirectory(), result.getMapMenuEntry());
144  }
145  treeModelModified = false;
146  }
147 
151  public void save() {
152  if (!treeModelModified) {
153  return;
154  }
155  treeModelModified = false;
156 
157  saveAlways();
158  }
159 
163  public void saveAlways() {
164  final int prevNum = mapMenuLoader.loadNumEntries();
165  final int num = saveEntries(root, 0, "");
167  for (int i = num; i < prevNum; i++) {
169  }
170  }
171 
179  private int saveEntries(@NotNull final DefaultMutableTreeNode treeNode, final int startIndex, @NotNull final String directory) {
180  final int[] index = { startIndex };
181  for (int i = 0; i < treeModel.getChildCount(treeNode); i++) {
182  final DefaultMutableTreeNode childTreeNode = (DefaultMutableTreeNode) treeModel.getChild(treeNode, i);
183  final MapMenuEntry childMapMenuEntry = (MapMenuEntry) childTreeNode.getUserObject();
184  final String title = childMapMenuEntry.getTitle();
185  final MapMenuEntryVisitor mapMenuEntryVisitor = new MapMenuEntryVisitor() {
186 
187  @Override
188  public void visit(@NotNull final MapMenuEntryDir mapMenuEntry) {
189  mapMenuLoader.saveEntry(index[0]++, childMapMenuEntry.getTitle(), "", directory, Type.DIR);
190  index[0] = saveEntries(childTreeNode, index[0], directory.isEmpty() ? title : directory + "/" + title);
191  }
192 
193  @Override
194  public void visit(@NotNull final MapMenuEntryMap mapMenuEntry) {
195  final MapMenuEntryMap mapMenuEntryMap = (MapMenuEntryMap) childMapMenuEntry;
196  mapMenuLoader.saveEntry(index[0]++, title, mapMenuEntryMap.getMapFile().getFile().getPath(), directory, Type.MAP);
197  }
198 
199  };
200  childMapMenuEntry.visit(mapMenuEntryVisitor);
201  }
202  return index[0];
203  }
204 
210  public void addMapMenuEntry(@NotNull final String directory, @NotNull final MapMenuEntry mapMenuEntry) {
211  addMapMenuEntry(directory, new DefaultMutableTreeNode(mapMenuEntry, mapMenuEntry.allowsChildren()));
212  }
213 
220  @NotNull
221  @SuppressWarnings("TypeMayBeWeakened")
222  public TreePath addMapMenuEntry(@NotNull final String directory, @NotNull final DefaultMutableTreeNode treeNode) {
223  if (LOG.isDebugEnabled()) {
224  LOG.debug(System.identityHashCode(this) + " addMapMenuEntry(" + directory + ", " + treeNode + ")");
225  }
226  final String[] paths = StringUtils.PATTERN_SLASH.split(directory);
227  DefaultMutableTreeNode dir2 = root;
228  for (final String path : paths) {
229  if (!path.isEmpty()) {
230  dir2 = getOrCreateDirectory(dir2, path);
231  }
232  }
233  treeModel.insertNodeInto(treeNode, dir2, dir2.getChildCount());
234  return new TreePath(treeModel.getPathToRoot(treeNode));
235  }
236 
244  @NotNull
245  public DefaultMutableTreeNode getOrCreateDirectory(@NotNull final MutableTreeNode this2, @NotNull final String path) {
246  if (!MapMenuEntryDir.isValidDirectory(path)) {
247  if (LOG.isDebugEnabled()) {
248  LOG.debug(System.identityHashCode(this) + " getOrCreateDirectory(" + this2 + ", " + path + ")=invalid directory name");
249  }
250  throw new IllegalArgumentException("invalid directory name '" + path + "'");
251  }
252 
253  for (int i = this2.getChildCount() - 1; i >= 0; i--) {
254  final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) this2.getChildAt(i);
255  final MapMenuEntry mapMenuEntry = (MapMenuEntry) treeNode.getUserObject();
256  if (mapMenuEntry.allowsChildren() && mapMenuEntry.getTitle().equals(path)) {
257  if (LOG.isDebugEnabled()) {
258  LOG.debug(System.identityHashCode(this) + " getOrCreateDirectory(" + this2 + ", " + path + ")=existing " + treeNode);
259  }
260  return treeNode;
261  }
262  }
263 
264  final DefaultMutableTreeNode new2 = new DefaultMutableTreeNode(new MapMenuEntryDir(path), true);
265  treeModel.insertNodeInto(new2, this2, this2.getChildCount());
266  if (LOG.isDebugEnabled()) {
267  LOG.debug(System.identityHashCode(this) + " getOrCreateDirectory(" + this2 + ", " + path + ")=new " + new2);
268  }
269  return new2;
270  }
271 
279  @NotNull
280  public TreePath insertNodeInto(@NotNull final MapMenuEntry mapEntry, @NotNull final DefaultMutableTreeNode parent, final int index) {
281  if (LOG.isDebugEnabled()) {
282  LOG.debug(System.identityHashCode(this) + " insertNodeInto(" + mapEntry + ", " + parent + ", " + index + ")");
283  }
284  final DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(mapEntry, mapEntry.allowsChildren());
285  treeModel.insertNodeInto(treeNode, parent, index);
286  save();
287  return new TreePath(treeModel.getPathToRoot(treeNode));
288  }
289 
294  public void removeNode(@NotNull final DefaultMutableTreeNode treeNode) {
295  if (treeNode == root) {
296  if (LOG.isDebugEnabled()) {
297  LOG.debug(System.identityHashCode(this) + " removeNode: not removing root " + treeNode);
298  }
299  } else {
300  if (LOG.isDebugEnabled()) {
301  LOG.debug(System.identityHashCode(this) + " removeNode " + treeNode);
302  }
303  final String directory = getDirectory(treeNode);
304  treeModel.removeNodeFromParent(treeNode);
305  deletedNodes.addFirst(new DeletedNode(directory, treeNode));
306  while (deletedNodes.size() > 10) {
307  deletedNodes.removeLast();
308  }
309  save();
310  }
311  }
312 
318  @NotNull
319  private String getDirectory(@NotNull final TreeNode treeNode) {
320  final TreeNode[] treePath = treeModel.getPathToRoot(treeNode);
321  if (treePath == null) {
322  throw new IllegalArgumentException("tree node not found: " + treeNode);
323  }
324  final StringBuilder sb = new StringBuilder();
325  for (int i = 1; i + 1 < treePath.length; i++) {
326  final TreeNode tmp = treePath[i];
327  final MapMenuEntry mapMenuEntry = (MapMenuEntry) ((DefaultMutableTreeNode) tmp).getUserObject();
328  if (sb.length() > 0) {
329  sb.append('/');
330  }
331  sb.append(mapMenuEntry.getTitle());
332  }
333  return sb.toString();
334  }
335 
341  @Nullable
342  public DeletedNode getDeletedNode(final boolean delete) {
343  return delete ? deletedNodes.pollFirst() : deletedNodes.peekFirst();
344  }
345 
350  public int size() {
351  return size(root);
352  }
353 
359  private int size(@NotNull final TreeNode root) {
360  final int childCount = treeModel.getChildCount(root);
361  int result = childCount;
362  for (int i = 0; i < childCount; i++) {
363  final DefaultMutableTreeNode childTreeNode = (DefaultMutableTreeNode) treeModel.getChild(root, i);
364  final MapMenuEntry mapMenuEntry = (MapMenuEntry) childTreeNode.getUserObject();
365  if (mapMenuEntry.allowsChildren()) {
366  result += size(childTreeNode);
367  }
368  }
369  if (LOG.isDebugEnabled()) {
370  LOG.debug(System.identityHashCode(this) + " size(" + root + ")=" + result);
371  }
372  return result;
373  }
374 
379  @NotNull
380  public JTree newTree() {
381  return new AutoscrollJTree(treeModel);
382  }
383 
388  @NotNull
389  public DefaultMutableTreeNode getRoot() {
390  return root;
391  }
392 
398  public static class DeletedNode {
399 
403  @NotNull
404  private final String directory;
405 
409  @NotNull
410  private final DefaultMutableTreeNode treeNode;
411 
417  private DeletedNode(@NotNull final String directory, @NotNull final DefaultMutableTreeNode treeNode) {
418  this.directory = directory;
419  this.treeNode = treeNode;
420  }
421 
426  @NotNull
427  public String getDirectory() {
428  return directory;
429  }
430 
435  @NotNull
436  public DefaultMutableTreeNode getTreeNode() {
437  return treeNode;
438  }
439 
440  }
441 
442 }
net.sf.gridarta.gui.mapmenu.MapMenuLoader.Result
Result value consisting of a MapMenuEntry and its location (directory).
Definition: MapMenuLoader.java:225
net.sf.gridarta.gui.mapmenu.MapMenuLoader.saveNumEntries
void saveNumEntries(final int num)
Sets the number of entries present in the preferences.
Definition: MapMenuLoader.java:109
net.sf.gridarta.gui.mapmenu.MapMenu.DeletedNode.DeletedNode
DeletedNode(@NotNull final String directory, @NotNull final DefaultMutableTreeNode treeNode)
Creates a new instance.
Definition: MapMenu.java:417
net.sf.gridarta.gui.mapmenu.MapMenu.deletedNodes
final Deque< DeletedNode > deletedNodes
Recently deleted nodes.
Definition: MapMenu.java:76
net.sf.gridarta.utils.StringUtils.PATTERN_SLASH
static final Pattern PATTERN_SLASH
The pattern that matches a single slash ("/").
Definition: StringUtils.java:79
net.sf.gridarta.gui.mapmenu.MapMenuEntry
Abstract base class for recent and bookmark menu entries.
Definition: MapMenuEntry.java:29
net.sf.gridarta
Base package of all Gridarta classes.
net.sf.gridarta.gui.mapmenu.MapMenu.DeletedNode.treeNode
final DefaultMutableTreeNode treeNode
The entry.
Definition: MapMenu.java:410
net.sf.gridarta.gui.mapmenu.MapMenu.getDirectory
String getDirectory(@NotNull final TreeNode treeNode)
Returns the directory of a TreeNode.
Definition: MapMenu.java:319
net.sf.gridarta.gui.mapmenu.MapMenuEntry.toString
String toString()
Definition: MapMenuEntry.java:82
net.sf.gridarta.gui.mapmenu.MapMenu.getDeletedNode
DeletedNode getDeletedNode(final boolean delete)
Returns the last deleted node.
Definition: MapMenu.java:342
net.sf.gridarta.gui.mapmenu.MapMenuEntryDir.isValidDirectory
static boolean isValidDirectory(@NotNull final String title)
Returns whether a title is valid.
Definition: MapMenuEntryDir.java:62
directory
This document describes some hints and requirements for general development on the CrossfireEditor If you plan to make changes to the editor code or setup please read the following and keep it in derived from a basic editor application called Gridder by Pasi Ker�nen so please communicate with best through the cf devel mailing before considering any fundamental changes About code DO NOT USE TABS No matter what Java development platform you are please configure insert indent Tabs are displayed totally different in every editor and there are millions of different editors out there The insertion of tabs in the source code is messing up the syntax formatting in a way that is UNREPAIRABLE Apart from please keep code indentation accurate This is not just good it helps to keep code readable and in that way dramatically decreases the chance for overlooked bugs Everyone is welcomed to correct indentation errors wherever they are spotted Before you start to do this please double check that your editor is really configured to insert spaces Line feeds may be checked in either in windows or in unix linux style All reasonable text and java editors can deal with both linefeed formats Converting line feeds is but in this case please make sure that only linefeed characters are changed and nothing else is affected Due to the platform independent nature of the editor has the potential to run on almost any given operating system the build process differs greatly between systems as well as java environments In the several people have attempted to add build scripts along with structural changes to optimize the setup on one particular system environment which has led to conflict Please do *not *attempt to change the structure or any directories for the mere purpose of improving a build process or performance in a java environment Build scripts may be placed in the root directory
Definition: Developer_README.txt:45
net.sf
net.sf.gridarta.gui.mapmenu.MapMenuLoader.loadNumEntries
int loadNumEntries()
Returns the number of entries present in the preferences.
Definition: MapMenuLoader.java:97
net.sf.gridarta.model.io.PathManager
This class contains methods for converting relative map paths to absolute map paths and vice versa.
Definition: PathManager.java:39
net.sf.gridarta.gui
Graphical User Interface of Gridarta.
net.sf.gridarta.gui.mapmenu.MapMenu.getOrCreateDirectory
DefaultMutableTreeNode getOrCreateDirectory(@NotNull final MutableTreeNode this2, @NotNull final String path)
Returns the MapMenuEntryDir for a given path.
Definition: MapMenu.java:245
net.sf.gridarta.gui.mapmenu.MapMenu
Manages the contents of a recent or bookmark menu.
Definition: MapMenu.java:46
net.sf.gridarta.gui.mapmenu.MapMenuLoader.Result.getMapMenuEntry
MapMenuEntry getMapMenuEntry()
Returns the entry.
Definition: MapMenuLoader.java:263
net.sf.gridarta.gui.mapmenu.MapMenu.DeletedNode
Result value consisting of a TreeNode and its location (directory).
Definition: MapMenu.java:398
net
net.sf.gridarta.gui.mapmenu.MapMenuEntry.getTitle
String getTitle()
Returns the entry's title.
Definition: MapMenuEntry.java:55
net.sf.gridarta.gui.mapmenu.MapMenuLoader.removeEntry
void removeEntry(final int index)
Removes an entry.
Definition: MapMenuLoader.java:209
net.sf.gridarta.gui.mapmenu.MapMenuLoader.loadEntry
Result loadEntry(final int index)
Loads an entry from preferences.
Definition: MapMenuLoader.java:123
net.sf.gridarta.gui.mapmenu.MapMenu.save
void save()
Saves the contents to preferences if modified since last save.
Definition: MapMenu.java:151
net.sf.gridarta.gui.mapmenu.MapMenu.insertNodeInto
TreePath insertNodeInto(@NotNull final MapMenuEntry mapEntry, @NotNull final DefaultMutableTreeNode parent, final int index)
Inserts a new node into the tree.
Definition: MapMenu.java:280
net.sf.gridarta.gui.mapmenu.MapMenuLoader.Result.getDirectory
String getDirectory()
Returns the entry's directory.
Definition: MapMenuLoader.java:254
net.sf.gridarta.gui.mapmenu.MapMenu.load
void load()
Loads the contents from preferences.
Definition: MapMenu.java:129
net.sf.gridarta.gui.mapmenu.MapMenu.mapMenuLoader
final MapMenuLoader mapMenuLoader
The MapMenuLoader for loading/storing the menu contents.
Definition: MapMenu.java:58
net.sf.gridarta.model.mapmodel.MapFile.getFile
File getFile()
Returns a File for this map file.
Definition: MapFile.java:102
net.sf.gridarta.gui.mapmenu.MapMenu.newTree
JTree newTree()
Creates a new JTree for this map menu.
Definition: MapMenu.java:380
net.sf.gridarta.gui.mapmenu.MapMenu.treeModel
final DefaultTreeModel treeModel
The DefaultTreeModel that contains all menu entries.
Definition: MapMenu.java:70
net.sf.gridarta.gui.mapmenu.MapMenuLoader.saveEntry
void saveEntry(final int index, @NotNull final String title, @NotNull final String filename, @NotNull final String directory, @NotNull final Type type)
Saves an entry to preferences.
Definition: MapMenuLoader.java:186
net.sf.gridarta.gui.mapmenu.MapMenuEntry.allowsChildren
abstract boolean allowsChildren()
Returns whether this entry is a directory.
net.sf.gridarta.gui.mapmenu.MapMenu.DeletedNode.directory
final String directory
The entry's directory.
Definition: MapMenu.java:404
net.sf.gridarta.gui.mapmenu
Definition: AbstractMapMenuPreferences.java:20
net.sf.gridarta.gui.mapmenu.MapMenu.DeletedNode.getDirectory
String getDirectory()
Returns the entry's directory.
Definition: MapMenu.java:427
net.sf.gridarta.gui.mapmenu.MapMenuLoader
Saves or restores MapMenu contents to Preferences.
Definition: MapMenuLoader.java:35
net.sf.gridarta.utils.StringUtils
Utility class for string manipulation.
Definition: StringUtils.java:31
net.sf.gridarta.gui.mapmenu.MapMenu.treeModelListener
final TreeModelListener treeModelListener
The TreeModelListener attached to treeModel for detecting modifications.
Definition: MapMenu.java:88
net.sf.gridarta.model.io
Reading and writing of maps, handling of paths.
Definition: AbstractAnimationObjectsReader.java:20
net.sf.gridarta.gui.mapmenu.MapMenuEntryMap.getMapFile
MapFile getMapFile()
Returns the map file.
Definition: MapMenuEntryMap.java:57
net.sf.gridarta.gui.mapmenu.MapMenuLoader.Type
Preferences values for entries.
Definition: MapMenuLoader.java:41
net.sf.gridarta.gui.mapmenu.MapMenuEntry.visit
abstract void visit(@NotNull MapMenuEntryVisitor visitor)
Calls the.
net.sf.gridarta.gui.mapmenu.MapMenu.root
final DefaultMutableTreeNode root
The root node of treeModel.
Definition: MapMenu.java:64
net.sf.gridarta.gui.mapmenu.MapMenu.size
int size()
Returns the number of entries in this menu.
Definition: MapMenu.java:350
net.sf.gridarta.model
net.sf.gridarta.gui.mapmenu.MapMenu.getRoot
DefaultMutableTreeNode getRoot()
Returns the root node.
Definition: MapMenu.java:389
net.sf.gridarta.gui.mapmenu.MapMenu.DeletedNode.getTreeNode
DefaultMutableTreeNode getTreeNode()
Returns the tree node.
Definition: MapMenu.java:436
net.sf.gridarta.gui.mapmenu.MapMenuLoader.Type.DIR
DIR
Preferences value for entries representing directories.
Definition: MapMenuLoader.java:46
net.sf.gridarta.gui.mapmenu.MapMenuEntryVisitor
Interface for classes operating on MapMenuEntry instances.
Definition: MapMenuEntryVisitor.java:28
net.sf.gridarta.gui.mapmenu.MapMenu.treeModelModified
boolean treeModelModified
Whether treeModel has been modified since last save.
Definition: MapMenu.java:81
net.sf.gridarta.gui.mapmenu.MapMenuLoader.Type.MAP
MAP
Preferences value for entries representing map files.
Definition: MapMenuLoader.java:51
net.sf.gridarta.gui.mapmenu.MapMenu.MapMenu
MapMenu(@NotNull final String key, @NotNull final PathManager pathManager)
Creates a new instance.
Definition: MapMenu.java:117
net.sf.gridarta.gui.mapmenu.MapMenu.LOG
static final Category LOG
The Logger for printing log messages.
Definition: MapMenu.java:52
net.sf.gridarta.gui.mapmenu.MapMenu.size
int size(@NotNull final TreeNode root)
Returns the number of entries in a sub-tree.
Definition: MapMenu.java:359
net.sf.gridarta.gui.mapmenu.MapMenu.saveEntries
int saveEntries(@NotNull final DefaultMutableTreeNode treeNode, final int startIndex, @NotNull final String directory)
Saves a TreeNode instance's contents to preferences.
Definition: MapMenu.java:179
net.sf.gridarta.gui.mapmenu.MapMenu.removeNode
void removeNode(@NotNull final DefaultMutableTreeNode treeNode)
Removes a node from the tree.
Definition: MapMenu.java:294
net.sf.gridarta.gui.mapmenu.MapMenuEntryMap
A MapMenuEntry that represents a map.
Definition: MapMenuEntryMap.java:29
net.sf.gridarta.gui.mapmenu.MapMenuEntryDir
A MapMenuEntry that represents a directory.
Definition: MapMenuEntryDir.java:28
net.sf.gridarta.gui.mapmenu.MapMenu.addMapMenuEntry
void addMapMenuEntry(@NotNull final String directory, @NotNull final MapMenuEntry mapMenuEntry)
Adds a MapMenuEntry to this menu.
Definition: MapMenu.java:210
net.sf.gridarta.gui.mapmenu.MapMenu.saveAlways
void saveAlways()
Saves the contents to preferences.
Definition: MapMenu.java:163
net.sf.gridarta.utils
Definition: ActionBuilderUtils.java:20
net.sf.gridarta.gui.mapmenu.AutoscrollJTree
A JTree that supports auto-scrolling while drag and drop is active.
Definition: AutoscrollJTree.java:39