/**
* 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);
}
}
}
}