r4255 - branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text
Author: echatellier Date: 2015-06-10 12:34:40 +0000 (Wed, 10 Jun 2015) New Revision: 4255 Url: http://forge.codelutin.com/projects/isis-fish/repository/revisions/4255 Log: Redo lost SyntaxEditor class :( Added: branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text/SyntaxEditor.java Modified: branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text/SyntaxEditorUI.java Added: branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text/SyntaxEditor.java =================================================================== --- branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text/SyntaxEditor.java (rev 0) +++ branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text/SyntaxEditor.java 2015-06-10 12:34:40 UTC (rev 4255) @@ -0,0 +1,361 @@ +package fr.ifremer.isisfish.ui.widget.text; + +import static org.nuiton.i18n.I18n.t; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.KeyStroke; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; +import org.nuiton.util.FileUtil; + +/** + * Add custom behavior to RSyntaxEditor UI. + * + * Like : + * <ul> + * <li>copy/paste</li> + * <li>file change detection</li> + * </ul> + * + * @author Eric Chatellier + */ +public class SyntaxEditor extends SyntaxEditorUI implements DocumentListener { + + /** serialVersionUID. */ + private static final long serialVersionUID = 8010988112139944408L; + + /** class logger */ + private static Log log = LogFactory.getLog(SyntaxEditor.class); + + /** Will ask user to perform save on close. */ + protected boolean askIfNotSaved = false; + + /** Currently edited file. */ + protected File file; + + /** Flag is file has been modified. */ + protected transient boolean modified; + + /** Timestamp when saved has been performed from editor. */ + protected transient long lastSaveTimestamp; + + /** Save action instance. */ + protected Action saveAction; + + /** Single watch service instance. */ + protected WatchService watchService; + + /** Watch service thread. */ + protected Thread watchThread; + + /** Ask reload file panel (not null if displayed). */ + protected JPanel askReloadPanel; + + public SyntaxEditor() { + // to listen for user modification + textArea.getDocument().addDocumentListener(this); + + // save action + int c = getToolkit().getMenuShortcutKeyMask(); + saveAction = new AbstractAction(t("isisfish.editor.save"), new ImageIcon(getClass().getResource("/icons/script_save.png"))) { + public void actionPerformed(ActionEvent e) { + save(); + } + }; + saveAction.setEnabled(false); + saveAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, c)); + } + + @Override + protected void finalize() throws Throwable { + try { + watchService.close(); + } finally { + super.finalize(); + } + } + + public boolean isAskIfNotSaved() { + return askIfNotSaved; + } + + public void setAskIfNotSaved(boolean askIfNotSaved) { + this.askIfNotSaved = askIfNotSaved; + } + + protected void setModified(boolean modified) { + this.modified = modified; + saveAction.setEnabled(modified); + } + + public boolean isModified() { + return modified; + } + + public void addDocumentListener(DocumentListener listener) { + textArea.getDocument().addDocumentListener(listener); + } + + public void removeDocumentListener(DocumentListener listener) { + textArea.getDocument().removeDocumentListener(listener); + } + + public boolean open(File file) { + // try to save previous file if necessary + boolean result = askAndSaveOrCancel(); + + if (result) { + + clearReloadPanel(); + + //try (Reader in = new BufferedReader(new FileReader(file), "utf-8")) { + try (Reader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { + + String ext = FileUtil.extension(file); + if ("java".equalsIgnoreCase(ext)) { + textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); + } else if ("xml".equalsIgnoreCase(ext)) { + textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML); + } else if ("sql".equalsIgnoreCase(ext)) { + textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_SQL); + } else if ("r".equalsIgnoreCase(ext)) { + textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_PERL); + } + + textArea.read(in, null); + textArea.setCaretPosition(0); + + setFile(file); // after textArea.read + } catch (Exception eee) { + if (log.isWarnEnabled()) { + log.warn("Can't read file", eee); + } + } + } + + return result; + } + + protected boolean askAndSaveOrCancel() { + boolean result = true; + if (isAskIfNotSaved() && isModified()) { + int val = JOptionPane.showConfirmDialog(this, + t("isisfish.editor.saveorcancel.message", file.getName()), + t("isisfish.editor.saveorcancel.title"), + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + switch (val) { + case JOptionPane.YES_OPTION: + save(); + result = true; + break; + case JOptionPane.NO_OPTION: + result = true; + break; + case JOptionPane.CANCEL_OPTION: + result = false; + break; + } + } + return result; + } + + public boolean close() { + boolean result = askAndSaveOrCancel(); + setText("Select a file to open"); + setFile(null); + return result; + } + + protected void setFile(File file) { + // XXX maybe remove old file watch service ? + this.file = file; + + setEnabled(file != null); + setModified(false); + + // detect external changes + if (file != null) { + detectExternalChanges(file); + } + } + + public void save() { + clearReloadPanel(); + + try (Writer out = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { + lastSaveTimestamp = System.currentTimeMillis(); + + textArea.write(out); + setModified(false); + } catch (IOException eee) { + if (log.isWarnEnabled()) { + log.warn("Can't save file", eee); + } + } + } + + public String getText() { + return textArea.getText(); + } + + public void copy() { + textArea.copy(); + } + + public void paste() { + textArea.paste(); + } + + public void cut() { + textArea.cut(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + setModified(true); + } + + @Override + public void removeUpdate(DocumentEvent e) { + setModified(true); + } + + @Override + public void changedUpdate(DocumentEvent e) { + setModified(true); + } + + public Action getSaveAction() { + return saveAction; + } + + protected void clearReloadPanel() { + if (askReloadPanel != null) { + hideBottomComponent(); + askReloadPanel = null; + } + } + + protected void detectExternalChanges(File file) { + if (askIfNotSaved) { + try { + if (watchService == null) { + watchService = FileSystems.getDefault().newWatchService(); + } + if (watchThread == null) { + watchThread = new WatchThread(); + watchThread.start(); + } + + // register new file to watch service + Path filePath = file.toPath(); + Path parentPath = filePath.getParent(); + parentPath.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE); + } catch (IOException ex) { + if (log.isErrorEnabled()) { + log.error("Can't listen for file", ex); + } + } + } + } + + protected void notifyFileChanged(final File file) { + if (askReloadPanel == null && file.equals(this.file)) { + + // check to not display self save + if (file.lastModified() - lastSaveTimestamp < 1000) { + return; + } + + setModified(true); // to allow erasure + + askReloadPanel = new JPanel(new BorderLayout()); + + askReloadPanel.setBackground(new Color(242, 242, 189)); // dark yellow + + askReloadPanel.add(new JLabel(t("isisfish.editor.reloadExternal"), + new ImageIcon(getClass().getResource("/icons/database_error.png")), JLabel.LEFT), BorderLayout.CENTER); + + JPanel actionsPanel = new JPanel(new FlowLayout()); + actionsPanel.setBackground(new Color(242, 242, 189)); // dark yellow + actionsPanel.add(new JButton(new AbstractAction(t("isisfish.editor.reload")) { + @Override + public void actionPerformed(ActionEvent e) { + setModified(false); // to never ask + open(file); + } + })); + actionsPanel.add(new JButton(new AbstractAction(t("isisfish.editor.cancel")) { + @Override + public void actionPerformed(ActionEvent e) { + clearReloadPanel(); + } + })); + + askReloadPanel.add(actionsPanel, BorderLayout.EAST); + addBottomComponent(askReloadPanel); + showBottomComponent(askReloadPanel); + } + } + + class WatchThread extends Thread { + public void run() { + try { + while (true) { + final WatchKey wk = watchService.take(); + for (WatchEvent<?> event : wk.pollEvents()) { + //we only register "ENTRY_MODIFY" so the context is always a Path. + Path changed = (Path)event.context(); + Path watchPath = (Path)wk.watchable(); + File fullFile = new File(watchPath.toFile(), changed.toString()); + notifyFileChanged(fullFile); + } + // reset the key + boolean valid = wk.reset(); + if (!valid) { + if (log.isErrorEnabled()) { + log.error("Watch service unregistered"); + } + } + } + } catch (InterruptedException ex) { + if (log.isErrorEnabled()) { + log.error("Thread interrupted", ex); + } + } + } + } +} Property changes on: branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text/SyntaxEditor.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: svn:eol-style + native Modified: branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text/SyntaxEditorUI.java =================================================================== --- branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text/SyntaxEditorUI.java 2015-06-10 08:16:42 UTC (rev 4254) +++ branches/isis-fish-4.4.0-editor/src/main/java/fr/ifremer/isisfish/ui/widget/text/SyntaxEditorUI.java 2015-06-10 12:34:40 UTC (rev 4255) @@ -115,6 +115,7 @@ replaceAction = new ShowReplaceDialogAction(); gotoAction = new GoToLineAction(parent); + int c = getToolkit().getMenuShortcutKeyMask(); // caret update will update setEnabled state textArea.addCaretListener(this); cutAction = new AbstractAction(t("isisfish.editor.cut"), new ImageIcon(getClass().getResource("/icons/cut.png"))) { @@ -124,12 +125,14 @@ } }; cutAction.setEnabled(false); + cutAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, c)); pasteAction = new AbstractAction(t("isisfish.editor.paste"), new ImageIcon(getClass().getResource("/icons/page_paste.png"))) { @Override public void actionPerformed(ActionEvent e) { textArea.paste(); } }; + pasteAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V, c)); copyAction = new AbstractAction(t("isisfish.editor.copy"), new ImageIcon(getClass().getResource("/icons/page_copy.png"))) { @Override public void actionPerformed(ActionEvent e) { @@ -137,9 +140,8 @@ } }; copyAction.setEnabled(false); + copyAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, c)); } - - /** * Set highligth style.
participants (1)
-
echatellier@users.forge.codelutin.com