Gridarta Editor
ScriptEditControl.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.textedit.scripteditor;
21 
22 import java.awt.Frame;
23 import java.io.File;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStreamWriter;
27 import java.io.Writer;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.prefs.Preferences;
31 import javax.swing.Action;
32 import javax.swing.JFileChooser;
33 import javax.swing.JOptionPane;
34 import javax.swing.filechooser.FileFilter;
38 import net.sf.gridarta.utils.Exiter;
40 import net.sf.japi.swing.action.ActionBuilder;
41 import net.sf.japi.swing.action.ActionBuilderFactory;
42 import net.sf.japi.swing.action.ActionMethod;
43 import org.apache.log4j.Category;
44 import org.apache.log4j.Logger;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
47 
54 public class ScriptEditControl implements EditorAction {
55 
59  @NotNull
60  private static final Category LOG = Logger.getLogger(ScriptEditControl.class);
61 
65  @NotNull
66  private static final ActionBuilder ACTION_BUILDER = ActionBuilderFactory.getInstance().getActionBuilder("net.sf.gridarta");
67 
68  // last active popup is stored here
69 
70  @Nullable
71  private static CFPythonPopup activePopup;
72 
73  @NotNull
74  private final ScriptEditView view; // view (window with text areas)
75 
76  @NotNull
77  private final List<String> tabs; // open tabs, contains absolute filenames (or "<>") in order left to right
78 
82  @NotNull
83  private final JFileChooser openFileChooser;
84 
85  @NotNull
86  private final FileFilter scriptFileFilter;
87 
88  @NotNull
89  private final String scriptSuffix;
90 
91  public ScriptEditControl(@NotNull final FileFilter scriptFileFilter, @NotNull final String scriptSuffix, @NotNull final Frame owner, final File defaultScriptDir, @NotNull final Preferences preferences, @NotNull final Exiter exiter) {
92  this.scriptFileFilter = scriptFileFilter;
93  this.scriptSuffix = scriptSuffix;
94  tabs = new ArrayList<>(); // start with empty vector
95  view = new ScriptEditView(this, owner, preferences, exiter); // initialize window
96  openFileChooser = createOpenFileChooser(defaultScriptDir);
97  }
98 
99  @Deprecated
100  public void setTextAreaDefaults(@NotNull final TextAreaDefaults textAreaDefaults) {
101  view.setTextAreaDefaults(textAreaDefaults);
102  }
103 
109  public static void registerActivePopup(@NotNull final CFPythonPopup activePopup) {
111  }
112 
116  @ActionMethod
117  public void newScript() {
118  tabs.add("<>"); // this script has no filename assigned yet
119  view.addTab("<New Script>", null);
120  }
121 
125  public void openScriptFile(@NotNull final String pathName) {
126  final File file = new File(pathName);
127 
128  if (!file.exists() || !file.isFile()) {
129  if (LOG.isInfoEnabled()) {
130  LOG.info("Error in ScriptEditControl.openScriptFile():");
131  LOG.info(" File '" + pathName + "' doesn't exist.");
132  }
133  return;
134  }
135 
136  tabs.add(file.getAbsolutePath());
137  view.addTab(file.getName(), file);
138  }
139 
146  @NotNull
147  private JFileChooser createOpenFileChooser(@NotNull final File defaultScriptDir) {
148  final JFileChooser fileChooser = new JFileChooser();
149  fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
150  fileChooser.setMultiSelectionEnabled(false);
151  fileChooser.setFileFilter(scriptFileFilter);
152 
153  // set default folder for new scripts
154  if (defaultScriptDir.exists() && defaultScriptDir.isDirectory()) {
155  FileChooserUtils.setCurrentDirectory(fileChooser, defaultScriptDir);
156  } else {
158  }
159  return fileChooser;
160  }
161 
165  public void openUser() {
166  openFileChooser.setDialogTitle("Open Script File");
167 
169  final int returnVal = openFileChooser.showOpenDialog(view);
170  if (returnVal != JFileChooser.APPROVE_OPTION) {
171  return;
172  }
173 
174  final File file = openFileChooser.getSelectedFile();
175  if (file.exists() && !file.isDirectory()) {
176  // everything okay do far, now open up that script file
177  openScriptFile(file.getAbsolutePath());
178  } else {
179  // user entered a filename which doesn't yet exist -> open new script
180  newScript();
181  }
182  }
183 
189  public boolean closeActiveTab() {
190  final String title = view.getActiveTitle();
191  if (title == null) {
192  return true;
193  }
194 
195  final JEditTextArea activeTextArea = getActiveTextArea();
196  if (activeTextArea != null && activeTextArea.isModified() && !ACTION_BUILDER.showQuestionDialog(view, "scriptEdit.confirmClose", title)) {
197  return false;
198  }
199 
200  if (view.getSelectedIndex() >= 0 && !tabs.isEmpty()) {
201  tabs.remove(view.getSelectedIndex()); // dump the filename
202  }
203  view.closeActiveTab(); // close tab in the view
204 
205  // hide view when last tab has been closed
206  if (view.getTabCount() <= 0) {
207  if (activePopup != null && (activePopup.isShowing() || activePopup.getMenu().isShowing())) {
208  activePopup.getMenu().setVisible(false);
209  }
210  view.setVisible(false);
211  }
212  return true;
213  }
214 
220  public boolean closeAllTabs() {
221  // simply keep closing active tabs till none are left
222  while (view.getSelectedIndex() >= 0 || !tabs.isEmpty()) {
223  if (!closeActiveTab()) {
224  return false;
225  }
226  }
227  return true;
228  }
229 
234  public void saveAsActiveTab() {
235  final String activePath = getActiveFilePath(); // active file path ('null' if undefined)
236  final JEditTextArea activeTextArea = getActiveTextArea();
237  if (activeTextArea == null) {
238  return;
239  }
240  final String text = activeTextArea.getText(); // Store text data to ensure that the right text is saved later.
241  // User could switch tabs or type text in the meantime, who knows.
242  final int tabIndex = view.getSelectedIndex(); // save tab-index of this script
243 
244  openFileChooser.setDialogTitle("Save Script File As");
245 
246  // if file already exists, select it
247  if (activePath != null) {
248  final File file = new File(activePath);
249 
250  if (file.getParentFile().exists() && file.getParentFile().isDirectory()) {
251  openFileChooser.setCurrentDirectory(file.getParentFile());
252  openFileChooser.setSelectedFile(file); // select this name
253  }
254  }
255 
256  final int returnVal = openFileChooser.showSaveDialog(view);
257  if (returnVal != JFileChooser.APPROVE_OPTION) {
258  return;
259  }
260 
261  File file = openFileChooser.getSelectedFile();
262  if (!file.getName().endsWith(scriptSuffix)) {
263  final String fileName = file.getAbsolutePath();
264  file = new File(fileName + scriptSuffix);
265  }
266 
267  // now it is our duty to double check if user attempts to overwrite
268  if (!file.exists() || (activePath != null && file.getAbsolutePath().equals(activePath)) || view.askConfirm("Overwrite?", "A file named \"" + file.getName() + "\" already exists.\n" + "Are you sure you want to overwrite it?")) {
269  // looks like we can finally save the data
270  if (saveTextToFile(file, text)) {
271  activeTextArea.resetModified();
272  }
273 
274  // now update the file path for this open script tab
275  if (tabIndex >= 0 && tabs.size() > tabIndex) {
276  // it might be nice here to check the content of the document at 'index',
277  // to make sure it stayed the same
278  //String path = (String)(tabs.elementAt(tabIndex));
279 
280  // set new path
281  tabs.set(tabIndex, file.getAbsolutePath());
282  view.setTitleAt(tabIndex, file.getName());
283  }
284  }
285  }
286 
290  public void saveActiveTab() {
291  if (getActiveFilePath() != null) {
292  final File file = new File(getActiveFilePath()); // get active path
293  final JEditTextArea activeTextArea = getActiveTextArea();
294  if (activeTextArea != null && saveTextToFile(file, activeTextArea.getText())) {
295  activeTextArea.resetModified();
296  }
297  } else {
298  LOG.error("ScriptEditControl.saveActiveTab(): Cannot save file without name!");
299  // Path is missing? This shouldn't happen, but let's do a saveAs instead...
300  saveAsActiveTab();
301  }
302  }
303 
311  public boolean saveTextToFile(@NotNull final File file, @NotNull final String text) {
312  try {
313  try (FileOutputStream fos = new FileOutputStream(file)) {
314  try (Writer osw = new OutputStreamWriter(fos)) {
315  osw.write(text);
316  }
317  }
318  } catch (final IOException e) {
319  // tell the user because it is important to know that saving failed
320  view.showMessage("Write Error", "The file \"" + file.getName() + "\" could not be written.\nPlease use the 'Save As...' menu.", JOptionPane.ERROR_MESSAGE);
321  return false;
322  }
323 
324  return true;
325  }
326 
330  @Nullable
331  JEditTextArea getActiveTextArea() {
332  return view.getActiveTextArea();
333  }
334 
338  @Nullable
339  String getActiveFilePath() {
340  if (view.getSelectedIndex() < 0 || tabs.size() <= 0) {
341  return null;
342  }
343 
344  // get stored path
345  final String path = tabs.get(view.getSelectedIndex());
346 
347  if (path == null || path.isEmpty() || path.equals("<>")) {
348  return null;
349  } else {
350  return path;
351  }
352  }
353 
354  @Override
355  public void setAction(@NotNull final Action action, @NotNull final String name) {
356  }
357 
358 }
void setTitleAt(final int index, @NotNull final String title)
Sets the title of the tab at specified index.
boolean isModified()
Return whether the text content has been modified from the "unmodified" state.
void setAction(@NotNull final Action action, @NotNull final String name)
Sets the Action instance for this editor action.
static final ActionBuilder ACTION_BUILDER
Action Builder.
static void registerActivePopup(@NotNull final CFPythonPopup activePopup)
Register last active popup.
Utility class for JFileChooser related functions.
ScriptEditControl(@NotNull final FileFilter scriptFileFilter, @NotNull final String scriptSuffix, @NotNull final Frame owner, final File defaultScriptDir, @NotNull final Preferences preferences, @NotNull final Exiter exiter)
void openScriptFile(@NotNull final String pathName)
Open a new empty script document.
void showMessage(@NotNull final String title, @NotNull final String message, final int messageType)
Shows the given message in the UI.
boolean closeAllTabs()
Close all opened script-tabs.
Base package of all Gridarta classes.
static void setCurrentDirectory(@NotNull final JFileChooser fileChooser, @Nullable final File dir)
Calls JFileChooser#setCurrentDirectory(File).
boolean askConfirm(@NotNull final String title, @NotNull final String message)
Shows the given confirmation message as popup frame.
Exits the application.
Definition: Exiter.java:28
A global editor action.
void openUser()
Open a file which is chosen by the user.
void setTextAreaDefaults(@NotNull final TextAreaDefaults textAreaDefaults)
This package contains the other part of the script editor.
String getActiveTitle()
Returns the title of the active tab.
JFileChooser createOpenFileChooser(@NotNull final File defaultScriptDir)
Creates the JFileChooser for opening a script file.
void closeActiveTab()
Closes the active script-tab.
void addTab(@NotNull final String title, @Nullable final File file)
Adds a new TextArea Panel to the TabbedPane.
ScriptEditControl - Manages events and data flow for the script editor entity.
String getText()
Returns the entire text of this text area.
void resetModified()
Reset the "modified" state.
void saveAsActiveTab()
Open a file browser and prompt the user for a location/name to store this file.
void setTextAreaDefaults(@NotNull final TextAreaDefaults textAreaDefaults)
boolean closeActiveTab()
Close the active script-tab.
static void sanitizeCurrentDirectory(@NotNull final JFileChooser fileChooser)
Makes sure the current directory of a JFileChooser is valid.
This class implements a popup window which shows all python methods in the &#39;CFPython&#39; package...
void newScript()
Open a new empty script document.
void saveActiveTab()
Save the active script-tab to the stored file path.
final JFileChooser openFileChooser
JFileChooser for opening script files.
Encapsulates default settings for a text area.
boolean saveTextToFile(@NotNull final File file, @NotNull final String text)
Write the given text into the specified file.
static final Category LOG
The Logger for printing log messages.