/** * Copyright 1999-2009 The Pegadi Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.pegadi.artis; // Pegadi imports import com.kitfox.svg.SVGCache; import com.kitfox.svg.app.beans.SVGIcon; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.*; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.event.*; import javax.swing.text.*; import javax.swing.text.DefaultEditorKit.CopyAction; import javax.swing.text.DefaultEditorKit.CutAction; import javax.swing.text.DefaultEditorKit.PasteAction; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; import java.awt.*; import java.awt.event.*; import java.beans.PropertyVetoException; import java.net.URI; import java.util.Enumeration; import java.util.Locale; import java.util.ResourceBundle; import java.util.Vector; public class NotesEditor extends Editor { public static ResourceBundle strings; Element xml; JDesktopPane desktop; JComboBox notesCombo; Vector notes; InternalFrameListener frameListener; AbstractAction undoAction; AbstractAction redoAction; LayoutManager tabLayout; JButton undoButton, redoButton, boldButton; private final Logger log = LoggerFactory.getLogger(getClass()); public NotesEditor(Element xml) { this(xml, null); } public NotesEditor(Element xml, Artis artis) { super(xml, artis); /* undoAction = new AbstractAction("undo") { public void actionPerformed(ActionEvent e) { ((NoteInternalFrame)notes.get(notesCombo.getSelectedIndex())).getUndoAction().actionPerformed(e); } }; redoAction = new AbstractAction("redo") { public void actionPerformed(ActionEvent e) { ((NoteInternalFrame)notes.get(notesCombo.getSelectedIndex())).getRedoAction().actionPerformed(e); } };*/ } /* public void undoAction_ActionPerformed() { log.debug("Undo!"); } public void redoAction_ActionPerformed() { log.debug("Redo!"); } */ public void closeAllNotes() { notesCombo.removeAllItems(); Enumeration e = notes.elements(); while(e.hasMoreElements()) ((NoteInternalFrame) e.nextElement()).dispose(); notes.removeAllElements(); } public AbstractAction[] getEditActions() { return new AbstractAction[]{ undoAction, redoAction }; } public void addFocusListener(FocusListener l) { listenerList.add(FocusListener.class,l); } protected void createUI(Locale loc) { super.createUI(loc); strings = ResourceBundle.getBundle("org.pegadi.artis.NotesEditorStrings",loc); setLayout(new BorderLayout()); desktop = new JDesktopPane(); desktop.setPreferredSize(new Dimension((int)desktop.getPreferredSize().getHeight() ,getWidth())); //add(desktop, BorderLayout.CENTER); add(new JScrollPane(desktop, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED),BorderLayout.CENTER); notesCombo = new JComboBox(); notesCombo.setPreferredSize(new Dimension(120,0)); notesCombo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { notesCombo_actionPerformed(e); } }); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout( new BoxLayout(buttonPanel,BoxLayout.X_AXIS)); buttonPanel.setBorder(new EmptyBorder(new Insets(0,0,0,0))); JPanel toolPanel = new JPanel(); toolPanel.setLayout(new BorderLayout()); JButton newButton = new JButton(new ImageIcon(getClass().getResource(strings.getString("button_new_icon")))); newButton.setMargin(new Insets(0,0,0,0)); newButton.setToolTipText(strings.getString("button_new_tooltip")); newButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { newButton_actionPerformed(); } }); JButton orderButton = new JButton(new ImageIcon(getClass().getResource(strings.getString("button_order_icon")))); orderButton.setMargin(new Insets(0,0,0,0)); orderButton.setToolTipText(strings.getString("button_order_tooltip")); orderButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { orderButton_actionPerformed(e); } }); undoButton = new JButton(new ImageIcon(getClass().getResource(strings.getString("undo_icon")))); undoButton.setMargin(new Insets(0,0,0,0)); undoButton.setToolTipText(strings.getString("action_undo")); undoButton.addActionListener( new AbstractAction("undo") { public void actionPerformed(ActionEvent e) { ((NoteInternalFrame)notes.get(notesCombo.getSelectedIndex())).getUndoAction().actionPerformed(e); } } ); redoButton = new JButton(new ImageIcon(getClass().getResource(strings.getString("redo_icon")))); redoButton.setMargin(new Insets(0,0,0,0)); redoButton.setToolTipText(strings.getString("action_redo")); redoButton.addActionListener( new AbstractAction("redo") { public void actionPerformed(ActionEvent e) { ((NoteInternalFrame)notes.get(notesCombo.getSelectedIndex())).getRedoAction().actionPerformed(e); } } ); Action boldAction = new StyledEditorKit.BoldAction(); boldAction.putValue(AbstractAction.SMALL_ICON, new ImageIcon(getClass().getResource(strings.getString("icon_bold")))); boldAction.putValue(Action.ACCELERATOR_KEY, javax.swing.KeyStroke.getKeyStroke((java.awt.event.KeyEvent.VK_B), java.awt.event.KeyEvent.CTRL_MASK, false)); boldAction.putValue(AbstractAction.NAME, ""); // so only the icon shows boldButton = new JButton(boldAction); boldButton.setMargin(new Insets(0,0,0,0)); boldButton.setToolTipText(strings.getString("button_bold_tooltip")); boldButton.setFocusable(false); buttonPanel.add(newButton); buttonPanel.add(orderButton); buttonPanel.add(undoButton); buttonPanel.add(redoButton); buttonPanel.add(boldButton); buttonPanel.setBorder(new EmptyBorder(new Insets(0,0,0,0))); toolPanel.add(buttonPanel,BorderLayout.WEST); toolPanel.add(notesCombo,BorderLayout.EAST); add(toolPanel, BorderLayout.NORTH); frameListener = new InternalFrameAdapter() { public void internalFrameClosing(InternalFrameEvent e) { int reallyClose = JOptionPane.showConfirmDialog(null, strings.getString("really_delete"), strings.getString("really_delete_title"), JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE); if(reallyClose==JOptionPane.YES_OPTION) { notesCombo.removeItem(e.getSource()); ((JInternalFrame)e.getSource()).dispose(); notes.removeElement(e.getSource()); setChanged(true); } } public void internalFrameActivated(InternalFrameEvent e){ notesCombo.setSelectedItem(e.getSource()); } }; } public JComboBox getNotesCombo(){ return notesCombo; } public JButton getUndoButton() { return undoButton; } public JButton getRedoButton() { return redoButton; } public void notesCombo_actionPerformed(ActionEvent ae){ JInternalFrame jif = (JInternalFrame) notesCombo.getSelectedItem(); if(jif!=null){ try{ jif.setSelected(true); } catch(PropertyVetoException pve){ log.error("Error setting Notes combobox selected", pve); } if(((NoteInternalFrame)jif).getUndoManager().canUndo()) { undoButton.setEnabled(true); } else undoButton.setEnabled(false); if (((NoteInternalFrame)jif).getUndoManager().canRedo()) { redoButton.setEnabled(true); } else redoButton.setEnabled(false); } } public void newTab(JInternalFrame jif){ notesCombo.addItem(jif); } public void newButton_actionPerformed() { JInternalFrame jif = new NoteInternalFrame(this); jif.setBounds(30,30,200,240); notes.addElement(jif); desktop.add(jif,JLayeredPane.DEFAULT_LAYER); try { jif.setMaximum(true); } catch (java.beans.PropertyVetoException ex) { log.error("Error maximizing NoteInternalFrame", ex); } jif.addInternalFrameListener(frameListener); jif.setVisible(true); } public void orderButton_actionPerformed(ActionEvent e) { int x = 20; int y = 20; for(int i = 0;i<notes.size();i++) { NoteInternalFrame nif = (NoteInternalFrame)notes.elementAt(i); // Ensure the notes are not iconified or maximized try { nif.setIcon(false); nif.setMaximum(false); nif.setSelected(true); } catch (java.beans.PropertyVetoException ex) { log.error("Error setting proper NoteInternalFrame properties for ordering", ex); } nif.moveToFront(); nif.setBounds(x,y,200,240); y+=25; x+=25; } } public synchronized void updateXML() { Document doc = mElement.getOwnerDocument(); // Remove all old nodes from root XML element. while (mElement.getChildNodes().getLength() > 0) { mElement.removeChild(mElement.getFirstChild()); } // Iterate all frames and add an element for each for(int i = 0;i<notes.size();i++) { NoteInternalFrame nif = (NoteInternalFrame)notes.elementAt(i); Element el = nif.getElement(doc); mElement.appendChild(el); } } //FIXME public int getLength() { return 0; } /** * Returns an icon representing this editor. * * @return The icon. * @see org.pegadi.artis.Editor#getDisplayIcon() */ public ImageIcon getDisplayIcon() { return new ImageIcon(getClass().getResource(NotesEditor.strings.getString("note_icon"))); } public void setXML(Element mElement) { this.mElement=mElement; notes = new Vector(); // Fetch all the <note> elements NodeList notesList = mElement.getElementsByTagName("note"); // Iterate the list and add each note for(int i=0;i<notesList.getLength();i++) { if(notesList.item(i) instanceof Element) { Element e = (Element)notesList.item(i); NoteInternalFrame nif = new NoteInternalFrame(this); int x = Integer.parseInt(e.getAttribute("x")); int y = Integer.parseInt(e.getAttribute("y")); int w = Integer.parseInt(e.getAttribute("w")); int h = Integer.parseInt(e.getAttribute("h")); nif.setBounds(x,y,w,h); // Read and add the text NodeList paragraphs = e.getElementsByTagName("p"); SimpleAttributeSet boldAttributes = new SimpleAttributeSet(); boldAttributes.addAttribute(StyleConstants.CharacterConstants.Bold, Boolean.TRUE); for(int p = 0;p<paragraphs.getLength();p++) { if(paragraphs.item(p) instanceof Element) { Element pe = (Element)paragraphs.item(p); NodeList childNodes = pe.getChildNodes(); for(int cn=0;cn<childNodes.getLength();cn++) { Node node = childNodes.item(cn); if(node instanceof Text) { nif.addText( ((Text) node).getData() , null); } else { NodeList boldNodes = node.getChildNodes(); for(int bn=0; bn<boldNodes.getLength(); bn++) { Node boldNode = boldNodes.item(bn); if(boldNode instanceof Text) { nif.addText( ((Text) boldNode).getData() , boldAttributes ); } } } } } } nif.updateTitle(); nif.addText(""); // Add note frame to desktop and the vector notes.addElement(nif); desktop.add(nif,JLayeredPane.DEFAULT_LAYER); String state = e.getAttribute("state"); nif.addInternalFrameListener(frameListener); nif.setVisible(true); try { if(state.equals("icon")) { if(!System.getProperty("java.version").startsWith("1.1")) nif.setIcon(true); } else if(state.equals("maximum")) nif.setMaximum(true); } catch (java.beans.PropertyVetoException ex) { log.error("Error setting notesFrame propterties", ex); } } } // Select the note shat was last added notesCombo.setSelectedIndex(notesCombo.getItemCount()-1); } public JMenu getMenu() { return null; } protected void cutPerformed(ActionEvent param1) { } protected void pastePerformed(ActionEvent parm1) { //TODO: implement this pegadi.artis.Editor abstract method } protected void copyPerformed(ActionEvent parm1) { //TODO: implement this pegadi.artis.Editor abstract method } public Action getInsertAction() { return null; } public String getDisplayName() { return strings.getString("display_name"); } public void fireNoteGotFocus(FocusEvent event) { Object[] listeners = listenerList.getListenerList(); for (Object listener : listeners) { if (listener instanceof FocusListener) { ((FocusListener) listener).focusGained(event); } } } } class NoteInternalFrame extends JInternalFrame { JTextPane textArea; DefaultStyledDocument document; /** The undo manager */ UndoManager undo; UndoAction undoAction; RedoAction redoAction; NotesEditor editor; JPopupMenu rightClickMenu; private final Logger log = LoggerFactory.getLogger(getClass()); public UndoManager getUndoManager() { return undo; } public UndoAction getUndoAction() { return undoAction; } public RedoAction getRedoAction() { return redoAction; } public NoteInternalFrame(NotesEditor editor) { super(); this.editor = editor; document = new DefaultStyledDocument(); textArea = new JTextPane(document); textArea.setDragEnabled(true); textArea.getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent e) { setChanged(); updateTitle(); } public void insertUpdate(DocumentEvent e) { setChanged(); updateTitle(); } public void removeUpdate(DocumentEvent e) { setChanged(); updateTitle(); } }); textArea.addKeyListener(new KeyAdapter() { public void keyTyped(KeyEvent e) { updateTitle(); } }); rightClickMenu = new JPopupMenu(); CutAction cutAction = new CutAction(); CopyAction copyAction = new CopyAction(); PasteAction pasteAction = new PasteAction(); URI iconURI = SVGCache.getSVGUniverse().loadSVG(getClass().getResource(NotesEditor.strings.getString("icon_cut"))); SVGIcon cutIcon = new SVGIcon(); cutIcon.setSvgURI(iconURI); cutIcon.setAntiAlias(true); cutIcon.setPreferredSize(new Dimension(16,16)); cutIcon.setScaleToFit(true); iconURI = SVGCache.getSVGUniverse().loadSVG(getClass().getResource(NotesEditor.strings.getString("icon_copy"))); SVGIcon copyIcon = new SVGIcon(); copyIcon.setSvgURI(iconURI); copyIcon.setAntiAlias(true); copyIcon.setPreferredSize(new Dimension(16,16)); copyIcon.setScaleToFit(true); iconURI = SVGCache.getSVGUniverse().loadSVG(getClass().getResource(NotesEditor.strings.getString("icon_paste"))); SVGIcon pasteIcon = new SVGIcon(); pasteIcon.setSvgURI(iconURI); pasteIcon.setAntiAlias(true); pasteIcon.setPreferredSize(new Dimension(16,16)); pasteIcon.setScaleToFit(true); //cutAction.putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource(NotesEditor.strings.getString("icon_cut") ))); cutAction.putValue(Action.SMALL_ICON, cutIcon); //copyAction.putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource(NotesEditor.strings.getString("icon_copy") ))); copyAction.putValue(Action.SMALL_ICON, copyIcon); //pasteAction.putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource(NotesEditor.strings.getString("icon_paste") ))); pasteAction.putValue(Action.SMALL_ICON, pasteIcon); cutAction.putValue(Action.ACCELERATOR_KEY, javax.swing.KeyStroke.getKeyStroke((java.awt.event.KeyEvent.VK_X), java.awt.event.KeyEvent.CTRL_MASK, false)); copyAction.putValue(Action.ACCELERATOR_KEY, javax.swing.KeyStroke.getKeyStroke((java.awt.event.KeyEvent.VK_C), java.awt.event.KeyEvent.CTRL_MASK, false)); pasteAction.putValue(Action.ACCELERATOR_KEY, javax.swing.KeyStroke.getKeyStroke((java.awt.event.KeyEvent.VK_V), java.awt.event.KeyEvent.CTRL_MASK, false)); JMenuItem cut = new JMenuItem(cutAction); JMenuItem copy = new JMenuItem(copyAction); JMenuItem paste = new JMenuItem(pasteAction); cut.setText(NotesEditor.strings.getString("action_cut")); copy.setText(NotesEditor.strings.getString("action_copy")); paste.setText(NotesEditor.strings.getString("action_paste")); rightClickMenu.add(cut); rightClickMenu.add(copy); rightClickMenu.add(paste); textArea.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if(e.isPopupTrigger()) { rightClickMenu.show(e.getComponent(), e.getX(), e.getY()); } } public void mouseReleased(MouseEvent e) { if(e.isPopupTrigger()) { rightClickMenu.show(e.getComponent(), e.getX(), e.getY()); } } }); setTitle(NotesEditor.strings.getString("empty_header")); textArea.setBackground(new Color(255,255,95)); textArea.setBorder(new EmptyBorder(2,2,2,2)); setResizable(true); setClosable(true); setMaximizable(true); setIconifiable(true); setResizable(true); getContentPane().add(new JScrollPane(textArea)); setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE); this.setFrameIcon(new ImageIcon(getClass().getResource(NotesEditor.strings.getString("note_icon")))); undo = new UndoManager(); undoAction = new UndoAction("Undo"); redoAction = new RedoAction("Redo"); undoAction.putValue(AbstractAction.ACCELERATOR_KEY,javax.swing.KeyStroke.getKeyStroke((java.awt.event.KeyEvent.VK_Z), java.awt.event.KeyEvent.CTRL_MASK, false)); redoAction.putValue(AbstractAction.ACCELERATOR_KEY,javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.KeyEvent.CTRL_MASK + java.awt.event.KeyEvent.SHIFT_MASK, false)); textArea.getDocument().addUndoableEditListener(new MyUndoableEditListener()); editor.newTab(this); } public String toString(){ return getTitle(); } public void setChanged() { this.editor.setChanged(true); } public void textArea_focusGained(FocusEvent e) { editor.fireNoteGotFocus(e); } public void setText(String text) { textArea.setText(text); } public void addText(String text) { addText(text, null); } public void addText(String text, AttributeSet styles) { try { document.insertString(document.getLength(), text, styles); } catch(BadLocationException ble) { log.error("Bad location", ble); } } public Element getElement(Document doc) { Element e = doc.createElement("note"); // First, set the type state if(isIcon()) e.setAttribute("state","icon"); else if(isMaximum()) e.setAttribute("state","maximum"); else e.setAttribute("state","normal"); // Store location, width and height String x = Integer.toString(getLocation().x); String y = Integer.toString(getLocation().y); String w = Integer.toString(getWidth()); String h = Integer.toString(getHeight()); e.setAttribute("x",x); e.setAttribute("y",y); e.setAttribute("w",w); e.setAttribute("h",h); // Create xml from the StyledDocument javax.swing.text.Element root = document.getDefaultRootElement(); int start = 0; int end = 0; for(int i=0; i<root.getElementCount(); i++) { javax.swing.text.Element ce = root.getElement(i); while(!(ce.getElement(0).isLeaf())) { ce = ce.getElement(0); } Element paragraph = doc.createElement("p"); for(int j=0; j<ce.getElementCount(); j++) { javax.swing.text.Element leaf = ce.getElement(j); AttributeSet attributes = leaf.getAttributes(); start = end; end = leaf.getEndOffset(); Text text = null; try { text = doc.createTextNode(document.getText(start, (end-start))); } catch(BadLocationException ble) { log.error("Bad location", ble); } if(attributes.containsAttribute(StyleConstants.CharacterConstants.Bold, Boolean.TRUE)) { Element bold = doc.createElement("b"); bold.appendChild(text); paragraph.appendChild(bold); } else { paragraph.appendChild(text); } } e.appendChild(paragraph); } return e; } public void updateTitle() { String title = textArea.getText().trim(); int firstLB = title.indexOf("\n"); if(firstLB==-1) { if(title.length()>20) title = title.substring(0,20); } else { title = title.substring(0,firstLB); if(firstLB>20) title = title.substring(0,20); } if(title.length()==0){ title = NotesEditor.strings.getString("empty_header"); } setTitle(title); //this is to make sure that the text also gets updatet in the combobox editor.getNotesCombo().repaint(); } //This one listens for edits that can be undone. protected class MyUndoableEditListener implements UndoableEditListener { public void undoableEditHappened(UndoableEditEvent e) { //Remember the edit and update the menus. undo.addEdit(e.getEdit()); undoAction.updateUndoState(); redoAction.updateRedoState(); } } class UndoAction extends AbstractAction { String origName; JButton button; public UndoAction(String name) { super(name); setEnabled(false); this.origName = name; } public void actionPerformed(ActionEvent e) { try { undo.undo(); } catch (CannotUndoException ex) { log.error("Undo error", ex); } updateUndoState(); redoAction.updateRedoState(); } protected void updateUndoState() { if (undo.canUndo()) { editor.getUndoButton().setEnabled(true); } else { editor.getUndoButton().setEnabled(false); } } } class RedoAction extends AbstractAction { String origName; JButton button; public RedoAction(String name) { super(name); setEnabled(false); origName = name; } public void actionPerformed(ActionEvent e) { try { undo.redo(); } catch (CannotRedoException ex) { log.error("Redo error", ex); } updateRedoState(); undoAction.updateUndoState(); } protected void updateRedoState() { if (undo.canRedo()) { editor.getRedoButton().setEnabled(true); } else { editor.getRedoButton().setEnabled(false); } } } }