Gridarta Editor
ScriptArchEditor.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.scripts;
21 
22 import java.awt.Color;
23 import java.awt.Component;
24 import java.awt.Container;
25 import java.awt.FlowLayout;
26 import java.awt.Frame;
27 import java.awt.Insets;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.io.File;
31 import java.io.IOException;
32 import javax.swing.AbstractButton;
33 import javax.swing.BorderFactory;
34 import javax.swing.Box;
35 import javax.swing.BoxLayout;
36 import javax.swing.JButton;
37 import javax.swing.JComboBox;
38 import javax.swing.JDialog;
39 import javax.swing.JFileChooser;
40 import javax.swing.JLabel;
41 import javax.swing.JOptionPane;
42 import javax.swing.JPanel;
43 import javax.swing.JTextField;
44 import javax.swing.WindowConstants;
45 import javax.swing.filechooser.FileFilter;
46 import javax.swing.text.JTextComponent;
62 import net.sf.japi.swing.action.ActionBuilder;
63 import net.sf.japi.swing.action.ActionBuilderFactory;
64 import net.sf.japi.util.Arrays2;
65 import org.jetbrains.annotations.NotNull;
66 
71 public class ScriptArchEditor<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> {
72 
76  @NotNull
77  private static final ActionBuilder ACTION_BUILDER = ActionBuilderFactory.getInstance().getActionBuilder("net.sf.gridarta");
78 
82  @NotNull
84 
88  @NotNull
89  private final String scriptEnding;
90 
94  @NotNull
95  private final PathManager pathManager;
96 
100  @NotNull
102 
103  @NotNull
104  private final JComboBox<?> eventTypeBox;
105 
106  @NotNull
107  private final FileFilter scriptFileFilter;
108 
112  @NotNull
114 
118  @NotNull
120 
121  @NotNull
122  private final JComboBox<String> pluginNameBox;
123 
124  @NotNull
125  private JTextComponent inputScriptPath;
126 
127  @NotNull
128  private JTextComponent inputOptions;
129 
133  @NotNull
135 
148  public ScriptArchEditor(@NotNull final ScriptedEventFactory<G, A, R> scriptedEventFactory, @NotNull final String scriptEnding, @NotNull final String name, @NotNull final ScriptArchUtils scriptArchUtils, @NotNull final FileFilter scriptFileFilter, @NotNull final ProjectSettings projectSettings, @NotNull final MapManager<?, ?, ?> mapManager, @NotNull final PathManager pathManager, @NotNull final ScriptEditControl scriptEditControl) {
149  this.scriptedEventFactory = scriptedEventFactory;
150  this.scriptEnding = scriptEnding;
151  this.scriptArchUtils = scriptArchUtils;
152  this.pathManager = pathManager;
153  this.scriptEditControl = scriptEditControl;
154 
155  pluginNameBox = new JComboBox<>(new String[] { name });
156  pluginNameBox.setSelectedIndex(0);
157 
158  eventTypeBox = createEventTypeBox(scriptArchUtils);
159  this.scriptFileFilter = scriptFileFilter;
160  this.projectSettings = projectSettings;
161  this.mapManager = mapManager;
162  }
163 
164  @NotNull
165  private static JComboBox<?> createEventTypeBox(@NotNull final ScriptArchUtils scriptArchUtils) {
166  final String[] valuesArray = scriptArchUtils.getEventNames();
167  final JComboBox<?> tmpEventTypeBox = new JComboBox<>(valuesArray);
168  tmpEventTypeBox.setSelectedIndex(Arrays2.linearEqualitySearch("say", valuesArray));
169  return tmpEventTypeBox;
170  }
171 
179  public void addEventScript(@NotNull final G gameObject, @NotNull final ScriptArchData<G, A, R> scriptArchData, @NotNull final Frame parent) {
180  final String archName = gameObject.getBestName();
181  // create a reasonable default script name for lazy users :-)
182  final String defScriptName = ScriptUtils.chooseDefaultScriptName(mapManager.getLocalMapDir(), archName, scriptEnding, pathManager);
183 
184  // initialize popup frame
185  final JDialog newScriptFrame = new JDialog(parent, ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventTitleNew"), true);
186  newScriptFrame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
187 
188  final JPanel mainPanel = new JPanel();
189  mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
190  mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 2, 5));
191 
192  // first line: heading
193  final Container line1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
194  final Component headingLabel = new JLabel(ActionBuilderUtils.format(ACTION_BUILDER, "scriptedEventHeading", archName));
195  headingLabel.setForeground(Color.black);
196  line1.add(headingLabel);
197  mainPanel.add(line1);
198 
199  // event type
200  mainPanel.add(Box.createVerticalStrut(10));
201  final Container line2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
202  final Component typeLabel = ActionBuilderUtils.newLabel(ACTION_BUILDER, "scriptedEventType");
203  line2.add(typeLabel);
204  line2.add(eventTypeBox);
205  //mainPanel.add(line2);
206  line2.add(Box.createHorizontalStrut(10));
207 
208  // plugin name
209  final Component pluginLabel = ActionBuilderUtils.newLabel(ACTION_BUILDER, "scriptedEventPlugin");
210  line2.add(pluginLabel);
211  line2.add(pluginNameBox);
212  mainPanel.add(line2);
213 
214  // path
215  mainPanel.add(Box.createVerticalStrut(5));
216  final Container line3 = new JPanel(new FlowLayout(FlowLayout.LEFT));
217  final Component scriptFileLabel = ActionBuilderUtils.newLabel(ACTION_BUILDER, "scriptedEventScript");
218  line3.add(scriptFileLabel);
219  mainPanel.add(line3);
220  inputScriptPath = new JTextField(defScriptName, 20);
221  final AbstractButton browseButton = new JButton("...");
222  browseButton.setMargin(new Insets(0, 10, 0, 10));
223  browseButton.addActionListener(new ActionListener() {
224 
225  @Override
226  public void actionPerformed(@NotNull final ActionEvent e) {
227  final File home = mapManager.getLocalMapDir();
228 
229  final JFileChooser fileChooser = new JFileChooser();
230  fileChooser.setDialogTitle(ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventSelectFile"));
231  fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
232  FileChooserUtils.setCurrentDirectory(fileChooser, home);
233  fileChooser.setMultiSelectionEnabled(false);
234  fileChooser.setFileFilter(scriptFileFilter);
235 
236  if (fileChooser.showOpenDialog(newScriptFrame) == JFileChooser.APPROVE_OPTION) {
237  // user has selected a file
238  final File f = fileChooser.getSelectedFile();
239  inputScriptPath.setText(ScriptUtils.localizeEventPath(mapManager.getLocalMapDir(), f, projectSettings.getMapsDirectory()));
240  }
241  }
242  });
243  line3.add(inputScriptPath);
244  line3.add(browseButton);
245  mainPanel.add(line3);
246 
247  // options
248  mainPanel.add(Box.createVerticalStrut(5));
249  final Container line4 = new JPanel(new FlowLayout(FlowLayout.LEFT));
250  line4.add(new JLabel(ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventOptions")));
251  inputOptions = new JTextField("", 20);
252  line4.add(inputOptions);
253  mainPanel.add(line4);
254 
255  // description
256  final Container line5 = new JPanel(new FlowLayout(FlowLayout.LEFT));
257  final JPanel textPanel = new JPanel();
258  textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.Y_AXIS));
259  final Component label1 = ActionBuilderUtils.newLabel(ACTION_BUILDER, "scriptedEventInfo1");
260  textPanel.add(label1);
261  final Component label2 = ActionBuilderUtils.newLabel(ACTION_BUILDER, "scriptedEventInfo2");
262  textPanel.add(label2);
263  line5.add(textPanel);
264  mainPanel.add(line5);
265 
266  // button panel:
267  mainPanel.add(Box.createVerticalStrut(10));
268  final Container line6 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
269  final AbstractButton nsOkButton = new JButton(ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventOk"));
270  nsOkButton.addActionListener(new ActionListener() {
271 
272  @Override
273  public void actionPerformed(@NotNull final ActionEvent e) {
274  createNewEvent(newScriptFrame, scriptArchData, gameObject);
275  }
276 
277  });
278  line6.add(nsOkButton);
279 
280  final AbstractButton cancelButton = new JButton(ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventCancel"));
281  cancelButton.addActionListener(new ActionListener() {
282 
283  @Override
284  public void actionPerformed(@NotNull final ActionEvent e) {
285  newScriptFrame.setVisible(false);
286  }
287 
288  });
289  line6.add(cancelButton);
290  mainPanel.add(line6);
291 
292  newScriptFrame.getContentPane().add(mainPanel);
293  newScriptFrame.pack();
294  newScriptFrame.setLocationRelativeTo(parent);
295  newScriptFrame.setVisible(true);
296  }
297 
298  public void createNewEvent(@NotNull final Component frame, @NotNull final ScriptArchData<G, A, R> scriptArchData, @NotNull final G gameObject) {
299  final StringBuilder scriptPath = new StringBuilder(inputScriptPath.getText().trim().replace('\\', '/'));
300  final String options = inputOptions.getText().trim();
301  final int eventType = scriptArchUtils.indexToEventType(eventTypeBox.getSelectedIndex());
302  final String pluginName = ((String) pluginNameBox.getSelectedItem()).trim();
303 
304  final File localMapDir = mapManager.getLocalMapDir();
305 
306  // first check if that event type is not already in use
307  final GameObject<G, A, R> replaceObject = scriptArchData.getScriptedEvent(eventType, gameObject);
308  if (replaceObject != null) {
309  // collision with existing event -> ask user: replace?
310  if (JOptionPane.showConfirmDialog(frame, ActionBuilderUtils.format(ACTION_BUILDER, "scriptedEventReplace", scriptArchUtils.typeName(eventType)), ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventReplaceTitle"), JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.NO_OPTION) {
311  // bail out
312  return;
313  }
314  }
315 
316  String absScriptPath;
317  if (scriptPath.length() > 0 && scriptPath.charAt(0) == '/') {
318  // script path is absolute
319  final File mapDir = projectSettings.getMapsDirectory();
320  if (!mapDir.exists()) {
321  // if map dir doesn't exist, this is not going to work
322  frame.setVisible(false);
323  ACTION_BUILDER.showMessageDialog(frame, "mapDirDoesntExist", mapDir);
324  return;
325  }
326 
327  absScriptPath = mapDir.getAbsolutePath() + scriptPath;
328  } else {
329  // script path is relative
330  absScriptPath = localMapDir.getAbsolutePath() + "/" + scriptPath;
331  }
332 
333  // now check if the specified path points to an existing script
334  File newScriptFile = new File(absScriptPath);
335  if (!newScriptFile.exists() && !absScriptPath.endsWith(scriptEnding)) {
336  absScriptPath += scriptEnding;
337  scriptPath.append(scriptEnding);
338  newScriptFile = new File(absScriptPath);
339  }
340 
341  if (newScriptFile.exists()) {
342  if (newScriptFile.isFile()) {
343  // file exists -> link it to the event
344  final ScriptedEvent<G, A, R> event;
345  try {
346  event = scriptedEventFactory.newScriptedEvent(eventType, pluginName, scriptPath.toString(), options);
347  } catch (final UndefinedEventArchetypeException ex) {
348  JOptionPane.showMessageDialog(frame, ActionBuilderUtils.format(ACTION_BUILDER, "scriptedEventCreateError", eventType, ex.getMessage()), ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventCreateErrorTitle"), JOptionPane.ERROR_MESSAGE);
349  return;
350  }
351  if (replaceObject != null) {
352  replaceObject.remove();
353  }
354  gameObject.addLast(event.getEventArch());
355  frame.setVisible(false); // close dialog
356  }
357 
358  return;
359  }
360 
361  if (!absScriptPath.endsWith(scriptEnding)) {
362  absScriptPath += scriptEnding;
363  scriptPath.append(scriptEnding);
364  newScriptFile = new File(absScriptPath);
365  }
366 
367  // file does not exist -> aks user: create new file?
368  if (JOptionPane.showConfirmDialog(frame, ActionBuilderUtils.format(ACTION_BUILDER, "scriptedEventCreate", newScriptFile.getName()), ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventCreateTitle"), JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE) != JOptionPane.YES_OPTION) {
369  return;
370  }
371 
372  boolean couldCreateFile = false; // true when file creation successful
373  try {
374  // try to create new empty file
375  couldCreateFile = newScriptFile.createNewFile();
376  } catch (final IOException e) {
377  /* ignore (really?) */
378  }
379 
380  if (!couldCreateFile) {
381  JOptionPane.showMessageDialog(frame, ActionBuilderUtils.format(ACTION_BUILDER, "scriptedEventFileError", newScriptFile.getName()), ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventFileErrorTitle"), JOptionPane.ERROR_MESSAGE);
382  return;
383  }
384 
385  // file has been created, now link it to the event
386  final ScriptedEvent<G, A, R> event;
387  try {
388  event = scriptedEventFactory.newScriptedEvent(eventType, pluginName, scriptPath.toString(), options);
389  } catch (final UndefinedEventArchetypeException ex) {
390  JOptionPane.showMessageDialog(frame, ActionBuilderUtils.format(ACTION_BUILDER, "scriptedEventCreateError", eventType, ex.getMessage()), ActionBuilderUtils.getString(ACTION_BUILDER, "scriptedEventCreateErrorTitle"), JOptionPane.ERROR_MESSAGE);
391  return;
392  }
393  if (replaceObject != null) {
394  replaceObject.remove();
395  }
396  gameObject.addLast(event.getEventArch());
397  frame.setVisible(false); // close dialog
398 
399  // open new script file
400  scriptEditControl.openScriptFile(newScriptFile.getAbsolutePath());
401  }
402 
403 }
final MapManager<?, ?, ?> mapManager
The MapManager to use.
Stores and manages information about scripted events.
Package with common types for event archetypes.
This class contains methods for converting relative map paths to absolute map paths and vice versa...
A MapManager manages all opened maps.
Definition: MapManager.java:37
Reading and writing of maps, handling of paths.
Settings that apply to a project.
File getLocalMapDir()
Returns a guess for a script directory to use.
Factory for creating ScriptedEvent instances.
Utility class for JFileChooser related functions.
final String scriptEnding
The ending for scripts.
ScriptArchEditor(@NotNull final ScriptedEventFactory< G, A, R > scriptedEventFactory, @NotNull final String scriptEnding, @NotNull final String name, @NotNull final ScriptArchUtils scriptArchUtils, @NotNull final FileFilter scriptFileFilter, @NotNull final ProjectSettings projectSettings, @NotNull final MapManager<?, ?, ?> mapManager, @NotNull final PathManager pathManager, @NotNull final ScriptEditControl scriptEditControl)
Creates a new instance.
final ProjectSettings projectSettings
The ProjectSettings to use.
final ScriptEditControl scriptEditControl
The ScriptEditControl to use.
void openScriptFile(@NotNull final String pathName)
Open a new empty script document.
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.
static void setCurrentDirectory(@NotNull final JFileChooser fileChooser, @Nullable final File dir)
Calls JFileChooser#setCurrentDirectory(File).
This package contains the classes for the script editor used within the editor to create and modify P...
Definition: Actions.java:20
Reflects a game object (object on a map).
Definition: GameObject.java:36
final ScriptedEventFactory< G, A, R > scriptedEventFactory
The ScriptedEventFactory instance to use.
GameObjects are the objects based on Archetypes found on maps.
This exception is thrown if an event game object cannot be created.
ScriptedEvent< G, A, R > newScriptedEvent(int eventType, @NotNull String pluginName, @NotNull String scriptPath, @NotNull String options)
Creates a new ScriptedEvent instance.
final PathManager pathManager
The PathManager for converting path names.
ScriptEditControl - Manages events and data flow for the script editor entity.
void addEventScript(@NotNull final G gameObject, @NotNull final ScriptArchData< G, A, R > scriptArchData, @NotNull final Frame parent)
A popup is opened and the user can create a new scripting event which gets attached to this gameObjec...
static String chooseDefaultScriptName(@NotNull final File baseDir, final String archetypeName, final String scriptEnding, @NotNull final PathManager pathManager)
Try to create a reasonable default script name for lazy users.
Utility class for ActionBuilder related functions.
G getEventArch()
Returns the underlying event game object.
static String localizeEventPath(@NotNull final File localMapDir, final File f, @NotNull final File mapDir)
This method is called when the user selects a new event to be created.
Dialog to create events linked to item scripting.
static JComboBox<?> createEventTypeBox(@NotNull final ScriptArchUtils scriptArchUtils)
File getMapsDirectory()
Returns the default maps directory.
static JLabel newLabel(@NotNull final ActionBuilder actionBuilder, @NotNull final String key)
Creates a new JLabel from a resource key.
static String format(@NotNull final ActionBuilder actionBuilder, @NotNull final String key, @NotNull final Object... args)
Returns the value of a key.
void createNewEvent(@NotNull final Component frame, @NotNull final ScriptArchData< G, A, R > scriptArchData, @NotNull final G gameObject)
final ScriptArchUtils scriptArchUtils
The ScriptArchUtils instance to use.
static final ActionBuilder ACTION_BUILDER
Action Builder.
String typeName(int eventType)
Returns a human readable name for an event type.
Class which stores information about one scripted event.
void remove()
Remove this GameObject from its container.
int indexToEventType(int index)
Converts a combo box index to an event type.