/* * NotesView.java - the main view for the Notes plugin for GMGen * Copyright (C) 2003 Devon Jones * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on May 24, 2003 */ package plugin.notes.gui; import gmgen.GMGenSystem; import gmgen.GMGenSystemView; import gmgen.gui.ExtendedHTMLDocument; import gmgen.gui.ExtendedHTMLEditorKit; import gmgen.gui.FlippingSplitPane; import gmgen.gui.ImageFileChooser; import gmgen.io.SimpleFileFilter; import gmgen.util.LogReceiver; import gmgen.util.LogUtilities; import gmgen.util.MiscUtilities; import pcgen.cdom.base.Constants; import pcgen.core.SettingsHandler; import pcgen.system.LanguageBundle; import pcgen.util.Logging; import plugin.notes.NotesPlugin; import javax.swing.*; import javax.swing.border.BevelBorder; import javax.swing.border.EtchedBorder; import javax.swing.event.*; import javax.swing.filechooser.FileFilter; import javax.swing.text.*; import javax.swing.text.html.HTML; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.dnd.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.io.*; import java.text.DateFormat; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import pcgen.gui2.tools.CommonMenuText; import pcgen.gui2.tools.Icons; /** * This class is the main view for the Notes Plugin. Mostof the work is done * here and in the NotesTreeNode Class. * * @author soulcatcher */ public class NotesView extends JPanel { /** * Extension with a point */ private static final String EXTENSION = "."+NotesPlugin.EXTENSION_NOTES; //$NON-NLS-1$ private static final String OPTION_NAME_LASTFILE = NotesPlugin.LOG_NAME + ".LastFile"; //$NON-NLS-1$ /** Drop Target for the Edit Area */ protected DropTarget editAreaDT; /** Drop Target for the File Bar */ protected DropTarget filesBarDT; /** Drop Target for the Tree */ protected DropTarget treeDT; /** Insert OL Action for JTextPane */ protected ExtendedHTMLEditorKit.InsertListAction actionListOrdered = new ExtendedHTMLEditorKit.InsertListAction("InsertOLItem", HTML.Tag.OL); /** Insert UL Action for JTextPane */ protected ExtendedHTMLEditorKit.InsertListAction actionListUnordered = new ExtendedHTMLEditorKit.InsertListAction("InsertULItem", HTML.Tag.UL); // End of variables declaration//GEN-END:variables protected NotesPlugin plugin; /** Root node of tree */ protected NotesTreeNode root; /** Redo Action for JTextPane */ protected RedoAction redoAction = new RedoAction(); /** Data Directory */ protected File dataDir; /** Undo Action for JTextPane */ protected UndoAction undoAction = new UndoAction(); /** Undo Manager */ protected UndoManager undo = new UndoManager(); /** Image extensions that this supports */ // TODO: Move Image extensions to properties protected final String[] extsIMG = {"gif", "jpg", "jpeg", "png"}; private JButton boldButton; private JButton bulletButton; private JButton centerJustifyButton; private JButton colorButton; private JButton copyButton; private JButton cutButton; private JButton deleteButton; private JButton enumButton; private JButton exportButton; private JButton fileLeft; private JButton fileRight; private JButton imageButton; private JButton italicButton; private JButton leftJustifyButton; private JButton newButton; private JButton pasteButton; private JButton revertButton; private JButton rightJustifyButton; private JButton saveButton; private JButton underlineButton; private JComboBox sizeCB; private JPanel filePane; private JPanel jPanel1; private JPanel jPanel2; // Variables declaration - do not modify//GEN-BEGIN:variables private JScrollPane jScrollPane1; private JScrollPane jScrollPane2; private FlippingSplitPane jSplitPane1; private JTextPane editor; private JToolBar alignmentBar; private JToolBar clipboardBar; private JToolBar fileBar; private JToolBar filesBar; private JToolBar formatBar; private JTree notesTree; /** * Creates new form NotesView * *@param dataDir Data directory where notes will be stored. * @param plugin */ public NotesView(File dataDir, NotesPlugin plugin) { this.plugin = plugin; this.dataDir = dataDir; initComponents(); initEditingComponents(); initDnDComponents(); initTree(); initFileBar(new ArrayList<>()); initLogging(); notesTree.setSelectionRow(0); } /** * Searches a text component for a particular action. * *@param textComponent Text component to search for the action in *@param name name of the action to get *@return the action */ public Action getActionByName(JTextComponent textComponent, String name) { // TODO: This should be static in a GUIUtilities file for (Action a : textComponent.getActions()) { if (a.getValue(Action.NAME).equals(name)) { return a; } } return null; } /** * {@literal handle File->Open.} Will open any .gmn files, and import them into your * notes structure */ public void handleOpen() { // TODO fix String sFile = SettingsHandler.getGMGenOption(OPTION_NAME_LASTFILE, System.getProperty("user.dir")); File defaultFile = new File(sFile); JFileChooser chooser = new JFileChooser(); chooser.setCurrentDirectory(defaultFile); for (FileFilter filter : plugin.getFileTypes()) { chooser.addChoosableFileFilter(filter); chooser.setFileFilter(filter); } chooser.setMultiSelectionEnabled(true); Component component = GMGenSystem.inst; Cursor originalCursor = component.getCursor(); component.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); int option = chooser.showOpenDialog(GMGenSystem.inst); if (option == JFileChooser.APPROVE_OPTION) { for (File noteFile : chooser.getSelectedFiles()) { SettingsHandler.setGMGenOption(OPTION_NAME_LASTFILE, noteFile.toString()); if (noteFile.toString().endsWith(EXTENSION)) { openGMN(noteFile); } } } GMGenSystem.inst.setCursor(originalCursor); refreshTree(); } /** * fills the 'edit' menu of the main menu * *@param editMenu The Edit Menu */ public void initEditMenu(JMenu editMenu) { JMenuItem paste = new JMenuItem(); CommonMenuText.name(paste, "mnuEditPaste"); //$NON-NLS-1$ paste.addActionListener(this::pasteButtonActionPerformed); editMenu.insert(paste, 0); JMenuItem copy = new JMenuItem(); CommonMenuText.name(copy, "mnuEditCopy"); //$NON-NLS-1$ copy.addActionListener(this::copyButtonActionPerformed); editMenu.insert(copy, 0); JMenuItem cut = new JMenuItem(); CommonMenuText.name(cut, "mnuEditCut"); //$NON-NLS-1$ cut.addActionListener(this::cutButtonActionPerformed); editMenu.insert(cut, 0); editMenu.insertSeparator(0); editMenu.insert(redoAction, 0); editMenu.insert(undoAction, 0); } /** * Opens a .gmn file * *@param notesFile .gmn file to open */ public void openGMN(File notesFile) { try { Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; if (node != root) { int choice = JOptionPane .showConfirmDialog( this, "Importing note " + notesFile.getName() + " into a node other then root, Continue?", "Importing to a node other then root", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (choice == JOptionPane.NO_OPTION) { return; } } InputStream in = new BufferedInputStream(new FileInputStream(notesFile)); ZipInputStream zin = new ZipInputStream(in); ZipEntry e; ProgressMonitor pm = new ProgressMonitor(GMGenSystem.inst, "Reading Notes Export", "Reading", 1, 1000); int progress = 1; while ((e = zin.getNextEntry()) != null) { unzip(zin, e.getName(), node.getDir()); progress++; if (progress > 99) { progress = 99; } pm.setProgress(progress); } zin.close(); pm.close(); } } catch (IOException e) { JOptionPane.showMessageDialog(this, "Error Reading File" + notesFile.getName()); Logging.errorPrint("Error Reading File" + notesFile.getName()); Logging.errorPrint(e.getMessage(), e); } } /** refreshs the tree, and updates it's UI */ public void refreshTree() { root.refresh(); notesTree.updateUI(); } /** called when window is closed, saves everything in the tree */ public void windowClosed() { if (root.isTreeDirty()) { GMGenSystemView.getTabPane().setSelectedComponent(this); } root.checkSave(); } //Note Import/Export methods /** * Exports a node out to a gmn file. * *@param node node to export to file */ protected void exportFile(NotesTreeNode node) { JFileChooser fLoad = new JFileChooser(); String sFile = SettingsHandler.getGMGenOption(OPTION_NAME_LASTFILE, ""); new File(sFile); FileFilter ff = NotesPlugin.getFileType(); fLoad.addChoosableFileFilter(ff); fLoad.setFileFilter(ff); int returnVal = fLoad.showSaveDialog(this); try { if (returnVal == JFileChooser.APPROVE_OPTION) { String fileName = fLoad.getSelectedFile().getName(); String dirName = fLoad.getSelectedFile().getParent(); String extension = EXTENSION; if (fileName.indexOf(extension) < 0) { fileName += extension; } File expFile = new File(dirName + File.separator + fileName); if (expFile.exists()) { int choice = JOptionPane.showConfirmDialog(this, "File Exists, Overwrite?", "File Exists", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (choice == JOptionPane.NO_OPTION) { return; } } SettingsHandler.setGMGenOption(OPTION_NAME_LASTFILE, expFile.toString()); writeNotesFile(expFile, node); } } catch (Exception e) { JOptionPane.showMessageDialog(this, "Error Writing File"); Logging.errorPrint("Error Writing to file: " + e.getMessage(), e); } } /** * gets the number of files in a directory so that you can have a progress * meter as we zip them into a gmn file. This function is recursive * *@param count File to count the children of *@return count of all files in this dir */ protected int fileCount(File count) { // TODO: Shouldn't this really be a static method in MiscUtils? int num = 0; for (File f : count.listFiles()) { if (f.isDirectory()) { num = num + fileCount(f); } else { num++; } } return num; } /** * Sets a border of an editing button to indicate that the function of the * button is active according to the text location of the cursor * *@param button Button to highlight */ protected void highlightButton(JButton button) { button.setBorder(new BevelBorder(BevelBorder.LOWERED)); } //Action methods /** * Performs an action of a particular name on the man editor. * *@param name name of the action to perform. *@param evt ActionEvent that sparked the calling of this function. */ protected void performTextPaneAction(String name, java.awt.event.ActionEvent evt) { Action action = getActionByName(editor, name); action.actionPerformed(evt); editor.grabFocus(); int cp = editor.getCaretPosition(); updateButtons(editor, cp); } /** * Sets a border of an editing button to indicate that the function of the * button is not active according to the text location of the cursor * *@param button button to set in standard mode */ protected void stdButton(JButton button) { button.setBorder(new EtchedBorder(EtchedBorder.LOWERED)); } /** * Unzips one file from a zipinputstream * *@param zin Zip input stream *@param homeDir Directory to unzip the file to *@param entry Description of the Parameter *@exception IOException read or write error */ protected void unzip(ZipInputStream zin, String entry, File homeDir) throws IOException { // TODO: This function really should be in MiscUtils as a static File outFile = new File(homeDir.getPath() + File.separator + entry); File parentDir = outFile.getParentFile(); parentDir.mkdirs(); outFile.createNewFile(); FileOutputStream out = new FileOutputStream(outFile); byte[] b = new byte[512]; int len = 0; while ((len = zin.read(b)) != -1) { out.write(b, 0, len); } out.close(); } //Methods for dealing with button appearance /** * Updates Editing buttons based on the location of the cursor * *@param textPane text pane to update buttons base on *@param pos current text position */ protected void updateButtons(JTextPane textPane, int pos) { StyledDocument doc = textPane.getStyledDocument(); AttributeSet set = doc.getCharacterElement(pos - 1).getAttributes(); AttributeSet set1 = doc.getCharacterElement(pos).getAttributes(); if (StyleConstants.isBold(set) && StyleConstants.isBold(set1)) { highlightButton(boldButton); } else { stdButton(boldButton); } if (StyleConstants.isItalic(set) && StyleConstants.isItalic(set1)) { highlightButton(italicButton); } else { stdButton(italicButton); } if (StyleConstants.isUnderline(set) && StyleConstants.isUnderline(set1)) { highlightButton(underlineButton); } else { stdButton(underlineButton); } int align = StyleConstants.getAlignment(set); stdButton(leftJustifyButton); stdButton(rightJustifyButton); stdButton(centerJustifyButton); if (align == StyleConstants.ALIGN_LEFT) { highlightButton(leftJustifyButton); } else if (align == StyleConstants.ALIGN_RIGHT) { highlightButton(rightJustifyButton); } else if (align == StyleConstants.ALIGN_CENTER) { highlightButton(centerJustifyButton); } int fontSize = StyleConstants.getFontSize(set); for (int i = 0; i < sizeCB.getItemCount(); i++) { String value = (String) sizeCB.getItemAt(i); if (value.equals(Integer.toString(fontSize))) { sizeCB.setSelectedItem(value); break; } } } /** * Writes out a directory to a zipoutputstream * *@param out Zip output stream to write to *@param parentDir parent dir of whole structure to be written out *@param currentDir dir to be zipped up *@param pm progress meter that will display the progress *@param progress progress up to this dir *@return current progress *@exception IOException write or read failed for some reason */ protected int writeNotesDir(ZipOutputStream out, File parentDir, File currentDir, ProgressMonitor pm, int progress) throws IOException { byte[] buffer = new byte[4096]; int bytes_read; int returnValue = progress; for (File f : currentDir.listFiles()) { if (pm.isCanceled()) { return 0; } if (f.isDirectory()) { returnValue = writeNotesDir(out, parentDir, f, pm, returnValue); } else { FileInputStream in = new FileInputStream(f); try { String parentPath = parentDir.getParentFile().getAbsolutePath(); ZipEntry entry = new ZipEntry(f.getAbsolutePath().substring( parentPath.length() + 1)); out.putNextEntry(entry); while ((bytes_read = in.read(buffer)) != -1) { out.write(buffer, 0, bytes_read); } } finally { try { in.close(); } catch (IOException e) { //TODO: Should this really be ignored? } } returnValue++; } } pm.setProgress(returnValue); return returnValue; } /** * Writes out a GMN file * *@param exportFile file to export to *@param node node to export *@exception IOException file write failed for some reason */ protected void writeNotesFile(File exportFile, NotesTreeNode node) throws IOException { File dir = node.getDir(); ZipOutputStream out = new ZipOutputStream(new FileOutputStream(exportFile)); int max = fileCount(dir); ProgressMonitor pm = new ProgressMonitor(GMGenSystem.inst, "Writing out Notes Export", "Writing", 0, max); try { writeNotesDir(out, dir, dir, pm, 0); } // Always close the streams, even if exceptions were thrown finally { try { out.close(); } catch (IOException e) { //TODO: Should this really be ignored? } } pm.close(); } //CoreUtility methods private File getCurrentDir() { Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; return node.getDir(); } return null; } /** * obtains an Image for input using a custom JFileChooser dialog * *@param startDir Directory to open JFielChooser to *@param exts Extensions to search for *@param desc Description for files *@return File pointing to the selected image */ private File getImageFromChooser(String startDir, String[] exts, String desc) { ImageFileChooser jImageDialog = new ImageFileChooser(new File(startDir)); jImageDialog.setDialogType(JFileChooser.CUSTOM_DIALOG); jImageDialog.setFileFilter(new SimpleFileFilter(exts, desc)); jImageDialog.setDialogTitle("Select an Image to Insert"); int optionSelected = JFileChooser.CANCEL_OPTION; optionSelected = jImageDialog.showDialog(this, "Insert"); if (optionSelected == JFileChooser.APPROVE_OPTION) { return jImageDialog.getSelectedFile(); } return null; } private void notesTreeNodesChanged() { Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; try { node.rename((String) node.getUserObject()); } catch (Exception e) { System.out.println(e.getMessage()); } } } //GEN-LAST:event_italicButtonActionPerformed private void boldButtonActionPerformed(java.awt.event.ActionEvent evt) { //GEN-FIRST:event_boldButtonActionPerformed performTextPaneAction("font-bold", evt); } //GEN-LAST:event_rightJustifyButtonActionPerformed private void centerJustifyButtonActionPerformed( java.awt.event.ActionEvent evt) { //GEN-FIRST:event_centerJustifyButtonActionPerformed Action action = new StyledEditorKit.AlignmentAction("Align Centre", StyleConstants.ALIGN_CENTER); action.actionPerformed(evt); editor.grabFocus(); int cp = editor.getCaretPosition(); updateButtons(editor, cp); } //GEN-LAST:event_leftJustifyButtonActionPerformed private void colorButtonActionPerformed() { //GEN-FIRST:event_colorButtonActionPerformed AttributeSet as = editor.getCharacterAttributes(); SimpleAttributeSet sas = new SimpleAttributeSet(as); Color newColor = JColorChooser.showDialog(GMGenSystem.inst, "Choose Text Color", editor.getStyledDocument().getForeground(as)); if (newColor != null) { StyleConstants.setForeground(sas, newColor); editor.setCharacterAttributes(sas, true); } editor.repaint(); } //GEN-LAST:event_pasteButtonActionPerformed private void copyButtonActionPerformed(java.awt.event.ActionEvent evt) { //GEN-FIRST:event_copyButtonActionPerformed performTextPaneAction(DefaultEditorKit.copyAction, evt); } //GEN-LAST:event_copyButtonActionPerformed private void cutButtonActionPerformed(java.awt.event.ActionEvent evt) { //GEN-FIRST:event_cutButtonActionPerformed performTextPaneAction(DefaultEditorKit.cutAction, evt); } //GEN-LAST:event_cutButtonActionPerformed private void deleteButtonActionPerformed() { //GEN-FIRST:event_deleteButtonActionPerformed Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; node.delete(); } notesTree.updateUI(); } //GEN-LAST:event_newButtonActionPerformed private void editorCaretUpdate(CaretEvent evt) { //GEN-FIRST:event_editorCaretUpdate int dot = evt.getDot(); updateButtons(editor, dot); Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; if (node.isDirty()) { revertButton.setEnabled(true); } else { revertButton.setEnabled(false); } } } // Key Events private void editorKeyTyped(KeyEvent evt) { editor.getCaretPosition(); editor.getStyledDocument(); if (evt.getKeyChar() == KeyEvent.VK_BACK_SPACE) { handleBackspace(); } else if (evt.getKeyChar() == KeyEvent.VK_ENTER) { handleEnter(); } } //GEN-LAST:event_editorCaretUpdate // Edit Events private void editorUndoableEditHappened(UndoableEditEvent e) { undo.addEdit(e.getEdit()); undoAction.updateUndoState(); redoAction.updateRedoState(); } //GEN-END:initComponents private void exportButtonActionPerformed() { //GEN-FIRST:event_exportButtonActionPerformed Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; exportFile(node); } } //GEN-LAST:event_fileRightActionPerformed private void fileLeftActionPerformed() { //GEN-FIRST:event_fileLeftActionPerformed if (filesBar.getComponentCount() > 1) { Component c = filesBar.getComponent(filesBar.getComponentCount() - 1); filesBar.remove(c); filesBar.add(c, 0); } filesBar.updateUI(); } //GEN-LAST:event_imageButtonActionPerformed private void fileRightActionPerformed() { //GEN-FIRST:event_fileRightActionPerformed if (filesBar.getComponentCount() > 1) { Component c = filesBar.getComponent(0); filesBar.remove(c); filesBar.add(c); } filesBar.updateUI(); } //methods dealing with Key Events private void handleBackspace() { // TODO: This sucks, clean it up Element elem; int pos = editor.getCaretPosition(); ExtendedHTMLDocument htmlDoc = (ExtendedHTMLDocument) editor.getStyledDocument(); try { if (pos > 0) { if ((editor.getSelectedText()) != null) { ExtendedHTMLEditorKit.delete(editor); return; } int sOffset = htmlDoc.getParagraphElement(pos).getStartOffset(); if (sOffset == editor.getSelectionStart()) { boolean content = true; if (ExtendedHTMLEditorKit.checkParentsTag(htmlDoc .getParagraphElement(editor.getCaretPosition()), HTML.Tag.LI)) { elem = ExtendedHTMLEditorKit.getListItemParent(htmlDoc .getCharacterElement(editor .getCaretPosition())); content = false; int so = elem.getStartOffset(); int eo = elem.getEndOffset(); if ((so + 1) < eo) { char[] temp = editor.getText(so, eo - so).toCharArray(); for (int i = 0; i < temp.length; i++) { if (!Character.isWhitespace(temp[i])) { content = true; } } } if (!content) { elem.getParentElement(); ExtendedHTMLEditorKit.removeTag(editor, elem, true); editor.setCaretPosition(sOffset - 1); return; } editor.setCaretPosition(editor.getCaretPosition() - 1); editor.moveCaretPosition(editor.getCaretPosition() - 2); editor.replaceSelection(""); return; } } editor.replaceSelection(""); return; } } catch (BadLocationException ble) { Logging.errorPrint(ble.getMessage(), ble); } } private void handleEnter() { // TODO: this sucks. clean it up Element elem; int pos = editor.getCaretPosition(); int repos = -1; ExtendedHTMLDocument htmlDoc = (ExtendedHTMLDocument) editor.getStyledDocument(); try { if (ExtendedHTMLEditorKit.checkParentsTag(htmlDoc .getParagraphElement(editor.getCaretPosition()), HTML.Tag.UL) || ExtendedHTMLEditorKit.checkParentsTag(htmlDoc .getParagraphElement(editor.getCaretPosition()), HTML.Tag.OL)) { elem = ExtendedHTMLEditorKit.getListItemParent(htmlDoc .getCharacterElement(editor.getCaretPosition())); int so = elem.getStartOffset(); int eo = elem.getEndOffset(); char[] temp = editor.getText(so, eo - so).toCharArray(); boolean content = false; for (int i = 0; i < temp.length; i++) { if (!Character.isWhitespace(temp[i])) { content = true; } } if (content) { int end = -1; int j = temp.length; do { j--; if (Character.isLetterOrDigit(temp[j])) { end = j; } } while ((end == -1) && (j >= 0)); j = end; do { j++; if (!Character.isSpaceChar(temp[j])) { repos = j - end - 1; } } while ((repos == -1) && (j < temp.length)); if (repos == -1) { repos = 0; } } if ((elem.getStartOffset() == elem.getEndOffset()) || !content) { manageListElement(htmlDoc); } else { if ((editor.getCaretPosition() + 1) == elem.getEndOffset()) { ExtendedHTMLEditorKit.insertListElement(editor, ""); editor.setCaretPosition(pos - repos); } else { int caret = editor.getCaretPosition(); String tempString = editor.getText(caret, eo - caret); editor.select(caret, eo - 1); editor.replaceSelection(""); ExtendedHTMLEditorKit.insertListElement(editor, tempString); Element newLi = ExtendedHTMLEditorKit.getListItemParent(htmlDoc .getCharacterElement(editor .getCaretPosition())); editor.setCaretPosition(newLi.getEndOffset()); } } } } catch (BadLocationException ble) { Logging.errorPrint(ble.getMessage(), ble); } } //GEN-LAST:event_revertButtonActionPerformed private void imageButtonActionPerformed() { //GEN-FIRST:event_imageButtonActionPerformed try { insertLocalImage(null); } catch (Exception e) { Logging.errorPrint(e.getMessage(), e); } } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ private void initComponents() { //GEN-BEGIN:initComponents jSplitPane1 = new FlippingSplitPane(); jScrollPane1 = new JScrollPane(); notesTree = new JTree(); jPanel1 = new JPanel(); jScrollPane2 = new JScrollPane(); editor = new JTextPane(); jPanel2 = new JPanel(); fileBar = new JToolBar(); newButton = new JButton(); saveButton = new JButton(); exportButton = new JButton(); revertButton = new JButton(); deleteButton = new JButton(); clipboardBar = new JToolBar(); cutButton = new JButton(); copyButton = new JButton(); pasteButton = new JButton(); formatBar = new JToolBar(); sizeCB = new JComboBox(); boldButton = new JButton(); italicButton = new JButton(); underlineButton = new JButton(); colorButton = new JButton(); bulletButton = new JButton(); enumButton = new JButton(); imageButton = new JButton(); alignmentBar = new JToolBar(); leftJustifyButton = new JButton(); centerJustifyButton = new JButton(); rightJustifyButton = new JButton(); filePane = new JPanel(); fileLeft = new JButton(); fileRight = new JButton(); filesBar = new JToolBar(); setLayout(new java.awt.BorderLayout()); jSplitPane1.setDividerLocation(175); jSplitPane1.setDividerSize(5); jScrollPane1.setViewportView(notesTree); jSplitPane1.setLeftComponent(jScrollPane1); jPanel1.setLayout(new java.awt.BorderLayout()); editor.addCaretListener(this::editorCaretUpdate); jScrollPane2.setViewportView(editor); jPanel1.add(jScrollPane2, java.awt.BorderLayout.CENTER); jPanel2.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 0, 0)); newButton.setIcon(Icons.stock_new.getImageIcon()); newButton.setToolTipText("New Node"); newButton.setBorder(new EtchedBorder()); newButton.setEnabled(false); newButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { newButtonActionPerformed(); } }); fileBar.add(newButton); saveButton.setIcon(Icons.stock_save.getImageIcon()); saveButton.setToolTipText("Save Node"); saveButton.setBorder(new EtchedBorder()); saveButton.setEnabled(false); saveButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { saveButtonActionPerformed(); } }); fileBar.add(saveButton); exportButton.setIcon(Icons.stock_export.getImageIcon()); exportButton.setToolTipText("Export"); exportButton.setBorder(new EtchedBorder()); exportButton.setEnabled(false); exportButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { exportButtonActionPerformed(); } }); fileBar.add(exportButton); revertButton.setIcon(Icons.stock_revert.getImageIcon()); revertButton.setToolTipText("Revert to Saved"); revertButton.setBorder(new EtchedBorder()); revertButton.setEnabled(false); revertButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { revertButtonActionPerformed(); } }); fileBar.add(revertButton); deleteButton.setIcon(Icons.stock_broken_image.getImageIcon()); deleteButton.setToolTipText("Delete Node"); deleteButton.setBorder(new EtchedBorder()); deleteButton.setEnabled(false); deleteButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { deleteButtonActionPerformed(); } }); fileBar.add(deleteButton); jPanel2.add(fileBar); cutButton.setIcon(Icons.stock_cut.getImageIcon()); cutButton.setToolTipText("Cut"); cutButton.setBorder(new EtchedBorder()); cutButton.addActionListener(this::cutButtonActionPerformed); clipboardBar.add(cutButton); copyButton.setIcon(Icons.stock_copy.getImageIcon()); copyButton.setToolTipText("Copy"); copyButton.setBorder(new EtchedBorder()); copyButton.addActionListener(this::copyButtonActionPerformed); clipboardBar.add(copyButton); pasteButton.setIcon(Icons.stock_paste.getImageIcon()); pasteButton.setToolTipText("Paste"); pasteButton.setBorder(new EtchedBorder()); pasteButton.addActionListener(this::pasteButtonActionPerformed); clipboardBar.add(pasteButton); jPanel2.add(clipboardBar); sizeCB.setToolTipText("Size"); sizeCB.setBorder(new EtchedBorder()); sizeCB.addActionListener(this::sizeCBActionPerformed); formatBar.add(sizeCB); boldButton.setIcon(Icons.stock_text_bold.getImageIcon()); boldButton.setToolTipText("Bold"); boldButton.setBorder(new EtchedBorder()); boldButton.addActionListener(this::boldButtonActionPerformed); formatBar.add(boldButton); italicButton.setIcon(Icons.stock_text_italic.getImageIcon()); italicButton.setToolTipText("Italic"); italicButton.setBorder(new EtchedBorder()); italicButton.addActionListener(this::italicButtonActionPerformed); formatBar.add(italicButton); underlineButton.setIcon(Icons.stock_text_underline.getImageIcon()); underlineButton.setToolTipText("Underline"); underlineButton.setBorder(new EtchedBorder()); underlineButton.addActionListener(this::underlineButtonActionPerformed); formatBar.add(underlineButton); colorButton.setForeground(new java.awt.Color(0, 0, 0)); colorButton.setIcon(Icons.createImageIcon("menu-mode-RGB-alt.png")); colorButton.setToolTipText("Color"); colorButton.setBorder(new EtchedBorder()); colorButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { colorButtonActionPerformed(); } }); formatBar.add(colorButton); bulletButton.setIcon(Icons.stock_list_bulet.getImageIcon()); bulletButton.setToolTipText("Bulleted List"); bulletButton.setAction(actionListUnordered); bulletButton.setBorder(new EtchedBorder()); formatBar.add(bulletButton); enumButton.setIcon(Icons.stock_list_enum.getImageIcon()); enumButton.setToolTipText("Numbered List"); enumButton.setAction(actionListOrdered); enumButton.setBorder(new EtchedBorder()); formatBar.add(enumButton); imageButton.setIcon(Icons.stock_insert_graphic.getImageIcon()); imageButton.setBorder(new EtchedBorder()); imageButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { imageButtonActionPerformed(); } }); formatBar.add(imageButton); jPanel2.add(formatBar); leftJustifyButton.setIcon(Icons.stock_text_align_left.getImageIcon()); leftJustifyButton.setToolTipText("Left Justify"); leftJustifyButton.setBorder(new EtchedBorder()); leftJustifyButton.addActionListener(this::leftJustifyButtonActionPerformed); alignmentBar.add(leftJustifyButton); centerJustifyButton.setIcon(Icons.stock_text_align_center.getImageIcon()); centerJustifyButton.setToolTipText("Center"); centerJustifyButton.setBorder(new EtchedBorder()); centerJustifyButton .addActionListener(this::centerJustifyButtonActionPerformed); alignmentBar.add(centerJustifyButton); rightJustifyButton.setIcon(Icons.stock_text_align_right.getImageIcon()); rightJustifyButton.setToolTipText("Right Justify"); rightJustifyButton.setBorder(new EtchedBorder()); rightJustifyButton .addActionListener(this::rightJustifyButtonActionPerformed); alignmentBar.add(rightJustifyButton); jPanel2.add(alignmentBar); jPanel1.add(jPanel2, java.awt.BorderLayout.NORTH); filePane.setLayout(new BoxLayout(filePane, BoxLayout.X_AXIS)); fileLeft.setText("<"); fileLeft.setBorder(new EtchedBorder()); fileLeft.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { fileLeftActionPerformed(); } }); filePane.add(fileLeft); fileRight.setText(">"); fileRight.setBorder(new EtchedBorder()); fileRight.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent evt) { fileRightActionPerformed(); } }); filePane.add(fileRight); filePane.add(filesBar); jPanel1.add(filePane, java.awt.BorderLayout.SOUTH); jSplitPane1.setRightComponent(jPanel1); add(jSplitPane1, java.awt.BorderLayout.CENTER); } private void initDnDComponents() { filesBarDT = new DropTarget(filesBar, new DropBarListener()); treeDT = new DropTarget(notesTree, new DropTreeListener()); } private void initEditingComponents() { bulletButton.setIcon(Icons.stock_list_bulet.getImageIcon()); bulletButton.setToolTipText("Bulleted List"); enumButton.setIcon(Icons.stock_list_enum.getImageIcon()); enumButton.setToolTipText("Numbered List"); enumButton.setText(""); bulletButton.setText(""); Vector<String> fontVector = new Vector<>(); fontVector.add("8"); fontVector.add("10"); fontVector.add("12"); fontVector.add("14"); fontVector.add("16"); fontVector.add("18"); fontVector.add("24"); fontVector.add("36"); fontVector.add("48"); DefaultComboBoxModel cbModel = new DefaultComboBoxModel(fontVector); sizeCB.setModel(cbModel); sizeCB.setBorder(new EtchedBorder(EtchedBorder.LOWERED)); stdButton(boldButton); stdButton(italicButton); stdButton(underlineButton); stdButton(colorButton); stdButton(leftJustifyButton); stdButton(centerJustifyButton); stdButton(rightJustifyButton); stdButton(newButton); stdButton(saveButton); stdButton(deleteButton); stdButton(cutButton); stdButton(copyButton); stdButton(pasteButton); } private void initFileBar(List<File> files) { filePane.removeAll(); filesBar.removeAll(); if (!files.isEmpty()) { filePane.add(fileLeft); filePane.add(fileRight); filePane.add(filesBar); for (File f : files) { filesBar.add(new JIcon(f, plugin)); } } filePane.updateUI(); } private void initLogging() { LogUtilities.inst().addReceiver(new NotesLogReciever()); } //Initialization methods private void initTree() { dataDir.listFiles(); root = new NotesTreeNode(dataDir.getName(), dataDir, notesTree); TreeModel model = new DefaultTreeModel(root); notesTree.setModel(model); notesTree.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent evt) { notesTreeActionPerformed(); } }); notesTree.getSelectionModel().setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION); notesTree.setEditable(true); model.addTreeModelListener(new TreeModelListener() { @Override public void treeNodesChanged(TreeModelEvent e) { notesTreeNodesChanged(); } @Override public void treeNodesInserted(TreeModelEvent e) { // TODO: Method does nothing? } @Override public void treeNodesRemoved(TreeModelEvent e) { // TODO: Method does nothing? } @Override public void treeStructureChanged(TreeModelEvent e) { // TODO: Method does nothing? } }); } //Image insertion methods /** * Method for inserting an image from a file * *@param whatImage pointer to file *@exception IOException if the file can't be read *@exception BadLocationException if the file does not exist *@exception RuntimeException cause */ private void insertLocalImage(File whatImage) throws IOException, BadLocationException, RuntimeException { File image = whatImage; if (whatImage == null) { File dir = getCurrentDir(); File newImage = getImageFromChooser(dir.getPath(), extsIMG, "Image File"); //null possible if user cancelled if (newImage != null && newImage.exists()) { image = new File(dir.getAbsolutePath() + File.separator + newImage.getName()); if (!image.exists()) { MiscUtilities.copy(newImage, image); } } } if (image != null) { int caretPos = editor.getCaretPosition(); ExtendedHTMLEditorKit htmlKit = (ExtendedHTMLEditorKit) editor.getEditorKit(); ExtendedHTMLDocument htmlDoc = (ExtendedHTMLDocument) editor.getStyledDocument(); htmlKit.insertHTML(htmlDoc, caretPos, "<IMG SRC=\"" + image + "\">", 0, 0, HTML.Tag.IMG); editor.setCaretPosition(caretPos + 1); } } //GEN-LAST:event_underlineButtonActionPerformed private void italicButtonActionPerformed(java.awt.event.ActionEvent evt) { //GEN-FIRST:event_italicButtonActionPerformed performTextPaneAction("font-italic", evt); } //GEN-LAST:event_centerJustifyButtonActionPerformed private void leftJustifyButtonActionPerformed(java.awt.event.ActionEvent evt) { //GEN-FIRST:event_leftJustifyButtonActionPerformed Action action = new StyledEditorKit.AlignmentAction("Left Justify", StyleConstants.ALIGN_LEFT); action.actionPerformed(evt); editor.grabFocus(); int cp = editor.getCaretPosition(); updateButtons(editor, cp); } private void manageListElement(ExtendedHTMLDocument htmlDoc) { Element h = ExtendedHTMLEditorKit.getListItemParent(htmlDoc .getCharacterElement(editor.getCaretPosition())); h.getParentElement(); ExtendedHTMLEditorKit.removeTag(editor, h, true); } //GEN-LAST:event_saveButtonActionPerformed private void newButtonActionPerformed() { //GEN-FIRST:event_newButtonActionPerformed Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; node.createChild(); } refreshTree(); } //Tree Events private void notesTreeActionPerformed() { // Add your handling code here: refreshTreeNodes(); } //GEN-LAST:event_boldButtonActionPerformed private void pasteButtonActionPerformed(java.awt.event.ActionEvent evt) { //GEN-FIRST:event_pasteButtonActionPerformed performTextPaneAction(DefaultEditorKit.pasteAction, evt); } //Gui methods private void refreshTreeNodes() { Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; editor = node.getTextPane(); root.checkCache(); // TODO: Uh-oh -- never call gc manually without strong reason // Runtime.getRuntime().gc(); JViewport vp = new JViewport(); vp.setView(editor); jScrollPane2.setViewport(vp); editAreaDT = new DropTarget(editor, new DropEditorListener()); editor.addCaretListener(this::editorCaretUpdate); editor.addKeyListener(new java.awt.event.KeyListener() { @Override public void keyTyped(KeyEvent e) { editorKeyTyped(e); } @Override public void keyPressed(KeyEvent e) { // TODO: Method does nothing? } @Override public void keyReleased(KeyEvent e) { // TODO: Method does nothing? } }); editor.getStyledDocument().addUndoableEditListener( this::editorUndoableEditHappened); if (node.isLeaf()) { deleteButton.setEnabled(true); } else { deleteButton.setEnabled(false); } if (node == root) { exportButton.setEnabled(false); } else { exportButton.setEnabled(true); } if (node.isDirty()) { revertButton.setEnabled(true); } else { revertButton.setEnabled(false); } initFileBar(node.getFiles()); saveButton.setEnabled(true); newButton.setEnabled(true); } else if (obj == null) { deleteButton.setEnabled(false); saveButton.setEnabled(false); revertButton.setEnabled(false); newButton.setEnabled(false); } } //GEN-LAST:event_exportButtonActionPerformed private void revertButtonActionPerformed() { //GEN-FIRST:event_revertButtonActionPerformed Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; node.revert(); refreshTreeNodes(); notesTree.updateUI(); } } //GEN-LAST:event_sizeCBActionPerformed private void rightJustifyButtonActionPerformed( java.awt.event.ActionEvent evt) { //GEN-FIRST:event_rightJustifyButtonActionPerformed Action action = new StyledEditorKit.AlignmentAction("Right Justify", StyleConstants.ALIGN_RIGHT); action.actionPerformed(evt); editor.grabFocus(); int cp = editor.getCaretPosition(); updateButtons(editor, cp); } //GEN-LAST:event_deleteButtonActionPerformed private void saveButtonActionPerformed() { //GEN-FIRST:event_saveButtonActionPerformed Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; node.save(); } revertButton.setEnabled(false); notesTree.updateUI(); } //GEN-LAST:event_fileLeftActionPerformed private void sizeCBActionPerformed(final ActionEvent evt) { //GEN-FIRST:event_sizeCBActionPerformed if (sizeCB.hasFocus()) { String fontS = (String) sizeCB.getSelectedItem(); performTextPaneAction("font-size-" + fontS, evt); } } //GEN-LAST:event_colorButtonActionPerformed private void underlineButtonActionPerformed(java.awt.event.ActionEvent evt) { //GEN-FIRST:event_underlineButtonActionPerformed performTextPaneAction("font-underline", evt); } /** * This is an abstract drop listener. Extend this to listen for drop events * for a particular Component */ public abstract class DropListener extends DropTargetAdapter { /** * Checks to see if dragEnter is supported for the actions on this event * Accepts only javaFileListFlavor data flavors * *@param dtde DropTargetDragEvent */ @Override public void dragEnter(DropTargetDragEvent dtde) { if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { dtde.acceptDrag(dtde.getDropAction()); } else { dtde.rejectDrag(); } } /** * Accepts a drag over if the data flavor is javaFileListFlavor, otherwise * rejects it. * *@param dtde DropTargetDragEvent */ @Override public void dragOver(DropTargetDragEvent dtde) { if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { dtde.acceptDrag(dtde.getDropAction()); } else { dtde.rejectDrag(); } } /** * implements a drop. you need to implements this in your class. * *@param dtde DropTargetDropEvent */ @Override public abstract void drop(DropTargetDropEvent dtde); } /** * Drop listener for the File bar on the bottom of the Notes screen */ public class DropBarListener extends DropListener { /** * implements drop.if we accept it, pass the event to the currently selected * node * *@param dtde DropTargetDropEvent */ @Override public void drop(DropTargetDropEvent dtde) { Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { dtde.dropComplete(node.handleDropJavaFileList(dtde)); refreshTreeNodes(); } else { dtde.rejectDrop(); } } else { dtde.rejectDrop(); } } } /** * Drop listener for the Editor pane on the notes screen */ public class DropEditorListener extends DropListener { /** * Determines if a file passed in is an image or not (based on extension * *@param image File to check *@return true if image, false if not */ public boolean isImageFile(File image) { for (int i = 0; i < extsIMG.length; i++) { if (image.getName().endsWith(extsIMG[i])) { return true; } } return false; } /** * implements drop. if we accept it, pass the event to the handler * *@param dtde Description of the Parameter */ @Override public void drop(DropTargetDropEvent dtde) { Object obj = notesTree.getLastSelectedPathComponent(); if (obj instanceof NotesTreeNode) { if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { dtde.dropComplete(handleDropJavaFileListAsImage(dtde)); refreshTreeNodes(); } else { dtde.rejectDrop(); } } else { dtde.rejectDrop(); } } /** * handles a drop. if the drop is an image, it will insert the image to the * proper place in the editor window. * *@param dtde DropTargetDropEvent *@return drop successful or not */ public boolean handleDropJavaFileListAsImage(DropTargetDropEvent dtde) { dtde.acceptDrop(dtde.getDropAction()); Transferable t = dtde.getTransferable(); try { List<File> fileList = ((List<File>) t .getTransferData(DataFlavor.javaFileListFlavor)); File dir = getCurrentDir(); for (int i = 0; i < fileList.size(); i++) { File newFile = fileList.get(i); if (newFile.exists()) { File destFile = new File(dir.getAbsolutePath() + File.separator + newFile.getName()); if (!isImageFile(destFile) || !destFile.exists()) { MiscUtilities.copy(newFile, destFile); } editor.setCaretPosition(editor.viewToModel(dtde .getLocation())); handleImageDropInsertion(destFile); } } } catch (Exception e) { Logging.errorPrint(e.getMessage(), e); return false; } return true; } /** * Inserts a dropped image into the editor pane * *@param image File to insert */ public void handleImageDropInsertion(File image) { for (String s : extsIMG) { if (image.getName().endsWith(s)) { try { insertLocalImage(image); } catch (Exception e) { Logging.errorPrint(e.getMessage(), e); } break; } } } } /** * Drop listener for the Tree */ public class DropTreeListener extends DropListener { /** * implements drop.if we accept it, pass the event to the currently selected * node * *@param dtde Description of the Parameter */ @Override public void drop(DropTargetDropEvent dtde) { Point p = dtde.getLocation(); TreePath path = notesTree.getPathForLocation(p.x, p.y); if (path == null) { dtde.rejectDrop(); return; } Object obj = path.getLastPathComponent(); if (obj instanceof NotesTreeNode) { NotesTreeNode node = (NotesTreeNode) obj; if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { dtde.dropComplete(node.handleDropJavaFileList(dtde)); refreshTreeNodes(); } else { dtde.rejectDrop(); } } else { dtde.rejectDrop(); } } } public class NotesLogReciever implements LogReceiver { NotesTreeNode log; public NotesLogReciever() { // Empty Constructor } /** * Logs a message associated with a specific owner. * * @param owner the owner of the message being logged. * @param message the message to log. */ @Override public void logMessage(String owner, String message) { if (log == null) { log = getChildNode("Logs", root); } NotesTreeNode node = getChildNode(owner, log); // TODO add option DateFormat dateFmt = // new SimpleDateFormat("MM-dd-yyyy hh.mm.ss a z"); DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); node.appendText("<br>"+Constants.LINE_SEPARATOR+"<b>" + dateFmt.format(Calendar.getInstance().getTime()) + "</b> " + message); } /** * Logs a message not associated with a specific owner. * * @param message the message to log. */ @Override public void logMessage(String message) { logMessage("Misc", message); } private NotesTreeNode getChildNode(String name, NotesTreeNode parentNode) { Enumeration<MutableTreeNode> newNodes = parentNode.children(); for (; newNodes.hasMoreElements();) { NotesTreeNode node = (NotesTreeNode) newNodes.nextElement(); if (node.getUserObject().equals(NotesTreeNode.checkName(name))) { return node; } } return parentNode.createChild(name); } } /** * Action implementing Redo for editor */ protected class RedoAction extends AbstractAction { /** Constructor for the RedoAction object */ public RedoAction() { super(getLocalizedRedo()); setEnabled(false); } /** * Redo Action is preformed, run undo on the undo manager * *@param e Action Event */ @Override public void actionPerformed(ActionEvent e) { try { undo.redo(); } catch (CannotRedoException ex) { Logging.errorPrint("Unable to redo: " + ex); } updateRedoState(); undoAction.updateUndoState(); } /** Update the current state of the redo labe */ protected void updateRedoState() { if (undo.canRedo()) { setEnabled(true); putValue(Action.NAME, undo.getRedoPresentationName()); } else { setEnabled(false); putValue(Action.NAME, getLocalizedRedo()); } } } private static String getLocalizedRedo() { return LanguageBundle.getString("in_mnuEditRedo"); //$NON-NLS-1$ } //Internal Classes /** * Action implementing Undo for editor */ protected class UndoAction extends AbstractAction { /** Constructor for the UndoAction object */ public UndoAction() { super(getLocalizedUndo()); setEnabled(false); } /** * Undo Action is preformed, run undo on the undo manager. * *@param e Action Event */ @Override public void actionPerformed(ActionEvent e) { try { undo.undo(); } catch (CannotUndoException ex) { Logging.errorPrint("Unable to undo: " + ex.getMessage(), ex); } updateUndoState(); redoAction.updateRedoState(); } /** Update the current state of the undo label */ protected void updateUndoState() { if (undo.canUndo()) { setEnabled(true); putValue(Action.NAME, undo.getUndoPresentationName()); } else { setEnabled(false); putValue(Action.NAME, getLocalizedUndo()); } } } private static String getLocalizedUndo() { return LanguageBundle.getString("in_mnuEditUndo"); //$NON-NLS-1$ } }