From b6b7ed68de1501987bbd2041de9a581a692a44ab Mon Sep 17 00:00:00 2001 From: Jeremy Laine Date: Thu, 19 Aug 2004 02:06:45 +0000 Subject: [PATCH] tentative merge of EKIT_0_9H and custom patches --- ekit/MakeEkit.ant | 5 +- ekit/MakeEkitApplet.ant | 1 + ekit/RunEkitApplet.csh | 1 - ekit/com/hexidec/ekit/Ekit.java | 6 +- ekit/com/hexidec/ekit/EkitApplet.java | 117 +- ekit/com/hexidec/ekit/EkitCore.java | 615 ++++- ekit/com/hexidec/ekit/EkitCore_Basic.java | 2836 ------------------- ekit/com/hexidec/ekit/EkitCore_Spell.java | 2879 -------------------- .../ekit/component/ExtendedHTMLEditorKit.java | 27 +- .../hexidec/ekit/component/RelativeImageView.java | 1891 +++++++------ ekit/com/hexidec/util/CMUS.proposal.txt | 1 - ekit/com/hexidec/util/CMUScodec.java | 1 - ekit/com/hexidec/util/Cartesian.java | 137 - ekit/com/hexidec/util/DMCScodec.java | 1 - ekit/com/hexidec/util/MutableFilter.java | 77 - ekit/com/hexidec/util/PatternReplacer.java | 78 - .../util/TreeSpider$FileTreeComparator.class | Bin 1055 -> 0 bytes ekit/com/hexidec/util/TreeSpider.class | Bin 7832 -> 0 bytes ekit/com/hexidec/util/TreeSpider.java | 1 - ekit/ekit.jar | Bin 125051 -> 114365 bytes ekit/ekitapplet.jar | Bin 124396 -> 113683 bytes 21 files changed, 1580 insertions(+), 7094 deletions(-) delete mode 100644 ekit/RunEkitApplet.csh delete mode 100644 ekit/com/hexidec/ekit/EkitCore_Basic.java delete mode 100644 ekit/com/hexidec/ekit/EkitCore_Spell.java delete mode 100644 ekit/com/hexidec/util/CMUS.proposal.txt delete mode 100644 ekit/com/hexidec/util/CMUScodec.java delete mode 100644 ekit/com/hexidec/util/Cartesian.java delete mode 100644 ekit/com/hexidec/util/DMCScodec.java delete mode 100644 ekit/com/hexidec/util/MutableFilter.java delete mode 100644 ekit/com/hexidec/util/PatternReplacer.java delete mode 100644 ekit/com/hexidec/util/TreeSpider$FileTreeComparator.class delete mode 100644 ekit/com/hexidec/util/TreeSpider.class delete mode 100644 ekit/com/hexidec/util/TreeSpider.java diff --git a/ekit/MakeEkit.ant b/ekit/MakeEkit.ant index aa9a739..56a656a 100644 --- a/ekit/MakeEkit.ant +++ b/ekit/MakeEkit.ant @@ -5,11 +5,13 @@ + + @@ -22,13 +24,14 @@ destfile="ekit.jar" basedir="." manifest="com/hexidec/ekit/ekit.manifest" - includes="com/hexidec/ekit/*.class,com/hexidec/ekit/action/*.class,com/hexidec/ekit/component/*.class,com/hexidec/ekit/icons/*.gif,com/hexidec/ekit/*.properties,com/hexidec/util/Translatrix.class"/> + includes="com/hexidec/ekit/*.class,com/hexidec/ekit/action/*.class,com/hexidec/ekit/component/*.class,com/hexidec/ekit/icons/*.gif,com/hexidec/ekit/*.properties,com/hexidec/util/Base64Codec.class,com/hexidec/util/Translatrix.class"/> + diff --git a/ekit/MakeEkitApplet.ant b/ekit/MakeEkitApplet.ant index 6eadb11..cb485a2 100644 --- a/ekit/MakeEkitApplet.ant +++ b/ekit/MakeEkitApplet.ant @@ -5,6 +5,7 @@ + diff --git a/ekit/RunEkitApplet.csh b/ekit/RunEkitApplet.csh deleted file mode 100644 index dd3bb47..0000000 --- a/ekit/RunEkitApplet.csh +++ /dev/null @@ -1 +0,0 @@ -appletviewer EkitAppletDemo.html diff --git a/ekit/com/hexidec/ekit/Ekit.java b/ekit/com/hexidec/ekit/Ekit.java index 15c7c6d..33f1e94 100644 --- a/ekit/com/hexidec/ekit/Ekit.java +++ b/ekit/com/hexidec/ekit/Ekit.java @@ -38,7 +38,7 @@ import javax.swing.JPanel; import javax.swing.JToolBar; import com.hexidec.ekit.EkitCore; -import com.hexidec.ekit.EkitCoreSpell; +//import com.hexidec.ekit.EkitCoreSpell; /** Ekit * App for editing and saving HTML in a Java text component @@ -77,7 +77,7 @@ public class Ekit extends JFrame implements WindowListener { if(useSpellChecker) { - ekitCore = new EkitCoreSpell(sDocument, sStyleSheet, sRawDocument, urlStyleSheet, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, debugMode, true, multiBar); +// ekitCore = new EkitCoreSpell(sDocument, sStyleSheet, sRawDocument, urlStyleSheet, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, debugMode, true, multiBar); } else { @@ -265,4 +265,4 @@ public class Ekit extends JFrame implements WindowListener } } -} \ No newline at end of file +} diff --git a/ekit/com/hexidec/ekit/EkitApplet.java b/ekit/com/hexidec/ekit/EkitApplet.java index e7e9201..b67aa53 100644 --- a/ekit/com/hexidec/ekit/EkitApplet.java +++ b/ekit/com/hexidec/ekit/EkitApplet.java @@ -2,7 +2,7 @@ GNU Lesser General Public License EkitApplet - Java Swing HTML Editor & Viewer Applet -Copyright (C) 2000-2003 Howard Kistler +Copyright (C) 2000 Howard Kistler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -22,6 +22,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package com.hexidec.ekit; import java.awt.BorderLayout; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; import java.awt.Toolkit; import java.awt.event.KeyEvent; import java.net.MalformedURLException; @@ -48,8 +50,6 @@ public class EkitApplet extends JApplet /* Components */ EkitCore ekitCore; - private JLabel jlblStatus; - /** Constructor */ public EkitApplet() @@ -77,7 +77,9 @@ public class EkitApplet extends JApplet murle.printStackTrace(System.err); } boolean showToolBar = true; + boolean showToolBarMulti = false; if(this.getParameter("TOOLBAR") != null) { showToolBar = this.getParameter("TOOLBAR").equalsIgnoreCase("true"); } + if(this.getParameter("TOOLBARMULTI") != null) { showToolBar = this.getParameter("TOOLBARMULTI").equalsIgnoreCase("true"); showToolBarMulti = this.getParameter("TOOLBARMULTI").equalsIgnoreCase("true"); } boolean showViewSource = ((this.getParameter("SOURCEVIEW") != null && this.getParameter("SOURCEVIEW").equalsIgnoreCase("true"))); String sLanguage = this.getParameter("LANGCODE"); String sCountry = this.getParameter("LANGCOUNTRY"); @@ -85,8 +87,17 @@ public class EkitApplet extends JApplet if(this.getParameter("EXCLUSIVE") != null) { editModeExclusive = this.getParameter("EXCLUSIVE").equalsIgnoreCase("true"); } boolean showMenuIcons = true; if(this.getParameter("MENUICONS") != null) { showMenuIcons = this.getParameter("MENUICONS").equalsIgnoreCase("true"); } + boolean spellChecker = false; + if(this.getParameter("SPELLCHECK") != null) { spellChecker = this.getParameter("SPELLCHECK").equalsIgnoreCase("true"); } - ekitCore = new EkitCore(sRawDocument, urlCSS, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, this.getParameter("BASEURL")); + if(spellChecker) + { + //ekitCore = new EkitCoreSpell(sRawDocument, urlCSS, showToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, showToolBarMulti, this.getParameter("BASEURL")); + } + else + { + ekitCore = new EkitCore(sRawDocument, urlCSS, showToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false, showToolBarMulti, this.getParameter("BASEURL")); + } /* Obtain image servlet information */ ekitCore.setServletURL(this.getParameter("SERVLETURL")); @@ -107,38 +118,78 @@ public class EkitApplet extends JApplet if(this.getParameter("MENU_HELP") != null) { if(this.getParameter("MENU_HELP").equalsIgnoreCase("true")) { vcMenus.add(EkitCore.KEY_MENU_HELP); } } else { vcMenus.add(EkitCore.KEY_MENU_HELP); } this.setJMenuBar(ekitCore.getCustomMenuBar(vcMenus)); - jlblStatus = new JLabel(); - /* Add the components to the app */ - this.getContentPane().setLayout(new BorderLayout()); - this.getContentPane().add(ekitCore, BorderLayout.CENTER); - this.getContentPane().add(jlblStatus, BorderLayout.SOUTH); if(showToolBar) { - Vector vcTools = new Vector(); - vcTools.add(EkitCore.KEY_TOOL_NEW); - vcTools.add(EkitCore.KEY_TOOL_SEP); - vcTools.add(EkitCore.KEY_TOOL_CUT); - vcTools.add(EkitCore.KEY_TOOL_COPY); - vcTools.add(EkitCore.KEY_TOOL_PASTE); - vcTools.add(EkitCore.KEY_TOOL_SEP); - vcTools.add(EkitCore.KEY_TOOL_BOLD); - vcTools.add(EkitCore.KEY_TOOL_ITALIC); - vcTools.add(EkitCore.KEY_TOOL_UNDERLINE); - vcTools.add(EkitCore.KEY_TOOL_STRIKE); - vcTools.add(EkitCore.KEY_TOOL_SUPER); - vcTools.add(EkitCore.KEY_TOOL_SUB); - vcTools.add(EkitCore.KEY_TOOL_ULIST); - vcTools.add(EkitCore.KEY_TOOL_OLIST); - vcTools.add(EkitCore.KEY_TOOL_SEP); - vcTools.add(EkitCore.KEY_TOOL_CLEAR); - vcTools.add(EkitCore.KEY_TOOL_SEP); - vcTools.add(EkitCore.KEY_TOOL_ANCHOR); - vcTools.add(EkitCore.KEY_TOOL_SEP); - vcTools.add(EkitCore.KEY_TOOL_SOURCE); - vcTools.add(EkitCore.KEY_TOOL_SEP); - vcTools.add(EkitCore.KEY_TOOL_STYLES); - this.getContentPane().add(ekitCore.getCustomToolBar(vcTools, showToolBar), BorderLayout.NORTH); + if(showToolBarMulti) + { + this.getContentPane().setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.NORTH; + gbc.gridheight = 1; + gbc.gridwidth = 1; + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridx = 1; + + gbc.gridy = 1; + Vector vcTools = new Vector(); + vcTools.add(EkitCore.KEY_TOOL_NEW); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_CUT); + vcTools.add(EkitCore.KEY_TOOL_COPY); + vcTools.add(EkitCore.KEY_TOOL_PASTE); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_CLEAR); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_ANCHOR); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_SOURCE); + this.getContentPane().add(ekitCore.customizeToolBar(EkitCore.TOOLBAR_MAIN, vcTools, showToolBar), gbc); + + gbc.gridy = 2; + this.getContentPane().add(ekitCore.getToolBarFormat(showToolBar), gbc); + + gbc.gridy = 3; + this.getContentPane().add(ekitCore.getToolBarStyles(showToolBar), gbc); + + gbc.anchor = GridBagConstraints.CENTER; + gbc.fill = GridBagConstraints.BOTH; + gbc.weighty = 1.0; + gbc.gridy = 4; + this.getContentPane().add(ekitCore, gbc); + } + else + { + Vector vcTools = new Vector(); + vcTools.add(EkitCore.KEY_TOOL_NEW); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_CUT); + vcTools.add(EkitCore.KEY_TOOL_COPY); + vcTools.add(EkitCore.KEY_TOOL_PASTE); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_BOLD); + vcTools.add(EkitCore.KEY_TOOL_ITALIC); + vcTools.add(EkitCore.KEY_TOOL_UNDERLINE); + vcTools.add(EkitCore.KEY_TOOL_STRIKE); + vcTools.add(EkitCore.KEY_TOOL_SUPER); + vcTools.add(EkitCore.KEY_TOOL_SUB); + vcTools.add(EkitCore.KEY_TOOL_ULIST); + vcTools.add(EkitCore.KEY_TOOL_OLIST); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_CLEAR); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_ANCHOR); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_SOURCE); + vcTools.add(EkitCore.KEY_TOOL_SEP); + vcTools.add(EkitCore.KEY_TOOL_STYLES); + + this.getContentPane().setLayout(new BorderLayout()); + this.getContentPane().add(ekitCore, BorderLayout.CENTER); + this.getContentPane().add(ekitCore.customizeToolBar(EkitCore.TOOLBAR_MAIN, vcTools, showToolBar), BorderLayout.NORTH); + } } } diff --git a/ekit/com/hexidec/ekit/EkitCore.java b/ekit/com/hexidec/ekit/EkitCore.java index 07df9cb..87aef09 100644 --- a/ekit/com/hexidec/ekit/EkitCore.java +++ b/ekit/com/hexidec/ekit/EkitCore.java @@ -2,7 +2,7 @@ GNU Lesser General Public License EkitCore - Base Java Swing HTML Editor & Viewer Class (Basic Version) -Copyright (C) 2000-2003 Howard Kistler +Copyright (C) 2000 Howard Kistler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -57,6 +57,7 @@ import java.util.ResourceBundle; import java.util.Vector; import javax.swing.AbstractAction; import javax.swing.Action; +import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -68,6 +69,7 @@ import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JFileChooser; import javax.swing.JFrame; +import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; @@ -75,11 +77,13 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; +import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.KeyStroke; +import javax.swing.border.*; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.DocumentEvent; @@ -94,6 +98,7 @@ import javax.swing.text.DefaultEditorKit; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Document; import javax.swing.text.Element; +import javax.swing.text.JTextComponent; import javax.swing.text.PlainDocument; import javax.swing.text.Position; import javax.swing.text.Style; @@ -118,7 +123,7 @@ import com.hexidec.util.Translatrix; * Main application class for editing and saving HTML in a Java text component * * @author Howard Kistler - * @version 0.9g + * @version 0.9h * * REQUIREMENTS * Java 2 (JDK 1.3 or 1.4) @@ -133,12 +138,13 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc private ExtendedHTMLEditorKit htmlKit; private ExtendedHTMLDocument htmlDoc; private StyleSheet styleSheet; - private JTextPane jtpSource; + private JTextArea jtpSource; private JScrollPane jspSource; private JToolBar jToolBar; - private JCheckBoxMenuItem jcbmiViewToolbar; - private JCheckBoxMenuItem jcbmiViewSource; + private JToolBar jToolBarMain; + private JToolBar jToolBarFormat; + private JToolBar jToolBarStyles; private JButtonNoFocus jbtnNewHTML; private JButtonNoFocus jbtnOpenHTML; @@ -191,9 +197,17 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc private JMenu jMenuTable; private JMenu jMenuForms; private JMenu jMenuSearch; + private JMenu jMenuTools; private JMenu jMenuHelp; private JMenu jMenuDebug; + private JMenu jMenuToolbars; + private JCheckBoxMenuItem jcbmiViewToolbar; + private JCheckBoxMenuItem jcbmiViewToolbarMain; + private JCheckBoxMenuItem jcbmiViewToolbarFormat; + private JCheckBoxMenuItem jcbmiViewToolbarStyles; + private JCheckBoxMenuItem jcbmiViewSource; + /* Constants */ // Menu Keys public static final String KEY_MENU_FILE = "file"; @@ -230,6 +244,10 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc public static final String KEY_TOOL_SOURCE = "viewsource"; public static final String KEY_TOOL_STYLES = "styleselect"; + public static final int TOOLBAR_MAIN = 0; + public static final int TOOLBAR_FORMAT = 1; + public static final int TOOLBAR_STYLES = 2; + // Menu & Tool Key Arrays private static Hashtable htMenus = new Hashtable(); private static Hashtable htTools = new Hashtable(); @@ -255,6 +273,7 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc private boolean lastSearchTopSetting = false; private File currentFile = null; + private String imageChooserStartDir = "."; private int indent = 0; private final int indentStep = 4; @@ -277,7 +296,9 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc * @param sDocument [String] A text or HTML document to load in the editor upon startup. * @param sStyleSheet [String] A CSS stylesheet to load in the editor upon startup. * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. + * @param sdocSource [StyledDocument] Optional document specification, using javax.swing.text.StyledDocument. * @param urlStyleSheet [URL] A URL reference to the CSS style sheet. + * @param includeToolBar [boolean] Specifies whether the app should include the toolbar(s). * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). @@ -285,8 +306,10 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. * @param base64 [boolean] Specifies whether the raw document is Base64 encoded or not. * @param debugMode [boolean] Specifies whether to show the Debug menu or not. + * @param hasSpellChecker [boolean] Specifies whether or not this uses the SpellChecker module + * @param multiBar [boolean] Specifies whether to use multiple toolbars or one big toolbar. */ - public EkitCore(String sDocument, String sStyleSheet, String sRawDocument, URL urlStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean debugMode, String baseurl) + public EkitCore(String sDocument, String sStyleSheet, String sRawDocument, StyledDocument sdocSource, URL urlStyleSheet, boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean debugMode, boolean hasSpellChecker, boolean multiBar, String baseurl) { super(); @@ -342,10 +365,19 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc jtpMain.addKeyListener(this); /* Create the source text area */ - jtpSource = new JTextPane(); + if(sdocSource == null) + { + jtpSource = new JTextArea(); + jtpSource.setText(jtpMain.getText()); + } + else + { + jtpSource = new JTextArea(sdocSource); + jtpMain.setText(jtpSource.getText()); + } jtpSource.setBackground(new Color(212, 212, 212)); jtpSource.setSelectionColor(new Color(255, 192, 192)); - jtpSource.setText(jtpMain.getText()); + jtpSource.setMargin(new Insets(4, 4, 4, 4)); jtpSource.getDocument().addDocumentListener(this); /* Add CaretListener for tracking caret location events */ @@ -505,10 +537,41 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc JMenuItem jmiSelWord = new JMenuItem((Action)actions.get(DefaultEditorKit.selectWordAction)); jmiSelWord.setText(Translatrix.getTranslationString("SelectWord")); jMenuEdit.add(jmiSelWord); /* VIEW Menu */ - jMenuView = new JMenu(Translatrix.getTranslationString("View")); + jMenuView = new JMenu(Translatrix.getTranslationString("View")); htMenus.put(KEY_MENU_VIEW, jMenuView); - jcbmiViewToolbar = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewToolbar"), false); jcbmiViewToolbar.setActionCommand("toggletoolbar"); jcbmiViewToolbar.addActionListener(this); jMenuView.add(jcbmiViewToolbar); - jcbmiViewSource = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewSource"), false); jcbmiViewSource.setActionCommand("viewsource"); jcbmiViewSource.addActionListener(this); jMenuView.add(jcbmiViewSource); + if(includeToolBar) + { + if(multiBar) + { + jMenuToolbars = new JMenu(Translatrix.getTranslationString("ViewToolbars")); + + jcbmiViewToolbarMain = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewToolbarMain"), false); + jcbmiViewToolbarMain.setActionCommand("toggletoolbarmain"); + jcbmiViewToolbarMain.addActionListener(this); + jMenuToolbars.add(jcbmiViewToolbarMain); + + jcbmiViewToolbarFormat = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewToolbarFormat"), false); + jcbmiViewToolbarFormat.setActionCommand("toggletoolbarformat"); + jcbmiViewToolbarFormat.addActionListener(this); + jMenuToolbars.add(jcbmiViewToolbarFormat); + + jcbmiViewToolbarStyles = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewToolbarStyles"), false); + jcbmiViewToolbarStyles.setActionCommand("toggletoolbarstyles"); + jcbmiViewToolbarStyles.addActionListener(this); + jMenuToolbars.add(jcbmiViewToolbarStyles); + + jMenuView.add(jMenuToolbars); + } + else + { + jcbmiViewToolbar = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewToolbar"), false); + jcbmiViewToolbar.setActionCommand("toggletoolbar"); + jcbmiViewToolbar.addActionListener(this); + + jMenuView.add(jcbmiViewToolbar); + } + } + jcbmiViewSource = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewSource"), false); jcbmiViewSource.setActionCommand("viewsource"); jcbmiViewSource.addActionListener(this); jMenuView.add(jcbmiViewSource); /* FONT Menu */ jMenuFont = new JMenu(Translatrix.getTranslationString("Font")); @@ -603,9 +666,10 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc jMenuInsert = new JMenu(Translatrix.getTranslationString("Insert")); htMenus.put(KEY_MENU_INSERT, jMenuInsert); jMenuInsert.add(new JMenuItem(actionInsertAnchor)); - JMenuItem jmiBreak = new JMenuItem(Translatrix.getTranslationString("InsertBreak")); jmiBreak.setActionCommand("insertbreak"); jmiBreak.addActionListener(this); jmiBreak.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, java.awt.Event.SHIFT_MASK, false)); jMenuInsert.add(jmiBreak); - JMenuItem jmiNBSP = new JMenuItem(Translatrix.getTranslationString("InsertNBSP")); jmiNBSP.setActionCommand("insertnbsp"); jmiNBSP.addActionListener(this); jMenuInsert.add(jmiNBSP); - JMenuItem jmiHRule = new JMenuItem((Action)actions.get("InsertHR")); jmiHRule.setText(Translatrix.getTranslationString("InsertHorizontalRule")); jMenuInsert.add(jmiHRule); + JMenuItem jmiSymbol = new JMenuItem(Translatrix.getTranslationString("InsertSymbol")); jmiSymbol.setActionCommand("insertsymbol"); jmiSymbol.addActionListener(this); jMenuInsert.add(jmiSymbol); + JMenuItem jmiBreak = new JMenuItem(Translatrix.getTranslationString("InsertBreak")); jmiBreak.setActionCommand("insertbreak"); jmiBreak.addActionListener(this); jmiBreak.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, java.awt.Event.SHIFT_MASK, false)); jMenuInsert.add(jmiBreak); + JMenuItem jmiNBSP = new JMenuItem(Translatrix.getTranslationString("InsertNBSP")); jmiNBSP.setActionCommand("insertnbsp"); jmiNBSP.addActionListener(this); jMenuInsert.add(jmiNBSP); + JMenuItem jmiHRule = new JMenuItem((Action)actions.get("InsertHR")); jmiHRule.setText(Translatrix.getTranslationString("InsertHorizontalRule")); jMenuInsert.add(jmiHRule); jMenuInsert.addSeparator(); JMenuItem jmiImageLocal = new JMenuItem(Translatrix.getTranslationString("InsertLocalImage") + menuDialog); jmiImageLocal.setActionCommand("insertlocalimage"); jmiImageLocal.addActionListener(this); jMenuInsert.add(jmiImageLocal); JMenuItem jmiImageServer = new JMenuItem(Translatrix.getTranslationString("InsertServerImage") + menuDialog); jmiImageServer.setActionCommand("insertserverimage"); jmiImageServer.addActionListener(this); jMenuInsert.add(jmiImageServer); @@ -636,6 +700,14 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc JMenuItem jmiFormButtonSubmit = new JMenuItem(Translatrix.getTranslationString("FormButtonSubmit")); jmiFormButtonSubmit.setActionCommand("insertbuttonsubmit"); jmiFormButtonSubmit.addActionListener(this); jMenuForms.add(jmiFormButtonSubmit); JMenuItem jmiFormButtonReset = new JMenuItem(Translatrix.getTranslationString("FormButtonReset")); jmiFormButtonReset.setActionCommand("insertbuttonreset"); jmiFormButtonReset.addActionListener(this); jMenuForms.add(jmiFormButtonReset); + /* TOOLS Menu */ + if(hasSpellChecker) + { + jMenuTools = new JMenu(Translatrix.getTranslationString("Tools")); + htMenus.put(KEY_MENU_TOOLS, jMenuTools); + JMenuItem jmiSpellcheck = new JMenuItem(Translatrix.getTranslationString("ToolSpellcheck")); jmiSpellcheck.setActionCommand("spellcheck"); jmiSpellcheck.addActionListener(this); jMenuTools.add(jmiSpellcheck); + } + /* SEARCH Menu */ jMenuSearch = new JMenu(Translatrix.getTranslationString("Search")); htMenus.put(KEY_MENU_SEARCH, jMenuSearch); @@ -666,38 +738,183 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc jMenuBar.add(jMenuInsert); jMenuBar.add(jMenuTable); jMenuBar.add(jMenuForms); + if(jMenuTools != null) { jMenuBar.add(jMenuTools); } jMenuBar.add(jMenuHelp); if(debugMode) { jMenuBar.add(jMenuDebug); } + /* Create toolbar tool objects */ + jbtnNewHTML = new JButtonNoFocus(getEkitIcon("New")); + jbtnNewHTML.setToolTipText(Translatrix.getTranslationString("NewDocument")); + jbtnNewHTML.setActionCommand("newdoc"); + jbtnNewHTML.addActionListener(this); + htTools.put(KEY_TOOL_NEW, jbtnNewHTML); + jbtnOpenHTML = new JButtonNoFocus(getEkitIcon("Open")); + jbtnOpenHTML.setToolTipText(Translatrix.getTranslationString("OpenDocument")); + jbtnOpenHTML.setActionCommand("openhtml"); + jbtnOpenHTML.addActionListener(this); + htTools.put(KEY_TOOL_OPEN, jbtnOpenHTML); + jbtnSaveHTML = new JButtonNoFocus(getEkitIcon("Save")); + jbtnSaveHTML.setToolTipText(Translatrix.getTranslationString("SaveDocument")); + jbtnSaveHTML.setActionCommand("saveas"); + jbtnSaveHTML.addActionListener(this); + htTools.put(KEY_TOOL_SAVE, jbtnSaveHTML); + jbtnCut = new JButtonNoFocus(new DefaultEditorKit.CutAction()); + jbtnCut.setIcon(getEkitIcon("Cut")); + jbtnCut.setText(null); + jbtnCut.setToolTipText(Translatrix.getTranslationString("Cut")); + htTools.put(KEY_TOOL_CUT, jbtnCut); + jbtnCopy = new JButtonNoFocus(new DefaultEditorKit.CopyAction()); + jbtnCopy.setIcon(getEkitIcon("Copy")); + jbtnCopy.setText(null); + jbtnCopy.setToolTipText(Translatrix.getTranslationString("Copy")); + htTools.put(KEY_TOOL_COPY, jbtnCopy); + jbtnPaste = new JButtonNoFocus(new DefaultEditorKit.PasteAction()); + jbtnPaste.setIcon(getEkitIcon("Paste")); + jbtnPaste.setText(null); + jbtnPaste.setToolTipText(Translatrix.getTranslationString("Paste")); + htTools.put(KEY_TOOL_PASTE, jbtnPaste); + jbtnBold = new JButtonNoFocus(actionFontBold); + jbtnBold.setIcon(getEkitIcon("Bold")); + jbtnBold.setText(null); + jbtnBold.setToolTipText(Translatrix.getTranslationString("FontBold")); + htTools.put(KEY_TOOL_BOLD, jbtnBold); + jbtnItalic = new JButtonNoFocus(actionFontItalic); + jbtnItalic.setIcon(getEkitIcon("Italic")); + jbtnItalic.setText(null); + jbtnItalic.setToolTipText(Translatrix.getTranslationString("FontItalic")); + htTools.put(KEY_TOOL_ITALIC, jbtnItalic); + jbtnUnderline = new JButtonNoFocus(actionFontUnderline); + jbtnUnderline.setIcon(getEkitIcon("Underline")); + jbtnUnderline.setText(null); + jbtnUnderline.setToolTipText(Translatrix.getTranslationString("FontUnderline")); + htTools.put(KEY_TOOL_UNDERLINE, jbtnUnderline); + jbtnStrike = new JButtonNoFocus(actionFontStrike); + jbtnStrike.setIcon(getEkitIcon("Strike")); + jbtnStrike.setText(null); + jbtnStrike.setToolTipText(Translatrix.getTranslationString("FontStrike")); + htTools.put(KEY_TOOL_STRIKE, jbtnStrike); + jbtnSuperscript = new JButtonNoFocus(actionFontSuperscript); + jbtnSuperscript.setIcon(getEkitIcon("Super")); + jbtnSuperscript.setText(null); + jbtnSuperscript.setToolTipText(Translatrix.getTranslationString("FontSuperscript")); + htTools.put(KEY_TOOL_SUPER, jbtnSuperscript); + jbtnSubscript = new JButtonNoFocus(actionFontSubscript); + jbtnSubscript.setIcon(getEkitIcon("Sub")); + jbtnSubscript.setText(null); + jbtnSubscript.setToolTipText(Translatrix.getTranslationString("FontSubscript")); + htTools.put(KEY_TOOL_SUB, jbtnSubscript); + jbtnUList = new JButtonNoFocus(actionListUnordered); + jbtnUList.setIcon(getEkitIcon("UList")); + jbtnUList.setText(null); + jbtnUList.setToolTipText(Translatrix.getTranslationString("ListUnordered")); + htTools.put(KEY_TOOL_ULIST, jbtnUList); + jbtnOList = new JButtonNoFocus(actionListOrdered); + jbtnOList.setIcon(getEkitIcon("OList")); + jbtnOList.setText(null); + jbtnOList.setToolTipText(Translatrix.getTranslationString("ListOrdered")); + htTools.put(KEY_TOOL_OLIST, jbtnOList); + jbtnClearFormat = new JButtonNoFocus(actionClearFormat); + jbtnClearFormat.setIcon(getEkitIcon("ClearFormat")); + jbtnClearFormat.setText(null); + jbtnClearFormat.setToolTipText(Translatrix.getTranslationString("FormatClear")); + htTools.put(KEY_TOOL_CLEAR, jbtnClearFormat); + jbtnAnchor = new JButtonNoFocus(actionInsertAnchor); + jbtnAnchor.setIcon(getEkitIcon("Anchor")); + jbtnAnchor.setText(null); + jbtnAnchor.setToolTipText(Translatrix.getTranslationString("ToolAnchor")); + htTools.put(KEY_TOOL_ANCHOR, jbtnAnchor); + jtbtnViewSource = new JToggleButtonNoFocus(getEkitIcon("Source")); + jtbtnViewSource.setText(null); + jtbtnViewSource.setToolTipText(Translatrix.getTranslationString("ViewSource")); + jtbtnViewSource.setActionCommand("viewsource"); + jtbtnViewSource.addActionListener(this); + jtbtnViewSource.setPreferredSize(jbtnAnchor.getPreferredSize()); + jtbtnViewSource.setMinimumSize(jbtnAnchor.getMinimumSize()); + jtbtnViewSource.setMaximumSize(jbtnAnchor.getMaximumSize()); + htTools.put(KEY_TOOL_SOURCE, jtbtnViewSource); + jcmbStyleSelector = new JComboBoxNoFocus(); + jcmbStyleSelector.setToolTipText(Translatrix.getTranslationString("PickCSSStyle")); + jcmbStyleSelector.setAction(new StylesAction(jcmbStyleSelector)); + htTools.put(KEY_TOOL_STYLES, jcmbStyleSelector); + /* Create the toolbar */ - jToolBar = new JToolBar(JToolBar.HORIZONTAL); - jToolBar.setFloatable(false); - jbtnNewHTML = new JButtonNoFocus(getEkitIcon("New")); jbtnNewHTML.setToolTipText(Translatrix.getTranslationString("NewDocument")); jbtnNewHTML.setActionCommand("newdoc"); jbtnNewHTML.addActionListener(this); jToolBar.add(jbtnNewHTML); htTools.put(KEY_TOOL_NEW, jbtnNewHTML); - jbtnOpenHTML = new JButtonNoFocus(getEkitIcon("Open")); jbtnOpenHTML.setToolTipText(Translatrix.getTranslationString("OpenDocument")); jbtnOpenHTML.setActionCommand("openhtml"); jbtnOpenHTML.addActionListener(this); jToolBar.add(jbtnOpenHTML); htTools.put(KEY_TOOL_OPEN, jbtnOpenHTML); - jbtnSaveHTML = new JButtonNoFocus(getEkitIcon("Save")); jbtnSaveHTML.setToolTipText(Translatrix.getTranslationString("SaveDocument")); jbtnSaveHTML.setActionCommand("saveas"); jbtnSaveHTML.addActionListener(this); jToolBar.add(jbtnSaveHTML); htTools.put(KEY_TOOL_SAVE, jbtnSaveHTML); - jToolBar.add(new JToolBar.Separator()); - jbtnCut = new JButtonNoFocus(new DefaultEditorKit.CutAction()); jbtnCut.setIcon(getEkitIcon("Cut")); jbtnCut.setText(null); jbtnCut.setToolTipText(Translatrix.getTranslationString("Cut")); jToolBar.add(jbtnCut); htTools.put(KEY_TOOL_CUT, jbtnCut); - jbtnCopy = new JButtonNoFocus(new DefaultEditorKit.CopyAction()); jbtnCopy.setIcon(getEkitIcon("Copy")); jbtnCopy.setText(null); jbtnCopy.setToolTipText(Translatrix.getTranslationString("Copy")); jToolBar.add(jbtnCopy); htTools.put(KEY_TOOL_COPY, jbtnCopy); - jbtnPaste = new JButtonNoFocus(new DefaultEditorKit.PasteAction()); jbtnPaste.setIcon(getEkitIcon("Paste")); jbtnPaste.setText(null); jbtnPaste.setToolTipText(Translatrix.getTranslationString("Paste")); jToolBar.add(jbtnPaste); htTools.put(KEY_TOOL_PASTE, jbtnPaste); - jToolBar.add(new JToolBar.Separator()); - jbtnBold = new JButtonNoFocus(actionFontBold); jbtnBold.setIcon(getEkitIcon("Bold")); jbtnBold.setText(null); jbtnBold.setToolTipText(Translatrix.getTranslationString("FontBold")); jToolBar.add(jbtnBold); htTools.put(KEY_TOOL_BOLD, jbtnBold); - jbtnItalic = new JButtonNoFocus(actionFontItalic); jbtnItalic.setIcon(getEkitIcon("Italic")); jbtnItalic.setText(null); jbtnItalic.setToolTipText(Translatrix.getTranslationString("FontItalic")); jToolBar.add(jbtnItalic); htTools.put(KEY_TOOL_ITALIC, jbtnItalic); - jbtnUnderline = new JButtonNoFocus(actionFontUnderline); jbtnUnderline.setIcon(getEkitIcon("Underline")); jbtnUnderline.setText(null); jbtnUnderline.setToolTipText(Translatrix.getTranslationString("FontUnderline")); jToolBar.add(jbtnUnderline); htTools.put(KEY_TOOL_UNDERLINE, jbtnUnderline); - jbtnStrike = new JButtonNoFocus(actionFontStrike); jbtnStrike.setIcon(getEkitIcon("Strike")); jbtnStrike.setText(null); jbtnStrike.setToolTipText(Translatrix.getTranslationString("FontStrike")); jToolBar.add(jbtnStrike); htTools.put(KEY_TOOL_STRIKE, jbtnStrike); - jbtnSuperscript = new JButtonNoFocus(actionFontSuperscript); jbtnSuperscript.setIcon(getEkitIcon("Super")); jbtnSuperscript.setText(null); jbtnSuperscript.setToolTipText(Translatrix.getTranslationString("FontSuperscript")); jToolBar.add(jbtnSuperscript); htTools.put(KEY_TOOL_SUPER, jbtnSuperscript); - jbtnSubscript = new JButtonNoFocus(actionFontSubscript); jbtnSubscript.setIcon(getEkitIcon("Sub")); jbtnSubscript.setText(null); jbtnSubscript.setToolTipText(Translatrix.getTranslationString("FontSubscript")); jToolBar.add(jbtnSubscript); htTools.put(KEY_TOOL_SUB, jbtnSubscript); - jbtnUList = new JButtonNoFocus(actionListUnordered); jbtnUList.setIcon(getEkitIcon("UList")); jbtnUList.setText(null); jbtnUList.setToolTipText(Translatrix.getTranslationString("ListUnordered")); jToolBar.add(jbtnUList); htTools.put(KEY_TOOL_ULIST, jbtnUList); - jbtnOList = new JButtonNoFocus(actionListOrdered); jbtnOList.setIcon(getEkitIcon("OList")); jbtnOList.setText(null); jbtnOList.setToolTipText(Translatrix.getTranslationString("ListOrdered")); jToolBar.add(jbtnOList); htTools.put(KEY_TOOL_OLIST, jbtnOList); - jbtnClearFormat = new JButtonNoFocus(actionClearFormat); jbtnClearFormat.setIcon(getEkitIcon("ClearFormat")); jbtnClearFormat.setText(null); jbtnClearFormat.setToolTipText(Translatrix.getTranslationString("FormatClear")); jToolBar.add(jbtnClearFormat); htTools.put(KEY_TOOL_CLEAR, jbtnClearFormat); - jToolBar.add(new JToolBar.Separator()); - jbtnAnchor = new JButtonNoFocus(actionInsertAnchor); jbtnAnchor.setIcon(getEkitIcon("Anchor")); jbtnAnchor.setText(null); jbtnAnchor.setToolTipText(Translatrix.getTranslationString("ToolAnchor")); jToolBar.add(jbtnAnchor); htTools.put(KEY_TOOL_ANCHOR, jbtnAnchor); - jToolBar.add(new JToolBar.Separator()); - jtbtnViewSource = new JToggleButtonNoFocus(getEkitIcon("Source")); jtbtnViewSource.setText(null); jtbtnViewSource.setToolTipText(Translatrix.getTranslationString("ViewSource")); jtbtnViewSource.setActionCommand("viewsource"); jtbtnViewSource.addActionListener(this); jtbtnViewSource.setPreferredSize(jbtnAnchor.getPreferredSize()); jtbtnViewSource.setMinimumSize(jbtnAnchor.getMinimumSize()); jtbtnViewSource.setMaximumSize(jbtnAnchor.getMaximumSize()); jToolBar.add(jtbtnViewSource); htTools.put(KEY_TOOL_SOURCE, jtbtnViewSource); - jToolBar.add(new JToolBar.Separator()); - jcmbStyleSelector = new JComboBoxNoFocus(); jToolBar.add(jcmbStyleSelector); jcmbStyleSelector.setAction(new StylesAction(jcmbStyleSelector)); htTools.put(KEY_TOOL_STYLES, jcmbStyleSelector); + if(multiBar) + { + jToolBarMain = new JToolBar(JToolBar.HORIZONTAL); + jToolBarMain.setFloatable(false); + jToolBarMain.add((JButtonNoFocus)(htTools.get(KEY_TOOL_NEW))); + jToolBarMain.add((JButtonNoFocus)(htTools.get(KEY_TOOL_OPEN))); + jToolBarMain.add((JButtonNoFocus)(htTools.get(KEY_TOOL_SAVE))); + jToolBarMain.add(new JToolBar.Separator()); + jToolBarMain.add((JButtonNoFocus)(htTools.get(KEY_TOOL_CUT))); + jToolBarMain.add((JButtonNoFocus)(htTools.get(KEY_TOOL_COPY))); + jToolBarMain.add((JButtonNoFocus)(htTools.get(KEY_TOOL_PASTE))); + jToolBarMain.add(new JToolBar.Separator()); + jToolBarMain.add((JButtonNoFocus)(htTools.get(KEY_TOOL_CLEAR))); + jToolBarMain.add(new JToolBar.Separator()); + jToolBarMain.add((JButtonNoFocus)(htTools.get(KEY_TOOL_ANCHOR))); + jToolBarMain.add(new JToolBar.Separator()); + jToolBarMain.add((JToggleButtonNoFocus)(htTools.get(KEY_TOOL_SOURCE))); + + jToolBarFormat = new JToolBar(JToolBar.HORIZONTAL); + jToolBarFormat.setFloatable(false); + jToolBarFormat.add((JButtonNoFocus)(htTools.get(KEY_TOOL_BOLD))); + jToolBarFormat.add((JButtonNoFocus)(htTools.get(KEY_TOOL_ITALIC))); + jToolBarFormat.add((JButtonNoFocus)(htTools.get(KEY_TOOL_UNDERLINE))); + jToolBarFormat.add(new JToolBar.Separator()); + jToolBarFormat.add((JButtonNoFocus)(htTools.get(KEY_TOOL_STRIKE))); + jToolBarFormat.add((JButtonNoFocus)(htTools.get(KEY_TOOL_SUPER))); + jToolBarFormat.add((JButtonNoFocus)(htTools.get(KEY_TOOL_SUB))); + jToolBarFormat.add(new JToolBar.Separator()); + jToolBarFormat.add((JButtonNoFocus)(htTools.get(KEY_TOOL_ULIST))); + jToolBarFormat.add((JButtonNoFocus)(htTools.get(KEY_TOOL_OLIST))); + + jToolBarStyles = new JToolBar(JToolBar.HORIZONTAL); + jToolBarStyles.setFloatable(false); + jToolBarStyles.add(new JLabel(Translatrix.getTranslationString("SelectorToolCSSStyles"))); + jToolBarStyles.add((JComboBoxNoFocus)(htTools.get(KEY_TOOL_STYLES))); + + // fix the weird size preference of toggle buttons + jtbtnViewSource.setPreferredSize(jbtnAnchor.getPreferredSize()); + jtbtnViewSource.setMinimumSize(jbtnAnchor.getMinimumSize()); + jtbtnViewSource.setMaximumSize(jbtnAnchor.getMaximumSize()); + } + else + { + jToolBar = new JToolBar(JToolBar.HORIZONTAL); + jToolBar.setFloatable(false); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_NEW))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_OPEN))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_SAVE))); + jToolBar.add(new JToolBar.Separator()); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_CUT))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_COPY))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_PASTE))); + jToolBar.add(new JToolBar.Separator()); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_BOLD))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_ITALIC))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_UNDERLINE))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_STRIKE))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_SUPER))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_SUB))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_ULIST))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_OLIST))); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_CLEAR))); + jToolBar.add(new JToolBar.Separator()); + jToolBar.add((JButtonNoFocus)(htTools.get(KEY_TOOL_ANCHOR))); + jToolBar.add(new JToolBar.Separator()); + jToolBar.add((JToggleButtonNoFocus)(htTools.get(KEY_TOOL_SOURCE))); + jToolBar.add(new JToolBar.Separator()); + jToolBar.add((JComboBoxNoFocus)(htTools.get(KEY_TOOL_STYLES))); + + // fix the weird size preference of toggle buttons + jtbtnViewSource.setPreferredSize(jbtnAnchor.getPreferredSize()); + jtbtnViewSource.setMinimumSize(jbtnAnchor.getMinimumSize()); + jtbtnViewSource.setMaximumSize(jbtnAnchor.getMaximumSize()); + } /* Create the scroll area for the text pane */ JScrollPane jspViewport = new JScrollPane(jtpMain); @@ -730,90 +947,151 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc this.add(jspltDisplay, BorderLayout.CENTER); } + public EkitCore(String sDocument, String sStyleSheet, String sRawDocument, URL urlStyleSheet, boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean debugMode, boolean hasSpellChecker, boolean multiBar, String baseurl) + { + this(sDocument, sStyleSheet, sRawDocument, (StyledDocument)null, urlStyleSheet, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, debugMode, hasSpellChecker, multiBar, baseurl); + } + + public EkitCore(String sDocument, String sStyleSheet, String sRawDocument, URL urlStyleSheet, boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean debugMode, boolean hasSpellChecker, boolean multiBar) + { + this(sDocument, sStyleSheet, sRawDocument, (StyledDocument)null, urlStyleSheet, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, debugMode, hasSpellChecker, multiBar, ""); + } + + public EkitCore(String sDocument, String sStyleSheet, String sRawDocument, URL urlStyleSheet, boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean debugMode, String baseurl) + { + this(sDocument, sStyleSheet, sRawDocument, (StyledDocument)null, urlStyleSheet, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, debugMode, false, false, baseurl); + } + + public EkitCore(String sDocument, String sStyleSheet, String sRawDocument, URL urlStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean debugMode, boolean hasSpellChecker, String baseurl) + { + this(sDocument, sStyleSheet, sRawDocument, (StyledDocument)null, urlStyleSheet, true, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, debugMode, hasSpellChecker, false, baseurl); + } + + public EkitCore(String sDocument, String sStyleSheet, String sRawDocument, URL urlStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean debugMode, String baseurl) + { + this(sDocument, sStyleSheet, sRawDocument, (StyledDocument)null, urlStyleSheet, true, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, debugMode, false, false, baseurl); + } + /** Common Constructor * @param sDocument [String] A text or HTML document to load in the editor upon startup. * @param sStyleSheet [String] A CSS stylesheet to load in the editor upon startup. + * @param includeToolBar [boolean] Specifies whether the app should include the toolbar(s). * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. */ - public EkitCore(String sDocument, String sStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64) + public EkitCore(String sDocument, String sStyleSheet, boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, String baseurl) { - this(sDocument, sStyleSheet, null, null, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false,""); + this(sDocument, sStyleSheet, null, null, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false, baseurl); + } + + /** StyledDocument Constructor With Spellchecker Specifier + * @param sdocSource [StyledDocument] The optional StyledDocument to use as the source Document. + * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. + * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. + * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). + * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. + * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. + * @param debugMode [boolean] Specifies whether to show the Debug menu or not. + * @param hasSpellChecker [boolean] Specifies whether or not this uses the SpellChecker module + */ + public EkitCore(StyledDocument sdocSource, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean debugMode, boolean hasSpellChecker, String baseurl) + { + this(null, null, null, sdocSource, null, true, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, false, debugMode, hasSpellChecker, false, baseurl); + } + + /** StyledDocument Constructor + * @param sdocSource [StyledDocument] The optional StyledDocument to use as the source Document. + * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. + * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. + * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). + * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. + * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. + * @param debugMode [boolean] Specifies whether to show the Debug menu or not. + */ + public EkitCore(StyledDocument sdocSource, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean debugMode, String baseurl) + { + this(null, null, null, sdocSource, null, true, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, false, debugMode, false, false, baseurl); } /** Default Language Constructor * @param sDocument [String] A text or HTML document to load in the editor upon startup. * @param sStyleSheet [String] A CSS stylesheet to load in the editor upon startup. + * @param includeToolBar [boolean] Specifies whether the app should include the toolbar(s). * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). */ - public EkitCore(String sDocument, String sStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, boolean base64) + public EkitCore(String sDocument, String sStyleSheet, boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, boolean base64, String baseurl) { - this(sDocument, sStyleSheet, null, null, showViewSource, showMenuIcons, editModeExclusive, null, null, base64, false, ""); + this(sDocument, sStyleSheet, null, null, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, null, null, base64, false, baseurl); } /** Raw/Base64 Document & Style Sheet URL Constructor (Ideal for EkitApplet) * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. + * @param includeToolBar [boolean] Specifies whether the app should include the toolbar(s). * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. */ - public EkitCore(String sRawDocument, URL urlStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, String baseurl) + public EkitCore(String sRawDocument, URL urlStyleSheet, boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean hasSpellChecker, boolean multiBar, String baseurl) { - this(null, null, sRawDocument, urlStyleSheet, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false, baseurl); + this(null, null, sRawDocument, urlStyleSheet, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false, hasSpellChecker, multiBar, baseurl); } /** Document Constructor * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. + * @param includeToolBar [boolean] Specifies whether the app should include the toolbar(s). * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. */ - public EkitCore(String sRawDocument, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64) + public EkitCore(String sRawDocument, boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, String baseurl) { - this(null, null, sRawDocument, null, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false,""); + this(null, null, sRawDocument, null, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false, baseurl); } /** Default Language & Document Constructor * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. + * @param includeToolBar [boolean] Specifies whether the app should include the toolbar(s). * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). */ - public EkitCore(String sRawDocument, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, boolean base64) + public EkitCore(String sRawDocument, boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, boolean base64, String baseurl) { - this(null, null, sRawDocument, null, showViewSource, showMenuIcons, editModeExclusive, null, null, base64, false,""); + this(null, null, sRawDocument, null, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, null, null, base64, false, baseurl); } /** Flags & Language Constructor + * @param includeToolBar [boolean] Specifies whether the app should include the toolbar(s). * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. */ - public EkitCore(boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry) + public EkitCore(boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry) { - this(null, null, null, null, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, false, false,""); + this(null, null, null, null, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, false, false, ""); } /** Flags Constructor + * @param includeToolBar [boolean] Specifies whether the app should include the toolbar(s). * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). */ - public EkitCore(boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive) + public EkitCore(boolean includeToolBar, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive) { - this(null, null, null, null, showViewSource, showMenuIcons, editModeExclusive, null, null, false, false,""); + this(null, null, null, null, includeToolBar, showViewSource, showMenuIcons, editModeExclusive, null, null, false, false, ""); } /** Language & Debug Constructor @@ -823,7 +1101,7 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc */ public EkitCore(String sLanguage, String sCountry, boolean debugMode) { - this(null, null, null, null, false, true, true, sLanguage, sCountry, false, debugMode,""); + this(null, null, null, null, true, false, true, true, sLanguage, sCountry, false, debugMode, ""); } /** Language Constructor @@ -832,7 +1110,7 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc */ public EkitCore(String sLanguage, String sCountry) { - this(null, null, null, null, false, true, true, sLanguage, sCountry, false, false,""); + this(null, null, null, null, true, false, true, true, sLanguage, sCountry, false, false, ""); } /** Debug Constructor @@ -840,14 +1118,14 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc */ public EkitCore(boolean debugMode) { - this(null, null, null, null, false, true, true, null, null, false, debugMode,""); + this(null, null, null, null, true, false, true, true, null, null, false, debugMode, ""); } /** Empty Constructor */ public EkitCore() { - this(null, null, null, null, false, true, true, null, null, false, false,""); + this(null, null, null, null, true, false, true, true, null, null, false, false, ""); } /* ActionListener method */ @@ -984,6 +1262,18 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc { jToolBar.setVisible(jcbmiViewToolbar.isSelected()); } + else if(command.equals("toggletoolbarmain")) + { + jToolBarMain.setVisible(jcbmiViewToolbarMain.isSelected()); + } + else if(command.equals("toggletoolbarformat")) + { + jToolBarFormat.setVisible(jcbmiViewToolbarFormat.isSelected()); + } + else if(command.equals("toggletoolbarstyles")) + { + jToolBarStyles.setVisible(jcbmiViewToolbarStyles.isSelected()); + } else if(command.equals("viewsource")) { toggleSourceWindow(); @@ -1030,6 +1320,10 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc { insertServerImage(); } + else if(command.equals("insertsymbol")) + { + insertSymbol(); + } else if(command.equals("insertnbsp")) { insertNonbreakingSpace(); @@ -1123,6 +1417,10 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc { SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("About"), true, Translatrix.getTranslationString("AboutMessage"), SimpleInfoDialog.INFO); } + else if(command.equals("spellcheck")) + { + checkDocumentSpelling(jtpMain.getDocument()); + } } catch(IOException ioe) { @@ -1334,7 +1632,7 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc { if(!exclusiveEdit) { - if(jspSource.isShowing()) + if(isSourceWindowActive()) { if(de.getDocument() instanceof HTMLDocument || de.getDocument() instanceof ExtendedHTMLDocument) { @@ -1360,6 +1658,7 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc jtpMain.setDocument(htmlDoc); jtpMain.getDocument().addUndoableEditListener(new CustomUndoableEditListener()); jtpMain.getDocument().addDocumentListener(this); + jtpMain.setCaretPosition(0); purgeUndos(); registerDocumentStyles(); } @@ -1674,6 +1973,22 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc jtpMain.setCaretPosition(caretPos + 1); } + /** Method for inserting a symbol + */ + private void insertSymbol() + throws IOException, BadLocationException, RuntimeException + { + int caretPos = jtpMain.getCaretPosition(); + SymbolDialog symbolInput = new SymbolDialog(this, Translatrix.getTranslationString("SymbolDialogTitle"), true); + String symbol = symbolInput.getInputText(); + symbolInput.dispose(); + if(symbol != null) + { + htmlDoc.insertString(caretPos, symbol, jtpMain.getInputAttributes()); + jtpMain.setCaretPosition(caretPos + 1); + } + } + /** Method for inserting a non-breaking space ( ) */ private void insertNonbreakingSpace() @@ -1785,10 +2100,10 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc private void doSearch(String searchFindTerm, String searchReplaceTerm, boolean bIsFindReplace, boolean bCaseSensitive, boolean bStartAtTop) { boolean bReplaceAll = false; - JTextPane searchPane = jtpMain; + JTextComponent searchPane = (JTextComponent)jtpMain; if(jspSource.isShowing() || jtpSource.hasFocus()) { - searchPane = jtpSource; + searchPane = (JTextComponent)jtpSource; } if(searchFindTerm == null || (bIsFindReplace && searchReplaceTerm == null)) { @@ -1844,14 +2159,14 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc */ private int findText(String findTerm, String replaceTerm, boolean bCaseSenstive, int iOffset) { - JTextPane jtpFindSource; - if(jspSource.isShowing() || jtpSource.hasFocus()) + JTextComponent jtpFindSource; + if(isSourceWindowActive() || jtpSource.hasFocus()) { - jtpFindSource = jtpSource; + jtpFindSource = (JTextComponent)jtpSource; } else { - jtpFindSource = jtpMain; + jtpFindSource = (JTextComponent)jtpMain; } int searchPlace = -1; try @@ -1901,10 +2216,11 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc { if(whatImage == null) { - whatImage = getImageFromChooser(".", extsIMG, Translatrix.getTranslationString("FiletypeIMG")); + whatImage = getImageFromChooser(imageChooserStartDir, extsIMG, Translatrix.getTranslationString("FiletypeIMG")); } if(whatImage != null) { + imageChooserStartDir = whatImage.getParent().toString(); int caretPos = jtpMain.getCaretPosition(); htmlKit.insertHTML(htmlDoc, caretPos, "", 0, 0, HTML.Tag.IMG); jtpMain.setCaretPosition(caretPos + 1); @@ -1986,6 +2302,10 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc return selectedFile; } + /** Empty spell check method, overwritten by spell checker extension class + */ + public void checkDocumentSpelling(Document doc) { ; } + /** Method for saving text as a complete HTML document */ private void writeOut(HTMLDocument doc, File whatFile) @@ -2102,6 +2422,34 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc refreshOnUpdate(); } + /** Method for loading HTML document + */ + public void loadDocument(File whatFile) + throws IOException, BadLocationException + { + try + { + loadDocument(whatFile, null); + } + catch(ChangedCharSetException ccse) + { + String charsetType = ccse.getCharSetSpec().toLowerCase(); + int pos = charsetType.indexOf("charset"); + if(pos == -1) + { + throw ccse; + } + while(pos < charsetType.length() && charsetType.charAt(pos) != '=') + { + pos++; + } + pos++; // Places file cursor past the equals sign (=) + String whatEncoding = charsetType.substring(pos).trim(); + loadDocument(whatFile, whatEncoding); + } + refreshOnUpdate(); + } + /** Method for loading HTML document into the app, including document encoding setting */ private void loadDocument(File whatFile, String whatEncoding) @@ -2109,6 +2457,7 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc { Reader r = null; htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); + htmlDoc.putProperty("com.hexidec.ekit.docsource", whatFile.toString()); try { if(whatEncoding == null) @@ -2350,7 +2699,7 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc /** Convenience method for obtaining the Source JTextPane */ - public JTextPane getSourcePane() + public JTextArea getSourcePane() { return jtpSource; } @@ -2396,45 +2745,116 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc */ public JToolBar getToolBar(boolean isShowing) { - jcbmiViewToolbar.setState(isShowing); - return jToolBar; + if(jToolBar != null) + { + jcbmiViewToolbar.setState(isShowing); + return jToolBar; + } + return (JToolBar)null; } - /** Convenience method for obtaining the pre-generated toolbar + /** Convenience method for obtaining the pre-generated main toolbar + */ + public JToolBar getToolBarMain(boolean isShowing) + { + if(jToolBarMain != null) + { + jcbmiViewToolbarMain.setState(isShowing); + return jToolBarMain; + } + return (JToolBar)null; + } + + /** Convenience method for obtaining the pre-generated main toolbar */ - public JToolBar getCustomToolBar(Vector vcTools, boolean isShowing) + public JToolBar getToolBarFormat(boolean isShowing) { - jcbmiViewToolbar.setState(isShowing); - jToolBar = new JToolBar(JToolBar.HORIZONTAL); - jToolBar.setFloatable(false); + if(jToolBarFormat != null) + { + jcbmiViewToolbarFormat.setState(isShowing); + return jToolBarFormat; + } + return (JToolBar)null; + } + + /** Convenience method for obtaining the pre-generated main toolbar + */ + public JToolBar getToolBarStyles(boolean isShowing) + { + if(jToolBarStyles != null) + { + jcbmiViewToolbarStyles.setState(isShowing); + return jToolBarStyles; + } + return (JToolBar)null; + } + + /** Convenience method for obtaining a custom toolbar + */ + public JToolBar customizeToolBar(int whichToolBar, Vector vcTools, boolean isShowing) + { + JToolBar jToolBarX = new JToolBar(JToolBar.HORIZONTAL); + jToolBarX.setFloatable(false); +/* + jToolBarX.setMargin(new Insets(2, 2, 2, 2)); + jToolBarX.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); + jToolBarX.setBorderPainted(true); +*/ for(int i = 0; i < vcTools.size(); i++) { String toolToAdd = ((String)(vcTools.elementAt(i))).toLowerCase(); if(toolToAdd.equals(KEY_TOOL_SEP)) { - jToolBar.add(new JToolBar.Separator()); + jToolBarX.add(new JToolBar.Separator()); } else if(htTools.containsKey(toolToAdd)) { if(htTools.get(toolToAdd) instanceof JButtonNoFocus) { - jToolBar.add((JButtonNoFocus)(htTools.get(toolToAdd))); + jToolBarX.add((JButtonNoFocus)(htTools.get(toolToAdd))); } else if(htTools.get(toolToAdd) instanceof JToggleButtonNoFocus) { - jToolBar.add((JToggleButtonNoFocus)(htTools.get(toolToAdd))); + jToolBarX.add((JToggleButtonNoFocus)(htTools.get(toolToAdd))); } else if(htTools.get(toolToAdd) instanceof JComboBoxNoFocus) { - jToolBar.add((JComboBoxNoFocus)(htTools.get(toolToAdd))); + jToolBarX.add((JComboBoxNoFocus)(htTools.get(toolToAdd))); } else { - jToolBar.add((JComponent)(htTools.get(toolToAdd))); + jToolBarX.add((JComponent)(htTools.get(toolToAdd))); } } } - return jToolBar; + if(whichToolBar == TOOLBAR_MAIN) + { + jToolBarMain = jToolBarX; + jToolBarMain.setVisible(isShowing); + jcbmiViewToolbarMain.setSelected(isShowing); + return jToolBarMain; + } + else if(whichToolBar == TOOLBAR_FORMAT) + { + jToolBarFormat = jToolBarX; + jToolBarFormat.setVisible(isShowing); + jcbmiViewToolbarFormat.setSelected(isShowing); + return jToolBarFormat; + } + else if(whichToolBar == TOOLBAR_STYLES) + { + jToolBarStyles = jToolBarX; + jToolBarStyles.setVisible(isShowing); + jcbmiViewToolbarStyles.setSelected(isShowing); + return jToolBarStyles; + } + else + { + jToolBarMain = jToolBarX; + jToolBarMain.setVisible(isShowing); + jcbmiViewToolbarMain.setSelected(isShowing); + return jToolBarMain; + } } /** Convenience method for obtaining the current file handle @@ -2455,7 +2875,14 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc */ public String getDocumentText() { - return jtpMain.getText(); + if(isSourceWindowActive()) + { + return jtpSource.getText(); + } + else + { + return jtpMain.getText(); + } } /** Convenience method for obtaining the document text @@ -2496,7 +2923,17 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc jtpMain.setText(sText); ((HTMLEditorKit)(jtpMain.getEditorKit())).setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR)); jtpSource.setText(jtpMain.getText()); - ((HTMLEditorKit)(jtpSource.getEditorKit())).setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR)); + } + + /** Convenience method for setting the source document + */ + public void setSourceDocument(StyledDocument sDoc) + { + jtpSource.getDocument().removeDocumentListener(this); + jtpSource.setDocument(sDoc); + jtpSource.getDocument().addDocumentListener(this); + jtpMain.setText(jtpSource.getText()); + ((HTMLEditorKit)(jtpMain.getEditorKit())).setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR)); } /** Convenience method for obtaining the document text @@ -2551,11 +2988,18 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc e.printStackTrace(System.err); } - /** Convenience method for toggling source window visibility + /** Convenience method for determining if the source window is active + */ + private boolean isSourceWindowActive() + { + return (jspSource == jspltDisplay.getRightComponent()); + } + + /** Method for toggling source window visibility */ private void toggleSourceWindow() { - if(!(jspSource.isShowing())) + if(!(isSourceWindowActive())) { jtpSource.setText(jtpMain.getText()); jspltDisplay.setRightComponent(jspSource); @@ -2569,6 +3013,7 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc jspltDisplay.setDividerLocation(iSplitPos); jspltDisplay.setEnabled(true); } + jtpSource.requestFocus(); } else { @@ -2578,8 +3023,8 @@ public class EkitCore extends JPanel implements ActionListener, KeyListener, Doc jtpMain.requestFocus(); } this.validate(); - jcbmiViewSource.setSelected(jspSource.isShowing()); - jtbtnViewSource.setSelected(jspSource.isShowing()); + jcbmiViewSource.setSelected(isSourceWindowActive()); + jtbtnViewSource.setSelected(isSourceWindowActive()); } /** Searches the specified element for CLASS attribute setting diff --git a/ekit/com/hexidec/ekit/EkitCore_Basic.java b/ekit/com/hexidec/ekit/EkitCore_Basic.java deleted file mode 100644 index ef23e15..0000000 --- a/ekit/com/hexidec/ekit/EkitCore_Basic.java +++ /dev/null @@ -1,2836 +0,0 @@ -/* -GNU Lesser General Public License - -EkitCore - Base Java Swing HTML Editor & Viewer Class (Basic Version) -Copyright (C) 2000-2003 Howard Kistler - -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 -*/ - -package com.hexidec.ekit; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.Insets; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Reader; -import java.net.URL; -import java.net.URLConnection; -import java.net.MalformedURLException; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Locale; -import java.util.MissingResourceException; -import java.util.ResourceBundle; -import java.util.Vector; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JEditorPane; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTextField; -import javax.swing.JTextPane; -import javax.swing.JToggleButton; -import javax.swing.JToolBar; -import javax.swing.KeyStroke; -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.UndoableEditEvent; -import javax.swing.event.UndoableEditListener; -import javax.swing.filechooser.FileFilter; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.ChangedCharSetException; -import javax.swing.text.DefaultEditorKit; -import javax.swing.text.DefaultStyledDocument; -import javax.swing.text.Document; -import javax.swing.text.Element; -import javax.swing.text.PlainDocument; -import javax.swing.text.Position; -import javax.swing.text.Style; -import javax.swing.text.StyleConstants; -import javax.swing.text.StyledDocument; -import javax.swing.text.StyledEditorKit; -import javax.swing.text.StyledEditorKit.FontSizeAction; -import javax.swing.text.html.HTML; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.HTMLEditorKit; -import javax.swing.text.html.StyleSheet; -import javax.swing.text.rtf.RTFEditorKit; -import javax.swing.undo.UndoManager; -import javax.swing.undo.CannotUndoException; - -import com.hexidec.ekit.action.*; -import com.hexidec.ekit.component.*; -import com.hexidec.util.Base64Codec; -import com.hexidec.util.Translatrix; - -/** EkitCore - * Main application class for editing and saving HTML in a Java text component - * - * @author Howard Kistler - * @version 0.9g - * - * REQUIREMENTS - * Java 2 (JDK 1.3 or 1.4) - * Swing Library - */ - -public class EkitCore extends JPanel implements ActionListener, KeyListener, DocumentListener -{ - /* Components */ - private JSplitPane jspltDisplay; - private JTextPane jtpMain; - private ExtendedHTMLEditorKit htmlKit; - private ExtendedHTMLDocument htmlDoc; - private StyleSheet styleSheet; - private JTextPane jtpSource; - private JScrollPane jspSource; - private JToolBar jToolBar; - - private JCheckBoxMenuItem jcbmiViewToolbar; - private JCheckBoxMenuItem jcbmiViewSource; - - private JButtonNoFocus jbtnNewHTML; - private JButtonNoFocus jbtnOpenHTML; - private JButtonNoFocus jbtnSaveHTML; - private JButtonNoFocus jbtnCut; - private JButtonNoFocus jbtnCopy; - private JButtonNoFocus jbtnPaste; - private JButtonNoFocus jbtnBold; - private JButtonNoFocus jbtnItalic; - private JButtonNoFocus jbtnUnderline; - private JButtonNoFocus jbtnStrike; - private JButtonNoFocus jbtnSuperscript; - private JButtonNoFocus jbtnSubscript; - private JButtonNoFocus jbtnUList; - private JButtonNoFocus jbtnOList; - private JButtonNoFocus jbtnClearFormat; - private JButtonNoFocus jbtnAnchor; - private JToggleButtonNoFocus jtbtnViewSource; - private JComboBoxNoFocus jcmbStyleSelector; - - private Frame frameHandler; - - private HTMLUtilities htmlUtilities = new HTMLUtilities(this); - - /* Actions */ - private StyledEditorKit.BoldAction actionFontBold; - private StyledEditorKit.ItalicAction actionFontItalic; - private StyledEditorKit.UnderlineAction actionFontUnderline; - private FormatAction actionFontStrike; - private FormatAction actionFontSuperscript; - private FormatAction actionFontSubscript; - private ListAutomationAction actionListUnordered; - private ListAutomationAction actionListOrdered; - private CustomAction actionSelectFont; - private CustomAction actionClearFormat; - private CustomAction actionInsertAnchor; - - protected UndoManager undoMngr; - protected UndoAction undoAction; - protected RedoAction redoAction; - - /* Menus */ - private JMenuBar jMenuBar; - private JMenu jMenuFile; - private JMenu jMenuEdit; - private JMenu jMenuView; - private JMenu jMenuFont; - private JMenu jMenuFormat; - private JMenu jMenuInsert; - private JMenu jMenuTable; - private JMenu jMenuForms; - private JMenu jMenuSearch; - private JMenu jMenuHelp; - private JMenu jMenuDebug; - - /* Constants */ - // Menu Keys - public static final String KEY_MENU_FILE = "file"; - public static final String KEY_MENU_EDIT = "edit"; - public static final String KEY_MENU_VIEW = "view"; - public static final String KEY_MENU_FONT = "font"; - public static final String KEY_MENU_FORMAT = "format"; - public static final String KEY_MENU_INSERT = "insert"; - public static final String KEY_MENU_TABLE = "table"; - public static final String KEY_MENU_FORMS = "forms"; - public static final String KEY_MENU_SEARCH = "search"; - public static final String KEY_MENU_TOOLS = "tools"; - public static final String KEY_MENU_HELP = "help"; - public static final String KEY_MENU_DEBUG = "debug"; - - // Tool Keys - public static final String KEY_TOOL_SEP = "separator"; - public static final String KEY_TOOL_NEW = "new"; - public static final String KEY_TOOL_OPEN = "open"; - public static final String KEY_TOOL_SAVE = "save"; - public static final String KEY_TOOL_CUT = "cut"; - public static final String KEY_TOOL_COPY = "copy"; - public static final String KEY_TOOL_PASTE = "paste"; - public static final String KEY_TOOL_BOLD = "bold"; - public static final String KEY_TOOL_ITALIC = "italic"; - public static final String KEY_TOOL_UNDERLINE = "underline"; - public static final String KEY_TOOL_STRIKE = "strike"; - public static final String KEY_TOOL_SUPER = "superscript"; - public static final String KEY_TOOL_SUB = "subscript"; - public static final String KEY_TOOL_ULIST = "ulist"; - public static final String KEY_TOOL_OLIST = "olist"; - public static final String KEY_TOOL_CLEAR = "clearformats"; - public static final String KEY_TOOL_ANCHOR = "anchor"; - public static final String KEY_TOOL_SOURCE = "viewsource"; - public static final String KEY_TOOL_STYLES = "styleselect"; - - // Menu & Tool Key Arrays - private static Hashtable htMenus = new Hashtable(); - private static Hashtable htTools = new Hashtable(); - - private final String appName = "Ekit"; - private final String menuDialog = "..."; /* text to append to a MenuItem label when menu item opens a dialog */ - - private final boolean useFormIndicator = true; /* Creates a highlighted background on a new FORM so that it may be more easily edited */ - private final String clrFormIndicator = "#cccccc"; - - // System Clipboard Settings - private java.awt.datatransfer.Clipboard sysClipboard; - private SecurityManager secManager; - - /* Variables */ - private int iSplitPos = 0; - - private boolean exclusiveEdit = true; - - private String lastSearchFindTerm = null; - private String lastSearchReplaceTerm = null; - private boolean lastSearchCaseSetting = false; - private boolean lastSearchTopSetting = false; - - private File currentFile = null; - - private int indent = 0; - private final int indentStep = 4; - - // File extensions for MutableFilter - private final String[] extsHTML = { "html", "htm", "shtml" }; - private final String[] extsCSS = { "css" }; - private final String[] extsIMG = { "gif", "jpg", "jpeg", "png" }; - private final String[] extsRTF = { "rtf" }; - private final String[] extsB64 = { "b64" }; - private final String[] extsSer = { "ser" }; - - /* Servlet Settings */ - private String ServletURL = null; - private String TreePilotSystemID = ""; - private String ImageDir = ""; - private static ResourceBundle TreePilotProperties; - - /** Master Constructor - * @param sDocument [String] A text or HTML document to load in the editor upon startup. - * @param sStyleSheet [String] A CSS stylesheet to load in the editor upon startup. - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param urlStyleSheet [URL] A URL reference to the CSS style sheet. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - * @param base64 [boolean] Specifies whether the raw document is Base64 encoded or not. - * @param debugMode [boolean] Specifies whether to show the Debug menu or not. - */ - public EkitCore(String sDocument, String sStyleSheet, String sRawDocument, URL urlStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean debugMode) - { - super(); - - exclusiveEdit = editModeExclusive; - - frameHandler = new Frame(); - - // Determine if system clipboard is available - secManager = System.getSecurityManager(); - if(secManager != null) - { - try - { - secManager.checkSystemClipboardAccess(); - sysClipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - } - catch (SecurityException se) - { - sysClipboard = null; - } - } - - /* Localize for language */ - Translatrix.setBundleName("com.hexidec.ekit.LanguageResources"); - Locale baseLocale = (Locale)null; - if(sLanguage != null && sCountry != null) - { - baseLocale = new Locale(sLanguage, sCountry); - } - Translatrix.setLocale(baseLocale); - - /* Load TreePilot properties */ - try - { - TreePilotProperties = ResourceBundle.getBundle("com.hexidec.ekit.TreePilot"); - } - catch(MissingResourceException mre) - { - logException("MissingResourceException while loading treepilot file", mre); - } - - /* Create the editor kit, document, and stylesheet */ - jtpMain = new JTextPane(); - htmlKit = new ExtendedHTMLEditorKit(); - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - styleSheet = htmlDoc.getStyleSheet(); - htmlKit.setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR)); - - /* Set up the text pane */ - jtpMain.setEditorKit(htmlKit); - jtpMain.setDocument(htmlDoc); - jtpMain.setMargin(new Insets(4, 4, 4, 4)); - jtpMain.addKeyListener(this); - - /* Create the source text area */ - jtpSource = new JTextPane(); - jtpSource.setBackground(new Color(212, 212, 212)); - jtpSource.setSelectionColor(new Color(255, 192, 192)); - jtpSource.setText(jtpMain.getText()); - jtpSource.getDocument().addDocumentListener(this); - - /* Add CaretListener for tracking caret location events */ - jtpMain.addCaretListener(new CaretListener() - { - public void caretUpdate(CaretEvent ce) - { - handleCaretPositionChange(ce); - } - }); - - /* Set up the undo features */ - undoMngr = new UndoManager(); - undoAction = new UndoAction(); - redoAction = new RedoAction(); - jtpMain.getDocument().addUndoableEditListener(new CustomUndoableEditListener()); - - /* Insert raw document, if exists */ - if(sRawDocument != null && sRawDocument.length() > 0) - { - if(base64) - { - jtpMain.setText(Base64Codec.decode(sRawDocument)); - } - else - { - jtpMain.setText(sRawDocument); - } - } - jtpMain.setCaretPosition(0); - jtpMain.getDocument().addDocumentListener(this); - - /* Import CSS from reference, if exists */ - if(urlStyleSheet != null) - { - try - { - String currDocText = jtpMain.getText(); - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - styleSheet = htmlDoc.getStyleSheet(); - BufferedReader br = new BufferedReader(new InputStreamReader(urlStyleSheet.openStream())); - styleSheet.loadRules(br, urlStyleSheet); - br.close(); - htmlDoc = new ExtendedHTMLDocument(styleSheet); - registerDocument(htmlDoc); - jtpMain.setText(currDocText); - jtpSource.setText(jtpMain.getText()); - } - catch(Exception e) - { - e.printStackTrace(System.out); - } - } - - /* Preload the specified HTML document, if exists */ - if(sDocument != null) - { - File defHTML = new File(sDocument); - if(defHTML.exists()) - { - try - { - openDocument(defHTML); - } - catch(Exception e) - { - logException("Exception in preloading HTML document", e); - } - } - } - - /* Preload the specified CSS document, if exists */ - if(sStyleSheet != null) - { - File defCSS = new File(sStyleSheet); - if(defCSS.exists()) - { - try - { - openStyleSheet(defCSS); - } - catch(Exception e) - { - logException("Exception in preloading CSS stylesheet", e); - } - } - } - - /* Collect the actions that the JTextPane is naturally aware of */ - Hashtable actions = new Hashtable(); - Action[] actionsArray = jtpMain.getActions(); - for(int i = 0; i < actionsArray.length; i++) - { - Action a = actionsArray[i]; - actions.put(a.getValue(Action.NAME), a); - } - - /* Create shared actions */ - actionFontBold = new StyledEditorKit.BoldAction(); - actionFontItalic = new StyledEditorKit.ItalicAction(); - actionFontUnderline = new StyledEditorKit.UnderlineAction(); - actionFontStrike = new FormatAction(this, Translatrix.getTranslationString("FontStrike"), HTML.Tag.STRIKE); - actionFontSuperscript = new FormatAction(this, Translatrix.getTranslationString("FontSuperscript"), HTML.Tag.SUP); - actionFontSubscript = new FormatAction(this, Translatrix.getTranslationString("FontSubscript"), HTML.Tag.SUB); - actionListUnordered = new ListAutomationAction(this, Translatrix.getTranslationString("ListUnordered"), HTML.Tag.UL); - actionListOrdered = new ListAutomationAction(this, Translatrix.getTranslationString("ListOrdered"), HTML.Tag.OL); - Hashtable customAttr = new Hashtable(); - customAttr.put("face",""); - actionSelectFont = new CustomAction(this, Translatrix.getTranslationString("FontSelect") + menuDialog, HTML.Tag.FONT, customAttr); - actionClearFormat = new CustomAction(this, Translatrix.getTranslationString("FormatClear"), new HTML.UnknownTag("")); - actionInsertAnchor = new CustomAction(this, Translatrix.getTranslationString("InsertAnchor") + menuDialog, HTML.Tag.A); - - /* Build the menus */ - /* FILE Menu */ - jMenuFile = new JMenu(Translatrix.getTranslationString("File")); - htMenus.put(KEY_MENU_FILE, jMenuFile); - JMenuItem jmiNew = new JMenuItem(Translatrix.getTranslationString("NewDocument")); jmiNew.setActionCommand("newdoc"); jmiNew.addActionListener(this); jmiNew.setAccelerator(KeyStroke.getKeyStroke('N', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiNew.setIcon(getEkitIcon("New")); }; jMenuFile.add(jmiNew); - JMenuItem jmiOpenHTML = new JMenuItem(Translatrix.getTranslationString("OpenDocument") + menuDialog); jmiOpenHTML.setActionCommand("openhtml"); jmiOpenHTML.addActionListener(this); jmiOpenHTML.setAccelerator(KeyStroke.getKeyStroke('O', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiOpenHTML.setIcon(getEkitIcon("Open")); }; jMenuFile.add(jmiOpenHTML); - JMenuItem jmiOpenCSS = new JMenuItem(Translatrix.getTranslationString("OpenStyle") + menuDialog); jmiOpenCSS.setActionCommand("opencss"); jmiOpenCSS.addActionListener(this); jMenuFile.add(jmiOpenCSS); - JMenuItem jmiOpenB64 = new JMenuItem(Translatrix.getTranslationString("OpenBase64Document") + menuDialog); jmiOpenB64.setActionCommand("openb64"); jmiOpenB64.addActionListener(this); jMenuFile.add(jmiOpenB64); - jMenuFile.addSeparator(); - JMenuItem jmiSave = new JMenuItem(Translatrix.getTranslationString("Save")); jmiSave.setActionCommand("save"); jmiSave.addActionListener(this); jmiSave.setAccelerator(KeyStroke.getKeyStroke('S', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiSave.setIcon(getEkitIcon("Save")); }; jMenuFile.add(jmiSave); - JMenuItem jmiSaveAs = new JMenuItem(Translatrix.getTranslationString("SaveAs") + menuDialog); jmiSaveAs.setActionCommand("saveas"); jmiSaveAs.addActionListener(this); jMenuFile.add(jmiSaveAs); - JMenuItem jmiSaveBody = new JMenuItem(Translatrix.getTranslationString("SaveBody") + menuDialog); jmiSaveBody.setActionCommand("savebody"); jmiSaveBody.addActionListener(this); jMenuFile.add(jmiSaveBody); - JMenuItem jmiSaveRTF = new JMenuItem(Translatrix.getTranslationString("SaveRTF") + menuDialog); jmiSaveRTF.setActionCommand("savertf"); jmiSaveRTF.addActionListener(this); jMenuFile.add(jmiSaveRTF); - JMenuItem jmiSaveB64 = new JMenuItem(Translatrix.getTranslationString("SaveB64") + menuDialog); jmiSaveB64.setActionCommand("saveb64"); jmiSaveB64.addActionListener(this); jMenuFile.add(jmiSaveB64); - jMenuFile.addSeparator(); - JMenuItem jmiSerialOut = new JMenuItem(Translatrix.getTranslationString("Serialize") + menuDialog); jmiSerialOut.setActionCommand("serialize"); jmiSerialOut.addActionListener(this); jMenuFile.add(jmiSerialOut); - JMenuItem jmiSerialIn = new JMenuItem(Translatrix.getTranslationString("ReadFromSer") + menuDialog); jmiSerialIn.setActionCommand("readfromser"); jmiSerialIn.addActionListener(this); jMenuFile.add(jmiSerialIn); - jMenuFile.addSeparator(); - JMenuItem jmiExit = new JMenuItem(Translatrix.getTranslationString("Exit")); jmiExit.setActionCommand("exit"); jmiExit.addActionListener(this); jMenuFile.add(jmiExit); - - /* EDIT Menu */ - jMenuEdit = new JMenu(Translatrix.getTranslationString("Edit")); - htMenus.put(KEY_MENU_EDIT, jMenuEdit); - if(sysClipboard != null) - { - // System Clipboard versions of menu commands - JMenuItem jmiCut = new JMenuItem(Translatrix.getTranslationString("Cut")); jmiCut.setActionCommand("textcut"); jmiCut.addActionListener(this); jmiCut.setAccelerator(KeyStroke.getKeyStroke('X', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiCut.setIcon(getEkitIcon("Cut")); } jMenuEdit.add(jmiCut); - JMenuItem jmiCopy = new JMenuItem(Translatrix.getTranslationString("Copy")); jmiCopy.setActionCommand("textcopy"); jmiCopy.addActionListener(this); jmiCopy.setAccelerator(KeyStroke.getKeyStroke('C', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiCopy.setIcon(getEkitIcon("Copy")); } jMenuEdit.add(jmiCopy); - JMenuItem jmiPaste = new JMenuItem(Translatrix.getTranslationString("Paste")); jmiPaste.setActionCommand("textpaste"); jmiPaste.addActionListener(this); jmiPaste.setAccelerator(KeyStroke.getKeyStroke('V', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiPaste.setIcon(getEkitIcon("Paste")); } jMenuEdit.add(jmiPaste); - } - else - { - // DefaultEditorKit versions of menu commands - JMenuItem jmiCut = new JMenuItem(new DefaultEditorKit.CutAction()); jmiCut.setText(Translatrix.getTranslationString("Cut")); jmiCut.setAccelerator(KeyStroke.getKeyStroke('X', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiCut.setIcon(getEkitIcon("Cut")); } jMenuEdit.add(jmiCut); - JMenuItem jmiCopy = new JMenuItem(new DefaultEditorKit.CopyAction()); jmiCopy.setText(Translatrix.getTranslationString("Copy")); jmiCopy.setAccelerator(KeyStroke.getKeyStroke('C', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiCopy.setIcon(getEkitIcon("Copy")); } jMenuEdit.add(jmiCopy); - JMenuItem jmiPaste = new JMenuItem(new DefaultEditorKit.PasteAction()); jmiPaste.setText(Translatrix.getTranslationString("Paste")); jmiPaste.setAccelerator(KeyStroke.getKeyStroke('V', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiPaste.setIcon(getEkitIcon("Paste")); } jMenuEdit.add(jmiPaste); - } - jMenuEdit.addSeparator(); - JMenuItem jmiUndo = new JMenuItem(undoAction); jmiUndo.setAccelerator(KeyStroke.getKeyStroke('Z', java.awt.Event.CTRL_MASK, false)); jMenuEdit.add(jmiUndo); - JMenuItem jmiRedo = new JMenuItem(redoAction); jmiRedo.setAccelerator(KeyStroke.getKeyStroke('Y', java.awt.Event.CTRL_MASK, false)); jMenuEdit.add(jmiRedo); - jMenuEdit.addSeparator(); - JMenuItem jmiSelAll = new JMenuItem((Action)actions.get(DefaultEditorKit.selectAllAction)); jmiSelAll.setText(Translatrix.getTranslationString("SelectAll")); jmiSelAll.setAccelerator(KeyStroke.getKeyStroke('A', java.awt.Event.CTRL_MASK, false)); jMenuEdit.add(jmiSelAll); - JMenuItem jmiSelPara = new JMenuItem((Action)actions.get(DefaultEditorKit.selectParagraphAction)); jmiSelPara.setText(Translatrix.getTranslationString("SelectParagraph")); jMenuEdit.add(jmiSelPara); - JMenuItem jmiSelLine = new JMenuItem((Action)actions.get(DefaultEditorKit.selectLineAction)); jmiSelLine.setText(Translatrix.getTranslationString("SelectLine")); jMenuEdit.add(jmiSelLine); - JMenuItem jmiSelWord = new JMenuItem((Action)actions.get(DefaultEditorKit.selectWordAction)); jmiSelWord.setText(Translatrix.getTranslationString("SelectWord")); jMenuEdit.add(jmiSelWord); - - /* VIEW Menu */ - jMenuView = new JMenu(Translatrix.getTranslationString("View")); - htMenus.put(KEY_MENU_VIEW, jMenuView); - jcbmiViewToolbar = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewToolbar"), false); jcbmiViewToolbar.setActionCommand("toggletoolbar"); jcbmiViewToolbar.addActionListener(this); jMenuView.add(jcbmiViewToolbar); - jcbmiViewSource = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewSource"), false); jcbmiViewSource.setActionCommand("viewsource"); jcbmiViewSource.addActionListener(this); jMenuView.add(jcbmiViewSource); - - /* FONT Menu */ - jMenuFont = new JMenu(Translatrix.getTranslationString("Font")); - htMenus.put(KEY_MENU_FONT, jMenuFont); - JMenuItem jmiBold = new JMenuItem(actionFontBold); jmiBold.setText(Translatrix.getTranslationString("FontBold")); jmiBold.setAccelerator(KeyStroke.getKeyStroke('B', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiBold.setIcon(getEkitIcon("Bold")); } jMenuFont.add(jmiBold); - JMenuItem jmiItalic = new JMenuItem(actionFontItalic); jmiItalic.setText(Translatrix.getTranslationString("FontItalic")); jmiItalic.setAccelerator(KeyStroke.getKeyStroke('I', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiItalic.setIcon(getEkitIcon("Italic")); } jMenuFont.add(jmiItalic); - JMenuItem jmiUnderline = new JMenuItem(actionFontUnderline); jmiUnderline.setText(Translatrix.getTranslationString("FontUnderline")); jmiUnderline.setAccelerator(KeyStroke.getKeyStroke('U', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiUnderline.setIcon(getEkitIcon("Underline")); } jMenuFont.add(jmiUnderline); - JMenuItem jmiStrike = new JMenuItem(actionFontStrike); jmiStrike.setText(Translatrix.getTranslationString("FontStrike")); if(showMenuIcons) { jmiStrike.setIcon(getEkitIcon("Strike")); } jMenuFont.add(jmiStrike); - jMenuFont.addSeparator(); - jMenuFont.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatBig"), HTML.Tag.BIG))); - jMenuFont.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatSmall"), HTML.Tag.SMALL))); - JMenu jMenuFontSize = new JMenu(Translatrix.getTranslationString("FontSize")); - String fontSizeKey = "size"; - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"1"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize1"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"2"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize2"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"3"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize3"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"4"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize4"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"5"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize5"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"6"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize6"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"7"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize7"), HTML.Tag.FONT, customAttr))); - jMenuFont.add(jMenuFontSize); - jMenuFont.addSeparator(); - JMenuItem jmiSupscript = new JMenuItem(actionFontSuperscript); if(showMenuIcons) { jmiSupscript.setIcon(getEkitIcon("Super")); } jMenuFont.add(jmiSupscript); - JMenuItem jmiSubscript = new JMenuItem(actionFontSubscript); if(showMenuIcons) { jmiSubscript.setIcon(getEkitIcon("Sub")); } jMenuFont.add(jmiSubscript); - jMenuFont.addSeparator(); - JMenuItem jmiSerif = new JMenuItem((Action)actions.get("font-family-Serif")); jmiSerif.setText(Translatrix.getTranslationString("FontSerif")); jMenuFont.add(jmiSerif); - JMenuItem jmiSansSerif = new JMenuItem((Action)actions.get("font-family-SansSerif")); jmiSansSerif.setText(Translatrix.getTranslationString("FontSansserif")); jMenuFont.add(jmiSansSerif); - JMenuItem jmiMonospaced = new JMenuItem((Action)actions.get("font-family-Monospaced")); jmiMonospaced.setText(Translatrix.getTranslationString("FontMonospaced")); jMenuFont.add(jmiMonospaced); - JMenuItem jmiSelectFont = new JMenuItem(actionSelectFont); jMenuFont.add(jmiSelectFont); - jMenuFont.addSeparator(); - JMenu jMenuFontColor = new JMenu(Translatrix.getTranslationString("Color")); - customAttr = new Hashtable(); customAttr.put("color","black"); - jMenuFontColor.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("CustomColor") + menuDialog, HTML.Tag.FONT, customAttr))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorAqua"), new Color( 0,255,255)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorBlack"), new Color( 0, 0, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorBlue"), new Color( 0, 0,255)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorFuschia"), new Color(255, 0,255)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorGray"), new Color(128,128,128)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorGreen"), new Color( 0,128, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorLime"), new Color( 0,255, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorMaroon"), new Color(128, 0, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorNavy"), new Color( 0, 0,128)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorOlive"), new Color(128,128, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorPurple"), new Color(128, 0,128)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorRed"), new Color(255, 0, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorSilver"), new Color(192,192,192)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorTeal"), new Color( 0,128,128)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorWhite"), new Color(255,255,255)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorYellow"), new Color(255,255, 0)))); - jMenuFont.add(jMenuFontColor); - - /* FORMAT Menu */ - jMenuFormat = new JMenu(Translatrix.getTranslationString("Format")); - htMenus.put(KEY_MENU_FORMAT, jMenuFormat); - JMenu jMenuFormatAlign = new JMenu(Translatrix.getTranslationString("Align")); - jMenuFormatAlign.add(new JMenuItem(new StyledEditorKit.AlignmentAction(Translatrix.getTranslationString("AlignLeft"), StyleConstants.ALIGN_LEFT))); - jMenuFormatAlign.add(new JMenuItem(new StyledEditorKit.AlignmentAction(Translatrix.getTranslationString("AlignCenter"), StyleConstants.ALIGN_CENTER))); - jMenuFormatAlign.add(new JMenuItem(new StyledEditorKit.AlignmentAction(Translatrix.getTranslationString("AlignRight"), StyleConstants.ALIGN_RIGHT))); - jMenuFormatAlign.add(new JMenuItem(new StyledEditorKit.AlignmentAction(Translatrix.getTranslationString("AlignJustified"), StyleConstants.ALIGN_JUSTIFIED))); - jMenuFormat.add(jMenuFormatAlign); - jMenuFormat.addSeparator(); - JMenu jMenuFormatHeading = new JMenu(Translatrix.getTranslationString("Heading")); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading1"), HTML.Tag.H1))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading2"), HTML.Tag.H2))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading3"), HTML.Tag.H3))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading4"), HTML.Tag.H4))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading5"), HTML.Tag.H5))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading6"), HTML.Tag.H6))); - jMenuFormat.add(jMenuFormatHeading); - jMenuFormat.addSeparator(); - JMenuItem jmiUList = new JMenuItem(actionListUnordered); if(showMenuIcons) { jmiUList.setIcon(getEkitIcon("UList")); } jMenuFormat.add(jmiUList); - JMenuItem jmiOList = new JMenuItem(actionListOrdered); if(showMenuIcons) { jmiOList.setIcon(getEkitIcon("OList")); } jMenuFormat.add(jmiOList); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("ListItem"), HTML.Tag.LI))); - jMenuFormat.addSeparator(); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatBlockquote"), HTML.Tag.BLOCKQUOTE))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatPre"), HTML.Tag.PRE))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatStrong"), HTML.Tag.STRONG))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatEmphasis"), HTML.Tag.EM))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatTT"), HTML.Tag.TT))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatSpan"), HTML.Tag.SPAN))); - jMenuFormat.addSeparator(); - JMenuItem jmiClearStyles = new JMenuItem(actionClearFormat); if(showMenuIcons) { jmiClearStyles.setIcon(getEkitIcon("ClearFormat")); }; jMenuFormat.add(jmiClearStyles); - - /* INSERT Menu */ - jMenuInsert = new JMenu(Translatrix.getTranslationString("Insert")); - htMenus.put(KEY_MENU_INSERT, jMenuInsert); - jMenuInsert.add(new JMenuItem(actionInsertAnchor)); - JMenuItem jmiBreak = new JMenuItem(Translatrix.getTranslationString("InsertBreak")); jmiBreak.setActionCommand("insertbreak"); jmiBreak.addActionListener(this); jmiBreak.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, java.awt.Event.SHIFT_MASK, false)); jMenuInsert.add(jmiBreak); - JMenuItem jmiNBSP = new JMenuItem(Translatrix.getTranslationString("InsertNBSP")); jmiNBSP.setActionCommand("insertnbsp"); jmiNBSP.addActionListener(this); jMenuInsert.add(jmiNBSP); - JMenuItem jmiHRule = new JMenuItem((Action)actions.get("InsertHR")); jmiHRule.setText(Translatrix.getTranslationString("InsertHorizontalRule")); jMenuInsert.add(jmiHRule); - jMenuInsert.addSeparator(); - JMenuItem jmiImageLocal = new JMenuItem(Translatrix.getTranslationString("InsertLocalImage") + menuDialog); jmiImageLocal.setActionCommand("insertlocalimage"); jmiImageLocal.addActionListener(this); jMenuInsert.add(jmiImageLocal); - JMenuItem jmiImageServer = new JMenuItem(Translatrix.getTranslationString("InsertServerImage") + menuDialog); jmiImageServer.setActionCommand("insertserverimage"); jmiImageServer.addActionListener(this); jMenuInsert.add(jmiImageServer); - - /* TABLE Menu */ - jMenuTable = new JMenu(Translatrix.getTranslationString("Table")); - htMenus.put(KEY_MENU_TABLE, jMenuTable); - JMenuItem jmiTable = new JMenuItem(Translatrix.getTranslationString("InsertTable") + menuDialog); jmiTable.setActionCommand("inserttable"); jmiTable.addActionListener(this); jMenuTable.add(jmiTable); - jMenuTable.addSeparator(); - JMenuItem jmiTableRow = new JMenuItem(Translatrix.getTranslationString("InsertTableRow")); jmiTableRow.setActionCommand("inserttablerow"); jmiTableRow.addActionListener(this); jMenuTable.add(jmiTableRow); - JMenuItem jmiTableCol = new JMenuItem(Translatrix.getTranslationString("InsertTableColumn")); jmiTableCol.setActionCommand("inserttablecolumn"); jmiTableCol.addActionListener(this); jMenuTable.add(jmiTableCol); - jMenuTable.addSeparator(); - JMenuItem jmiTableRowDel = new JMenuItem(Translatrix.getTranslationString("DeleteTableRow")); jmiTableRowDel.setActionCommand("deletetablerow"); jmiTableRowDel.addActionListener(this); jMenuTable.add(jmiTableRowDel); - JMenuItem jmiTableColDel = new JMenuItem(Translatrix.getTranslationString("DeleteTableColumn")); jmiTableColDel.setActionCommand("deletetablecolumn"); jmiTableColDel.addActionListener(this); jMenuTable.add(jmiTableColDel); - - /* FORMS Menu */ - jMenuForms = new JMenu(Translatrix.getTranslationString("Forms")); - htMenus.put(KEY_MENU_FORMS, jMenuForms); - JMenuItem jmiFormInsertForm = new JMenuItem(Translatrix.getTranslationString("FormInsertForm")); jmiFormInsertForm.setActionCommand("insertform"); jmiFormInsertForm.addActionListener(this); jMenuForms.add(jmiFormInsertForm); - jMenuForms.addSeparator(); - JMenuItem jmiFormTextfield = new JMenuItem(Translatrix.getTranslationString("FormTextfield")); jmiFormTextfield.setActionCommand("inserttextfield"); jmiFormTextfield.addActionListener(this); jMenuForms.add(jmiFormTextfield); - JMenuItem jmiFormTextarea = new JMenuItem(Translatrix.getTranslationString("FormTextarea")); jmiFormTextarea.setActionCommand("inserttextarea"); jmiFormTextarea.addActionListener(this); jMenuForms.add(jmiFormTextarea); - JMenuItem jmiFormCheckbox = new JMenuItem(Translatrix.getTranslationString("FormCheckbox")); jmiFormCheckbox.setActionCommand("insertcheckbox"); jmiFormCheckbox.addActionListener(this); jMenuForms.add(jmiFormCheckbox); - JMenuItem jmiFormRadio = new JMenuItem(Translatrix.getTranslationString("FormRadio")); jmiFormRadio.setActionCommand("insertradiobutton"); jmiFormRadio.addActionListener(this); jMenuForms.add(jmiFormRadio); - JMenuItem jmiFormPassword = new JMenuItem(Translatrix.getTranslationString("FormPassword")); jmiFormPassword.setActionCommand("insertpassword"); jmiFormPassword.addActionListener(this); jMenuForms.add(jmiFormPassword); - jMenuForms.addSeparator(); - JMenuItem jmiFormButton = new JMenuItem(Translatrix.getTranslationString("FormButton")); jmiFormButton.setActionCommand("insertbutton"); jmiFormButton.addActionListener(this); jMenuForms.add(jmiFormButton); - JMenuItem jmiFormButtonSubmit = new JMenuItem(Translatrix.getTranslationString("FormButtonSubmit")); jmiFormButtonSubmit.setActionCommand("insertbuttonsubmit"); jmiFormButtonSubmit.addActionListener(this); jMenuForms.add(jmiFormButtonSubmit); - JMenuItem jmiFormButtonReset = new JMenuItem(Translatrix.getTranslationString("FormButtonReset")); jmiFormButtonReset.setActionCommand("insertbuttonreset"); jmiFormButtonReset.addActionListener(this); jMenuForms.add(jmiFormButtonReset); - - /* SEARCH Menu */ - jMenuSearch = new JMenu(Translatrix.getTranslationString("Search")); - htMenus.put(KEY_MENU_SEARCH, jMenuSearch); - JMenuItem jmiFind = new JMenuItem(Translatrix.getTranslationString("SearchFind")); jmiFind.setActionCommand("find"); jmiFind.addActionListener(this); jmiFind.setAccelerator(KeyStroke.getKeyStroke('F', java.awt.Event.CTRL_MASK, false)); jMenuSearch.add(jmiFind); - JMenuItem jmiFindAgain = new JMenuItem(Translatrix.getTranslationString("SearchFindAgain")); jmiFindAgain.setActionCommand("findagain"); jmiFindAgain.addActionListener(this); jmiFindAgain.setAccelerator(KeyStroke.getKeyStroke('G', java.awt.Event.CTRL_MASK, false)); jMenuSearch.add(jmiFindAgain); - JMenuItem jmiReplace = new JMenuItem(Translatrix.getTranslationString("SearchReplace")); jmiReplace.setActionCommand("replace"); jmiReplace.addActionListener(this); jmiReplace.setAccelerator(KeyStroke.getKeyStroke('R', java.awt.Event.CTRL_MASK, false)); jMenuSearch.add(jmiReplace); - - /* HELP Menu */ - jMenuHelp = new JMenu(Translatrix.getTranslationString("Help")); - htMenus.put(KEY_MENU_HELP, jMenuHelp); - JMenuItem jmiAbout = new JMenuItem(Translatrix.getTranslationString("About")); jmiAbout.setActionCommand("helpabout"); jmiAbout.addActionListener(this); jMenuHelp.add(jmiAbout); - - /* DEBUG Menu */ - jMenuDebug = new JMenu(Translatrix.getTranslationString("Debug")); - htMenus.put(KEY_MENU_DEBUG, jMenuDebug); - JMenuItem jmiDesc = new JMenuItem(Translatrix.getTranslationString("DescribeDoc")); jmiDesc.setActionCommand("describe"); jmiDesc.addActionListener(this); jMenuDebug.add(jmiDesc); - JMenuItem jmiDescCSS = new JMenuItem(Translatrix.getTranslationString("DescribeCSS")); jmiDescCSS.setActionCommand("describecss"); jmiDescCSS.addActionListener(this); jMenuDebug.add(jmiDescCSS); - JMenuItem jmiTag = new JMenuItem(Translatrix.getTranslationString("WhatTags")); jmiTag.setActionCommand("whattags"); jmiTag.addActionListener(this); jMenuDebug.add(jmiTag); - - /* Create menubar and add menus */ - jMenuBar = new JMenuBar(); - jMenuBar.add(jMenuFile); - jMenuBar.add(jMenuEdit); - jMenuBar.add(jMenuView); - jMenuBar.add(jMenuFont); - jMenuBar.add(jMenuFormat); - jMenuBar.add(jMenuSearch); - jMenuBar.add(jMenuInsert); - jMenuBar.add(jMenuTable); - jMenuBar.add(jMenuForms); - jMenuBar.add(jMenuHelp); - if(debugMode) - { - jMenuBar.add(jMenuDebug); - } - - /* Create the toolbar */ - jToolBar = new JToolBar(JToolBar.HORIZONTAL); - jToolBar.setFloatable(false); - jbtnNewHTML = new JButtonNoFocus(getEkitIcon("New")); jbtnNewHTML.setToolTipText(Translatrix.getTranslationString("NewDocument")); jbtnNewHTML.setActionCommand("newdoc"); jbtnNewHTML.addActionListener(this); jToolBar.add(jbtnNewHTML); htTools.put(KEY_TOOL_NEW, jbtnNewHTML); - jbtnOpenHTML = new JButtonNoFocus(getEkitIcon("Open")); jbtnOpenHTML.setToolTipText(Translatrix.getTranslationString("OpenDocument")); jbtnOpenHTML.setActionCommand("openhtml"); jbtnOpenHTML.addActionListener(this); jToolBar.add(jbtnOpenHTML); htTools.put(KEY_TOOL_OPEN, jbtnOpenHTML); - jbtnSaveHTML = new JButtonNoFocus(getEkitIcon("Save")); jbtnSaveHTML.setToolTipText(Translatrix.getTranslationString("SaveDocument")); jbtnSaveHTML.setActionCommand("saveas"); jbtnSaveHTML.addActionListener(this); jToolBar.add(jbtnSaveHTML); htTools.put(KEY_TOOL_SAVE, jbtnSaveHTML); - jToolBar.add(new JToolBar.Separator()); - jbtnCut = new JButtonNoFocus(new DefaultEditorKit.CutAction()); jbtnCut.setIcon(getEkitIcon("Cut")); jbtnCut.setText(null); jbtnCut.setToolTipText(Translatrix.getTranslationString("Cut")); jToolBar.add(jbtnCut); htTools.put(KEY_TOOL_CUT, jbtnCut); - jbtnCopy = new JButtonNoFocus(new DefaultEditorKit.CopyAction()); jbtnCopy.setIcon(getEkitIcon("Copy")); jbtnCopy.setText(null); jbtnCopy.setToolTipText(Translatrix.getTranslationString("Copy")); jToolBar.add(jbtnCopy); htTools.put(KEY_TOOL_COPY, jbtnCopy); - jbtnPaste = new JButtonNoFocus(new DefaultEditorKit.PasteAction()); jbtnPaste.setIcon(getEkitIcon("Paste")); jbtnPaste.setText(null); jbtnPaste.setToolTipText(Translatrix.getTranslationString("Paste")); jToolBar.add(jbtnPaste); htTools.put(KEY_TOOL_PASTE, jbtnPaste); - jToolBar.add(new JToolBar.Separator()); - jbtnBold = new JButtonNoFocus(actionFontBold); jbtnBold.setIcon(getEkitIcon("Bold")); jbtnBold.setText(null); jbtnBold.setToolTipText(Translatrix.getTranslationString("FontBold")); jToolBar.add(jbtnBold); htTools.put(KEY_TOOL_BOLD, jbtnBold); - jbtnItalic = new JButtonNoFocus(actionFontItalic); jbtnItalic.setIcon(getEkitIcon("Italic")); jbtnItalic.setText(null); jbtnItalic.setToolTipText(Translatrix.getTranslationString("FontItalic")); jToolBar.add(jbtnItalic); htTools.put(KEY_TOOL_ITALIC, jbtnItalic); - jbtnUnderline = new JButtonNoFocus(actionFontUnderline); jbtnUnderline.setIcon(getEkitIcon("Underline")); jbtnUnderline.setText(null); jbtnUnderline.setToolTipText(Translatrix.getTranslationString("FontUnderline")); jToolBar.add(jbtnUnderline); htTools.put(KEY_TOOL_UNDERLINE, jbtnUnderline); - jbtnStrike = new JButtonNoFocus(actionFontStrike); jbtnStrike.setIcon(getEkitIcon("Strike")); jbtnStrike.setText(null); jbtnStrike.setToolTipText(Translatrix.getTranslationString("FontStrike")); jToolBar.add(jbtnStrike); htTools.put(KEY_TOOL_STRIKE, jbtnStrike); - jbtnSuperscript = new JButtonNoFocus(actionFontSuperscript); jbtnSuperscript.setIcon(getEkitIcon("Super")); jbtnSuperscript.setText(null); jbtnSuperscript.setToolTipText(Translatrix.getTranslationString("FontSuperscript")); jToolBar.add(jbtnSuperscript); htTools.put(KEY_TOOL_SUPER, jbtnSuperscript); - jbtnSubscript = new JButtonNoFocus(actionFontSubscript); jbtnSubscript.setIcon(getEkitIcon("Sub")); jbtnSubscript.setText(null); jbtnSubscript.setToolTipText(Translatrix.getTranslationString("FontSubscript")); jToolBar.add(jbtnSubscript); htTools.put(KEY_TOOL_SUB, jbtnSubscript); - jbtnUList = new JButtonNoFocus(actionListUnordered); jbtnUList.setIcon(getEkitIcon("UList")); jbtnUList.setText(null); jbtnUList.setToolTipText(Translatrix.getTranslationString("ListUnordered")); jToolBar.add(jbtnUList); htTools.put(KEY_TOOL_ULIST, jbtnUList); - jbtnOList = new JButtonNoFocus(actionListOrdered); jbtnOList.setIcon(getEkitIcon("OList")); jbtnOList.setText(null); jbtnOList.setToolTipText(Translatrix.getTranslationString("ListOrdered")); jToolBar.add(jbtnOList); htTools.put(KEY_TOOL_OLIST, jbtnOList); - jbtnClearFormat = new JButtonNoFocus(actionClearFormat); jbtnClearFormat.setIcon(getEkitIcon("ClearFormat")); jbtnClearFormat.setText(null); jbtnClearFormat.setToolTipText(Translatrix.getTranslationString("FormatClear")); jToolBar.add(jbtnClearFormat); htTools.put(KEY_TOOL_CLEAR, jbtnClearFormat); - jToolBar.add(new JToolBar.Separator()); - jbtnAnchor = new JButtonNoFocus(actionInsertAnchor); jbtnAnchor.setIcon(getEkitIcon("Anchor")); jbtnAnchor.setText(null); jbtnAnchor.setToolTipText(Translatrix.getTranslationString("ToolAnchor")); jToolBar.add(jbtnAnchor); htTools.put(KEY_TOOL_ANCHOR, jbtnAnchor); - jToolBar.add(new JToolBar.Separator()); - jtbtnViewSource = new JToggleButtonNoFocus(getEkitIcon("Source")); jtbtnViewSource.setText(null); jtbtnViewSource.setToolTipText(Translatrix.getTranslationString("ViewSource")); jtbtnViewSource.setActionCommand("viewsource"); jtbtnViewSource.addActionListener(this); jtbtnViewSource.setPreferredSize(jbtnAnchor.getPreferredSize()); jtbtnViewSource.setMinimumSize(jbtnAnchor.getMinimumSize()); jtbtnViewSource.setMaximumSize(jbtnAnchor.getMaximumSize()); jToolBar.add(jtbtnViewSource); htTools.put(KEY_TOOL_SOURCE, jtbtnViewSource); - jToolBar.add(new JToolBar.Separator()); - jcmbStyleSelector = new JComboBoxNoFocus(); jToolBar.add(jcmbStyleSelector); jcmbStyleSelector.setAction(new StylesAction(jcmbStyleSelector)); htTools.put(KEY_TOOL_STYLES, jcmbStyleSelector); - - /* Create the scroll area for the text pane */ - JScrollPane jspViewport = new JScrollPane(jtpMain); - jspViewport.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); - jspViewport.setPreferredSize(new Dimension(400, 400)); - jspViewport.setMinimumSize(new Dimension(32, 32)); - - /* Create the scroll area for the source viewer */ - jspSource = new JScrollPane(jtpSource); - jspSource.setPreferredSize(new Dimension(400, 100)); - jspSource.setMinimumSize(new Dimension(32, 32)); - - jspltDisplay = new JSplitPane(JSplitPane.VERTICAL_SPLIT); - jspltDisplay.setTopComponent(jspViewport); - if(showViewSource) - { - jspltDisplay.setBottomComponent(jspSource); - } - else - { - jspltDisplay.setBottomComponent(null); - } - - iSplitPos = jspltDisplay.getDividerLocation(); - - registerDocumentStyles(); - - /* Add the components to the app */ - this.setLayout(new BorderLayout()); - this.add(jspltDisplay, BorderLayout.CENTER); - } - - /** Common Constructor - * @param sDocument [String] A text or HTML document to load in the editor upon startup. - * @param sStyleSheet [String] A CSS stylesheet to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(String sDocument, String sStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64) - { - this(sDocument, sStyleSheet, null, null, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false); - } - - /** Default Language Constructor - * @param sDocument [String] A text or HTML document to load in the editor upon startup. - * @param sStyleSheet [String] A CSS stylesheet to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - */ - public EkitCore(String sDocument, String sStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, boolean base64) - { - this(sDocument, sStyleSheet, null, null, showViewSource, showMenuIcons, editModeExclusive, null, null, base64, false); - } - - /** Raw/Base64 Document & Style Sheet URL Constructor (Ideal for EkitApplet) - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(String sRawDocument, URL urlStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64) - { - this(null, null, sRawDocument, urlStyleSheet, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false); - } - - /** Document Constructor - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(String sRawDocument, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64) - { - this(null, null, sRawDocument, null, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false); - } - - /** Default Language & Document Constructor - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - */ - public EkitCore(String sRawDocument, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, boolean base64) - { - this(null, null, sRawDocument, null, showViewSource, showMenuIcons, editModeExclusive, null, null, base64, false); - } - - /** Flags & Language Constructor - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry) - { - this(null, null, null, null, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, false, false); - } - - /** Flags Constructor - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - */ - public EkitCore(boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive) - { - this(null, null, null, null, showViewSource, showMenuIcons, editModeExclusive, null, null, false, false); - } - - /** Language & Debug Constructor - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - * @param debugMode [boolean] Specifies whether to show the Debug menu or not. - */ - public EkitCore(String sLanguage, String sCountry, boolean debugMode) - { - this(null, null, null, null, false, true, true, sLanguage, sCountry, false, debugMode); - } - - /** Language Constructor - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(String sLanguage, String sCountry) - { - this(null, null, null, null, false, true, true, sLanguage, sCountry, false, false); - } - - /** Debug Constructor - * @param debugMode [boolean] Specifies whether to show the Debug menu or not. - */ - public EkitCore(boolean debugMode) - { - this(null, null, null, null, false, true, true, null, null, false, debugMode); - } - - /** Empty Constructor - */ - public EkitCore() - { - this(null, null, null, null, false, true, true, null, null, false, false); - } - - /* ActionListener method */ - public void actionPerformed(ActionEvent ae) - { - try - { - String command = ae.getActionCommand(); - if(command.equals("newdoc")) - { - SimpleInfoDialog sidAsk = new SimpleInfoDialog(this.getFrame(), "", true, Translatrix.getTranslationString("AskNewDocument"), SimpleInfoDialog.QUESTION); - String decision = sidAsk.getDecisionValue(); - if(decision.equals(Translatrix.getTranslationString("DialogAccept"))) - { - if(styleSheet != null) - { - htmlDoc = new ExtendedHTMLDocument(styleSheet); - } - else - { - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - } - jtpMain.setText(""); - jtpSource.setText(jtpMain.getText()); - registerDocument(htmlDoc); - currentFile = null; - updateTitle(); - } - } - else if(command.equals("openhtml")) - { - openDocument(null); - } - else if(command.equals("opencss")) - { - openStyleSheet(null); - } - else if(command.equals("openb64")) - { - openDocumentBase64(null); - } - else if(command.equals("save")) - { - writeOut((HTMLDocument)(jtpMain.getDocument()), currentFile); - updateTitle(); - } - else if(command.equals("saveas")) - { - writeOut((HTMLDocument)(jtpMain.getDocument()), null); - } - else if(command.equals("savebody")) - { - writeOutFragment((HTMLDocument)(jtpMain.getDocument()),"body"); - } - else if(command.equals("savertf")) - { - writeOutRTF((StyledDocument)(jtpMain.getStyledDocument())); - } - else if(command.equals("saveb64")) - { - writeOutBase64(jtpSource.getText()); - } - else if(command.equals("textcut")) - { - if(jspSource.isShowing() && jtpSource.hasFocus()) - { - jtpSource.cut(); - } - else - { - jtpMain.cut(); - } - } - else if(command.equals("textcopy")) - { - if(jspSource.isShowing() && jtpSource.hasFocus()) - { - jtpSource.copy(); - } - else - { - jtpMain.copy(); - } - } - else if(command.equals("textpaste")) - { - if(jspSource.isShowing() && jtpSource.hasFocus()) - { - jtpSource.paste(); - } - else - { - jtpMain.paste(); - } - } - else if(command.equals("describe")) - { - System.out.println("------------DOCUMENT------------"); - System.out.println("Content Type : " + jtpMain.getContentType()); - System.out.println("Editor Kit : " + jtpMain.getEditorKit()); - System.out.println("Doc Tree :"); - System.out.println(""); - describeDocument(jtpMain.getStyledDocument()); - System.out.println("--------------------------------"); - System.out.println(""); - } - else if(command.equals("describecss")) - { - System.out.println("-----------STYLESHEET-----------"); - System.out.println("Stylesheet Rules"); - Enumeration rules = styleSheet.getStyleNames(); - while(rules.hasMoreElements()) - { - String ruleName = (String)(rules.nextElement()); - Style styleRule = styleSheet.getStyle(ruleName); - System.out.println(styleRule.toString()); - } - System.out.println("--------------------------------"); - System.out.println(""); - } - else if(command.equals("whattags")) - { - System.out.println("Caret Position : " + jtpMain.getCaretPosition()); - AttributeSet attribSet = jtpMain.getCharacterAttributes(); - Enumeration attribs = attribSet.getAttributeNames(); - System.out.println("Attributes : "); - while(attribs.hasMoreElements()) - { - String attribName = attribs.nextElement().toString(); - System.out.println(" " + attribName + " | " + attribSet.getAttribute(attribName)); - } - } - else if(command.equals("toggletoolbar")) - { - jToolBar.setVisible(jcbmiViewToolbar.isSelected()); - } - else if(command.equals("viewsource")) - { - toggleSourceWindow(); - } - else if(command.equals("serialize")) - { - serializeOut((HTMLDocument)(jtpMain.getDocument())); - } - else if(command.equals("readfromser")) - { - serializeIn(); - } - else if(command.equals("inserttable")) - { - String[] fieldNames = { "rows", "cols", "border", "cellspacing", "cellpadding", "width" }; - String[] fieldTypes = { "text", "text", "text", "text", "text", "text" }; - insertTable((Hashtable)null, fieldNames, fieldTypes); - } - else if(command.equals("inserttablerow")) - { - insertTableRow(); - } - else if(command.equals("inserttablecolumn")) - { - insertTableColumn(); - } - else if(command.equals("deletetablerow")) - { - deleteTableRow(); - } - else if(command.equals("deletetablecolumn")) - { - deleteTableColumn(); - } - else if(command.equals("insertbreak")) - { - insertBreak(); - } - else if(command.equals("insertlocalimage")) - { - insertLocalImage(null); - } - else if(command.equals("insertserverimage")) - { - insertServerImage(); - } - else if(command.equals("insertnbsp")) - { - insertNonbreakingSpace(); - } - else if(command.equals("insertform")) - { - String[] fieldNames = { "name", "method", "enctype" }; - String[] fieldTypes = { "text", "combo", "text" }; - String[] fieldValues = { "", "POST,GET", "text" }; - insertFormElement(HTML.Tag.FORM, "form", (Hashtable)null, fieldNames, fieldTypes, fieldValues, true); - } - else if(command.equals("inserttextfield")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "text"); - String[] fieldNames = { "name", "value", "size", "maxlength" }; - String[] fieldTypes = { "text", "text", "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("inserttextarea")) - { - String[] fieldNames = { "name", "rows", "cols" }; - String[] fieldTypes = { "text", "text", "text" }; - insertFormElement(HTML.Tag.TEXTAREA, "textarea", (Hashtable)null, fieldNames, fieldTypes, true); - } - else if(command.equals("insertcheckbox")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "checkbox"); - String[] fieldNames = { "name", "checked" }; - String[] fieldTypes = { "text", "bool" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertradiobutton")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "radio"); - String[] fieldNames = { "name", "checked" }; - String[] fieldTypes = { "text", "bool" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertpassword")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "password"); - String[] fieldNames = { "name", "value", "size", "maxlength" }; - String[] fieldTypes = { "text", "text", "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertbutton")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "button"); - String[] fieldNames = { "name", "value" }; - String[] fieldTypes = { "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertbuttonsubmit")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "submit"); - String[] fieldNames = { "name", "value" }; - String[] fieldTypes = { "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertbuttonreset")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "reset"); - String[] fieldNames = { "name", "value" }; - String[] fieldTypes = { "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("find")) - { - doSearch((String)null, (String)null, false, lastSearchCaseSetting, lastSearchTopSetting); - } - else if(command.equals("findagain")) - { - doSearch(lastSearchFindTerm, (String)null, false, lastSearchCaseSetting, false); - } - else if(command.equals("replace")) - { - doSearch((String)null, (String)null, true, lastSearchCaseSetting, lastSearchTopSetting); - } - else if(command.equals("exit")) - { - this.dispose(); - } - else if(command.equals("helpabout")) - { - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("About"), true, Translatrix.getTranslationString("AboutMessage"), SimpleInfoDialog.INFO); - } - } - catch(IOException ioe) - { - logException("IOException in actionPerformed method", ioe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorIOException"), SimpleInfoDialog.ERROR); - } - catch(BadLocationException ble) - { - logException("BadLocationException in actionPerformed method", ble); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorBadLocationException"), SimpleInfoDialog.ERROR); - } - catch(NumberFormatException nfe) - { - logException("NumberFormatException in actionPerformed method", nfe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorNumberFormatException"), SimpleInfoDialog.ERROR); - } - catch(ClassNotFoundException cnfe) - { - logException("ClassNotFound Exception in actionPerformed method", cnfe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorClassNotFoundException "), SimpleInfoDialog.ERROR); - } - catch(RuntimeException re) - { - logException("RuntimeException in actionPerformed method", re); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorRuntimeException"), SimpleInfoDialog.ERROR); - } - } - - /* KeyListener methods */ - public void keyTyped(KeyEvent ke) - { - Element elem; - String selectedText; - int pos = this.getCaretPosition(); - int repos = -1; - if(ke.getKeyChar() == KeyEvent.VK_BACK_SPACE) - { - try - { - if(pos > 0) - { - if((selectedText = jtpMain.getSelectedText()) != null) - { - htmlUtilities.delete(); - return; - } - else - { - int sOffset = htmlDoc.getParagraphElement(pos).getStartOffset(); - if(sOffset == jtpMain.getSelectionStart()) - { - boolean content = true; - if(htmlUtilities.checkParentsTag(HTML.Tag.LI)) - { - elem = htmlUtilities.getListItemParent(); - content = false; - int so = elem.getStartOffset(); - int eo = elem.getEndOffset(); - if(so + 1 < eo) - { - char[] temp = jtpMain.getText(so, eo - so).toCharArray(); - for(int i=0; i < temp.length; i++) - { - if(!(new Character(temp[i])).isWhitespace(temp[i])) - { - content = true; - } - } - } - if(!content) - { - Element listElement = elem.getParentElement(); - htmlUtilities.removeTag(elem, true); - this.setCaretPosition(sOffset - 1); - return; - } - else - { - jtpMain.setCaretPosition(jtpMain.getCaretPosition() - 1); - jtpMain.moveCaretPosition(jtpMain.getCaretPosition() - 2); - jtpMain.replaceSelection(""); - return; - } - } - else if(htmlUtilities.checkParentsTag(HTML.Tag.TABLE)) - { - jtpMain.setCaretPosition(jtpMain.getCaretPosition() - 1); - ke.consume(); - return; - } - } - jtpMain.replaceSelection(""); - return; - } - } - } - catch (BadLocationException ble) - { - logException("BadLocationException in keyTyped method", ble); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorBadLocationException"), SimpleInfoDialog.ERROR); - } - catch (IOException ioe) - { - logException("IOException in keyTyped method", ioe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorIOException"), SimpleInfoDialog.ERROR); - } - } - else if(ke.getKeyChar() == KeyEvent.VK_ENTER) - { - try - { - if(htmlUtilities.checkParentsTag(HTML.Tag.UL) == true | htmlUtilities.checkParentsTag(HTML.Tag.OL) == true) - { - elem = htmlUtilities.getListItemParent(); - int so = elem.getStartOffset(); - int eo = elem.getEndOffset(); - char[] temp = this.getTextPane().getText(so,eo-so).toCharArray(); - boolean content = false; - for(int i=0;i= 0); - j = end; - do - { - j++; - if(!new Character(temp[j]).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(elem); - } - else - { - if(this.getCaretPosition() + 1 == elem.getEndOffset()) - { - insertListStyle(elem); - this.setCaretPosition(pos - repos); - } - else - { - int caret = this.getCaretPosition(); - String tempString = this.getTextPane().getText(caret, eo - caret); - this.getTextPane().select(caret, eo - 1); - this.getTextPane().replaceSelection(""); - htmlUtilities.insertListElement(tempString); - Element newLi = htmlUtilities.getListItemParent(); - this.setCaretPosition(newLi.getEndOffset() - 1); - } - } - } - } - catch (BadLocationException ble) - { - logException("BadLocationException in keyTyped method", ble); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorBadLocationException"), SimpleInfoDialog.ERROR); - } - catch (IOException ioe) - { - logException("IOException in keyTyped method", ioe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorIOException"), SimpleInfoDialog.ERROR); - } - } - } - public void keyPressed(KeyEvent e) {} - public void keyReleased(KeyEvent e) {} - - public void insertListStyle(Element element) - throws BadLocationException,IOException - { - if(element.getParentElement().getName() == "ol") - { - actionListOrdered.actionPerformed(new ActionEvent(new Object(), 0, "newListPoint")); - } - else - { - actionListUnordered.actionPerformed(new ActionEvent(new Object(), 0, "newListPoint")); - } - } - - /* DocumentListener methods */ - public void changedUpdate(DocumentEvent de) { handleDocumentChange(de); } - public void insertUpdate(DocumentEvent de) { handleDocumentChange(de); } - public void removeUpdate(DocumentEvent de) { handleDocumentChange(de); } - public void handleDocumentChange(DocumentEvent de) - { - if(!exclusiveEdit) - { - if(jspSource.isShowing()) - { - if(de.getDocument() instanceof HTMLDocument || de.getDocument() instanceof ExtendedHTMLDocument) - { - jtpSource.getDocument().removeDocumentListener(this); - jtpSource.setText(jtpMain.getText()); - jtpSource.getDocument().addDocumentListener(this); - } - else if(de.getDocument() instanceof PlainDocument || de.getDocument() instanceof DefaultStyledDocument) - { - jtpMain.getDocument().removeDocumentListener(this); - jtpMain.setText(jtpSource.getText()); - jtpMain.getDocument().addDocumentListener(this); - } - } - } - } - - /** Method for setting a document as the current document for the text pane - * and re-registering the controls and settings for it - */ - public void registerDocument(ExtendedHTMLDocument htmlDoc) - { - jtpMain.setDocument(htmlDoc); - jtpMain.getDocument().addUndoableEditListener(new CustomUndoableEditListener()); - jtpMain.getDocument().addDocumentListener(this); - purgeUndos(); - registerDocumentStyles(); - } - - /** Method for locating the available CSS style for the document and adding - * them to the styles selector - */ - public void registerDocumentStyles() - { - if(jcmbStyleSelector == null || htmlDoc == null) - { - return; - } - jcmbStyleSelector.setEnabled(false); - jcmbStyleSelector.removeAllItems(); - jcmbStyleSelector.addItem(Translatrix.getTranslationString("NoCSSStyle")); - for(Enumeration e = htmlDoc.getStyleNames(); e.hasMoreElements();) - { - String name = (String) e.nextElement(); - if(name.length() > 0 && name.charAt(0) == '.') - { - jcmbStyleSelector.addItem(name.substring(1)); - } - } - jcmbStyleSelector.setEnabled(true); - } - - /** Method for inserting an HTML Table - */ - private void insertTable(Hashtable attribs, String[] fieldNames, String[] fieldTypes) - throws IOException, BadLocationException, RuntimeException, NumberFormatException - { - int caretPos = jtpMain.getCaretPosition(); - StringBuffer compositeElement = new StringBuffer(" 0) - { - Enumeration attribEntries = attribs.keys(); - while(attribEntries.hasMoreElements()) - { - Object entryKey = attribEntries.nextElement(); - Object entryValue = attribs.get(entryKey); - if(entryValue != null && entryValue != "") - { - compositeElement.append(" " + entryKey + "=" + '"' + entryValue + '"'); - } - } - } - int rows = 0; - int cols = 0; - if(fieldNames != null && fieldNames.length > 0) - { - PropertiesDialog propertiesDialog = new PropertiesDialog(this.getFrame(), fieldNames, fieldTypes, Translatrix.getTranslationString("FormDialogTitle"), true); - propertiesDialog.show(); - String decision = propertiesDialog.getDecisionValue(); - if(decision.equals(Translatrix.getTranslationString("DialogCancel"))) - { - propertiesDialog.dispose(); - propertiesDialog = null; - return; - } - else - { - for(int iter = 0; iter < fieldNames.length; iter++) - { - String fieldName = fieldNames[iter]; - String propValue = propertiesDialog.getFieldValue(fieldName); - if(propValue != null && propValue != "" && propValue.length() > 0) - { - if(fieldName.equals("rows")) - { - rows = Integer.parseInt(propValue); - } - else if(fieldName.equals("cols")) - { - cols = Integer.parseInt(propValue); - } - else - { - compositeElement.append(" " + fieldName + "=" + '"' + propValue + '"'); - } - } - } - } - propertiesDialog.dispose(); - propertiesDialog = null; - } - compositeElement.append(">"); - for(int i = 0; i < rows; i++) - { - compositeElement.append(""); - for(int j = 0; j < cols; j++) - { - compositeElement.append(""); - } - compositeElement.append(""); - } - compositeElement.append("

 

"); - htmlKit.insertHTML(htmlDoc, caretPos, compositeElement.toString(), 0, 0, HTML.Tag.TABLE); - jtpMain.setCaretPosition(caretPos + 1); - refreshOnUpdate(); - } - - /** Method for inserting a row into an HTML Table - */ - private void insertTableRow() - { - int caretPos = jtpMain.getCaretPosition(); - Element element = htmlDoc.getCharacterElement(jtpMain.getCaretPosition()); - Element elementParent = element.getParentElement(); - int startPoint = -1; - int columnCount = -1; - while(elementParent != null && !elementParent.getName().equals("body")) - { - if(elementParent.getName().equals("tr")) - { - startPoint = elementParent.getStartOffset(); - columnCount = elementParent.getElementCount(); - break; - } - else - { - elementParent = elementParent.getParentElement(); - } - } - if(startPoint > -1 && columnCount > -1) - { - jtpMain.setCaretPosition(startPoint); - StringBuffer sRow = new StringBuffer(); - sRow.append(""); - for(int i = 0; i < columnCount; i++) - { - sRow.append(""); - } - sRow.append(""); - ActionEvent actionEvent = new ActionEvent(jtpMain, 0, "insertTableRow"); - new HTMLEditorKit.InsertHTMLTextAction("insertTableRow", sRow.toString(), HTML.Tag.TABLE, HTML.Tag.TR).actionPerformed(actionEvent); - refreshOnUpdate(); - jtpMain.setCaretPosition(caretPos); - } - } - - /** Method for inserting a column into an HTML Table - */ - private void insertTableColumn() - { - int caretPos = jtpMain.getCaretPosition(); - Element element = htmlDoc.getCharacterElement(jtpMain.getCaretPosition()); - Element elementParent = element.getParentElement(); - int startPoint = -1; - int rowCount = -1; - int cellOffset = 0; - while(elementParent != null && !elementParent.getName().equals("body")) - { - if(elementParent.getName().equals("table")) - { - startPoint = elementParent.getStartOffset(); - rowCount = elementParent.getElementCount(); - break; - } - else if(elementParent.getName().equals("tr")) - { - int rowStart = elementParent.getStartOffset(); - int rowCells = elementParent.getElementCount(); - for(int i = 0; i < rowCells; i++) - { - Element currentCell = elementParent.getElement(i); - if(jtpMain.getCaretPosition() >= currentCell.getStartOffset() && jtpMain.getCaretPosition() <= currentCell.getEndOffset()) - { - cellOffset = i; - } - } - elementParent = elementParent.getParentElement(); - } - else - { - elementParent = elementParent.getParentElement(); - } - } - if(startPoint > -1 && rowCount > -1) - { - jtpMain.setCaretPosition(startPoint); - String sCell = ""; - ActionEvent actionEvent = new ActionEvent(jtpMain, 0, "insertTableCell"); - for(int i = 0; i < rowCount; i++) - { - Element row = elementParent.getElement(i); - Element whichCell = row.getElement(cellOffset); - jtpMain.setCaretPosition(whichCell.getStartOffset()); - new HTMLEditorKit.InsertHTMLTextAction("insertTableCell", sCell, HTML.Tag.TR, HTML.Tag.TD, HTML.Tag.TH, HTML.Tag.TD).actionPerformed(actionEvent); - } - refreshOnUpdate(); - jtpMain.setCaretPosition(caretPos); - } - } - - /** Method for inserting a cell into an HTML Table - */ - private void insertTableCell() - { - String sCell = ""; - ActionEvent actionEvent = new ActionEvent(jtpMain, 0, "insertTableCell"); - new HTMLEditorKit.InsertHTMLTextAction("insertTableCell", sCell, HTML.Tag.TR, HTML.Tag.TD, HTML.Tag.TH, HTML.Tag.TD).actionPerformed(actionEvent); - refreshOnUpdate(); - } - - /** Method for deleting a row from an HTML Table - */ - private void deleteTableRow() - throws BadLocationException - { - int caretPos = jtpMain.getCaretPosition(); - Element element = htmlDoc.getCharacterElement(jtpMain.getCaretPosition()); - Element elementParent = element.getParentElement(); - int startPoint = -1; - int endPoint = -1; - while(elementParent != null && !elementParent.getName().equals("body")) - { - if(elementParent.getName().equals("tr")) - { - startPoint = elementParent.getStartOffset(); - endPoint = elementParent.getEndOffset(); - break; - } - else - { - elementParent = elementParent.getParentElement(); - } - } - if(startPoint > -1 && endPoint > startPoint) - { - htmlDoc.remove(startPoint, endPoint - startPoint); - jtpMain.setDocument(htmlDoc); - registerDocument(htmlDoc); - refreshOnUpdate(); - if(caretPos >= htmlDoc.getLength()) - { - caretPos = htmlDoc.getLength() - 1; - } - jtpMain.setCaretPosition(caretPos); - } - } - - /** Method for deleting a column from an HTML Table - */ - private void deleteTableColumn() - throws BadLocationException - { - int caretPos = jtpMain.getCaretPosition(); - Element element = htmlDoc.getCharacterElement(jtpMain.getCaretPosition()); - Element elementParent = element.getParentElement(); - Element elementCell = (Element)null; - Element elementRow = (Element)null; - Element elementTable = (Element)null; - // Locate the table, row, and cell location of the cursor - while(elementParent != null && !elementParent.getName().equals("body")) - { - if(elementParent.getName().equals("td")) - { - elementCell = elementParent; - } - else if(elementParent.getName().equals("tr")) - { - elementRow = elementParent; - } - else if(elementParent.getName().equals("table")) - { - elementTable = elementParent; - } - elementParent = elementParent.getParentElement(); - } - int whichColumn = -1; - if(elementCell != null && elementRow != null && elementTable != null) - { - // Find the column the cursor is in - for(int i = 0; i < elementRow.getElementCount(); i++) - { - if(elementCell == elementRow.getElement(i)) - { - whichColumn = i; - } - } - if(whichColumn > -1) - { - // Iterate through the table rows, deleting cells from the indicated column - for(int i = 0; i < elementTable.getElementCount(); i++) - { - elementRow = elementTable.getElement(i); - elementCell = (elementRow.getElementCount() > whichColumn ? elementRow.getElement(whichColumn) : elementRow.getElement(elementRow.getElementCount() - 1)); - int columnCellStart = elementCell.getStartOffset(); - int columnCellEnd = elementCell.getEndOffset(); - htmlDoc.remove(columnCellStart, columnCellEnd - columnCellStart); - } - jtpMain.setDocument(htmlDoc); - registerDocument(htmlDoc); - refreshOnUpdate(); - if(caretPos >= htmlDoc.getLength()) - { - caretPos = htmlDoc.getLength() - 1; - } - jtpMain.setCaretPosition(caretPos); - } - } - } - - /** Method for inserting a break (BR) element - */ - private void insertBreak() - throws IOException, BadLocationException, RuntimeException - { - int caretPos = jtpMain.getCaretPosition(); - htmlKit.insertHTML(htmlDoc, caretPos, "
", 0, 0, HTML.Tag.BR); - jtpMain.setCaretPosition(caretPos + 1); - } - - /** Method for inserting a non-breaking space ( ) - */ - private void insertNonbreakingSpace() - throws IOException, BadLocationException, RuntimeException - { - int caretPos = jtpMain.getCaretPosition(); - htmlDoc.insertString(caretPos, "\240", jtpMain.getInputAttributes()); - jtpMain.setCaretPosition(caretPos + 1); - } - - /** Method for inserting a form element - */ - private void insertFormElement(HTML.Tag baseTag, String baseElement, Hashtable attribs, String[] fieldNames, String[] fieldTypes, String[] fieldValues, boolean hasClosingTag) - throws IOException, BadLocationException, RuntimeException - { - int caretPos = jtpMain.getCaretPosition(); - StringBuffer compositeElement = new StringBuffer("<" + baseElement); - if(attribs != null && attribs.size() > 0) - { - Enumeration attribEntries = attribs.keys(); - while(attribEntries.hasMoreElements()) - { - Object entryKey = attribEntries.nextElement(); - Object entryValue = attribs.get(entryKey); - if(entryValue != null && entryValue != "") - { - compositeElement.append(" " + entryKey + "=" + '"' + entryValue + '"'); - } - } - } - if(fieldNames != null && fieldNames.length > 0) - { - PropertiesDialog propertiesDialog = new PropertiesDialog(this.getFrame(), fieldNames, fieldTypes, fieldValues, Translatrix.getTranslationString("FormDialogTitle"), true); - propertiesDialog.show(); - String decision = propertiesDialog.getDecisionValue(); - if(decision.equals(Translatrix.getTranslationString("DialogCancel"))) - { - propertiesDialog.dispose(); - propertiesDialog = null; - return; - } - else - { - for(int iter = 0; iter < fieldNames.length; iter++) - { - String fieldName = fieldNames[iter]; - String propValue = propertiesDialog.getFieldValue(fieldName); - if(propValue != null && propValue.length() > 0) - { - if(fieldName.equals("checked")) - { - if(propValue.equals("true")) - { - compositeElement.append(" " + fieldName + "=" + '"' + propValue + '"'); - } - } - else - { - compositeElement.append(" " + fieldName + "=" + '"' + propValue + '"'); - } - } - } - } - propertiesDialog.dispose(); - propertiesDialog = null; - } - // --- Convenience for editing, this makes the FORM visible - if(useFormIndicator && baseElement.equals("form")) - { - compositeElement.append(" bgcolor=" + '"' + clrFormIndicator + '"'); - } - // --- END - compositeElement.append(">"); - if(hasClosingTag) - { - compositeElement.append(""); - } - if(baseTag == HTML.Tag.FORM) - { - compositeElement.append("

 

"); - } - htmlKit.insertHTML(htmlDoc, caretPos, compositeElement.toString(), 0, 0, baseTag); - // jtpMain.setCaretPosition(caretPos + 1); - refreshOnUpdate(); - } - - /** Alternate method call for inserting a form element - */ - private void insertFormElement(HTML.Tag baseTag, String baseElement, Hashtable attribs, String[] fieldNames, String[] fieldTypes, boolean hasClosingTag) - throws IOException, BadLocationException, RuntimeException - { - insertFormElement(baseTag, baseElement, attribs, fieldNames, fieldTypes, new String[fieldNames.length], hasClosingTag); - } - - /** Method that handles initial list insertion and deletion - */ - public void manageListElement(Element element) - { - Element h = htmlUtilities.getListItemParent(); - Element listElement = h.getParentElement(); - if(h != null) - { - htmlUtilities.removeTag(h, true); - } - } - - /** Method to initiate a find/replace operation - */ - private void doSearch(String searchFindTerm, String searchReplaceTerm, boolean bIsFindReplace, boolean bCaseSensitive, boolean bStartAtTop) - { - boolean bReplaceAll = false; - JTextPane searchPane = jtpMain; - if(jspSource.isShowing() || jtpSource.hasFocus()) - { - searchPane = jtpSource; - } - if(searchFindTerm == null || (bIsFindReplace && searchReplaceTerm == null)) - { - SearchDialog sdSearchInput = new SearchDialog(this.getFrame(), Translatrix.getTranslationString("SearchDialogTitle"), true, bIsFindReplace, bCaseSensitive, bStartAtTop); - searchFindTerm = sdSearchInput.getFindTerm(); - searchReplaceTerm = sdSearchInput.getReplaceTerm(); - bCaseSensitive = sdSearchInput.getCaseSensitive(); - bStartAtTop = sdSearchInput.getStartAtTop(); - bReplaceAll = sdSearchInput.getReplaceAll(); - } - if(searchFindTerm != null && (!bIsFindReplace || searchReplaceTerm != null)) - { - if(bReplaceAll) - { - int results = findText(searchFindTerm, searchReplaceTerm, bCaseSensitive, 0); - int findOffset = 0; - if(results > -1) - { - while(results > -1) - { - findOffset = findOffset + searchReplaceTerm.length(); - results = findText(searchFindTerm, searchReplaceTerm, bCaseSensitive, findOffset); - } - } - else - { - SimpleInfoDialog sidWarn = new SimpleInfoDialog(this.getFrame(), "", true, Translatrix.getTranslationString("ErrorNoOccurencesFound") + ":\n" + searchFindTerm, SimpleInfoDialog.WARNING); - } - } - else - { - int results = findText(searchFindTerm, searchReplaceTerm, bCaseSensitive, (bStartAtTop ? 0 : searchPane.getCaretPosition())); - if(results == -1) - { - SimpleInfoDialog sidWarn = new SimpleInfoDialog(this.getFrame(), "", true, Translatrix.getTranslationString("ErrorNoMatchFound") + ":\n" + searchFindTerm, SimpleInfoDialog.WARNING); - } - } - lastSearchFindTerm = new String(searchFindTerm); - if(searchReplaceTerm != null) - { - lastSearchReplaceTerm = new String(searchReplaceTerm); - } - else - { - lastSearchReplaceTerm = (String)null; - } - lastSearchCaseSetting = bCaseSensitive; - lastSearchTopSetting = bStartAtTop; - } - } - - /** Method for finding (and optionally replacing) a string in the text - */ - private int findText(String findTerm, String replaceTerm, boolean bCaseSenstive, int iOffset) - { - JTextPane jtpFindSource; - if(jspSource.isShowing() || jtpSource.hasFocus()) - { - jtpFindSource = jtpSource; - } - else - { - jtpFindSource = jtpMain; - } - int searchPlace = -1; - try - { - Document baseDocument = jtpFindSource.getDocument(); - searchPlace = - (bCaseSenstive ? - baseDocument.getText(0, baseDocument.getLength()).indexOf(findTerm, iOffset) : - baseDocument.getText(0, baseDocument.getLength()).toLowerCase().indexOf(findTerm.toLowerCase(), iOffset) - ); - if(searchPlace > -1) - { - if(replaceTerm != null) - { - AttributeSet attribs = null; - if(baseDocument instanceof HTMLDocument) - { - Element element = ((HTMLDocument)baseDocument).getCharacterElement(searchPlace); - attribs = element.getAttributes(); - } - baseDocument.remove(searchPlace, findTerm.length()); - baseDocument.insertString(searchPlace, replaceTerm, attribs); - jtpFindSource.setCaretPosition(searchPlace + replaceTerm.length()); - jtpFindSource.requestFocus(); - jtpFindSource.select(searchPlace, searchPlace + replaceTerm.length()); - } - else - { - jtpFindSource.setCaretPosition(searchPlace + findTerm.length()); - jtpFindSource.requestFocus(); - jtpFindSource.select(searchPlace, searchPlace + findTerm.length()); - } - } - } - catch(BadLocationException ble) - { - logException("BadLocationException in actionPerformed method", ble); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorBadLocationException"), SimpleInfoDialog.ERROR); - } - return searchPlace; - } - - /** Method for inserting an image from a file - */ - private void insertLocalImage(File whatImage) - throws IOException, BadLocationException, RuntimeException - { - if(whatImage == null) - { - whatImage = getImageFromChooser(".", extsIMG, Translatrix.getTranslationString("FiletypeIMG")); - } - if(whatImage != null) - { - int caretPos = jtpMain.getCaretPosition(); - htmlKit.insertHTML(htmlDoc, caretPos, "", 0, 0, HTML.Tag.IMG); - jtpMain.setCaretPosition(caretPos + 1); - refreshOnUpdate(); - } - } - - /** Method for inserting an image from a server - */ - private void insertServerImage() - throws BadLocationException - { - if(ServletURL != null) - { - try - { - URL theServlet = new URL(ServletURL + "?GetImages=" + TreePilotSystemID + "&ImageExtensions=" + TreePilotProperties.getString("ValidImageExtensions")); - URLConnection conn = theServlet.openConnection(); - ObjectInputStream in = new ObjectInputStream(conn.getInputStream()); - String[] imageList = (String[]) in.readObject(); - int caretPos = jtpMain.getCaretPosition(); - ImageDialog imageDialog = new ImageDialog(this, ImageDir + TreePilotSystemID, imageList, "Image Chooser", true); - String selectedImage = imageDialog.getSelectedImage(); - imageDialog.dispose(); - if(selectedImage != null && !selectedImage.equals("")) - { - htmlKit.insertHTML(htmlDoc, caretPos, selectedImage, 0, 0, HTML.Tag.IMG); - jtpMain.setCaretPosition(caretPos + 1); - } - jtpMain.requestFocus(); - in.close(); - } - catch (MalformedURLException mue) - { - System.err.println("MalFormedURLException during insertImage " + mue); - } - catch (IOException ie) - { - System.err.println("IOException during insertImage " + ie); - } - catch (ClassNotFoundException cnfe) - { - System.err.println("ClassNotFoundException during insertImage" + cnfe); - } - } - } - - /** Method for inserting an image - */ - public String insertFile() - { - String selectedFile = null; - if(ServletURL != null) - { - try - { - URL theServlet = new URL(ServletURL + "?GetFiles=" + TreePilotSystemID + "&FileExtensions=" + TreePilotProperties.getString("ValidFileExtensions")); - URLConnection conn = theServlet.openConnection(); - ObjectInputStream in = new ObjectInputStream(conn.getInputStream()); - String[] fileList = (String[]) in.readObject(); - FileDialog fileDialog = new FileDialog(this, ImageDir + TreePilotSystemID, fileList, "File Chooser", true); - selectedFile = fileDialog.getSelectedFile(); - fileDialog.dispose(); - in.close(); - } - catch (MalformedURLException mue) - { - System.err.println("MalFormedURLException during insertFile " + mue); - } - catch (IOException ie) - { - System.err.println("IOException during insertFile " + ie); - } - catch (ClassNotFoundException cnfe) - { - System.err.println("ClassNotFoundException during insertFile" + cnfe); - } - } - return selectedFile; - } - - /** Method for saving text as a complete HTML document - */ - private void writeOut(HTMLDocument doc, File whatFile) - throws IOException, BadLocationException - { - if(whatFile == null) - { - whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsHTML, Translatrix.getTranslationString("FiletypeHTML")); - } - if(whatFile != null) - { - FileWriter fw = new FileWriter(whatFile); - htmlKit.write(fw, doc, 0, doc.getLength()); - fw.flush(); - fw.close(); - currentFile = whatFile; - updateTitle(); - } - refreshOnUpdate(); - } - - /** Method for saving text as an HTML fragment - */ - private void writeOutFragment(HTMLDocument doc, String containingTag) - throws IOException, BadLocationException - { - File whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsHTML, Translatrix.getTranslationString("FiletypeHTML")); - if(whatFile != null) - { - FileWriter fw = new FileWriter(whatFile); -// Element eleBody = locateElementInDocument((StyledDocument)doc, containingTag); -// htmlKit.write(fw, doc, eleBody.getStartOffset(), eleBody.getEndOffset()); - String docTextCase = jtpSource.getText().toLowerCase(); - int tagStart = docTextCase.indexOf("<" + containingTag.toLowerCase()); - int tagStartClose = docTextCase.indexOf(">", tagStart) + 1; - String closeTag = ""; - int tagEndOpen = docTextCase.indexOf(closeTag); - if(tagStartClose < 0) { tagStartClose = 0; } - if(tagEndOpen < 0 || tagEndOpen > docTextCase.length()) { tagEndOpen = docTextCase.length(); } - String bodyText = jtpSource.getText().substring(tagStartClose, tagEndOpen); - fw.write(bodyText, 0, bodyText.length()); - fw.flush(); - fw.close(); - } - refreshOnUpdate(); - } - - /** Method for saving text as an RTF document - */ - private void writeOutRTF(StyledDocument doc) - throws IOException, BadLocationException - { - File whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsRTF, Translatrix.getTranslationString("FiletypeRTF")); - if(whatFile != null) - { - FileOutputStream fos = new FileOutputStream(whatFile); - RTFEditorKit rtfKit = new RTFEditorKit(); - rtfKit.write(fos, doc, 0, doc.getLength()); - fos.flush(); - fos.close(); - } - refreshOnUpdate(); - } - - /** Method for saving text as a Base64 encoded document - */ - private void writeOutBase64(String text) - throws IOException, BadLocationException - { - File whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsB64, Translatrix.getTranslationString("FiletypeB64")); - if(whatFile != null) - { - String base64text = Base64Codec.encode(text); - FileWriter fw = new FileWriter(whatFile); - fw.write(base64text, 0, base64text.length()); - fw.flush(); - fw.close(); - } - refreshOnUpdate(); - } - - /** Method to invoke loading HTML into the app - */ - private void openDocument(File whatFile) - throws IOException, BadLocationException - { - if(whatFile == null) - { - whatFile = getFileFromChooser(".", JFileChooser.OPEN_DIALOG, extsHTML, Translatrix.getTranslationString("FiletypeHTML")); - } - if(whatFile != null) - { - try - { - loadDocument(whatFile, null); - } - catch(ChangedCharSetException ccse) - { - String charsetType = ccse.getCharSetSpec().toLowerCase(); - int pos = charsetType.indexOf("charset"); - if(pos == -1) - { - throw ccse; - } - while(pos < charsetType.length() && charsetType.charAt(pos) != '=') - { - pos++; - } - pos++; // Places file cursor past the equals sign (=) - String whatEncoding = charsetType.substring(pos).trim(); - loadDocument(whatFile, whatEncoding); - } - } - refreshOnUpdate(); - } - - /** Method for loading HTML document into the app, including document encoding setting - */ - private void loadDocument(File whatFile, String whatEncoding) - throws IOException, BadLocationException - { - Reader r = null; - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - try - { - if(whatEncoding == null) - { - r = new InputStreamReader(new FileInputStream(whatFile)); - } - else - { - r = new InputStreamReader(new FileInputStream(whatFile), whatEncoding); - htmlDoc.putProperty("IgnoreCharsetDirective", new Boolean(true)); - } - htmlKit.read(r, htmlDoc, 0); - r.close(); - registerDocument(htmlDoc); - jtpSource.setText(jtpMain.getText()); - currentFile = whatFile; - updateTitle(); - } - finally - { - if(r != null) - { - r.close(); - } - } - } - - /** Method for loading a Base64 encoded document - */ - private void openDocumentBase64(File whatFile) - throws IOException, BadLocationException - { - if(whatFile == null) - { - whatFile = getFileFromChooser(".", JFileChooser.OPEN_DIALOG, extsB64, Translatrix.getTranslationString("FiletypeB64")); - } - if(whatFile != null) - { - FileReader fr = new FileReader(whatFile); - int nextChar = 0; - StringBuffer encodedText = new StringBuffer(); - try - { - while((nextChar = fr.read()) != -1) - { - encodedText.append((char)nextChar); - } - fr.close(); - jtpSource.setText(Base64Codec.decode(encodedText.toString())); - jtpMain.setText(jtpSource.getText()); - registerDocument((ExtendedHTMLDocument)(jtpMain.getDocument())); - } - finally - { - if(fr != null) - { - fr.close(); - } - } - } - } - - /** Method for loading a Stylesheet into the app - */ - private void openStyleSheet(File fileCSS) - throws IOException - { - if(fileCSS == null) - { - fileCSS = getFileFromChooser(".", JFileChooser.OPEN_DIALOG, extsCSS, Translatrix.getTranslationString("FiletypeCSS")); - } - if(fileCSS != null) - { - String currDocText = jtpMain.getText(); - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - styleSheet = htmlDoc.getStyleSheet(); - URL cssUrl = fileCSS.toURL(); - InputStream is = cssUrl.openStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - styleSheet.loadRules(br, cssUrl); - br.close(); - htmlDoc = new ExtendedHTMLDocument(styleSheet); - registerDocument(htmlDoc); - jtpMain.setText(currDocText); - jtpSource.setText(jtpMain.getText()); - } - refreshOnUpdate(); - } - - /** Method for serializing the document out to a file - */ - public void serializeOut(HTMLDocument doc) - throws IOException - { - File whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsSer, Translatrix.getTranslationString("FiletypeSer")); - if(whatFile != null) - { - ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(whatFile)); - oos.writeObject(doc); - oos.flush(); - oos.close(); - } - refreshOnUpdate(); - } - - /** Method for reading in a serialized document from a file - */ - public void serializeIn() - throws IOException, ClassNotFoundException - { - File whatFile = getFileFromChooser(".", JFileChooser.OPEN_DIALOG, extsSer, Translatrix.getTranslationString("FiletypeSer")); - if(whatFile != null) - { - ObjectInputStream ois = new ObjectInputStream(new FileInputStream(whatFile)); - htmlDoc = (ExtendedHTMLDocument)(ois.readObject()); - ois.close(); - registerDocument(htmlDoc); - validate(); - } - refreshOnUpdate(); - } - - /** Method for obtaining a File for input/output using a JFileChooser dialog - */ - private File getFileFromChooser(String startDir, int dialogType, String[] exts, String desc) - { - JFileChooser jfileDialog = new JFileChooser(startDir); - jfileDialog.setDialogType(dialogType); - jfileDialog.setFileFilter(new MutableFilter(exts, desc)); - int optionSelected = JFileChooser.CANCEL_OPTION; - if(dialogType == JFileChooser.OPEN_DIALOG) - { - optionSelected = jfileDialog.showOpenDialog(this); - } - else if(dialogType == JFileChooser.SAVE_DIALOG) - { - optionSelected = jfileDialog.showSaveDialog(this); - } - else // default to an OPEN_DIALOG - { - optionSelected = jfileDialog.showOpenDialog(this); - } - if(optionSelected == JFileChooser.APPROVE_OPTION) - { - return jfileDialog.getSelectedFile(); - } - return (File)null; - } - - /** Method for obtaining an Image for input using a custom JFileChooser dialog - */ - private File getImageFromChooser(String startDir, String[] exts, String desc) - { - ImageFileChooser jImageDialog = new ImageFileChooser(startDir); - jImageDialog.setDialogType(JFileChooser.CUSTOM_DIALOG); - jImageDialog.setFileFilter(new MutableFilter(exts, desc)); - jImageDialog.setDialogTitle(Translatrix.getTranslationString("ImageDialogTitle")); - int optionSelected = JFileChooser.CANCEL_OPTION; - optionSelected = jImageDialog.showDialog(this, Translatrix.getTranslationString("Insert")); - if(optionSelected == JFileChooser.APPROVE_OPTION) - { - return jImageDialog.getSelectedFile(); - } - return (File)null; - } - - /** Method for describing the node hierarchy of the document - */ - private void describeDocument(StyledDocument doc) - { - Element[] elements = doc.getRootElements(); - for(int i = 0; i < elements.length; i++) - { - indent = indentStep; - for(int j = 0; j < indent; j++) { System.out.print(" "); } - System.out.print(elements[i]); - traverseElement(elements[i]); - System.out.println(""); - } - } - - /** Traverses nodes for the describing method - */ - private void traverseElement(Element element) - { - indent += indentStep; - for(int i = 0; i < element.getElementCount(); i++) - { - for(int j = 0; j < indent; j++) { System.out.print(" "); } - System.out.print(element.getElement(i)); - traverseElement(element.getElement(i)); - } - indent -= indentStep; - } - - /** Method to locate a node element by name - */ - private Element locateElementInDocument(StyledDocument doc, String elementName) - { - Element[] elements = doc.getRootElements(); - for(int i = 0; i < elements.length; i++) - { - if(elements[i].getName().equalsIgnoreCase(elementName)) - { - return elements[i]; - } - else - { - Element rtnElement = locateChildElementInDocument(elements[i], elementName); - if(rtnElement != null) - { - return rtnElement; - } - } - } - return (Element)null; - } - - /** Traverses nodes for the locating method - */ - private Element locateChildElementInDocument(Element element, String elementName) - { - for(int i = 0; i < element.getElementCount(); i++) - { - if(element.getElement(i).getName().equalsIgnoreCase(elementName)) - { - return element.getElement(i); - } - } - return (Element)null; - } - - /** Convenience method for obtaining the WYSIWYG JTextPane - */ - public JTextPane getTextPane() - { - return jtpMain; - } - - /** Convenience method for obtaining the Source JTextPane - */ - public JTextPane getSourcePane() - { - return jtpSource; - } - - /** Convenience method for obtaining the application as a Frame - */ - public Frame getFrame() - { - return frameHandler; - } - - /** Convenience method for setting the parent Frame - */ - public void setFrame(Frame parentFrame) - { - frameHandler = parentFrame; - } - - /** Convenience method for obtaining the pre-generated menu bar - */ - public JMenuBar getMenuBar() - { - return jMenuBar; - } - - /** Convenience method for obtaining a custom menu bar - */ - public JMenuBar getCustomMenuBar(Vector vcMenus) - { - jMenuBar = new JMenuBar(); - for(int i = 0; i < vcMenus.size(); i++) - { - String menuToAdd = ((String)(vcMenus.elementAt(i))).toLowerCase(); - if(htMenus.containsKey(menuToAdd)) - { - jMenuBar.add((JMenu)(htMenus.get(menuToAdd))); - } - } - return jMenuBar; - } - - /** Convenience method for obtaining the pre-generated toolbar - */ - public JToolBar getToolBar(boolean isShowing) - { - jcbmiViewToolbar.setState(isShowing); - return jToolBar; - } - - /** Convenience method for obtaining the pre-generated toolbar - */ - public JToolBar getCustomToolBar(Vector vcTools, boolean isShowing) - { - jcbmiViewToolbar.setState(isShowing); - jToolBar = new JToolBar(JToolBar.HORIZONTAL); - jToolBar.setFloatable(false); - for(int i = 0; i < vcTools.size(); i++) - { - String toolToAdd = ((String)(vcTools.elementAt(i))).toLowerCase(); - if(toolToAdd.equals(KEY_TOOL_SEP)) - { - jToolBar.add(new JToolBar.Separator()); - } - else if(htTools.containsKey(toolToAdd)) - { - if(htTools.get(toolToAdd) instanceof JButtonNoFocus) - { - jToolBar.add((JButtonNoFocus)(htTools.get(toolToAdd))); - } - else if(htTools.get(toolToAdd) instanceof JToggleButtonNoFocus) - { - jToolBar.add((JToggleButtonNoFocus)(htTools.get(toolToAdd))); - } - else if(htTools.get(toolToAdd) instanceof JComboBoxNoFocus) - { - jToolBar.add((JComboBoxNoFocus)(htTools.get(toolToAdd))); - } - else - { - jToolBar.add((JComponent)(htTools.get(toolToAdd))); - } - } - } - return jToolBar; - } - - /** Convenience method for obtaining the current file handle - */ - public File getCurrentFile() - { - return currentFile; - } - - /** Convenience method for obtaining the application name - */ - public String getAppName() - { - return appName; - } - - /** Convenience method for obtaining the document text - */ - public String getDocumentText() - { - return jtpMain.getText(); - } - - /** Convenience method for obtaining the document text - * contained within a tag pair - */ - public String getDocumentSubText(String tagBlock) - { - return getSubText(tagBlock); - } - - /** Method for extracting the text within a tag - */ - private String getSubText(String containingTag) - { - jtpSource.setText(jtpMain.getText()); - String docTextCase = jtpSource.getText().toLowerCase(); - int tagStart = docTextCase.indexOf("<" + containingTag.toLowerCase()); - int tagStartClose = docTextCase.indexOf(">", tagStart) + 1; - String closeTag = ""; - int tagEndOpen = docTextCase.indexOf(closeTag); - if(tagStartClose < 0) { tagStartClose = 0; } - if(tagEndOpen < 0 || tagEndOpen > docTextCase.length()) { tagEndOpen = docTextCase.length(); } - return jtpSource.getText().substring(tagStartClose, tagEndOpen); - } - - /** Convenience method for obtaining the document text - * contained within the BODY tags (a common request) - */ - public String getDocumentBody() - { - return getSubText("body"); - } - - /** Convenience method for setting the document text - */ - public void setDocumentText(String sText) - { - jtpMain.setText(sText); - ((HTMLEditorKit)(jtpMain.getEditorKit())).setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR)); - jtpSource.setText(jtpMain.getText()); - ((HTMLEditorKit)(jtpSource.getEditorKit())).setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR)); - } - - /** Convenience method for obtaining the document text - */ - private void updateTitle() - { - frameHandler.setTitle(appName + (currentFile == null ? "" : " - " + currentFile.getName())); - } - - /** Convenience method for clearing out the UndoManager - */ - public void purgeUndos() - { - if(undoMngr != null) - { - undoMngr.discardAllEdits(); - undoAction.updateUndoState(); - redoAction.updateRedoState(); - } - } - - /** Convenience method for refreshing and displaying changes - */ - public void refreshOnUpdate() - { - jtpMain.setText(jtpMain.getText()); - jtpSource.setText(jtpMain.getText()); - purgeUndos(); - this.repaint(); - } - - /** Convenience method for deallocating the app resources - */ - public void dispose() - { - frameHandler.dispose(); - System.exit(0); - } - - /** Convenience method for fetching icon images from jar file - */ - private ImageIcon getEkitIcon(String iconName) - { - return new ImageIcon(Toolkit.getDefaultToolkit().getImage(getClass().getResource("icons/" + iconName + "HK.gif"))); - } - - /** Convenience method for outputting exceptions - */ - private void logException(String internalMessage, Exception e) - { - System.err.println(internalMessage); - e.printStackTrace(System.err); - } - - /** Convenience method for toggling source window visibility - */ - private void toggleSourceWindow() - { - if(!(jspSource.isShowing())) - { - jtpSource.setText(jtpMain.getText()); - jspltDisplay.setRightComponent(jspSource); - if(exclusiveEdit) - { - jspltDisplay.setDividerLocation(0); - jspltDisplay.setEnabled(false); - } - else - { - jspltDisplay.setDividerLocation(iSplitPos); - jspltDisplay.setEnabled(true); - } - } - else - { - jtpMain.setText(jtpSource.getText()); - iSplitPos = jspltDisplay.getDividerLocation(); - jspltDisplay.remove(jspSource); - jtpMain.requestFocus(); - } - this.validate(); - jcbmiViewSource.setSelected(jspSource.isShowing()); - jtbtnViewSource.setSelected(jspSource.isShowing()); - } - - /** Searches the specified element for CLASS attribute setting - */ - private String findStyle(Element element) - { - AttributeSet as = element.getAttributes(); - if(as == null) - { - return null; - } - Object val = as.getAttribute(HTML.Attribute.CLASS); - if(val != null && (val instanceof String)) - { - return (String)val; - } - for(Enumeration e = as.getAttributeNames(); e.hasMoreElements();) - { - Object key = e.nextElement(); - if(key instanceof HTML.Tag) - { - AttributeSet eas = (AttributeSet)(as.getAttribute(key)); - if(eas != null) - { - val = eas.getAttribute(HTML.Attribute.CLASS); - if(val != null) - { - return (String)val; - } - } - } - - } - return null; - } - - /** Handles caret tracking and related events, such as displaying the current style - * of the text under the caret - */ - private void handleCaretPositionChange(CaretEvent ce) - { - int caretPos = ce.getDot(); - Element element = htmlDoc.getCharacterElement(caretPos); -/* ----- TAG EXPLICATOR CODE ------------------------------------------- - javax.swing.text.ElementIterator ei = new javax.swing.text.ElementIterator(htmlDoc); - Element ele; - while((ele = ei.next()) != null) - { - System.out.println("ELEMENT : " + ele.getName()); - } - System.out.println("ELEMENT:" + element.getName()); - Element elementParent = element.getParentElement(); - System.out.println("ATTRS:"); - AttributeSet attribs = elementParent.getAttributes(); - for(Enumeration eAttrs = attribs.getAttributeNames(); eAttrs.hasMoreElements();) - { - System.out.println(" " + eAttrs.nextElement().toString()); - } - while(elementParent != null && !elementParent.getName().equals("body")) - { - String parentName = elementParent.getName(); - System.out.println("PARENT:" + parentName); - System.out.println("ATTRS:"); - attribs = elementParent.getAttributes(); - for(Enumeration eAttr = attribs.getAttributeNames(); eAttr.hasMoreElements();) - { - System.out.println(" " + eAttr.nextElement().toString()); - } - elementParent = elementParent.getParentElement(); - } ----- END ------------------------------------------- -*/ - if(element == null) - { - return; - } - String style = null; - Vector vcStyles = new Vector(); - while(element != null) - { - if(style == null) - { - style = findStyle(element); - } - vcStyles.add(element); - element = element.getParentElement(); - } - int stylefound = -1; - if(style != null) - { - for(int i = 0; i < jcmbStyleSelector.getItemCount(); i++) - { - String in = (String)(jcmbStyleSelector.getItemAt(i)); - if(in.equalsIgnoreCase(style)) - { - stylefound = i; - break; - } - } - } - if(stylefound > -1) - { - Action ac = jcmbStyleSelector.getAction(); - ac.setEnabled(false); - jcmbStyleSelector.setSelectedIndex(stylefound); - ac.setEnabled(true); - } - else - { - jcmbStyleSelector.setSelectedIndex(0); - } - } - - /** Server-side image handling methods - */ - protected void setServletURL(String url) - { - ServletURL = url; - } - - protected void setImageDir(String sysDir) - { - ImageDir = sysDir; - } - - public void setTreePilotSystemID(String theSystem) - { - TreePilotSystemID = theSystem; - } - - /** Utility methods - */ - public ExtendedHTMLDocument getExtendedHtmlDoc() - { - return (ExtendedHTMLDocument)htmlDoc; - } - - public int getCaretPosition() - { - return jtpMain.getCaretPosition(); - } - - public void setCaretPosition(int newPositon) - { - boolean end = true; - do - { - end = true; - try - { - jtpMain.setCaretPosition(newPositon); - } - catch (IllegalArgumentException iae) - { - end = false; - newPositon--; - } - } while(!end && newPositon >= 0); - } - -/* Inner Classes --------------------------------------------- */ - - /** Class for implementing Undo as an autonomous action - */ - class UndoAction extends AbstractAction - { - public UndoAction() - { - super(Translatrix.getTranslationString("Undo")); - setEnabled(false); - } - - public void actionPerformed(ActionEvent e) - { - try - { - undoMngr.undo(); - } - catch(CannotUndoException ex) - { - ex.printStackTrace(); - } - updateUndoState(); - redoAction.updateRedoState(); - } - - protected void updateUndoState() - { - if(undoMngr.canUndo()) - { - setEnabled(true); - putValue(Action.NAME, undoMngr.getUndoPresentationName()); - } - else - { - setEnabled(false); - putValue(Action.NAME, Translatrix.getTranslationString("Undo")); - } - } - } - - /** Class for implementing Redo as an autonomous action - */ - class RedoAction extends AbstractAction - { - public RedoAction() - { - super(Translatrix.getTranslationString("Redo")); - setEnabled(false); - } - - public void actionPerformed(ActionEvent e) - { - try - { - undoMngr.redo(); - } - catch(CannotUndoException ex) - { - ex.printStackTrace(); - } - updateRedoState(); - undoAction.updateUndoState(); - } - - protected void updateRedoState() - { - if(undoMngr.canRedo()) - { - setEnabled(true); - putValue(Action.NAME, undoMngr.getRedoPresentationName()); - } - else - { - setEnabled(false); - putValue(Action.NAME, Translatrix.getTranslationString("Redo")); - } - } - } - - /** Class for implementing the Undo listener to handle the Undo and Redo actions - */ - class CustomUndoableEditListener implements UndoableEditListener - { - public void undoableEditHappened(UndoableEditEvent uee) - { - undoMngr.addEdit(uee.getEdit()); - undoAction.updateUndoState(); - redoAction.updateRedoState(); - } - } - -} diff --git a/ekit/com/hexidec/ekit/EkitCore_Spell.java b/ekit/com/hexidec/ekit/EkitCore_Spell.java deleted file mode 100644 index cc31e19..0000000 --- a/ekit/com/hexidec/ekit/EkitCore_Spell.java +++ /dev/null @@ -1,2879 +0,0 @@ -/* -GNU Lesser General Public License - -EkitCore - Base Java Swing HTML Editor & Viewer Class (Spellcheck Version) -Copyright (C) 2000-2003 Howard Kistler - -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 -*/ - -package com.hexidec.ekit; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Frame; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.Insets; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Reader; -import java.net.URL; -import java.net.URLConnection; -import java.net.MalformedURLException; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Locale; -import java.util.MissingResourceException; -import java.util.ResourceBundle; -import java.util.Vector; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JEditorPane; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTextField; -import javax.swing.JTextPane; -import javax.swing.JToggleButton; -import javax.swing.JToolBar; -import javax.swing.KeyStroke; -import javax.swing.event.CaretEvent; -import javax.swing.event.CaretListener; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.UndoableEditEvent; -import javax.swing.event.UndoableEditListener; -import javax.swing.filechooser.FileFilter; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.ChangedCharSetException; -import javax.swing.text.DefaultEditorKit; -import javax.swing.text.DefaultStyledDocument; -import javax.swing.text.Document; -import javax.swing.text.Element; -import javax.swing.text.PlainDocument; -import javax.swing.text.Position; -import javax.swing.text.Style; -import javax.swing.text.StyleConstants; -import javax.swing.text.StyledDocument; -import javax.swing.text.StyledEditorKit; -import javax.swing.text.StyledEditorKit.FontSizeAction; -import javax.swing.text.html.HTML; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.HTMLEditorKit; -import javax.swing.text.html.StyleSheet; -import javax.swing.text.rtf.RTFEditorKit; -import javax.swing.undo.UndoManager; -import javax.swing.undo.CannotUndoException; - -import com.hexidec.ekit.action.*; -import com.hexidec.ekit.component.*; -import com.hexidec.util.Base64Codec; -import com.hexidec.util.Translatrix; - -import com.swabunga.spell.engine.*; -import com.swabunga.spell.event.*; -import com.swabunga.spell.swing.*; - -/** EkitCore - * Main application class for editing and saving HTML in a Java text component - * - * @author Howard Kistler - * @version 0.9g - * - * REQUIREMENTS - * Java 2 (JDK 1.3 or 1.4) - * Swing Library - */ - -public class EkitCore extends JPanel implements ActionListener, KeyListener, DocumentListener, SpellCheckListener -{ - /* Components */ - private JSplitPane jspltDisplay; - private JTextPane jtpMain; - private ExtendedHTMLEditorKit htmlKit; - private ExtendedHTMLDocument htmlDoc; - private StyleSheet styleSheet; - private JTextPane jtpSource; - private JScrollPane jspSource; - private JToolBar jToolBar; - - private JCheckBoxMenuItem jcbmiViewToolbar; - private JCheckBoxMenuItem jcbmiViewSource; - - private JButtonNoFocus jbtnNewHTML; - private JButtonNoFocus jbtnOpenHTML; - private JButtonNoFocus jbtnSaveHTML; - private JButtonNoFocus jbtnCut; - private JButtonNoFocus jbtnCopy; - private JButtonNoFocus jbtnPaste; - private JButtonNoFocus jbtnBold; - private JButtonNoFocus jbtnItalic; - private JButtonNoFocus jbtnUnderline; - private JButtonNoFocus jbtnStrike; - private JButtonNoFocus jbtnSuperscript; - private JButtonNoFocus jbtnSubscript; - private JButtonNoFocus jbtnUList; - private JButtonNoFocus jbtnOList; - private JButtonNoFocus jbtnClearFormat; - private JButtonNoFocus jbtnAnchor; - private JToggleButtonNoFocus jtbtnViewSource; - private JComboBoxNoFocus jcmbStyleSelector; - - private Frame frameHandler; - - private HTMLUtilities htmlUtilities = new HTMLUtilities(this); - - /* Actions */ - private StyledEditorKit.BoldAction actionFontBold; - private StyledEditorKit.ItalicAction actionFontItalic; - private StyledEditorKit.UnderlineAction actionFontUnderline; - private FormatAction actionFontStrike; - private FormatAction actionFontSuperscript; - private FormatAction actionFontSubscript; - private ListAutomationAction actionListUnordered; - private ListAutomationAction actionListOrdered; - private CustomAction actionSelectFont; - private CustomAction actionClearFormat; - private CustomAction actionInsertAnchor; - - protected UndoManager undoMngr; - protected UndoAction undoAction; - protected RedoAction redoAction; - - /* Menus */ - private JMenuBar jMenuBar; - private JMenu jMenuFile; - private JMenu jMenuEdit; - private JMenu jMenuView; - private JMenu jMenuFont; - private JMenu jMenuFormat; - private JMenu jMenuInsert; - private JMenu jMenuTable; - private JMenu jMenuForms; - private JMenu jMenuSearch; - private JMenu jMenuTools; - private JMenu jMenuHelp; - private JMenu jMenuDebug; - - /* Constants */ - // Menu Keys - public static final String KEY_MENU_FILE = "file"; - public static final String KEY_MENU_EDIT = "edit"; - public static final String KEY_MENU_VIEW = "view"; - public static final String KEY_MENU_FONT = "font"; - public static final String KEY_MENU_FORMAT = "format"; - public static final String KEY_MENU_INSERT = "insert"; - public static final String KEY_MENU_TABLE = "table"; - public static final String KEY_MENU_FORMS = "forms"; - public static final String KEY_MENU_SEARCH = "search"; - public static final String KEY_MENU_TOOLS = "tools"; - public static final String KEY_MENU_HELP = "help"; - public static final String KEY_MENU_DEBUG = "debug"; - - // Tool Keys - public static final String KEY_TOOL_SEP = "separator"; - public static final String KEY_TOOL_NEW = "new"; - public static final String KEY_TOOL_OPEN = "open"; - public static final String KEY_TOOL_SAVE = "save"; - public static final String KEY_TOOL_CUT = "cut"; - public static final String KEY_TOOL_COPY = "copy"; - public static final String KEY_TOOL_PASTE = "paste"; - public static final String KEY_TOOL_BOLD = "bold"; - public static final String KEY_TOOL_ITALIC = "italic"; - public static final String KEY_TOOL_UNDERLINE = "underline"; - public static final String KEY_TOOL_STRIKE = "strike"; - public static final String KEY_TOOL_SUPER = "superscript"; - public static final String KEY_TOOL_SUB = "subscript"; - public static final String KEY_TOOL_ULIST = "ulist"; - public static final String KEY_TOOL_OLIST = "olist"; - public static final String KEY_TOOL_CLEAR = "clearformats"; - public static final String KEY_TOOL_ANCHOR = "anchor"; - public static final String KEY_TOOL_SOURCE = "viewsource"; - public static final String KEY_TOOL_STYLES = "styleselect"; - - // Menu & Tool Key Arrays - private static Hashtable htMenus = new Hashtable(); - private static Hashtable htTools = new Hashtable(); - - private final String appName = "Ekit"; - private final String menuDialog = "..."; /* text to append to a MenuItem label when menu item opens a dialog */ - - private final boolean useFormIndicator = true; /* Creates a highlighted background on a new FORM so that it may be more easily edited */ - private final String clrFormIndicator = "#cccccc"; - - // System Clipboard Settings - private java.awt.datatransfer.Clipboard sysClipboard; - private SecurityManager secManager; - - /* Variables */ - private int iSplitPos = 0; - - private boolean exclusiveEdit = true; - - private String lastSearchFindTerm = null; - private String lastSearchReplaceTerm = null; - private boolean lastSearchCaseSetting = false; - private boolean lastSearchTopSetting = false; - - private File currentFile = null; - - private int indent = 0; - private final int indentStep = 4; - - // File extensions for MutableFilter - private final String[] extsHTML = { "html", "htm", "shtml" }; - private final String[] extsCSS = { "css" }; - private final String[] extsIMG = { "gif", "jpg", "jpeg", "png" }; - private final String[] extsRTF = { "rtf" }; - private final String[] extsB64 = { "b64" }; - private final String[] extsSer = { "ser" }; - - /* Spell Checker Settings */ - private static String dictFile; - private SpellChecker spellCheck = null; - private JSpellDialog spellDialog; - - /* Servlet Settings */ - private String ServletURL = null; - private String TreePilotSystemID = ""; - private String ImageDir = ""; - private static ResourceBundle TreePilotProperties; - - /** Master Constructor - * @param sDocument [String] A text or HTML document to load in the editor upon startup. - * @param sStyleSheet [String] A CSS stylesheet to load in the editor upon startup. - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param urlStyleSheet [URL] A URL reference to the CSS style sheet. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - * @param base64 [boolean] Specifies whether the raw document is Base64 encoded or not. - * @param debugMode [boolean] Specifies whether to show the Debug menu or not. - */ - public EkitCore(String sDocument, String sStyleSheet, String sRawDocument, URL urlStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64, boolean debugMode) - { - super(); - - exclusiveEdit = editModeExclusive; - - frameHandler = new Frame(); - - // Determine if system clipboard is available - secManager = System.getSecurityManager(); - if(secManager != null) - { - try - { - secManager.checkSystemClipboardAccess(); - sysClipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - } - catch (SecurityException se) - { - sysClipboard = null; - } - } - - /* Localize for language */ - Translatrix.setBundleName("com.hexidec.ekit.LanguageResources"); - Locale baseLocale = (Locale)null; - if(sLanguage != null && sCountry != null) - { - baseLocale = new Locale(sLanguage, sCountry); - } - Translatrix.setLocale(baseLocale); - - /* Load TreePilot properties */ - try - { - TreePilotProperties = ResourceBundle.getBundle("com.hexidec.ekit.TreePilot"); - } - catch(MissingResourceException mre) - { - logException("MissingResourceException while loading treepilot file", mre); - } - - /* Create the editor kit, document, and stylesheet */ - jtpMain = new JTextPane(); - htmlKit = new ExtendedHTMLEditorKit(); - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - styleSheet = htmlDoc.getStyleSheet(); - htmlKit.setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR)); - - /* Set up the text pane */ - jtpMain.setEditorKit(htmlKit); - jtpMain.setDocument(htmlDoc); - jtpMain.setMargin(new Insets(4, 4, 4, 4)); - jtpMain.addKeyListener(this); - - /* Create the source text area */ - jtpSource = new JTextPane(); - jtpSource.setBackground(new Color(212, 212, 212)); - jtpSource.setSelectionColor(new Color(255, 192, 192)); - jtpSource.setText(jtpMain.getText()); - jtpSource.getDocument().addDocumentListener(this); - - /* Add CaretListener for tracking caret location events */ - jtpMain.addCaretListener(new CaretListener() - { - public void caretUpdate(CaretEvent ce) - { - handleCaretPositionChange(ce); - } - }); - - /* Set up the undo features */ - undoMngr = new UndoManager(); - undoAction = new UndoAction(); - redoAction = new RedoAction(); - jtpMain.getDocument().addUndoableEditListener(new CustomUndoableEditListener()); - - /* Insert raw document, if exists */ - if(sRawDocument != null && sRawDocument.length() > 0) - { - if(base64) - { - jtpMain.setText(Base64Codec.decode(sRawDocument)); - } - else - { - jtpMain.setText(sRawDocument); - } - } - jtpMain.setCaretPosition(0); - jtpMain.getDocument().addDocumentListener(this); - - /* Import CSS from reference, if exists */ - if(urlStyleSheet != null) - { - try - { - String currDocText = jtpMain.getText(); - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - styleSheet = htmlDoc.getStyleSheet(); - BufferedReader br = new BufferedReader(new InputStreamReader(urlStyleSheet.openStream())); - styleSheet.loadRules(br, urlStyleSheet); - br.close(); - htmlDoc = new ExtendedHTMLDocument(styleSheet); - registerDocument(htmlDoc); - jtpMain.setText(currDocText); - jtpSource.setText(jtpMain.getText()); - } - catch(Exception e) - { - e.printStackTrace(System.out); - } - } - - /* Preload the specified HTML document, if exists */ - if(sDocument != null) - { - File defHTML = new File(sDocument); - if(defHTML.exists()) - { - try - { - openDocument(defHTML); - } - catch(Exception e) - { - logException("Exception in preloading HTML document", e); - } - } - } - - /* Preload the specified CSS document, if exists */ - if(sStyleSheet != null) - { - File defCSS = new File(sStyleSheet); - if(defCSS.exists()) - { - try - { - openStyleSheet(defCSS); - } - catch(Exception e) - { - logException("Exception in preloading CSS stylesheet", e); - } - } - } - - /* Collect the actions that the JTextPane is naturally aware of */ - Hashtable actions = new Hashtable(); - Action[] actionsArray = jtpMain.getActions(); - for(int i = 0; i < actionsArray.length; i++) - { - Action a = actionsArray[i]; - actions.put(a.getValue(Action.NAME), a); - } - - /* Create shared actions */ - actionFontBold = new StyledEditorKit.BoldAction(); - actionFontItalic = new StyledEditorKit.ItalicAction(); - actionFontUnderline = new StyledEditorKit.UnderlineAction(); - actionFontStrike = new FormatAction(this, Translatrix.getTranslationString("FontStrike"), HTML.Tag.STRIKE); - actionFontSuperscript = new FormatAction(this, Translatrix.getTranslationString("FontSuperscript"), HTML.Tag.SUP); - actionFontSubscript = new FormatAction(this, Translatrix.getTranslationString("FontSubscript"), HTML.Tag.SUB); - actionListUnordered = new ListAutomationAction(this, Translatrix.getTranslationString("ListUnordered"), HTML.Tag.UL); - actionListOrdered = new ListAutomationAction(this, Translatrix.getTranslationString("ListOrdered"), HTML.Tag.OL); - Hashtable customAttr = new Hashtable(); - customAttr.put("face",""); - actionSelectFont = new CustomAction(this, Translatrix.getTranslationString("FontSelect") + menuDialog, HTML.Tag.FONT, customAttr); - actionClearFormat = new CustomAction(this, Translatrix.getTranslationString("FormatClear"), new HTML.UnknownTag("")); - actionInsertAnchor = new CustomAction(this, Translatrix.getTranslationString("InsertAnchor") + menuDialog, HTML.Tag.A); - - /* Build the menus */ - /* FILE Menu */ - jMenuFile = new JMenu(Translatrix.getTranslationString("File")); - htMenus.put(KEY_MENU_FILE, jMenuFile); - JMenuItem jmiNew = new JMenuItem(Translatrix.getTranslationString("NewDocument")); jmiNew.setActionCommand("newdoc"); jmiNew.addActionListener(this); jmiNew.setAccelerator(KeyStroke.getKeyStroke('N', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiNew.setIcon(getEkitIcon("New")); }; jMenuFile.add(jmiNew); - JMenuItem jmiOpenHTML = new JMenuItem(Translatrix.getTranslationString("OpenDocument") + menuDialog); jmiOpenHTML.setActionCommand("openhtml"); jmiOpenHTML.addActionListener(this); jmiOpenHTML.setAccelerator(KeyStroke.getKeyStroke('O', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiOpenHTML.setIcon(getEkitIcon("Open")); }; jMenuFile.add(jmiOpenHTML); - JMenuItem jmiOpenCSS = new JMenuItem(Translatrix.getTranslationString("OpenStyle") + menuDialog); jmiOpenCSS.setActionCommand("opencss"); jmiOpenCSS.addActionListener(this); jMenuFile.add(jmiOpenCSS); - JMenuItem jmiOpenB64 = new JMenuItem(Translatrix.getTranslationString("OpenBase64Document") + menuDialog); jmiOpenB64.setActionCommand("openb64"); jmiOpenB64.addActionListener(this); jMenuFile.add(jmiOpenB64); - jMenuFile.addSeparator(); - JMenuItem jmiSave = new JMenuItem(Translatrix.getTranslationString("Save")); jmiSave.setActionCommand("save"); jmiSave.addActionListener(this); jmiSave.setAccelerator(KeyStroke.getKeyStroke('S', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiSave.setIcon(getEkitIcon("Save")); }; jMenuFile.add(jmiSave); - JMenuItem jmiSaveAs = new JMenuItem(Translatrix.getTranslationString("SaveAs") + menuDialog); jmiSaveAs.setActionCommand("saveas"); jmiSaveAs.addActionListener(this); jMenuFile.add(jmiSaveAs); - JMenuItem jmiSaveBody = new JMenuItem(Translatrix.getTranslationString("SaveBody") + menuDialog); jmiSaveBody.setActionCommand("savebody"); jmiSaveBody.addActionListener(this); jMenuFile.add(jmiSaveBody); - JMenuItem jmiSaveRTF = new JMenuItem(Translatrix.getTranslationString("SaveRTF") + menuDialog); jmiSaveRTF.setActionCommand("savertf"); jmiSaveRTF.addActionListener(this); jMenuFile.add(jmiSaveRTF); - JMenuItem jmiSaveB64 = new JMenuItem(Translatrix.getTranslationString("SaveB64") + menuDialog); jmiSaveB64.setActionCommand("saveb64"); jmiSaveB64.addActionListener(this); jMenuFile.add(jmiSaveB64); - jMenuFile.addSeparator(); - JMenuItem jmiSerialOut = new JMenuItem(Translatrix.getTranslationString("Serialize") + menuDialog); jmiSerialOut.setActionCommand("serialize"); jmiSerialOut.addActionListener(this); jMenuFile.add(jmiSerialOut); - JMenuItem jmiSerialIn = new JMenuItem(Translatrix.getTranslationString("ReadFromSer") + menuDialog); jmiSerialIn.setActionCommand("readfromser"); jmiSerialIn.addActionListener(this); jMenuFile.add(jmiSerialIn); - jMenuFile.addSeparator(); - JMenuItem jmiExit = new JMenuItem(Translatrix.getTranslationString("Exit")); jmiExit.setActionCommand("exit"); jmiExit.addActionListener(this); jMenuFile.add(jmiExit); - - /* EDIT Menu */ - jMenuEdit = new JMenu(Translatrix.getTranslationString("Edit")); - htMenus.put(KEY_MENU_EDIT, jMenuEdit); - if(sysClipboard != null) - { - // System Clipboard versions of menu commands - JMenuItem jmiCut = new JMenuItem(Translatrix.getTranslationString("Cut")); jmiCut.setActionCommand("textcut"); jmiCut.addActionListener(this); jmiCut.setAccelerator(KeyStroke.getKeyStroke('X', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiCut.setIcon(getEkitIcon("Cut")); } jMenuEdit.add(jmiCut); - JMenuItem jmiCopy = new JMenuItem(Translatrix.getTranslationString("Copy")); jmiCopy.setActionCommand("textcopy"); jmiCopy.addActionListener(this); jmiCopy.setAccelerator(KeyStroke.getKeyStroke('C', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiCopy.setIcon(getEkitIcon("Copy")); } jMenuEdit.add(jmiCopy); - JMenuItem jmiPaste = new JMenuItem(Translatrix.getTranslationString("Paste")); jmiPaste.setActionCommand("textpaste"); jmiPaste.addActionListener(this); jmiPaste.setAccelerator(KeyStroke.getKeyStroke('V', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiPaste.setIcon(getEkitIcon("Paste")); } jMenuEdit.add(jmiPaste); - } - else - { - // DefaultEditorKit versions of menu commands - JMenuItem jmiCut = new JMenuItem(new DefaultEditorKit.CutAction()); jmiCut.setText(Translatrix.getTranslationString("Cut")); jmiCut.setAccelerator(KeyStroke.getKeyStroke('X', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiCut.setIcon(getEkitIcon("Cut")); } jMenuEdit.add(jmiCut); - JMenuItem jmiCopy = new JMenuItem(new DefaultEditorKit.CopyAction()); jmiCopy.setText(Translatrix.getTranslationString("Copy")); jmiCopy.setAccelerator(KeyStroke.getKeyStroke('C', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiCopy.setIcon(getEkitIcon("Copy")); } jMenuEdit.add(jmiCopy); - JMenuItem jmiPaste = new JMenuItem(new DefaultEditorKit.PasteAction()); jmiPaste.setText(Translatrix.getTranslationString("Paste")); jmiPaste.setAccelerator(KeyStroke.getKeyStroke('V', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiPaste.setIcon(getEkitIcon("Paste")); } jMenuEdit.add(jmiPaste); - } - jMenuEdit.addSeparator(); - JMenuItem jmiUndo = new JMenuItem(undoAction); jmiUndo.setAccelerator(KeyStroke.getKeyStroke('Z', java.awt.Event.CTRL_MASK, false)); jMenuEdit.add(jmiUndo); - JMenuItem jmiRedo = new JMenuItem(redoAction); jmiRedo.setAccelerator(KeyStroke.getKeyStroke('Y', java.awt.Event.CTRL_MASK, false)); jMenuEdit.add(jmiRedo); - jMenuEdit.addSeparator(); - JMenuItem jmiSelAll = new JMenuItem((Action)actions.get(DefaultEditorKit.selectAllAction)); jmiSelAll.setText(Translatrix.getTranslationString("SelectAll")); jmiSelAll.setAccelerator(KeyStroke.getKeyStroke('A', java.awt.Event.CTRL_MASK, false)); jMenuEdit.add(jmiSelAll); - JMenuItem jmiSelPara = new JMenuItem((Action)actions.get(DefaultEditorKit.selectParagraphAction)); jmiSelPara.setText(Translatrix.getTranslationString("SelectParagraph")); jMenuEdit.add(jmiSelPara); - JMenuItem jmiSelLine = new JMenuItem((Action)actions.get(DefaultEditorKit.selectLineAction)); jmiSelLine.setText(Translatrix.getTranslationString("SelectLine")); jMenuEdit.add(jmiSelLine); - JMenuItem jmiSelWord = new JMenuItem((Action)actions.get(DefaultEditorKit.selectWordAction)); jmiSelWord.setText(Translatrix.getTranslationString("SelectWord")); jMenuEdit.add(jmiSelWord); - - /* VIEW Menu */ - jMenuView = new JMenu(Translatrix.getTranslationString("View")); - htMenus.put(KEY_MENU_VIEW, jMenuView); - jcbmiViewToolbar = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewToolbar"), false); jcbmiViewToolbar.setActionCommand("toggletoolbar"); jcbmiViewToolbar.addActionListener(this); jMenuView.add(jcbmiViewToolbar); - jcbmiViewSource = new JCheckBoxMenuItem(Translatrix.getTranslationString("ViewSource"), false); jcbmiViewSource.setActionCommand("viewsource"); jcbmiViewSource.addActionListener(this); jMenuView.add(jcbmiViewSource); - - /* FONT Menu */ - jMenuFont = new JMenu(Translatrix.getTranslationString("Font")); - htMenus.put(KEY_MENU_FONT, jMenuFont); - JMenuItem jmiBold = new JMenuItem(actionFontBold); jmiBold.setText(Translatrix.getTranslationString("FontBold")); jmiBold.setAccelerator(KeyStroke.getKeyStroke('B', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiBold.setIcon(getEkitIcon("Bold")); } jMenuFont.add(jmiBold); - JMenuItem jmiItalic = new JMenuItem(actionFontItalic); jmiItalic.setText(Translatrix.getTranslationString("FontItalic")); jmiItalic.setAccelerator(KeyStroke.getKeyStroke('I', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiItalic.setIcon(getEkitIcon("Italic")); } jMenuFont.add(jmiItalic); - JMenuItem jmiUnderline = new JMenuItem(actionFontUnderline); jmiUnderline.setText(Translatrix.getTranslationString("FontUnderline")); jmiUnderline.setAccelerator(KeyStroke.getKeyStroke('U', java.awt.Event.CTRL_MASK, false)); if(showMenuIcons) { jmiUnderline.setIcon(getEkitIcon("Underline")); } jMenuFont.add(jmiUnderline); - JMenuItem jmiStrike = new JMenuItem(actionFontStrike); jmiStrike.setText(Translatrix.getTranslationString("FontStrike")); if(showMenuIcons) { jmiStrike.setIcon(getEkitIcon("Strike")); } jMenuFont.add(jmiStrike); - jMenuFont.addSeparator(); - jMenuFont.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatBig"), HTML.Tag.BIG))); - jMenuFont.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatSmall"), HTML.Tag.SMALL))); - JMenu jMenuFontSize = new JMenu(Translatrix.getTranslationString("FontSize")); - String fontSizeKey = "size"; - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"1"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize1"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"2"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize2"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"3"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize3"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"4"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize4"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"5"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize5"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"6"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize6"), HTML.Tag.FONT, customAttr))); - customAttr = new Hashtable(); customAttr.put(fontSizeKey,"7"); - jMenuFontSize.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("FontSize7"), HTML.Tag.FONT, customAttr))); - jMenuFont.add(jMenuFontSize); - jMenuFont.addSeparator(); - JMenuItem jmiSupscript = new JMenuItem(actionFontSuperscript); if(showMenuIcons) { jmiSupscript.setIcon(getEkitIcon("Super")); } jMenuFont.add(jmiSupscript); - JMenuItem jmiSubscript = new JMenuItem(actionFontSubscript); if(showMenuIcons) { jmiSubscript.setIcon(getEkitIcon("Sub")); } jMenuFont.add(jmiSubscript); - jMenuFont.addSeparator(); - JMenuItem jmiSerif = new JMenuItem((Action)actions.get("font-family-Serif")); jmiSerif.setText(Translatrix.getTranslationString("FontSerif")); jMenuFont.add(jmiSerif); - JMenuItem jmiSansSerif = new JMenuItem((Action)actions.get("font-family-SansSerif")); jmiSansSerif.setText(Translatrix.getTranslationString("FontSansserif")); jMenuFont.add(jmiSansSerif); - JMenuItem jmiMonospaced = new JMenuItem((Action)actions.get("font-family-Monospaced")); jmiMonospaced.setText(Translatrix.getTranslationString("FontMonospaced")); jMenuFont.add(jmiMonospaced); - JMenuItem jmiSelectFont = new JMenuItem(actionSelectFont); jMenuFont.add(jmiSelectFont); - jMenuFont.addSeparator(); - JMenu jMenuFontColor = new JMenu(Translatrix.getTranslationString("Color")); - customAttr = new Hashtable(); customAttr.put("color","black"); - jMenuFontColor.add(new JMenuItem(new CustomAction(this, Translatrix.getTranslationString("CustomColor") + menuDialog, HTML.Tag.FONT, customAttr))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorAqua"), new Color( 0,255,255)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorBlack"), new Color( 0, 0, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorBlue"), new Color( 0, 0,255)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorFuschia"), new Color(255, 0,255)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorGray"), new Color(128,128,128)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorGreen"), new Color( 0,128, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorLime"), new Color( 0,255, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorMaroon"), new Color(128, 0, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorNavy"), new Color( 0, 0,128)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorOlive"), new Color(128,128, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorPurple"), new Color(128, 0,128)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorRed"), new Color(255, 0, 0)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorSilver"), new Color(192,192,192)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorTeal"), new Color( 0,128,128)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorWhite"), new Color(255,255,255)))); - jMenuFontColor.add(new JMenuItem(new StyledEditorKit.ForegroundAction(Translatrix.getTranslationString("ColorYellow"), new Color(255,255, 0)))); - jMenuFont.add(jMenuFontColor); - - /* FORMAT Menu */ - jMenuFormat = new JMenu(Translatrix.getTranslationString("Format")); - htMenus.put(KEY_MENU_FORMAT, jMenuFormat); - JMenu jMenuFormatAlign = new JMenu(Translatrix.getTranslationString("Align")); - jMenuFormatAlign.add(new JMenuItem(new StyledEditorKit.AlignmentAction(Translatrix.getTranslationString("AlignLeft"), StyleConstants.ALIGN_LEFT))); - jMenuFormatAlign.add(new JMenuItem(new StyledEditorKit.AlignmentAction(Translatrix.getTranslationString("AlignCenter"), StyleConstants.ALIGN_CENTER))); - jMenuFormatAlign.add(new JMenuItem(new StyledEditorKit.AlignmentAction(Translatrix.getTranslationString("AlignRight"), StyleConstants.ALIGN_RIGHT))); - jMenuFormatAlign.add(new JMenuItem(new StyledEditorKit.AlignmentAction(Translatrix.getTranslationString("AlignJustified"), StyleConstants.ALIGN_JUSTIFIED))); - jMenuFormat.add(jMenuFormatAlign); - jMenuFormat.addSeparator(); - JMenu jMenuFormatHeading = new JMenu(Translatrix.getTranslationString("Heading")); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading1"), HTML.Tag.H1))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading2"), HTML.Tag.H2))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading3"), HTML.Tag.H3))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading4"), HTML.Tag.H4))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading5"), HTML.Tag.H5))); - jMenuFormatHeading.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("Heading6"), HTML.Tag.H6))); - jMenuFormat.add(jMenuFormatHeading); - jMenuFormat.addSeparator(); - JMenuItem jmiUList = new JMenuItem(actionListUnordered); if(showMenuIcons) { jmiUList.setIcon(getEkitIcon("UList")); } jMenuFormat.add(jmiUList); - JMenuItem jmiOList = new JMenuItem(actionListOrdered); if(showMenuIcons) { jmiOList.setIcon(getEkitIcon("OList")); } jMenuFormat.add(jmiOList); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("ListItem"), HTML.Tag.LI))); - jMenuFormat.addSeparator(); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatBlockquote"), HTML.Tag.BLOCKQUOTE))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatPre"), HTML.Tag.PRE))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatStrong"), HTML.Tag.STRONG))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatEmphasis"), HTML.Tag.EM))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatTT"), HTML.Tag.TT))); - jMenuFormat.add(new JMenuItem(new FormatAction(this, Translatrix.getTranslationString("FormatSpan"), HTML.Tag.SPAN))); - jMenuFormat.addSeparator(); - JMenuItem jmiClearStyles = new JMenuItem(actionClearFormat); if(showMenuIcons) { jmiClearStyles.setIcon(getEkitIcon("ClearFormat")); }; jMenuFormat.add(jmiClearStyles); - - /* INSERT Menu */ - jMenuInsert = new JMenu(Translatrix.getTranslationString("Insert")); - htMenus.put(KEY_MENU_INSERT, jMenuInsert); - jMenuInsert.add(new JMenuItem(actionInsertAnchor)); - JMenuItem jmiBreak = new JMenuItem(Translatrix.getTranslationString("InsertBreak")); jmiBreak.setActionCommand("insertbreak"); jmiBreak.addActionListener(this); jmiBreak.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, java.awt.Event.SHIFT_MASK, false)); jMenuInsert.add(jmiBreak); - JMenuItem jmiNBSP = new JMenuItem(Translatrix.getTranslationString("InsertNBSP")); jmiNBSP.setActionCommand("insertnbsp"); jmiNBSP.addActionListener(this); jMenuInsert.add(jmiNBSP); - JMenuItem jmiHRule = new JMenuItem((Action)actions.get("InsertHR")); jmiHRule.setText(Translatrix.getTranslationString("InsertHorizontalRule")); jMenuInsert.add(jmiHRule); - jMenuInsert.addSeparator(); - JMenuItem jmiImageLocal = new JMenuItem(Translatrix.getTranslationString("InsertLocalImage") + menuDialog); jmiImageLocal.setActionCommand("insertlocalimage"); jmiImageLocal.addActionListener(this); jMenuInsert.add(jmiImageLocal); - JMenuItem jmiImageServer = new JMenuItem(Translatrix.getTranslationString("InsertServerImage") + menuDialog); jmiImageServer.setActionCommand("insertserverimage"); jmiImageServer.addActionListener(this); jMenuInsert.add(jmiImageServer); - - /* TABLE Menu */ - jMenuTable = new JMenu(Translatrix.getTranslationString("Table")); - htMenus.put(KEY_MENU_TABLE, jMenuTable); - JMenuItem jmiTable = new JMenuItem(Translatrix.getTranslationString("InsertTable") + menuDialog); jmiTable.setActionCommand("inserttable"); jmiTable.addActionListener(this); jMenuTable.add(jmiTable); - jMenuTable.addSeparator(); - JMenuItem jmiTableRow = new JMenuItem(Translatrix.getTranslationString("InsertTableRow")); jmiTableRow.setActionCommand("inserttablerow"); jmiTableRow.addActionListener(this); jMenuTable.add(jmiTableRow); - JMenuItem jmiTableCol = new JMenuItem(Translatrix.getTranslationString("InsertTableColumn")); jmiTableCol.setActionCommand("inserttablecolumn"); jmiTableCol.addActionListener(this); jMenuTable.add(jmiTableCol); - jMenuTable.addSeparator(); - JMenuItem jmiTableRowDel = new JMenuItem(Translatrix.getTranslationString("DeleteTableRow")); jmiTableRowDel.setActionCommand("deletetablerow"); jmiTableRowDel.addActionListener(this); jMenuTable.add(jmiTableRowDel); - JMenuItem jmiTableColDel = new JMenuItem(Translatrix.getTranslationString("DeleteTableColumn")); jmiTableColDel.setActionCommand("deletetablecolumn"); jmiTableColDel.addActionListener(this); jMenuTable.add(jmiTableColDel); - - /* FORMS Menu */ - jMenuForms = new JMenu(Translatrix.getTranslationString("Forms")); - htMenus.put(KEY_MENU_FORMS, jMenuForms); - JMenuItem jmiFormInsertForm = new JMenuItem(Translatrix.getTranslationString("FormInsertForm")); jmiFormInsertForm.setActionCommand("insertform"); jmiFormInsertForm.addActionListener(this); jMenuForms.add(jmiFormInsertForm); - jMenuForms.addSeparator(); - JMenuItem jmiFormTextfield = new JMenuItem(Translatrix.getTranslationString("FormTextfield")); jmiFormTextfield.setActionCommand("inserttextfield"); jmiFormTextfield.addActionListener(this); jMenuForms.add(jmiFormTextfield); - JMenuItem jmiFormTextarea = new JMenuItem(Translatrix.getTranslationString("FormTextarea")); jmiFormTextarea.setActionCommand("inserttextarea"); jmiFormTextarea.addActionListener(this); jMenuForms.add(jmiFormTextarea); - JMenuItem jmiFormCheckbox = new JMenuItem(Translatrix.getTranslationString("FormCheckbox")); jmiFormCheckbox.setActionCommand("insertcheckbox"); jmiFormCheckbox.addActionListener(this); jMenuForms.add(jmiFormCheckbox); - JMenuItem jmiFormRadio = new JMenuItem(Translatrix.getTranslationString("FormRadio")); jmiFormRadio.setActionCommand("insertradiobutton"); jmiFormRadio.addActionListener(this); jMenuForms.add(jmiFormRadio); - JMenuItem jmiFormPassword = new JMenuItem(Translatrix.getTranslationString("FormPassword")); jmiFormPassword.setActionCommand("insertpassword"); jmiFormPassword.addActionListener(this); jMenuForms.add(jmiFormPassword); - jMenuForms.addSeparator(); - JMenuItem jmiFormButton = new JMenuItem(Translatrix.getTranslationString("FormButton")); jmiFormButton.setActionCommand("insertbutton"); jmiFormButton.addActionListener(this); jMenuForms.add(jmiFormButton); - JMenuItem jmiFormButtonSubmit = new JMenuItem(Translatrix.getTranslationString("FormButtonSubmit")); jmiFormButtonSubmit.setActionCommand("insertbuttonsubmit"); jmiFormButtonSubmit.addActionListener(this); jMenuForms.add(jmiFormButtonSubmit); - JMenuItem jmiFormButtonReset = new JMenuItem(Translatrix.getTranslationString("FormButtonReset")); jmiFormButtonReset.setActionCommand("insertbuttonreset"); jmiFormButtonReset.addActionListener(this); jMenuForms.add(jmiFormButtonReset); - - /* SEARCH Menu */ - jMenuSearch = new JMenu(Translatrix.getTranslationString("Search")); - htMenus.put(KEY_MENU_SEARCH, jMenuSearch); - JMenuItem jmiFind = new JMenuItem(Translatrix.getTranslationString("SearchFind")); jmiFind.setActionCommand("find"); jmiFind.addActionListener(this); jmiFind.setAccelerator(KeyStroke.getKeyStroke('F', java.awt.Event.CTRL_MASK, false)); jMenuSearch.add(jmiFind); - JMenuItem jmiFindAgain = new JMenuItem(Translatrix.getTranslationString("SearchFindAgain")); jmiFindAgain.setActionCommand("findagain"); jmiFindAgain.addActionListener(this); jmiFindAgain.setAccelerator(KeyStroke.getKeyStroke('G', java.awt.Event.CTRL_MASK, false)); jMenuSearch.add(jmiFindAgain); - JMenuItem jmiReplace = new JMenuItem(Translatrix.getTranslationString("SearchReplace")); jmiReplace.setActionCommand("replace"); jmiReplace.addActionListener(this); jmiReplace.setAccelerator(KeyStroke.getKeyStroke('R', java.awt.Event.CTRL_MASK, false)); jMenuSearch.add(jmiReplace); - - /* TOOLS Menu */ - jMenuTools = new JMenu(Translatrix.getTranslationString("Tools")); - htMenus.put(KEY_MENU_TOOLS, jMenuTools); - JMenuItem jmiSpellcheck = new JMenuItem(Translatrix.getTranslationString("ToolSpellcheck")); jmiSpellcheck.setActionCommand("spellcheck"); jmiSpellcheck.addActionListener(this); jMenuTools.add(jmiSpellcheck); - - /* HELP Menu */ - jMenuHelp = new JMenu(Translatrix.getTranslationString("Help")); - htMenus.put(KEY_MENU_HELP, jMenuHelp); - JMenuItem jmiAbout = new JMenuItem(Translatrix.getTranslationString("About")); jmiAbout.setActionCommand("helpabout"); jmiAbout.addActionListener(this); jMenuHelp.add(jmiAbout); - - /* DEBUG Menu */ - jMenuDebug = new JMenu(Translatrix.getTranslationString("Debug")); - htMenus.put(KEY_MENU_DEBUG, jMenuDebug); - JMenuItem jmiDesc = new JMenuItem(Translatrix.getTranslationString("DescribeDoc")); jmiDesc.setActionCommand("describe"); jmiDesc.addActionListener(this); jMenuDebug.add(jmiDesc); - JMenuItem jmiDescCSS = new JMenuItem(Translatrix.getTranslationString("DescribeCSS")); jmiDescCSS.setActionCommand("describecss"); jmiDescCSS.addActionListener(this); jMenuDebug.add(jmiDescCSS); - JMenuItem jmiTag = new JMenuItem(Translatrix.getTranslationString("WhatTags")); jmiTag.setActionCommand("whattags"); jmiTag.addActionListener(this); jMenuDebug.add(jmiTag); - - /* Create menubar and add menus */ - jMenuBar = new JMenuBar(); - jMenuBar.add(jMenuFile); - jMenuBar.add(jMenuEdit); - jMenuBar.add(jMenuView); - jMenuBar.add(jMenuFont); - jMenuBar.add(jMenuFormat); - jMenuBar.add(jMenuSearch); - jMenuBar.add(jMenuInsert); - jMenuBar.add(jMenuTable); - jMenuBar.add(jMenuForms); - jMenuBar.add(jMenuTools); - jMenuBar.add(jMenuHelp); - if(debugMode) - { - jMenuBar.add(jMenuDebug); - } - - /* Create the toolbar */ - jToolBar = new JToolBar(JToolBar.HORIZONTAL); - jToolBar.setFloatable(false); - jbtnNewHTML = new JButtonNoFocus(getEkitIcon("New")); jbtnNewHTML.setToolTipText(Translatrix.getTranslationString("NewDocument")); jbtnNewHTML.setActionCommand("newdoc"); jbtnNewHTML.addActionListener(this); jToolBar.add(jbtnNewHTML); htTools.put(KEY_TOOL_NEW, jbtnNewHTML); - jbtnOpenHTML = new JButtonNoFocus(getEkitIcon("Open")); jbtnOpenHTML.setToolTipText(Translatrix.getTranslationString("OpenDocument")); jbtnOpenHTML.setActionCommand("openhtml"); jbtnOpenHTML.addActionListener(this); jToolBar.add(jbtnOpenHTML); htTools.put(KEY_TOOL_OPEN, jbtnOpenHTML); - jbtnSaveHTML = new JButtonNoFocus(getEkitIcon("Save")); jbtnSaveHTML.setToolTipText(Translatrix.getTranslationString("SaveDocument")); jbtnSaveHTML.setActionCommand("saveas"); jbtnSaveHTML.addActionListener(this); jToolBar.add(jbtnSaveHTML); htTools.put(KEY_TOOL_SAVE, jbtnSaveHTML); - jToolBar.add(new JToolBar.Separator()); - jbtnCut = new JButtonNoFocus(new DefaultEditorKit.CutAction()); jbtnCut.setIcon(getEkitIcon("Cut")); jbtnCut.setText(null); jbtnCut.setToolTipText(Translatrix.getTranslationString("Cut")); jToolBar.add(jbtnCut); htTools.put(KEY_TOOL_CUT, jbtnCut); - jbtnCopy = new JButtonNoFocus(new DefaultEditorKit.CopyAction()); jbtnCopy.setIcon(getEkitIcon("Copy")); jbtnCopy.setText(null); jbtnCopy.setToolTipText(Translatrix.getTranslationString("Copy")); jToolBar.add(jbtnCopy); htTools.put(KEY_TOOL_COPY, jbtnCopy); - jbtnPaste = new JButtonNoFocus(new DefaultEditorKit.PasteAction()); jbtnPaste.setIcon(getEkitIcon("Paste")); jbtnPaste.setText(null); jbtnPaste.setToolTipText(Translatrix.getTranslationString("Paste")); jToolBar.add(jbtnPaste); htTools.put(KEY_TOOL_PASTE, jbtnPaste); - jToolBar.add(new JToolBar.Separator()); - jbtnBold = new JButtonNoFocus(actionFontBold); jbtnBold.setIcon(getEkitIcon("Bold")); jbtnBold.setText(null); jbtnBold.setToolTipText(Translatrix.getTranslationString("FontBold")); jToolBar.add(jbtnBold); htTools.put(KEY_TOOL_BOLD, jbtnBold); - jbtnItalic = new JButtonNoFocus(actionFontItalic); jbtnItalic.setIcon(getEkitIcon("Italic")); jbtnItalic.setText(null); jbtnItalic.setToolTipText(Translatrix.getTranslationString("FontItalic")); jToolBar.add(jbtnItalic); htTools.put(KEY_TOOL_ITALIC, jbtnItalic); - jbtnUnderline = new JButtonNoFocus(actionFontUnderline); jbtnUnderline.setIcon(getEkitIcon("Underline")); jbtnUnderline.setText(null); jbtnUnderline.setToolTipText(Translatrix.getTranslationString("FontUnderline")); jToolBar.add(jbtnUnderline); htTools.put(KEY_TOOL_UNDERLINE, jbtnUnderline); - jbtnStrike = new JButtonNoFocus(actionFontStrike); jbtnStrike.setIcon(getEkitIcon("Strike")); jbtnStrike.setText(null); jbtnStrike.setToolTipText(Translatrix.getTranslationString("FontStrike")); jToolBar.add(jbtnStrike); htTools.put(KEY_TOOL_STRIKE, jbtnStrike); - jbtnSuperscript = new JButtonNoFocus(actionFontSuperscript); jbtnSuperscript.setIcon(getEkitIcon("Super")); jbtnSuperscript.setText(null); jbtnSuperscript.setToolTipText(Translatrix.getTranslationString("FontSuperscript")); jToolBar.add(jbtnSuperscript); htTools.put(KEY_TOOL_SUPER, jbtnSuperscript); - jbtnSubscript = new JButtonNoFocus(actionFontSubscript); jbtnSubscript.setIcon(getEkitIcon("Sub")); jbtnSubscript.setText(null); jbtnSubscript.setToolTipText(Translatrix.getTranslationString("FontSubscript")); jToolBar.add(jbtnSubscript); htTools.put(KEY_TOOL_SUB, jbtnSubscript); - jbtnUList = new JButtonNoFocus(actionListUnordered); jbtnUList.setIcon(getEkitIcon("UList")); jbtnUList.setText(null); jbtnUList.setToolTipText(Translatrix.getTranslationString("ListUnordered")); jToolBar.add(jbtnUList); htTools.put(KEY_TOOL_ULIST, jbtnUList); - jbtnOList = new JButtonNoFocus(actionListOrdered); jbtnOList.setIcon(getEkitIcon("OList")); jbtnOList.setText(null); jbtnOList.setToolTipText(Translatrix.getTranslationString("ListOrdered")); jToolBar.add(jbtnOList); htTools.put(KEY_TOOL_OLIST, jbtnOList); - jbtnClearFormat = new JButtonNoFocus(actionClearFormat); jbtnClearFormat.setIcon(getEkitIcon("ClearFormat")); jbtnClearFormat.setText(null); jbtnClearFormat.setToolTipText(Translatrix.getTranslationString("FormatClear")); jToolBar.add(jbtnClearFormat); htTools.put(KEY_TOOL_CLEAR, jbtnClearFormat); - jToolBar.add(new JToolBar.Separator()); - jbtnAnchor = new JButtonNoFocus(actionInsertAnchor); jbtnAnchor.setIcon(getEkitIcon("Anchor")); jbtnAnchor.setText(null); jbtnAnchor.setToolTipText(Translatrix.getTranslationString("ToolAnchor")); jToolBar.add(jbtnAnchor); htTools.put(KEY_TOOL_ANCHOR, jbtnAnchor); - jToolBar.add(new JToolBar.Separator()); - jtbtnViewSource = new JToggleButtonNoFocus(getEkitIcon("Source")); jtbtnViewSource.setText(null); jtbtnViewSource.setToolTipText(Translatrix.getTranslationString("ViewSource")); jtbtnViewSource.setActionCommand("viewsource"); jtbtnViewSource.addActionListener(this); jtbtnViewSource.setPreferredSize(jbtnAnchor.getPreferredSize()); jtbtnViewSource.setMinimumSize(jbtnAnchor.getMinimumSize()); jtbtnViewSource.setMaximumSize(jbtnAnchor.getMaximumSize()); jToolBar.add(jtbtnViewSource); htTools.put(KEY_TOOL_SOURCE, jtbtnViewSource); - jToolBar.add(new JToolBar.Separator()); - jcmbStyleSelector = new JComboBoxNoFocus(); jToolBar.add(jcmbStyleSelector); jcmbStyleSelector.setAction(new StylesAction(jcmbStyleSelector)); htTools.put(KEY_TOOL_STYLES, jcmbStyleSelector); - - /* Create the scroll area for the text pane */ - JScrollPane jspViewport = new JScrollPane(jtpMain); - jspViewport.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); - jspViewport.setPreferredSize(new Dimension(400, 400)); - jspViewport.setMinimumSize(new Dimension(32, 32)); - - /* Create the scroll area for the source viewer */ - jspSource = new JScrollPane(jtpSource); - jspSource.setPreferredSize(new Dimension(400, 100)); - jspSource.setMinimumSize(new Dimension(32, 32)); - - jspltDisplay = new JSplitPane(JSplitPane.VERTICAL_SPLIT); - jspltDisplay.setTopComponent(jspViewport); - if(showViewSource) - { - jspltDisplay.setBottomComponent(jspSource); - } - else - { - jspltDisplay.setBottomComponent(null); - } - - iSplitPos = jspltDisplay.getDividerLocation(); - - registerDocumentStyles(); - - /* Create spell checker */ - try - { - dictFile = Translatrix.getTranslationString("DictionaryFile"); - SpellDictionary dictionary = new SpellDictionary(dictFile); // uses my custom loader in SpellDictionary - spellCheck = new SpellChecker(dictionary); - spellCheck.addSpellCheckListener(this); - } - catch(Exception e) - { - e.printStackTrace(); - } - spellDialog = new JSpellDialog(this.getFrame(), Translatrix.getTranslationString("ToolSpellcheckDialog"), true); - - /* Add the components to the app */ - this.setLayout(new BorderLayout()); - this.add(jspltDisplay, BorderLayout.CENTER); - } - - /** Common Constructor - * @param sDocument [String] A text or HTML document to load in the editor upon startup. - * @param sStyleSheet [String] A CSS stylesheet to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(String sDocument, String sStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64) - { - this(sDocument, sStyleSheet, null, null, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false); - } - - /** Default Language Constructor - * @param sDocument [String] A text or HTML document to load in the editor upon startup. - * @param sStyleSheet [String] A CSS stylesheet to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - */ - public EkitCore(String sDocument, String sStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, boolean base64) - { - this(sDocument, sStyleSheet, null, null, showViewSource, showMenuIcons, editModeExclusive, null, null, base64, false); - } - - /** Raw/Base64 Document & Style Sheet URL Constructor (Ideal for EkitApplet) - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(String sRawDocument, URL urlStyleSheet, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64) - { - this(null, null, sRawDocument, urlStyleSheet, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false); - } - - /** Document Constructor - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(String sRawDocument, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry, boolean base64) - { - this(null, null, sRawDocument, null, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, base64, false); - } - - /** Default Language & Document Constructor - * @param sRawDocument [String] A document encoded as a String to load in the editor upon startup. - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - */ - public EkitCore(String sRawDocument, boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, boolean base64) - { - this(null, null, sRawDocument, null, showViewSource, showMenuIcons, editModeExclusive, null, null, base64, false); - } - - /** Flags & Language Constructor - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive, String sLanguage, String sCountry) - { - this(null, null, null, null, showViewSource, showMenuIcons, editModeExclusive, sLanguage, sCountry, false, false); - } - - /** Flags Constructor - * @param showViewSource [boolean] Specifies whether or not to show the View Source window on startup. - * @param showMenuIcons [boolean] Specifies whether or not to show icon pictures in menus. - * @param editModeExclusive [boolean] Specifies whether or not to use exclusive edit mode (recommended on). - */ - public EkitCore(boolean showViewSource, boolean showMenuIcons, boolean editModeExclusive) - { - this(null, null, null, null, showViewSource, showMenuIcons, editModeExclusive, null, null, false, false); - } - - /** Language & Debug Constructor - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - * @param debugMode [boolean] Specifies whether to show the Debug menu or not. - */ - public EkitCore(String sLanguage, String sCountry, boolean debugMode) - { - this(null, null, null, null, false, true, true, sLanguage, sCountry, false, debugMode); - } - - /** Language Constructor - * @param sLanguage [String] The language portion of the Internationalization Locale to run Ekit in. - * @param sCountry [String] The country portion of the Internationalization Locale to run Ekit in. - */ - public EkitCore(String sLanguage, String sCountry) - { - this(null, null, null, null, false, true, true, sLanguage, sCountry, false, false); - } - - /** Debug Constructor - * @param debugMode [boolean] Specifies whether to show the Debug menu or not. - */ - public EkitCore(boolean debugMode) - { - this(null, null, null, null, false, true, true, null, null, false, debugMode); - } - - /** Empty Constructor - */ - public EkitCore() - { - this(null, null, null, null, false, true, true, null, null, false, false); - } - - /* ActionListener method */ - public void actionPerformed(ActionEvent ae) - { - try - { - String command = ae.getActionCommand(); - if(command.equals("newdoc")) - { - SimpleInfoDialog sidAsk = new SimpleInfoDialog(this.getFrame(), "", true, Translatrix.getTranslationString("AskNewDocument"), SimpleInfoDialog.QUESTION); - String decision = sidAsk.getDecisionValue(); - if(decision.equals(Translatrix.getTranslationString("DialogAccept"))) - { - if(styleSheet != null) - { - htmlDoc = new ExtendedHTMLDocument(styleSheet); - } - else - { - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - } - jtpMain.setText(""); - jtpSource.setText(jtpMain.getText()); - registerDocument(htmlDoc); - currentFile = null; - updateTitle(); - } - } - else if(command.equals("openhtml")) - { - openDocument(null); - } - else if(command.equals("opencss")) - { - openStyleSheet(null); - } - else if(command.equals("openb64")) - { - openDocumentBase64(null); - } - else if(command.equals("save")) - { - writeOut((HTMLDocument)(jtpMain.getDocument()), currentFile); - updateTitle(); - } - else if(command.equals("saveas")) - { - writeOut((HTMLDocument)(jtpMain.getDocument()), null); - } - else if(command.equals("savebody")) - { - writeOutFragment((HTMLDocument)(jtpMain.getDocument()),"body"); - } - else if(command.equals("savertf")) - { - writeOutRTF((StyledDocument)(jtpMain.getStyledDocument())); - } - else if(command.equals("saveb64")) - { - writeOutBase64(jtpSource.getText()); - } - else if(command.equals("textcut")) - { - if(jspSource.isShowing() && jtpSource.hasFocus()) - { - jtpSource.cut(); - } - else - { - jtpMain.cut(); - } - } - else if(command.equals("textcopy")) - { - if(jspSource.isShowing() && jtpSource.hasFocus()) - { - jtpSource.copy(); - } - else - { - jtpMain.copy(); - } - } - else if(command.equals("textpaste")) - { - if(jspSource.isShowing() && jtpSource.hasFocus()) - { - jtpSource.paste(); - } - else - { - jtpMain.paste(); - } - } - else if(command.equals("describe")) - { - System.out.println("------------DOCUMENT------------"); - System.out.println("Content Type : " + jtpMain.getContentType()); - System.out.println("Editor Kit : " + jtpMain.getEditorKit()); - System.out.println("Doc Tree :"); - System.out.println(""); - describeDocument(jtpMain.getStyledDocument()); - System.out.println("--------------------------------"); - System.out.println(""); - } - else if(command.equals("describecss")) - { - System.out.println("-----------STYLESHEET-----------"); - System.out.println("Stylesheet Rules"); - Enumeration rules = styleSheet.getStyleNames(); - while(rules.hasMoreElements()) - { - String ruleName = (String)(rules.nextElement()); - Style styleRule = styleSheet.getStyle(ruleName); - System.out.println(styleRule.toString()); - } - System.out.println("--------------------------------"); - System.out.println(""); - } - else if(command.equals("whattags")) - { - System.out.println("Caret Position : " + jtpMain.getCaretPosition()); - AttributeSet attribSet = jtpMain.getCharacterAttributes(); - Enumeration attribs = attribSet.getAttributeNames(); - System.out.println("Attributes : "); - while(attribs.hasMoreElements()) - { - String attribName = attribs.nextElement().toString(); - System.out.println(" " + attribName + " | " + attribSet.getAttribute(attribName)); - } - } - else if(command.equals("toggletoolbar")) - { - jToolBar.setVisible(jcbmiViewToolbar.isSelected()); - } - else if(command.equals("viewsource")) - { - toggleSourceWindow(); - } - else if(command.equals("serialize")) - { - serializeOut((HTMLDocument)(jtpMain.getDocument())); - } - else if(command.equals("readfromser")) - { - serializeIn(); - } - else if(command.equals("inserttable")) - { - String[] fieldNames = { "rows", "cols", "border", "cellspacing", "cellpadding", "width" }; - String[] fieldTypes = { "text", "text", "text", "text", "text", "text" }; - insertTable((Hashtable)null, fieldNames, fieldTypes); - } - else if(command.equals("inserttablerow")) - { - insertTableRow(); - } - else if(command.equals("inserttablecolumn")) - { - insertTableColumn(); - } - else if(command.equals("deletetablerow")) - { - deleteTableRow(); - } - else if(command.equals("deletetablecolumn")) - { - deleteTableColumn(); - } - else if(command.equals("insertbreak")) - { - insertBreak(); - } - else if(command.equals("insertlocalimage")) - { - insertLocalImage(null); - } - else if(command.equals("insertserverimage")) - { - insertServerImage(); - } - else if(command.equals("insertnbsp")) - { - insertNonbreakingSpace(); - } - else if(command.equals("insertform")) - { - String[] fieldNames = { "name", "method", "enctype" }; - String[] fieldTypes = { "text", "combo", "text" }; - String[] fieldValues = { "", "POST,GET", "text" }; - insertFormElement(HTML.Tag.FORM, "form", (Hashtable)null, fieldNames, fieldTypes, fieldValues, true); - } - else if(command.equals("inserttextfield")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "text"); - String[] fieldNames = { "name", "value", "size", "maxlength" }; - String[] fieldTypes = { "text", "text", "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("inserttextarea")) - { - String[] fieldNames = { "name", "rows", "cols" }; - String[] fieldTypes = { "text", "text", "text" }; - insertFormElement(HTML.Tag.TEXTAREA, "textarea", (Hashtable)null, fieldNames, fieldTypes, true); - } - else if(command.equals("insertcheckbox")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "checkbox"); - String[] fieldNames = { "name", "checked" }; - String[] fieldTypes = { "text", "bool" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertradiobutton")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "radio"); - String[] fieldNames = { "name", "checked" }; - String[] fieldTypes = { "text", "bool" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertpassword")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "password"); - String[] fieldNames = { "name", "value", "size", "maxlength" }; - String[] fieldTypes = { "text", "text", "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertbutton")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "button"); - String[] fieldNames = { "name", "value" }; - String[] fieldTypes = { "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertbuttonsubmit")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "submit"); - String[] fieldNames = { "name", "value" }; - String[] fieldTypes = { "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("insertbuttonreset")) - { - Hashtable htAttribs = new Hashtable(); - htAttribs.put("type", "reset"); - String[] fieldNames = { "name", "value" }; - String[] fieldTypes = { "text", "text" }; - insertFormElement(HTML.Tag.INPUT, "input", htAttribs, fieldNames, fieldTypes, false); - } - else if(command.equals("find")) - { - doSearch((String)null, (String)null, false, lastSearchCaseSetting, lastSearchTopSetting); - } - else if(command.equals("findagain")) - { - doSearch(lastSearchFindTerm, (String)null, false, lastSearchCaseSetting, false); - } - else if(command.equals("replace")) - { - doSearch((String)null, (String)null, true, lastSearchCaseSetting, lastSearchTopSetting); - } - else if(command.equals("exit")) - { - this.dispose(); - } - else if(command.equals("helpabout")) - { - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("About"), true, Translatrix.getTranslationString("AboutMessage"), SimpleInfoDialog.INFO); - } - else if(command.equals("spellcheck")) - { - spellCheck.checkSpelling(new DocumentWordTokenizer(jtpMain.getDocument())); - } - } - catch(IOException ioe) - { - logException("IOException in actionPerformed method", ioe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorIOException"), SimpleInfoDialog.ERROR); - } - catch(BadLocationException ble) - { - logException("BadLocationException in actionPerformed method", ble); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorBadLocationException"), SimpleInfoDialog.ERROR); - } - catch(NumberFormatException nfe) - { - logException("NumberFormatException in actionPerformed method", nfe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorNumberFormatException"), SimpleInfoDialog.ERROR); - } - catch(ClassNotFoundException cnfe) - { - logException("ClassNotFound Exception in actionPerformed method", cnfe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorClassNotFoundException "), SimpleInfoDialog.ERROR); - } - catch(RuntimeException re) - { - logException("RuntimeException in actionPerformed method", re); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorRuntimeException"), SimpleInfoDialog.ERROR); - } - } - - /* KeyListener methods */ - public void keyTyped(KeyEvent ke) - { - Element elem; - String selectedText; - int pos = this.getCaretPosition(); - int repos = -1; - if(ke.getKeyChar() == KeyEvent.VK_BACK_SPACE) - { - try - { - if(pos > 0) - { - if((selectedText = jtpMain.getSelectedText()) != null) - { - htmlUtilities.delete(); - return; - } - else - { - int sOffset = htmlDoc.getParagraphElement(pos).getStartOffset(); - if(sOffset == jtpMain.getSelectionStart()) - { - boolean content = true; - if(htmlUtilities.checkParentsTag(HTML.Tag.LI)) - { - elem = htmlUtilities.getListItemParent(); - content = false; - int so = elem.getStartOffset(); - int eo = elem.getEndOffset(); - if(so + 1 < eo) - { - char[] temp = jtpMain.getText(so, eo - so).toCharArray(); - for(int i=0; i < temp.length; i++) - { - if(!(new Character(temp[i])).isWhitespace(temp[i])) - { - content = true; - } - } - } - if(!content) - { - Element listElement = elem.getParentElement(); - htmlUtilities.removeTag(elem, true); - this.setCaretPosition(sOffset - 1); - return; - } - else - { - jtpMain.setCaretPosition(jtpMain.getCaretPosition() - 1); - jtpMain.moveCaretPosition(jtpMain.getCaretPosition() - 2); - jtpMain.replaceSelection(""); - return; - } - } - else if(htmlUtilities.checkParentsTag(HTML.Tag.TABLE)) - { - jtpMain.setCaretPosition(jtpMain.getCaretPosition() - 1); - ke.consume(); - return; - } - } - jtpMain.replaceSelection(""); - return; - } - } - } - catch (BadLocationException ble) - { - logException("BadLocationException in keyTyped method", ble); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorBadLocationException"), SimpleInfoDialog.ERROR); - } - catch (IOException ioe) - { - logException("IOException in keyTyped method", ioe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorIOException"), SimpleInfoDialog.ERROR); - } - } - else if(ke.getKeyChar() == KeyEvent.VK_ENTER) - { - try - { - if(htmlUtilities.checkParentsTag(HTML.Tag.UL) == true | htmlUtilities.checkParentsTag(HTML.Tag.OL) == true) - { - elem = htmlUtilities.getListItemParent(); - int so = elem.getStartOffset(); - int eo = elem.getEndOffset(); - char[] temp = this.getTextPane().getText(so,eo-so).toCharArray(); - boolean content = false; - for(int i=0;i= 0); - j = end; - do - { - j++; - if(!new Character(temp[j]).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(elem); - } - else - { - if(this.getCaretPosition() + 1 == elem.getEndOffset()) - { - insertListStyle(elem); - this.setCaretPosition(pos - repos); - } - else - { - int caret = this.getCaretPosition(); - String tempString = this.getTextPane().getText(caret, eo - caret); - this.getTextPane().select(caret, eo - 1); - this.getTextPane().replaceSelection(""); - htmlUtilities.insertListElement(tempString); - Element newLi = htmlUtilities.getListItemParent(); - this.setCaretPosition(newLi.getEndOffset() - 1); - } - } - } - } - catch (BadLocationException ble) - { - logException("BadLocationException in keyTyped method", ble); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorBadLocationException"), SimpleInfoDialog.ERROR); - } - catch (IOException ioe) - { - logException("IOException in keyTyped method", ioe); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorIOException"), SimpleInfoDialog.ERROR); - } - } - } - public void keyPressed(KeyEvent e) {} - public void keyReleased(KeyEvent e) {} - - public void insertListStyle(Element element) - throws BadLocationException,IOException - { - if(element.getParentElement().getName() == "ol") - { - actionListOrdered.actionPerformed(new ActionEvent(new Object(), 0, "newListPoint")); - } - else - { - actionListUnordered.actionPerformed(new ActionEvent(new Object(), 0, "newListPoint")); - } - } - - /* DocumentListener methods */ - public void changedUpdate(DocumentEvent de) { handleDocumentChange(de); } - public void insertUpdate(DocumentEvent de) { handleDocumentChange(de); } - public void removeUpdate(DocumentEvent de) { handleDocumentChange(de); } - public void handleDocumentChange(DocumentEvent de) - { - if(!exclusiveEdit) - { - if(jspSource.isShowing()) - { - if(de.getDocument() instanceof HTMLDocument || de.getDocument() instanceof ExtendedHTMLDocument) - { - jtpSource.getDocument().removeDocumentListener(this); - jtpSource.setText(jtpMain.getText()); - jtpSource.getDocument().addDocumentListener(this); - } - else if(de.getDocument() instanceof PlainDocument || de.getDocument() instanceof DefaultStyledDocument) - { - jtpMain.getDocument().removeDocumentListener(this); - jtpMain.setText(jtpSource.getText()); - jtpMain.getDocument().addDocumentListener(this); - } - } - } - } - - /* SpellCheckListener methods */ - public void spellingError(SpellCheckEvent event) - { - spellDialog.show(event); - } - - /** Method for setting a document as the current document for the text pane - * and re-registering the controls and settings for it - */ - public void registerDocument(ExtendedHTMLDocument htmlDoc) - { - jtpMain.setDocument(htmlDoc); - jtpMain.getDocument().addUndoableEditListener(new CustomUndoableEditListener()); - jtpMain.getDocument().addDocumentListener(this); - purgeUndos(); - registerDocumentStyles(); - } - - /** Method for locating the available CSS style for the document and adding - * them to the styles selector - */ - public void registerDocumentStyles() - { - if(jcmbStyleSelector == null || htmlDoc == null) - { - return; - } - jcmbStyleSelector.setEnabled(false); - jcmbStyleSelector.removeAllItems(); - jcmbStyleSelector.addItem(Translatrix.getTranslationString("NoCSSStyle")); - for(Enumeration e = htmlDoc.getStyleNames(); e.hasMoreElements();) - { - String name = (String) e.nextElement(); - if(name.length() > 0 && name.charAt(0) == '.') - { - jcmbStyleSelector.addItem(name.substring(1)); - } - } - jcmbStyleSelector.setEnabled(true); - } - - /** Method for inserting an HTML Table - */ - private void insertTable(Hashtable attribs, String[] fieldNames, String[] fieldTypes) - throws IOException, BadLocationException, RuntimeException, NumberFormatException - { - int caretPos = jtpMain.getCaretPosition(); - StringBuffer compositeElement = new StringBuffer(" 0) - { - Enumeration attribEntries = attribs.keys(); - while(attribEntries.hasMoreElements()) - { - Object entryKey = attribEntries.nextElement(); - Object entryValue = attribs.get(entryKey); - if(entryValue != null && entryValue != "") - { - compositeElement.append(" " + entryKey + "=" + '"' + entryValue + '"'); - } - } - } - int rows = 0; - int cols = 0; - if(fieldNames != null && fieldNames.length > 0) - { - PropertiesDialog propertiesDialog = new PropertiesDialog(this.getFrame(), fieldNames, fieldTypes, Translatrix.getTranslationString("FormDialogTitle"), true); - propertiesDialog.show(); - String decision = propertiesDialog.getDecisionValue(); - if(decision.equals(Translatrix.getTranslationString("DialogCancel"))) - { - propertiesDialog.dispose(); - propertiesDialog = null; - return; - } - else - { - for(int iter = 0; iter < fieldNames.length; iter++) - { - String fieldName = fieldNames[iter]; - String propValue = propertiesDialog.getFieldValue(fieldName); - if(propValue != null && propValue != "" && propValue.length() > 0) - { - if(fieldName.equals("rows")) - { - rows = Integer.parseInt(propValue); - } - else if(fieldName.equals("cols")) - { - cols = Integer.parseInt(propValue); - } - else - { - compositeElement.append(" " + fieldName + "=" + '"' + propValue + '"'); - } - } - } - } - propertiesDialog.dispose(); - propertiesDialog = null; - } - compositeElement.append(">"); - for(int i = 0; i < rows; i++) - { - compositeElement.append(""); - for(int j = 0; j < cols; j++) - { - compositeElement.append(""); - } - compositeElement.append(""); - } - compositeElement.append("

 

"); - htmlKit.insertHTML(htmlDoc, caretPos, compositeElement.toString(), 0, 0, HTML.Tag.TABLE); - jtpMain.setCaretPosition(caretPos + 1); - refreshOnUpdate(); - } - - /** Method for inserting a row into an HTML Table - */ - private void insertTableRow() - { - int caretPos = jtpMain.getCaretPosition(); - Element element = htmlDoc.getCharacterElement(jtpMain.getCaretPosition()); - Element elementParent = element.getParentElement(); - int startPoint = -1; - int columnCount = -1; - while(elementParent != null && !elementParent.getName().equals("body")) - { - if(elementParent.getName().equals("tr")) - { - startPoint = elementParent.getStartOffset(); - columnCount = elementParent.getElementCount(); - break; - } - else - { - elementParent = elementParent.getParentElement(); - } - } - if(startPoint > -1 && columnCount > -1) - { - jtpMain.setCaretPosition(startPoint); - StringBuffer sRow = new StringBuffer(); - sRow.append(""); - for(int i = 0; i < columnCount; i++) - { - sRow.append(""); - } - sRow.append(""); - ActionEvent actionEvent = new ActionEvent(jtpMain, 0, "insertTableRow"); - new HTMLEditorKit.InsertHTMLTextAction("insertTableRow", sRow.toString(), HTML.Tag.TABLE, HTML.Tag.TR).actionPerformed(actionEvent); - refreshOnUpdate(); - jtpMain.setCaretPosition(caretPos); - } - } - - /** Method for inserting a column into an HTML Table - */ - private void insertTableColumn() - { - int caretPos = jtpMain.getCaretPosition(); - Element element = htmlDoc.getCharacterElement(jtpMain.getCaretPosition()); - Element elementParent = element.getParentElement(); - int startPoint = -1; - int rowCount = -1; - int cellOffset = 0; - while(elementParent != null && !elementParent.getName().equals("body")) - { - if(elementParent.getName().equals("table")) - { - startPoint = elementParent.getStartOffset(); - rowCount = elementParent.getElementCount(); - break; - } - else if(elementParent.getName().equals("tr")) - { - int rowStart = elementParent.getStartOffset(); - int rowCells = elementParent.getElementCount(); - for(int i = 0; i < rowCells; i++) - { - Element currentCell = elementParent.getElement(i); - if(jtpMain.getCaretPosition() >= currentCell.getStartOffset() && jtpMain.getCaretPosition() <= currentCell.getEndOffset()) - { - cellOffset = i; - } - } - elementParent = elementParent.getParentElement(); - } - else - { - elementParent = elementParent.getParentElement(); - } - } - if(startPoint > -1 && rowCount > -1) - { - jtpMain.setCaretPosition(startPoint); - String sCell = ""; - ActionEvent actionEvent = new ActionEvent(jtpMain, 0, "insertTableCell"); - for(int i = 0; i < rowCount; i++) - { - Element row = elementParent.getElement(i); - Element whichCell = row.getElement(cellOffset); - jtpMain.setCaretPosition(whichCell.getStartOffset()); - new HTMLEditorKit.InsertHTMLTextAction("insertTableCell", sCell, HTML.Tag.TR, HTML.Tag.TD, HTML.Tag.TH, HTML.Tag.TD).actionPerformed(actionEvent); - } - refreshOnUpdate(); - jtpMain.setCaretPosition(caretPos); - } - } - - /** Method for inserting a cell into an HTML Table - */ - private void insertTableCell() - { - String sCell = ""; - ActionEvent actionEvent = new ActionEvent(jtpMain, 0, "insertTableCell"); - new HTMLEditorKit.InsertHTMLTextAction("insertTableCell", sCell, HTML.Tag.TR, HTML.Tag.TD, HTML.Tag.TH, HTML.Tag.TD).actionPerformed(actionEvent); - refreshOnUpdate(); - } - - /** Method for deleting a row from an HTML Table - */ - private void deleteTableRow() - throws BadLocationException - { - int caretPos = jtpMain.getCaretPosition(); - Element element = htmlDoc.getCharacterElement(jtpMain.getCaretPosition()); - Element elementParent = element.getParentElement(); - int startPoint = -1; - int endPoint = -1; - while(elementParent != null && !elementParent.getName().equals("body")) - { - if(elementParent.getName().equals("tr")) - { - startPoint = elementParent.getStartOffset(); - endPoint = elementParent.getEndOffset(); - break; - } - else - { - elementParent = elementParent.getParentElement(); - } - } - if(startPoint > -1 && endPoint > startPoint) - { - htmlDoc.remove(startPoint, endPoint - startPoint); - jtpMain.setDocument(htmlDoc); - registerDocument(htmlDoc); - refreshOnUpdate(); - if(caretPos >= htmlDoc.getLength()) - { - caretPos = htmlDoc.getLength() - 1; - } - jtpMain.setCaretPosition(caretPos); - } - } - - /** Method for deleting a column from an HTML Table - */ - private void deleteTableColumn() - throws BadLocationException - { - int caretPos = jtpMain.getCaretPosition(); - Element element = htmlDoc.getCharacterElement(jtpMain.getCaretPosition()); - Element elementParent = element.getParentElement(); - Element elementCell = (Element)null; - Element elementRow = (Element)null; - Element elementTable = (Element)null; - // Locate the table, row, and cell location of the cursor - while(elementParent != null && !elementParent.getName().equals("body")) - { - if(elementParent.getName().equals("td")) - { - elementCell = elementParent; - } - else if(elementParent.getName().equals("tr")) - { - elementRow = elementParent; - } - else if(elementParent.getName().equals("table")) - { - elementTable = elementParent; - } - elementParent = elementParent.getParentElement(); - } - int whichColumn = -1; - if(elementCell != null && elementRow != null && elementTable != null) - { - // Find the column the cursor is in - for(int i = 0; i < elementRow.getElementCount(); i++) - { - if(elementCell == elementRow.getElement(i)) - { - whichColumn = i; - } - } - if(whichColumn > -1) - { - // Iterate through the table rows, deleting cells from the indicated column - for(int i = 0; i < elementTable.getElementCount(); i++) - { - elementRow = elementTable.getElement(i); - elementCell = (elementRow.getElementCount() > whichColumn ? elementRow.getElement(whichColumn) : elementRow.getElement(elementRow.getElementCount() - 1)); - int columnCellStart = elementCell.getStartOffset(); - int columnCellEnd = elementCell.getEndOffset(); - htmlDoc.remove(columnCellStart, columnCellEnd - columnCellStart); - } - jtpMain.setDocument(htmlDoc); - registerDocument(htmlDoc); - refreshOnUpdate(); - if(caretPos >= htmlDoc.getLength()) - { - caretPos = htmlDoc.getLength() - 1; - } - jtpMain.setCaretPosition(caretPos); - } - } - } - - /** Method for inserting a break (BR) element - */ - private void insertBreak() - throws IOException, BadLocationException, RuntimeException - { - int caretPos = jtpMain.getCaretPosition(); - htmlKit.insertHTML(htmlDoc, caretPos, "
", 0, 0, HTML.Tag.BR); - jtpMain.setCaretPosition(caretPos + 1); - } - - /** Method for inserting a non-breaking space ( ) - */ - private void insertNonbreakingSpace() - throws IOException, BadLocationException, RuntimeException - { - int caretPos = jtpMain.getCaretPosition(); - htmlDoc.insertString(caretPos, "\240", jtpMain.getInputAttributes()); - jtpMain.setCaretPosition(caretPos + 1); - } - - /** Method for inserting a form element - */ - private void insertFormElement(HTML.Tag baseTag, String baseElement, Hashtable attribs, String[] fieldNames, String[] fieldTypes, String[] fieldValues, boolean hasClosingTag) - throws IOException, BadLocationException, RuntimeException - { - int caretPos = jtpMain.getCaretPosition(); - StringBuffer compositeElement = new StringBuffer("<" + baseElement); - if(attribs != null && attribs.size() > 0) - { - Enumeration attribEntries = attribs.keys(); - while(attribEntries.hasMoreElements()) - { - Object entryKey = attribEntries.nextElement(); - Object entryValue = attribs.get(entryKey); - if(entryValue != null && entryValue != "") - { - compositeElement.append(" " + entryKey + "=" + '"' + entryValue + '"'); - } - } - } - if(fieldNames != null && fieldNames.length > 0) - { - PropertiesDialog propertiesDialog = new PropertiesDialog(this.getFrame(), fieldNames, fieldTypes, fieldValues, Translatrix.getTranslationString("FormDialogTitle"), true); - propertiesDialog.show(); - String decision = propertiesDialog.getDecisionValue(); - if(decision.equals(Translatrix.getTranslationString("DialogCancel"))) - { - propertiesDialog.dispose(); - propertiesDialog = null; - return; - } - else - { - for(int iter = 0; iter < fieldNames.length; iter++) - { - String fieldName = fieldNames[iter]; - String propValue = propertiesDialog.getFieldValue(fieldName); - if(propValue != null && propValue.length() > 0) - { - if(fieldName.equals("checked")) - { - if(propValue.equals("true")) - { - compositeElement.append(" " + fieldName + "=" + '"' + propValue + '"'); - } - } - else - { - compositeElement.append(" " + fieldName + "=" + '"' + propValue + '"'); - } - } - } - } - propertiesDialog.dispose(); - propertiesDialog = null; - } - // --- Convenience for editing, this makes the FORM visible - if(useFormIndicator && baseElement.equals("form")) - { - compositeElement.append(" bgcolor=" + '"' + clrFormIndicator + '"'); - } - // --- END - compositeElement.append(">"); - if(hasClosingTag) - { - compositeElement.append(""); - } - if(baseTag == HTML.Tag.FORM) - { - compositeElement.append("

 

"); - } - htmlKit.insertHTML(htmlDoc, caretPos, compositeElement.toString(), 0, 0, baseTag); - // jtpMain.setCaretPosition(caretPos + 1); - refreshOnUpdate(); - } - - /** Alternate method call for inserting a form element - */ - private void insertFormElement(HTML.Tag baseTag, String baseElement, Hashtable attribs, String[] fieldNames, String[] fieldTypes, boolean hasClosingTag) - throws IOException, BadLocationException, RuntimeException - { - insertFormElement(baseTag, baseElement, attribs, fieldNames, fieldTypes, new String[fieldNames.length], hasClosingTag); - } - - /** Method that handles initial list insertion and deletion - */ - public void manageListElement(Element element) - { - Element h = htmlUtilities.getListItemParent(); - Element listElement = h.getParentElement(); - if(h != null) - { - htmlUtilities.removeTag(h, true); - } - } - - /** Method to initiate a find/replace operation - */ - private void doSearch(String searchFindTerm, String searchReplaceTerm, boolean bIsFindReplace, boolean bCaseSensitive, boolean bStartAtTop) - { - boolean bReplaceAll = false; - JTextPane searchPane = jtpMain; - if(jspSource.isShowing() || jtpSource.hasFocus()) - { - searchPane = jtpSource; - } - if(searchFindTerm == null || (bIsFindReplace && searchReplaceTerm == null)) - { - SearchDialog sdSearchInput = new SearchDialog(this.getFrame(), Translatrix.getTranslationString("SearchDialogTitle"), true, bIsFindReplace, bCaseSensitive, bStartAtTop); - searchFindTerm = sdSearchInput.getFindTerm(); - searchReplaceTerm = sdSearchInput.getReplaceTerm(); - bCaseSensitive = sdSearchInput.getCaseSensitive(); - bStartAtTop = sdSearchInput.getStartAtTop(); - bReplaceAll = sdSearchInput.getReplaceAll(); - } - if(searchFindTerm != null && (!bIsFindReplace || searchReplaceTerm != null)) - { - if(bReplaceAll) - { - int results = findText(searchFindTerm, searchReplaceTerm, bCaseSensitive, 0); - int findOffset = 0; - if(results > -1) - { - while(results > -1) - { - findOffset = findOffset + searchReplaceTerm.length(); - results = findText(searchFindTerm, searchReplaceTerm, bCaseSensitive, findOffset); - } - } - else - { - SimpleInfoDialog sidWarn = new SimpleInfoDialog(this.getFrame(), "", true, Translatrix.getTranslationString("ErrorNoOccurencesFound") + ":\n" + searchFindTerm, SimpleInfoDialog.WARNING); - } - } - else - { - int results = findText(searchFindTerm, searchReplaceTerm, bCaseSensitive, (bStartAtTop ? 0 : searchPane.getCaretPosition())); - if(results == -1) - { - SimpleInfoDialog sidWarn = new SimpleInfoDialog(this.getFrame(), "", true, Translatrix.getTranslationString("ErrorNoMatchFound") + ":\n" + searchFindTerm, SimpleInfoDialog.WARNING); - } - } - lastSearchFindTerm = new String(searchFindTerm); - if(searchReplaceTerm != null) - { - lastSearchReplaceTerm = new String(searchReplaceTerm); - } - else - { - lastSearchReplaceTerm = (String)null; - } - lastSearchCaseSetting = bCaseSensitive; - lastSearchTopSetting = bStartAtTop; - } - } - - /** Method for finding (and optionally replacing) a string in the text - */ - private int findText(String findTerm, String replaceTerm, boolean bCaseSenstive, int iOffset) - { - JTextPane jtpFindSource; - if(jspSource.isShowing() || jtpSource.hasFocus()) - { - jtpFindSource = jtpSource; - } - else - { - jtpFindSource = jtpMain; - } - int searchPlace = -1; - try - { - Document baseDocument = jtpFindSource.getDocument(); - searchPlace = - (bCaseSenstive ? - baseDocument.getText(0, baseDocument.getLength()).indexOf(findTerm, iOffset) : - baseDocument.getText(0, baseDocument.getLength()).toLowerCase().indexOf(findTerm.toLowerCase(), iOffset) - ); - if(searchPlace > -1) - { - if(replaceTerm != null) - { - AttributeSet attribs = null; - if(baseDocument instanceof HTMLDocument) - { - Element element = ((HTMLDocument)baseDocument).getCharacterElement(searchPlace); - attribs = element.getAttributes(); - } - baseDocument.remove(searchPlace, findTerm.length()); - baseDocument.insertString(searchPlace, replaceTerm, attribs); - jtpFindSource.setCaretPosition(searchPlace + replaceTerm.length()); - jtpFindSource.requestFocus(); - jtpFindSource.select(searchPlace, searchPlace + replaceTerm.length()); - } - else - { - jtpFindSource.setCaretPosition(searchPlace + findTerm.length()); - jtpFindSource.requestFocus(); - jtpFindSource.select(searchPlace, searchPlace + findTerm.length()); - } - } - } - catch(BadLocationException ble) - { - logException("BadLocationException in actionPerformed method", ble); - SimpleInfoDialog sidAbout = new SimpleInfoDialog(this.getFrame(), Translatrix.getTranslationString("Error"), true, Translatrix.getTranslationString("ErrorBadLocationException"), SimpleInfoDialog.ERROR); - } - return searchPlace; - } - - /** Method for inserting an image from a file - */ - private void insertLocalImage(File whatImage) - throws IOException, BadLocationException, RuntimeException - { - if(whatImage == null) - { - whatImage = getImageFromChooser(".", extsIMG, Translatrix.getTranslationString("FiletypeIMG")); - } - if(whatImage != null) - { - int caretPos = jtpMain.getCaretPosition(); - htmlKit.insertHTML(htmlDoc, caretPos, "", 0, 0, HTML.Tag.IMG); - jtpMain.setCaretPosition(caretPos + 1); - refreshOnUpdate(); - } - } - - /** Method for inserting an image from a server - */ - private void insertServerImage() - throws BadLocationException - { - if(ServletURL != null) - { - try - { - URL theServlet = new URL(ServletURL + "?GetImages=" + TreePilotSystemID + "&ImageExtensions=" + TreePilotProperties.getString("ValidImageExtensions")); - URLConnection conn = theServlet.openConnection(); - ObjectInputStream in = new ObjectInputStream(conn.getInputStream()); - String[] imageList = (String[]) in.readObject(); - int caretPos = jtpMain.getCaretPosition(); - ImageDialog imageDialog = new ImageDialog(this, ImageDir + TreePilotSystemID, imageList, "Image Chooser", true); - String selectedImage = imageDialog.getSelectedImage(); - imageDialog.dispose(); - if(selectedImage != null && !selectedImage.equals("")) - { - htmlKit.insertHTML(htmlDoc, caretPos, selectedImage, 0, 0, HTML.Tag.IMG); - jtpMain.setCaretPosition(caretPos + 1); - } - jtpMain.requestFocus(); - in.close(); - } - catch (MalformedURLException mue) - { - System.err.println("MalFormedURLException during insertImage " + mue); - } - catch (IOException ie) - { - System.err.println("IOException during insertImage " + ie); - } - catch (ClassNotFoundException cnfe) - { - System.err.println("ClassNotFoundException during insertImage" + cnfe); - } - } - } - - /** Method for inserting an image - */ - public String insertFile() - { - String selectedFile = null; - if(ServletURL != null) - { - try - { - URL theServlet = new URL(ServletURL + "?GetFiles=" + TreePilotSystemID + "&FileExtensions=" + TreePilotProperties.getString("ValidFileExtensions")); - URLConnection conn = theServlet.openConnection(); - ObjectInputStream in = new ObjectInputStream(conn.getInputStream()); - String[] fileList = (String[]) in.readObject(); - FileDialog fileDialog = new FileDialog(this, ImageDir + TreePilotSystemID, fileList, "File Chooser", true); - selectedFile = fileDialog.getSelectedFile(); - fileDialog.dispose(); - in.close(); - } - catch (MalformedURLException mue) - { - System.err.println("MalFormedURLException during insertFile " + mue); - } - catch (IOException ie) - { - System.err.println("IOException during insertFile " + ie); - } - catch (ClassNotFoundException cnfe) - { - System.err.println("ClassNotFoundException during insertFile" + cnfe); - } - } - return selectedFile; - } - - /** Method for saving text as a complete HTML document - */ - private void writeOut(HTMLDocument doc, File whatFile) - throws IOException, BadLocationException - { - if(whatFile == null) - { - whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsHTML, Translatrix.getTranslationString("FiletypeHTML")); - } - if(whatFile != null) - { - FileWriter fw = new FileWriter(whatFile); - htmlKit.write(fw, doc, 0, doc.getLength()); - fw.flush(); - fw.close(); - currentFile = whatFile; - updateTitle(); - } - refreshOnUpdate(); - } - - /** Method for saving text as an HTML fragment - */ - private void writeOutFragment(HTMLDocument doc, String containingTag) - throws IOException, BadLocationException - { - File whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsHTML, Translatrix.getTranslationString("FiletypeHTML")); - if(whatFile != null) - { - FileWriter fw = new FileWriter(whatFile); -// Element eleBody = locateElementInDocument((StyledDocument)doc, containingTag); -// htmlKit.write(fw, doc, eleBody.getStartOffset(), eleBody.getEndOffset()); - String docTextCase = jtpSource.getText().toLowerCase(); - int tagStart = docTextCase.indexOf("<" + containingTag.toLowerCase()); - int tagStartClose = docTextCase.indexOf(">", tagStart) + 1; - String closeTag = ""; - int tagEndOpen = docTextCase.indexOf(closeTag); - if(tagStartClose < 0) { tagStartClose = 0; } - if(tagEndOpen < 0 || tagEndOpen > docTextCase.length()) { tagEndOpen = docTextCase.length(); } - String bodyText = jtpSource.getText().substring(tagStartClose, tagEndOpen); - fw.write(bodyText, 0, bodyText.length()); - fw.flush(); - fw.close(); - } - refreshOnUpdate(); - } - - /** Method for saving text as an RTF document - */ - private void writeOutRTF(StyledDocument doc) - throws IOException, BadLocationException - { - File whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsRTF, Translatrix.getTranslationString("FiletypeRTF")); - if(whatFile != null) - { - FileOutputStream fos = new FileOutputStream(whatFile); - RTFEditorKit rtfKit = new RTFEditorKit(); - rtfKit.write(fos, doc, 0, doc.getLength()); - fos.flush(); - fos.close(); - } - refreshOnUpdate(); - } - - /** Method for saving text as a Base64 encoded document - */ - private void writeOutBase64(String text) - throws IOException, BadLocationException - { - File whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsB64, Translatrix.getTranslationString("FiletypeB64")); - if(whatFile != null) - { - String base64text = Base64Codec.encode(text); - FileWriter fw = new FileWriter(whatFile); - fw.write(base64text, 0, base64text.length()); - fw.flush(); - fw.close(); - } - refreshOnUpdate(); - } - - /** Method to invoke loading HTML into the app - */ - private void openDocument(File whatFile) - throws IOException, BadLocationException - { - if(whatFile == null) - { - whatFile = getFileFromChooser(".", JFileChooser.OPEN_DIALOG, extsHTML, Translatrix.getTranslationString("FiletypeHTML")); - } - if(whatFile != null) - { - try - { - loadDocument(whatFile, null); - } - catch(ChangedCharSetException ccse) - { - String charsetType = ccse.getCharSetSpec().toLowerCase(); - int pos = charsetType.indexOf("charset"); - if(pos == -1) - { - throw ccse; - } - while(pos < charsetType.length() && charsetType.charAt(pos) != '=') - { - pos++; - } - pos++; // Places file cursor past the equals sign (=) - String whatEncoding = charsetType.substring(pos).trim(); - loadDocument(whatFile, whatEncoding); - } - } - refreshOnUpdate(); - } - - /** Method for loading HTML document into the app, including document encoding setting - */ - private void loadDocument(File whatFile, String whatEncoding) - throws IOException, BadLocationException - { - Reader r = null; - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - try - { - if(whatEncoding == null) - { - r = new InputStreamReader(new FileInputStream(whatFile)); - } - else - { - r = new InputStreamReader(new FileInputStream(whatFile), whatEncoding); - htmlDoc.putProperty("IgnoreCharsetDirective", new Boolean(true)); - } - htmlKit.read(r, htmlDoc, 0); - r.close(); - registerDocument(htmlDoc); - jtpSource.setText(jtpMain.getText()); - currentFile = whatFile; - updateTitle(); - } - finally - { - if(r != null) - { - r.close(); - } - } - } - - /** Method for loading a Base64 encoded document - */ - private void openDocumentBase64(File whatFile) - throws IOException, BadLocationException - { - if(whatFile == null) - { - whatFile = getFileFromChooser(".", JFileChooser.OPEN_DIALOG, extsB64, Translatrix.getTranslationString("FiletypeB64")); - } - if(whatFile != null) - { - FileReader fr = new FileReader(whatFile); - int nextChar = 0; - StringBuffer encodedText = new StringBuffer(); - try - { - while((nextChar = fr.read()) != -1) - { - encodedText.append((char)nextChar); - } - fr.close(); - jtpSource.setText(Base64Codec.decode(encodedText.toString())); - jtpMain.setText(jtpSource.getText()); - registerDocument((ExtendedHTMLDocument)(jtpMain.getDocument())); - } - finally - { - if(fr != null) - { - fr.close(); - } - } - } - } - - /** Method for loading a Stylesheet into the app - */ - private void openStyleSheet(File fileCSS) - throws IOException - { - if(fileCSS == null) - { - fileCSS = getFileFromChooser(".", JFileChooser.OPEN_DIALOG, extsCSS, Translatrix.getTranslationString("FiletypeCSS")); - } - if(fileCSS != null) - { - String currDocText = jtpMain.getText(); - htmlDoc = (ExtendedHTMLDocument)(htmlKit.createDefaultDocument()); - styleSheet = htmlDoc.getStyleSheet(); - URL cssUrl = fileCSS.toURL(); - InputStream is = cssUrl.openStream(); - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - styleSheet.loadRules(br, cssUrl); - br.close(); - htmlDoc = new ExtendedHTMLDocument(styleSheet); - registerDocument(htmlDoc); - jtpMain.setText(currDocText); - jtpSource.setText(jtpMain.getText()); - } - refreshOnUpdate(); - } - - /** Method for serializing the document out to a file - */ - public void serializeOut(HTMLDocument doc) - throws IOException - { - File whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG, extsSer, Translatrix.getTranslationString("FiletypeSer")); - if(whatFile != null) - { - ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(whatFile)); - oos.writeObject(doc); - oos.flush(); - oos.close(); - } - refreshOnUpdate(); - } - - /** Method for reading in a serialized document from a file - */ - public void serializeIn() - throws IOException, ClassNotFoundException - { - File whatFile = getFileFromChooser(".", JFileChooser.OPEN_DIALOG, extsSer, Translatrix.getTranslationString("FiletypeSer")); - if(whatFile != null) - { - ObjectInputStream ois = new ObjectInputStream(new FileInputStream(whatFile)); - htmlDoc = (ExtendedHTMLDocument)(ois.readObject()); - ois.close(); - registerDocument(htmlDoc); - validate(); - } - refreshOnUpdate(); - } - - /** Method for obtaining a File for input/output using a JFileChooser dialog - */ - private File getFileFromChooser(String startDir, int dialogType, String[] exts, String desc) - { - JFileChooser jfileDialog = new JFileChooser(startDir); - jfileDialog.setDialogType(dialogType); - jfileDialog.setFileFilter(new MutableFilter(exts, desc)); - int optionSelected = JFileChooser.CANCEL_OPTION; - if(dialogType == JFileChooser.OPEN_DIALOG) - { - optionSelected = jfileDialog.showOpenDialog(this); - } - else if(dialogType == JFileChooser.SAVE_DIALOG) - { - optionSelected = jfileDialog.showSaveDialog(this); - } - else // default to an OPEN_DIALOG - { - optionSelected = jfileDialog.showOpenDialog(this); - } - if(optionSelected == JFileChooser.APPROVE_OPTION) - { - return jfileDialog.getSelectedFile(); - } - return (File)null; - } - - /** Method for obtaining an Image for input using a custom JFileChooser dialog - */ - private File getImageFromChooser(String startDir, String[] exts, String desc) - { - ImageFileChooser jImageDialog = new ImageFileChooser(startDir); - jImageDialog.setDialogType(JFileChooser.CUSTOM_DIALOG); - jImageDialog.setFileFilter(new MutableFilter(exts, desc)); - jImageDialog.setDialogTitle(Translatrix.getTranslationString("ImageDialogTitle")); - int optionSelected = JFileChooser.CANCEL_OPTION; - optionSelected = jImageDialog.showDialog(this, Translatrix.getTranslationString("Insert")); - if(optionSelected == JFileChooser.APPROVE_OPTION) - { - return jImageDialog.getSelectedFile(); - } - return (File)null; - } - - /** Method for describing the node hierarchy of the document - */ - private void describeDocument(StyledDocument doc) - { - Element[] elements = doc.getRootElements(); - for(int i = 0; i < elements.length; i++) - { - indent = indentStep; - for(int j = 0; j < indent; j++) { System.out.print(" "); } - System.out.print(elements[i]); - traverseElement(elements[i]); - System.out.println(""); - } - } - - /** Traverses nodes for the describing method - */ - private void traverseElement(Element element) - { - indent += indentStep; - for(int i = 0; i < element.getElementCount(); i++) - { - for(int j = 0; j < indent; j++) { System.out.print(" "); } - System.out.print(element.getElement(i)); - traverseElement(element.getElement(i)); - } - indent -= indentStep; - } - - /** Method to locate a node element by name - */ - private Element locateElementInDocument(StyledDocument doc, String elementName) - { - Element[] elements = doc.getRootElements(); - for(int i = 0; i < elements.length; i++) - { - if(elements[i].getName().equalsIgnoreCase(elementName)) - { - return elements[i]; - } - else - { - Element rtnElement = locateChildElementInDocument(elements[i], elementName); - if(rtnElement != null) - { - return rtnElement; - } - } - } - return (Element)null; - } - - /** Traverses nodes for the locating method - */ - private Element locateChildElementInDocument(Element element, String elementName) - { - for(int i = 0; i < element.getElementCount(); i++) - { - if(element.getElement(i).getName().equalsIgnoreCase(elementName)) - { - return element.getElement(i); - } - } - return (Element)null; - } - - /** Convenience method for obtaining the WYSIWYG JTextPane - */ - public JTextPane getTextPane() - { - return jtpMain; - } - - /** Convenience method for obtaining the Source JTextPane - */ - public JTextPane getSourcePane() - { - return jtpSource; - } - - /** Convenience method for obtaining the application as a Frame - */ - public Frame getFrame() - { - return frameHandler; - } - - /** Convenience method for setting the parent Frame - */ - public void setFrame(Frame parentFrame) - { - frameHandler = parentFrame; - } - - /** Convenience method for obtaining the pre-generated menu bar - */ - public JMenuBar getMenuBar() - { - return jMenuBar; - } - - /** Convenience method for obtaining a custom menu bar - */ - public JMenuBar getCustomMenuBar(Vector vcMenus) - { - jMenuBar = new JMenuBar(); - for(int i = 0; i < vcMenus.size(); i++) - { - String menuToAdd = ((String)(vcMenus.elementAt(i))).toLowerCase(); - if(htMenus.containsKey(menuToAdd)) - { - jMenuBar.add((JMenu)(htMenus.get(menuToAdd))); - } - } - return jMenuBar; - } - - /** Convenience method for obtaining the pre-generated toolbar - */ - public JToolBar getToolBar(boolean isShowing) - { - jcbmiViewToolbar.setState(isShowing); - return jToolBar; - } - - /** Convenience method for obtaining the pre-generated toolbar - */ - public JToolBar getCustomToolBar(Vector vcTools, boolean isShowing) - { - jcbmiViewToolbar.setState(isShowing); - jToolBar = new JToolBar(JToolBar.HORIZONTAL); - jToolBar.setFloatable(false); - for(int i = 0; i < vcTools.size(); i++) - { - String toolToAdd = ((String)(vcTools.elementAt(i))).toLowerCase(); - if(toolToAdd.equals(KEY_TOOL_SEP)) - { - jToolBar.add(new JToolBar.Separator()); - } - else if(htTools.containsKey(toolToAdd)) - { - if(htTools.get(toolToAdd) instanceof JButtonNoFocus) - { - jToolBar.add((JButtonNoFocus)(htTools.get(toolToAdd))); - } - else if(htTools.get(toolToAdd) instanceof JToggleButtonNoFocus) - { - jToolBar.add((JToggleButtonNoFocus)(htTools.get(toolToAdd))); - } - else if(htTools.get(toolToAdd) instanceof JComboBoxNoFocus) - { - jToolBar.add((JComboBoxNoFocus)(htTools.get(toolToAdd))); - } - else - { - jToolBar.add((JComponent)(htTools.get(toolToAdd))); - } - } - } - return jToolBar; - } - - /** Convenience method for obtaining the current file handle - */ - public File getCurrentFile() - { - return currentFile; - } - - /** Convenience method for obtaining the application name - */ - public String getAppName() - { - return appName; - } - - /** Convenience method for obtaining the document text - */ - public String getDocumentText() - { - return jtpMain.getText(); - } - - /** Convenience method for obtaining the document text - * contained within a tag pair - */ - public String getDocumentSubText(String tagBlock) - { - return getSubText(tagBlock); - } - - /** Method for extracting the text within a tag - */ - private String getSubText(String containingTag) - { - jtpSource.setText(jtpMain.getText()); - String docTextCase = jtpSource.getText().toLowerCase(); - int tagStart = docTextCase.indexOf("<" + containingTag.toLowerCase()); - int tagStartClose = docTextCase.indexOf(">", tagStart) + 1; - String closeTag = ""; - int tagEndOpen = docTextCase.indexOf(closeTag); - if(tagStartClose < 0) { tagStartClose = 0; } - if(tagEndOpen < 0 || tagEndOpen > docTextCase.length()) { tagEndOpen = docTextCase.length(); } - return jtpSource.getText().substring(tagStartClose, tagEndOpen); - } - - /** Convenience method for obtaining the document text - * contained within the BODY tags (a common request) - */ - public String getDocumentBody() - { - return getSubText("body"); - } - - /** Convenience method for setting the document text - */ - public void setDocumentText(String sText) - { - jtpMain.setText(sText); - ((HTMLEditorKit)(jtpMain.getEditorKit())).setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR)); - jtpSource.setText(jtpMain.getText()); - ((HTMLEditorKit)(jtpSource.getEditorKit())).setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR)); - } - - /** Convenience method for obtaining the document text - */ - private void updateTitle() - { - frameHandler.setTitle(appName + (currentFile == null ? "" : " - " + currentFile.getName())); - } - - /** Convenience method for clearing out the UndoManager - */ - public void purgeUndos() - { - if(undoMngr != null) - { - undoMngr.discardAllEdits(); - undoAction.updateUndoState(); - redoAction.updateRedoState(); - } - } - - /** Convenience method for refreshing and displaying changes - */ - public void refreshOnUpdate() - { - jtpMain.setText(jtpMain.getText()); - jtpSource.setText(jtpMain.getText()); - purgeUndos(); - this.repaint(); - } - - /** Convenience method for deallocating the app resources - */ - public void dispose() - { - frameHandler.dispose(); - spellCheck.dispose(); - spellCheck = null; - spellDialog = null; - System.exit(0); - } - - /** Convenience method for fetching icon images from jar file - */ - private ImageIcon getEkitIcon(String iconName) - { - return new ImageIcon(Toolkit.getDefaultToolkit().getImage(getClass().getResource("icons/" + iconName + "HK.gif"))); - } - - /** Convenience method for outputting exceptions - */ - private void logException(String internalMessage, Exception e) - { - System.err.println(internalMessage); - e.printStackTrace(System.err); - } - - /** Convenience method for toggling source window visibility - */ - private void toggleSourceWindow() - { - if(!(jspSource.isShowing())) - { - jtpSource.setText(jtpMain.getText()); - jspltDisplay.setRightComponent(jspSource); - if(exclusiveEdit) - { - jspltDisplay.setDividerLocation(0); - jspltDisplay.setEnabled(false); - } - else - { - jspltDisplay.setDividerLocation(iSplitPos); - jspltDisplay.setEnabled(true); - } - } - else - { - jtpMain.setText(jtpSource.getText()); - iSplitPos = jspltDisplay.getDividerLocation(); - jspltDisplay.remove(jspSource); - jtpMain.requestFocus(); - } - this.validate(); - jcbmiViewSource.setSelected(jspSource.isShowing()); - jtbtnViewSource.setSelected(jspSource.isShowing()); - } - - /** Searches the specified element for CLASS attribute setting - */ - private String findStyle(Element element) - { - AttributeSet as = element.getAttributes(); - if(as == null) - { - return null; - } - Object val = as.getAttribute(HTML.Attribute.CLASS); - if(val != null && (val instanceof String)) - { - return (String)val; - } - for(Enumeration e = as.getAttributeNames(); e.hasMoreElements();) - { - Object key = e.nextElement(); - if(key instanceof HTML.Tag) - { - AttributeSet eas = (AttributeSet)(as.getAttribute(key)); - if(eas != null) - { - val = eas.getAttribute(HTML.Attribute.CLASS); - if(val != null) - { - return (String)val; - } - } - } - - } - return null; - } - - /** Handles caret tracking and related events, such as displaying the current style - * of the text under the caret - */ - private void handleCaretPositionChange(CaretEvent ce) - { - int caretPos = ce.getDot(); - Element element = htmlDoc.getCharacterElement(caretPos); -/* ----- TAG EXPLICATOR CODE ------------------------------------------- - javax.swing.text.ElementIterator ei = new javax.swing.text.ElementIterator(htmlDoc); - Element ele; - while((ele = ei.next()) != null) - { - System.out.println("ELEMENT : " + ele.getName()); - } - System.out.println("ELEMENT:" + element.getName()); - Element elementParent = element.getParentElement(); - System.out.println("ATTRS:"); - AttributeSet attribs = elementParent.getAttributes(); - for(Enumeration eAttrs = attribs.getAttributeNames(); eAttrs.hasMoreElements();) - { - System.out.println(" " + eAttrs.nextElement().toString()); - } - while(elementParent != null && !elementParent.getName().equals("body")) - { - String parentName = elementParent.getName(); - System.out.println("PARENT:" + parentName); - System.out.println("ATTRS:"); - attribs = elementParent.getAttributes(); - for(Enumeration eAttr = attribs.getAttributeNames(); eAttr.hasMoreElements();) - { - System.out.println(" " + eAttr.nextElement().toString()); - } - elementParent = elementParent.getParentElement(); - } ----- END ------------------------------------------- -*/ - if(element == null) - { - return; - } - String style = null; - Vector vcStyles = new Vector(); - while(element != null) - { - if(style == null) - { - style = findStyle(element); - } - vcStyles.add(element); - element = element.getParentElement(); - } - int stylefound = -1; - if(style != null) - { - for(int i = 0; i < jcmbStyleSelector.getItemCount(); i++) - { - String in = (String)(jcmbStyleSelector.getItemAt(i)); - if(in.equalsIgnoreCase(style)) - { - stylefound = i; - break; - } - } - } - if(stylefound > -1) - { - Action ac = jcmbStyleSelector.getAction(); - ac.setEnabled(false); - jcmbStyleSelector.setSelectedIndex(stylefound); - ac.setEnabled(true); - } - else - { - jcmbStyleSelector.setSelectedIndex(0); - } - } - - /** Server-side image handling methods - */ - protected void setServletURL(String url) - { - ServletURL = url; - } - - protected void setImageDir(String sysDir) - { - ImageDir = sysDir; - } - - public void setTreePilotSystemID(String theSystem) - { - TreePilotSystemID = theSystem; - } - - /** Utility methods - */ - public ExtendedHTMLDocument getExtendedHtmlDoc() - { - return (ExtendedHTMLDocument)htmlDoc; - } - - public int getCaretPosition() - { - return jtpMain.getCaretPosition(); - } - - public void setCaretPosition(int newPositon) - { - boolean end = true; - do - { - end = true; - try - { - jtpMain.setCaretPosition(newPositon); - } - catch (IllegalArgumentException iae) - { - end = false; - newPositon--; - } - } while(!end && newPositon >= 0); - } - -/* Inner Classes --------------------------------------------- */ - - /** Class for implementing Undo as an autonomous action - */ - class UndoAction extends AbstractAction - { - public UndoAction() - { - super(Translatrix.getTranslationString("Undo")); - setEnabled(false); - } - - public void actionPerformed(ActionEvent e) - { - try - { - undoMngr.undo(); - } - catch(CannotUndoException ex) - { - ex.printStackTrace(); - } - updateUndoState(); - redoAction.updateRedoState(); - } - - protected void updateUndoState() - { - if(undoMngr.canUndo()) - { - setEnabled(true); - putValue(Action.NAME, undoMngr.getUndoPresentationName()); - } - else - { - setEnabled(false); - putValue(Action.NAME, Translatrix.getTranslationString("Undo")); - } - } - } - - /** Class for implementing Redo as an autonomous action - */ - class RedoAction extends AbstractAction - { - public RedoAction() - { - super(Translatrix.getTranslationString("Redo")); - setEnabled(false); - } - - public void actionPerformed(ActionEvent e) - { - try - { - undoMngr.redo(); - } - catch(CannotUndoException ex) - { - ex.printStackTrace(); - } - updateRedoState(); - undoAction.updateUndoState(); - } - - protected void updateRedoState() - { - if(undoMngr.canRedo()) - { - setEnabled(true); - putValue(Action.NAME, undoMngr.getRedoPresentationName()); - } - else - { - setEnabled(false); - putValue(Action.NAME, Translatrix.getTranslationString("Redo")); - } - } - } - - /** Class for implementing the Undo listener to handle the Undo and Redo actions - */ - class CustomUndoableEditListener implements UndoableEditListener - { - public void undoableEditHappened(UndoableEditEvent uee) - { - undoMngr.addEdit(uee.getEdit()); - undoAction.updateUndoState(); - redoAction.updateRedoState(); - } - } - -} diff --git a/ekit/com/hexidec/ekit/component/ExtendedHTMLEditorKit.java b/ekit/com/hexidec/ekit/component/ExtendedHTMLEditorKit.java index 07c1122..a8807ba 100644 --- a/ekit/com/hexidec/ekit/component/ExtendedHTMLEditorKit.java +++ b/ekit/com/hexidec/ekit/component/ExtendedHTMLEditorKit.java @@ -2,7 +2,7 @@ GNU Lesser General Public License ExtendedHTMLEditorKit -Copyright (C) 2001-2002 Frits Jalvingh & Howard Kistler +Copyright (C) 2001 Frits Jalvingh, Jerry Pommer & Howard Kistler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -21,17 +21,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package com.hexidec.ekit.component; +import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.StyleConstants; import javax.swing.text.View; import javax.swing.text.ViewFactory; import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.StyleSheet; -/* WACKY GERMAN CODE */ import com.hexidec.ekit.component.ExtendedHTMLDocument; -import javax.swing.text.html.StyleSheet; -import javax.swing.text.Document; /** * This class extends HTMLEditorKit so that it can provide other renderer classes @@ -64,16 +63,16 @@ public class ExtendedHTMLEditorKit extends HTMLEditorKit return new HTMLFactoryExtended(BaseUrl); } -/* WACKY GERMAN CODE */ - public Document createDefaultDocument() { - StyleSheet styles = getStyleSheet(); - StyleSheet ss = new StyleSheet(); - ss.addStyleSheet(styles); - ExtendedHTMLDocument doc = new ExtendedHTMLDocument(ss); - doc.setParser(getParser()); - doc.setAsynchronousLoadPriority(4); - doc.setTokenThreshold(100); - return doc; + public Document createDefaultDocument() + { + StyleSheet styles = getStyleSheet(); + StyleSheet ss = new StyleSheet(); + ss.addStyleSheet(styles); + ExtendedHTMLDocument doc = new ExtendedHTMLDocument(ss); + doc.setParser(getParser()); + doc.setAsynchronousLoadPriority(4); + doc.setTokenThreshold(100); + return doc; } /* Inner Classes --------------------------------------------- */ diff --git a/ekit/com/hexidec/ekit/component/RelativeImageView.java b/ekit/com/hexidec/ekit/component/RelativeImageView.java index 6ef7b6c..c2ba48a 100644 --- a/ekit/com/hexidec/ekit/component/RelativeImageView.java +++ b/ekit/com/hexidec/ekit/component/RelativeImageView.java @@ -1,946 +1,945 @@ -/* -GNU Lesser General Public License - -RelativeImageView -Copyright (C) 2001-2002 Frits Jalvingh & Howard Kistler - -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 -*/ - -package com.hexidec.ekit.component; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.Toolkit; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.awt.image.ImageObserver; -import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.InputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Dictionary; -import javax.swing.Icon; -import javax.swing.ImageIcon; -import javax.swing.JEditorPane; -import javax.swing.text.AbstractDocument; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.Element; -import javax.swing.text.JTextComponent; -import javax.swing.text.MutableAttributeSet; -import javax.swing.text.Position; -import javax.swing.text.SimpleAttributeSet; -import javax.swing.text.StyledDocument; -import javax.swing.text.View; -import javax.swing.text.ViewFactory; -import javax.swing.text.html.HTML; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.StyleSheet; -import javax.swing.event.DocumentEvent; - -/** - * @author Frits Jalvingh - * @version 1.0 - * - * This code was modeled after an artice on - * - * JavaWorld by Bob Kenworthy. - */ - -public class RelativeImageView extends View implements ImageObserver, MouseListener, MouseMotionListener -{ - public static final String TOP = "top"; - public static final String TEXTTOP = "texttop"; - public static final String MIDDLE = "middle"; - public static final String ABSMIDDLE = "absmiddle"; - public static final String CENTER = "center"; - public static final String BOTTOM = "bottom"; - public static final String IMAGE_CACHE_PROPERTY = "imageCache"; - - private static Icon sPendingImageIcon; - private static Icon sMissingImageIcon; - private static final String PENDING_IMAGE_SRC = "icons/ImagePendingHK.gif"; - private static final String MISSING_IMAGE_SRC = "icons/ImageMissingHK.gif"; - private static final int DEFAULT_WIDTH = 32; - private static final int DEFAULT_HEIGHT = 32; - private static final int DEFAULT_BORDER = 1; - - private AttributeSet attr; - private Element fElement; - private Image fImage; - private int fHeight; - private int fWidth; - private Container fContainer; - private Rectangle fBounds; - private Component fComponent; - private Point fGrowBase; // base of drag while growing image - private boolean fGrowProportionally; // should grow be proportional? - private String BaseUrl; - private boolean bLoading; // set to true while the receiver is locked, to indicate the reciever is loading the image. This is used in imageUpdate. - - /** Constructor - * Creates a new view that represents an IMG element. - * @param elem the element to create a view for - */ - public RelativeImageView(Element elem,String baseurl) - { - super(elem); - BaseUrl = baseurl; - initialize(elem); - StyleSheet sheet = getStyleSheet(); - attr = sheet.getViewAttributes(this); - } - - private void initialize(Element elem) - { - synchronized(this) - { - bLoading = true; - fWidth = 0; - fHeight = 0; - } - int width = 0; - int height = 0; - boolean customWidth = false; - boolean customHeight = false; - try - { - fElement = elem; - // request image from document's cache - AttributeSet attr = elem.getAttributes(); - if(true || isURL()) - { - URL src = getSourceURL(); - if(src != null) - { - Dictionary cache = (Dictionary)getDocument().getProperty(IMAGE_CACHE_PROPERTY); - if(cache != null) - { - fImage = (Image)cache.get(src); - } - else - { - fImage = Toolkit.getDefaultToolkit().getImage(src); - } - } - } - else - { - // load image from relative path - String src = (String)fElement.getAttributes().getAttribute(HTML.Attribute.SRC); - src = processSrcPath(src); - fImage = Toolkit.getDefaultToolkit().createImage(src); - try - { - waitForImage(); - } - catch(InterruptedException ie) - { - fImage = null; - // possibly replace with the ImageBroken icon, if that's what is happening - } - } - - // get height & width from params or image or defaults - height = getIntAttr(HTML.Attribute.HEIGHT, -1); - customHeight = (height > 0); - if(!customHeight && fImage != null) - { - height = fImage.getHeight(this); - } - if(height <= 0) - { - height = DEFAULT_HEIGHT; - } - - width = getIntAttr(HTML.Attribute.WIDTH, -1); - customWidth = (width > 0); - if(!customWidth && fImage != null) - { - width = fImage.getWidth(this); - } - if(width <= 0) - { - width = DEFAULT_WIDTH; - } - - if(fImage != null) - { - if(customHeight && customWidth) - { - Toolkit.getDefaultToolkit().prepareImage(fImage, height, width, this); - } - else - { - Toolkit.getDefaultToolkit().prepareImage(fImage, -1, -1, this); - } - } - } - finally - { - synchronized(this) - { - bLoading = false; - if(customHeight || fHeight == 0) - { - fHeight = height; - } - if(customWidth || fWidth == 0) - { - fWidth = width; - } - } - } - } - - /** Determines if path is in the form of a URL - */ - private boolean isURL() - { - String src = (String)fElement.getAttributes().getAttribute(HTML.Attribute.SRC); - return src.toLowerCase().startsWith("file") || src.toLowerCase().startsWith("http"); - } - - /** Checks to see if the absolute path is availabe thru an application - * global static variable or thru a system variable. If so, appends - * the relative path to the absolute path and returns the String. - */ - private String processSrcPath(String src) - { - String val = src; - File imageFile = new File(src); - if(imageFile.isAbsolute()) - { - return src; - } - boolean found = false; - Document doc = getDocument(); - if(doc != null) - { - String pv = (String)doc.getProperty("com.hexidec.ekit.docsource"); - if(pv != null) - { - File f = new File(pv); - val = (new File(f.getParent(), imageFile.getPath().toString())).toString(); - found = true; - } - } - if(!found) - { - String imagePath = System.getProperty("system.image.path.key"); - if(imagePath != null) - { - val = (new File(imagePath, imageFile.getPath())).toString(); - } - } - return val; - } - - /** Method insures that the image is loaded and not a broken reference - */ - private void waitForImage() - throws InterruptedException - { - int w = fImage.getWidth(this); - int h = fImage.getHeight(this); - while (true) - { - int flags = Toolkit.getDefaultToolkit().checkImage(fImage, w, h, this); - if(((flags & ERROR) != 0) || ((flags & ABORT) != 0 )) - { - throw new InterruptedException(); - } - else if((flags & (ALLBITS | FRAMEBITS)) != 0) - { - return; - } - Thread.sleep(10); - } - } - - /** Fetches the attributes to use when rendering. This is - * implemented to multiplex the attributes specified in the - * model with a StyleSheet. - */ - public AttributeSet getAttributes() - { - return attr; - } - - /** Method tests whether the image within a link - */ - boolean isLink() - { - AttributeSet anchorAttr = (AttributeSet)fElement.getAttributes().getAttribute(HTML.Tag.A); - if(anchorAttr != null) - { - return anchorAttr.isDefined(HTML.Attribute.HREF); - } - return false; - } - - /** Method returns the size of the border to use - */ - int getBorder() - { - return getIntAttr(HTML.Attribute.BORDER, isLink() ? DEFAULT_BORDER : 0); - } - - /** Method returns the amount of extra space to add along an axis - */ - int getSpace(int axis) - { - return getIntAttr((axis == X_AXIS) ? HTML.Attribute.HSPACE : HTML.Attribute.VSPACE, 0); - } - - /** Method returns the border's color, or null if this is not a link - */ - Color getBorderColor() - { - StyledDocument doc = (StyledDocument)getDocument(); - return doc.getForeground(getAttributes()); - } - - /** Method returns the image's vertical alignment - */ - float getVerticalAlignment() - { - String align = (String)fElement.getAttributes().getAttribute(HTML.Attribute.ALIGN); - if(align != null) - { - align = align.toLowerCase(); - if(align.equals(TOP) || align.equals(TEXTTOP)) - { - return 0.0f; - } - else if(align.equals(this.CENTER) || align.equals(MIDDLE) || align.equals(ABSMIDDLE)) - { - return 0.5f; - } - } - return 1.0f; // default alignment is bottom - } - - boolean hasPixels(ImageObserver obs) - { - return ((fImage != null) && (fImage.getHeight(obs) > 0) && (fImage.getWidth(obs) > 0)); - } - - /** Method returns a URL for the image source, or null if it could not be determined - */ - private URL getSourceURL() - { - String src = (String)fElement.getAttributes().getAttribute(HTML.Attribute.SRC); - if(src == null) - { - return null; - } - //URL reference = ((HTMLDocument)getDocument()).getBase(); - try - { - URL reference = new URL(BaseUrl); - URL u = new URL(reference,src); - return u; - } - catch(MalformedURLException mue) - { - return null; - } - } - - /** Method looks up an integer-valued attribute (not recursive!) - */ - private int getIntAttr(HTML.Attribute name, int iDefault) - { - AttributeSet attr = fElement.getAttributes(); - if(attr.isDefined(name)) - { - int i; - String val = (String)attr.getAttribute(name); - if(val == null) - { - i = iDefault; - } - else - { - try - { - i = Math.max(0, Integer.parseInt(val)); - } - catch(NumberFormatException nfe) - { - i = iDefault; - } - } - return i; - } - else - { - return iDefault; - } - } - - /** - * Establishes the parent view for this view. - * Seize this moment to cache the AWT Container I'm in. - */ - public void setParent(View parent) - { - super.setParent(parent); - fContainer = ((parent != null) ? getContainer() : null); - if((parent == null) && (fComponent != null)) - { - fComponent.getParent().remove(fComponent); - fComponent = null; - } - } - - /** My attributes may have changed. */ - public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) - { - super.changedUpdate(e, a, f); - float align = getVerticalAlignment(); - - int height = fHeight; - int width = fWidth; - - initialize(getElement()); - - boolean hChanged = fHeight != height; - boolean wChanged = fWidth != width; - if(hChanged || wChanged || getVerticalAlignment() != align) - { - getParent().preferenceChanged(this, hChanged, wChanged); - } - } - - - /** - * Paints the image. - * - * @param g the rendering surface to use - * @param a the allocated region to render into - * @see View#paint - */ - public void paint(Graphics g, Shape a) - { - Color oldColor = g.getColor(); - fBounds = a.getBounds(); - int border = getBorder(); - int x = fBounds.x + border + getSpace(X_AXIS); - int y = fBounds.y + border + getSpace(Y_AXIS); - int width = fWidth; - int height = fHeight; - int sel = getSelectionState(); - - // If no pixels yet, draw gray outline and icon - if(!hasPixels(this)) - { - g.setColor(Color.lightGray); - g.drawRect(x, y, width - 1, height - 1); - g.setColor(oldColor); - loadImageStatusIcons(); - Icon icon = ((fImage == null) ? sMissingImageIcon : sPendingImageIcon); - if(icon != null) - { - icon.paintIcon(getContainer(), g, x, y); - } - } - - // Draw image - if(fImage != null) - { - g.drawImage(fImage, x, y, width, height, this); - } - - // If selected exactly, we need a black border & grow-box - Color bc = getBorderColor(); - if(sel == 2) - { - // Make sure there's room for a border - int delta = 2 - border; - if(delta > 0) - { - x += delta; - y += delta; - width -= delta << 1; - height -= delta << 1; - border = 2; - } - bc = null; - g.setColor(Color.black); - // Draw grow box - g.fillRect(x + width - 5, y + height - 5, 5, 5); - } - - // Draw border - if(border > 0) - { - if(bc != null) - { - g.setColor(bc); - } - // Draw a thick rectangle: - for(int i = 1; i <= border; i++) - { - g.drawRect(x - i, y - i, width - 1 + i + i, height - 1 + i + i); - } - g.setColor(oldColor); - } - } - - /** Request that this view be repainted. Assumes the view is still at its last-drawn location. - */ - protected void repaint(long delay) - { - if((fContainer != null) && (fBounds != null)) - { - fContainer.repaint(delay, fBounds.x, fBounds.y, fBounds.width, fBounds.height); - } - } - - /** - * Determines whether the image is selected, and if it's the only thing selected. - * @return 0 if not selected, 1 if selected, 2 if exclusively selected. - * "Exclusive" selection is only returned when editable. - */ - protected int getSelectionState() - { - int p0 = fElement.getStartOffset(); - int p1 = fElement.getEndOffset(); - if(fContainer instanceof JTextComponent) - { - JTextComponent textComp = (JTextComponent)fContainer; - int start = textComp.getSelectionStart(); - int end = textComp.getSelectionEnd(); - if((start <= p0) && (end >= p1)) - { - if((start == p0) && (end == p1) && isEditable()) - { - return 2; - } - else - { - return 1; - } - } - } - return 0; - } - - protected boolean isEditable() - { - return ((fContainer instanceof JEditorPane) && ((JEditorPane)fContainer).isEditable()); - } - - /** Returns the text editor's highlight color. - */ - protected Color getHighlightColor() - { - JTextComponent textComp = (JTextComponent)fContainer; - return textComp.getSelectionColor(); - } - - // Progressive display ------------------------------------------------- - - // This can come on any thread. If we are in the process of reloading - // the image and determining our state (loading == true) we don't fire - // preference changed, or repaint, we just reset the fWidth/fHeight as - // necessary and return. This is ok as we know when loading finishes - // it will pick up the new height/width, if necessary. - - private static boolean sIsInc = true; - private static int sIncRate = 100; - - public boolean imageUpdate(Image img, int flags, int x, int y, int width, int height) - { - if((fImage == null) || (fImage != img)) - { - return false; - } - - // Bail out if there was an error - if((flags & (ABORT|ERROR)) != 0) - { - fImage = null; - repaint(0); - return false; - } - - // Resize image if necessary - short changed = 0; - if((flags & ImageObserver.HEIGHT) != 0) - { - if(!getElement().getAttributes().isDefined(HTML.Attribute.HEIGHT)) - { - changed |= 1; - } - } - if((flags & ImageObserver.WIDTH) != 0) - { - if(!getElement().getAttributes().isDefined(HTML.Attribute.WIDTH)) - { - changed |= 2; - } - } - - synchronized(this) - { - if((changed & 1) == 1) - { - fWidth = width; - } - if((changed & 2) == 2) - { - fHeight = height; - } - if(bLoading) - { - // No need to resize or repaint, still in the process of loading - return true; - } - } - - if(changed != 0) - { - // May need to resize myself, asynchronously - Document doc = getDocument(); - try - { - if(doc instanceof AbstractDocument) - { - ((AbstractDocument)doc).readLock(); - } - preferenceChanged(this, true, true); - } - finally - { - if(doc instanceof AbstractDocument) - { - ((AbstractDocument)doc).readUnlock(); - } - } - return true; - } - - // Repaint when done or when new pixels arrive - if((flags & (FRAMEBITS|ALLBITS)) != 0) - { - repaint(0); - } - else if((flags & SOMEBITS) != 0) - { - if(sIsInc) - { - repaint(sIncRate); - } - } - return ((flags & ALLBITS) == 0); - } - - // Layout -------------------------------------------------------------- - - /** Determines the preferred span for this view along an axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @returns the span the view would like to be rendered into. - * Typically the view is told to render into the span - * that is returned, although there is no guarantee. - * The parent may choose to resize or break the view. - */ - public float getPreferredSpan(int axis) - { - int extra = 2 * (getBorder() + getSpace(axis)); - switch(axis) - { - case View.X_AXIS: - return fWidth+extra; - case View.Y_AXIS: - return fHeight+extra; - default: - throw new IllegalArgumentException("Invalid axis in getPreferredSpan() : " + axis); - } - } - - /** Determines the desired alignment for this view along an - * axis. This is implemented to give the alignment to the - * bottom of the icon along the y axis, and the default - * along the x axis. - * - * @param axis may be either X_AXIS or Y_AXIS - * @returns the desired alignment. This should be a value - * between 0.0 and 1.0 where 0 indicates alignment at the - * origin and 1.0 indicates alignment to the full span - * away from the origin. An alignment of 0.5 would be the - * center of the view. - */ - public float getAlignment(int axis) - { - switch(axis) - { - case View.Y_AXIS: - return getVerticalAlignment(); - default: - return super.getAlignment(axis); - } - } - - /** Provides a mapping from the document model coordinate space - * to the coordinate space of the view mapped to it. - * - * @param pos the position to convert - * @param a the allocated region to render into - * @return the bounding box of the given position - * @exception BadLocationException if the given position does not represent a - * valid location in the associated document - * @see View#modelToView - */ - public Shape modelToView(int pos, Shape a, Position.Bias b) - throws BadLocationException - { - int p0 = getStartOffset(); - int p1 = getEndOffset(); - if((pos >= p0) && (pos <= p1)) - { - Rectangle r = a.getBounds(); - if(pos == p1) - { - r.x += r.width; - } - r.width = 0; - return r; - } - return null; - } - - /** Provides a mapping from the view coordinate space to the logical - * coordinate space of the model. - * - * @param x the X coordinate - * @param y the Y coordinate - * @param a the allocated region to render into - * @return the location within the model that best represents the - * given point of view - * @see View#viewToModel - */ - public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) - { - Rectangle alloc = (Rectangle) a; - if(x < (alloc.x + alloc.width)) - { - bias[0] = Position.Bias.Forward; - return getStartOffset(); - } - bias[0] = Position.Bias.Backward; - return getEndOffset(); - } - - /** Change the size of this image. This alters the HEIGHT and WIDTH - * attributes of the Element and causes a re-layout. - */ - protected void resize(int width, int height) - { - if((width == fWidth) && (height == fHeight)) - { - return; - } - fWidth = width; - fHeight= height; - // Replace attributes in document - MutableAttributeSet attr = new SimpleAttributeSet(); - attr.addAttribute(HTML.Attribute.WIDTH ,Integer.toString(width)); - attr.addAttribute(HTML.Attribute.HEIGHT,Integer.toString(height)); - ((StyledDocument)getDocument()).setCharacterAttributes(fElement.getStartOffset(), fElement.getEndOffset(), attr, false); - } - - // Mouse event handling ------------------------------------------------ - - /** Select or grow image when clicked. - */ - public void mousePressed(MouseEvent e) - { - Dimension size = fComponent.getSize(); - if((e.getX() >= (size.width - 7)) && (e.getY() >= (size.height - 7)) && (getSelectionState() == 2)) - { - // Click in selected grow-box: - Point loc = fComponent.getLocationOnScreen(); - fGrowBase = new Point(loc.x + e.getX() - fWidth, loc.y + e.getY() - fHeight); - fGrowProportionally = e.isShiftDown(); - } - else - { - // Else select image: - fGrowBase = null; - JTextComponent comp = (JTextComponent)fContainer; - int start = fElement.getStartOffset(); - int end = fElement.getEndOffset(); - int mark = comp.getCaret().getMark(); - int dot = comp.getCaret().getDot(); - if(e.isShiftDown()) - { - // extend selection if shift key down: - if(mark <= start) - { - comp.moveCaretPosition(end); - } - else - { - comp.moveCaretPosition(start); - } - } - else - { - // just select image, without shift: - if(mark != start) - { - comp.setCaretPosition(start); - } - if(dot != end) - { - comp.moveCaretPosition(end); - } - } - } - } - - /** Resize image if initial click was in grow-box: */ - public void mouseDragged(MouseEvent e) - { - if(fGrowBase != null) - { - Point loc = fComponent.getLocationOnScreen(); - int width = Math.max(2, loc.x + e.getX() - fGrowBase.x); - int height= Math.max(2, loc.y + e.getY() - fGrowBase.y); - if(e.isShiftDown() && fImage != null) - { - // Make sure size is proportional to actual image size - float imgWidth = fImage.getWidth(this); - float imgHeight = fImage.getHeight(this); - if((imgWidth > 0) && (imgHeight > 0)) - { - float prop = imgHeight / imgWidth; - float pwidth = height / prop; - float pheight = width * prop; - if(pwidth > width) - { - width = (int)pwidth; - } - else - { - height = (int)pheight; - } - } - } - resize(width,height); - } - } - - public void mouseReleased(MouseEvent me) - { - fGrowBase = null; - //! Should post some command to make the action undo-able - } - - /** On double-click, open image properties dialog. - */ - public void mouseClicked(MouseEvent me) - { - if(me.getClickCount() == 2) - { - //$ IMPLEMENT - } - } - - public void mouseEntered(MouseEvent me) { ; } - public void mouseMoved(MouseEvent me) { ; } - public void mouseExited(MouseEvent me) { ; } - - // Static icon accessors ----------------------------------------------- - - private Icon makeIcon(final String gifFile) - throws IOException - { - /* Copy resource into a byte array. This is - * necessary because several browsers consider - * Class.getResource a security risk because it - * can be used to load additional classes. - * Class.getResourceAsStream just returns raw - * bytes, which we can convert to an image. - */ - InputStream resource = RelativeImageView.class.getResourceAsStream(gifFile); - - if(resource == null) - { - return null; - } - BufferedInputStream in = new BufferedInputStream(resource); - ByteArrayOutputStream out = new ByteArrayOutputStream(1024); - byte[] buffer = new byte[1024]; - int n; - while((n = in.read(buffer)) > 0) - { - out.write(buffer, 0, n); - } - in.close(); - out.flush(); - - buffer = out.toByteArray(); - if(buffer.length == 0) - { - System.err.println("WARNING : " + gifFile + " is zero-length"); - return null; - } - return new ImageIcon(buffer); - } - - private void loadImageStatusIcons() - { - try - { - if(sPendingImageIcon == null) - { - sPendingImageIcon = makeIcon(PENDING_IMAGE_SRC); - } - if(sMissingImageIcon == null) - { - sMissingImageIcon = makeIcon(MISSING_IMAGE_SRC); - } - } - catch(Exception e) - { - System.err.println("ImageView : Couldn't load image icons"); - } - } - - protected StyleSheet getStyleSheet() - { - HTMLDocument doc = (HTMLDocument)getDocument(); - return doc.getStyleSheet(); - } - -} +/* +GNU Lesser General Public License + +RelativeImageView +Copyright (C) 2001 Frits Jalvingh & Howard Kistler + +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 +*/ + +package com.hexidec.ekit.component; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.image.ImageObserver; +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Dictionary; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JEditorPane; +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.JTextComponent; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.Position; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyledDocument; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; +import javax.swing.text.html.HTML; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.StyleSheet; +import javax.swing.event.DocumentEvent; + +/** + * @author Frits Jalvingh + * @version 1.0 + * + * This code was modeled after an artice on + * + * JavaWorld by Bob Kenworthy. + */ + +public class RelativeImageView extends View implements ImageObserver, MouseListener, MouseMotionListener +{ + public static final String TOP = "top"; + public static final String TEXTTOP = "texttop"; + public static final String MIDDLE = "middle"; + public static final String ABSMIDDLE = "absmiddle"; + public static final String CENTER = "center"; + public static final String BOTTOM = "bottom"; + public static final String IMAGE_CACHE_PROPERTY = "imageCache"; + + private static Icon sPendingImageIcon; + private static Icon sMissingImageIcon; + private static final String PENDING_IMAGE_SRC = "icons/ImagePendingHK.gif"; + private static final String MISSING_IMAGE_SRC = "icons/ImageMissingHK.gif"; + private static final int DEFAULT_WIDTH = 32; + private static final int DEFAULT_HEIGHT = 32; + private static final int DEFAULT_BORDER = 1; + + private AttributeSet attr; + private Element fElement; + private Image fImage; + private int fHeight; + private int fWidth; + private Container fContainer; + private Rectangle fBounds; + private Component fComponent; + private Point fGrowBase; // base of drag while growing image + private boolean fGrowProportionally; // should grow be proportional? + private String BaseUrl; + private boolean bLoading; // set to true while the receiver is locked, to indicate the reciever is loading the image. This is used in imageUpdate. + + /** Constructor + * Creates a new view that represents an IMG element. + * @param elem the element to create a view for + */ + public RelativeImageView(Element elem,String baseurl) + { + super(elem); + BaseUrl = baseurl; + initialize(elem); + StyleSheet sheet = getStyleSheet(); + attr = sheet.getViewAttributes(this); + } + + private void initialize(Element elem) + { + synchronized(this) + { + bLoading = true; + fWidth = 0; + fHeight = 0; + } + int width = 0; + int height = 0; + boolean customWidth = false; + boolean customHeight = false; + try + { + fElement = elem; + // request image from document's cache + AttributeSet attr = elem.getAttributes(); + if(true || isURL()) + { + URL src = getSourceURL(); + if(src != null) + { + Dictionary cache = (Dictionary)getDocument().getProperty(IMAGE_CACHE_PROPERTY); + if(cache != null) + { + fImage = (Image)cache.get(src); + } + else + { + fImage = Toolkit.getDefaultToolkit().getImage(src); + } + } + } + else + { + // load image from relative path + String src = (String)fElement.getAttributes().getAttribute(HTML.Attribute.SRC); + src = processSrcPath(src); + fImage = Toolkit.getDefaultToolkit().createImage(src); + try + { + waitForImage(); + } + catch(InterruptedException ie) + { + fImage = null; + // possibly replace with the ImageBroken icon, if that's what is happening + } + } + + // get height & width from params or image or defaults + height = getIntAttr(HTML.Attribute.HEIGHT, -1); + customHeight = (height > 0); + if(!customHeight && fImage != null) + { + height = fImage.getHeight(this); + } + if(height <= 0) + { + height = DEFAULT_HEIGHT; + } + + width = getIntAttr(HTML.Attribute.WIDTH, -1); + customWidth = (width > 0); + if(!customWidth && fImage != null) + { + width = fImage.getWidth(this); + } + if(width <= 0) + { + width = DEFAULT_WIDTH; + } + + if(fImage != null) + { + if(customHeight && customWidth) + { + Toolkit.getDefaultToolkit().prepareImage(fImage, height, width, this); + } + else + { + Toolkit.getDefaultToolkit().prepareImage(fImage, -1, -1, this); + } + } + } + finally + { + synchronized(this) + { + bLoading = false; + if(customHeight || fHeight == 0) + { + fHeight = height; + } + if(customWidth || fWidth == 0) + { + fWidth = width; + } + } + } + } + + /** Determines if path is in the form of a URL + */ + private boolean isURL() + { + String src = (String)fElement.getAttributes().getAttribute(HTML.Attribute.SRC); + return src.toLowerCase().startsWith("file") || src.toLowerCase().startsWith("http"); + } + + /** Checks to see if the absolute path is availabe thru an application + * global static variable or thru a system variable. If so, appends + * the relative path to the absolute path and returns the String. + */ + private String processSrcPath(String src) + { + String val = src; + File imageFile = new File(src); + if(imageFile.isAbsolute()) + { + return src; + } + boolean found = false; + Document doc = getDocument(); + if(doc != null) + { + String pv = (String)doc.getProperty("com.hexidec.ekit.docsource"); + if(pv != null) + { + File f = new File(pv); + val = (new File(f.getParent(), imageFile.getPath().toString())).toString(); + found = true; + } + } + if(!found) + { + String imagePath = System.getProperty("system.image.path.key"); + if(imagePath != null) + { + val = (new File(imagePath, imageFile.getPath())).toString(); + } + } + return val; + } + + /** Method insures that the image is loaded and not a broken reference + */ + private void waitForImage() + throws InterruptedException + { + int w = fImage.getWidth(this); + int h = fImage.getHeight(this); + while (true) + { + int flags = Toolkit.getDefaultToolkit().checkImage(fImage, w, h, this); + if(((flags & ERROR) != 0) || ((flags & ABORT) != 0 )) + { + throw new InterruptedException(); + } + else if((flags & (ALLBITS | FRAMEBITS)) != 0) + { + return; + } + Thread.sleep(10); + } + } + + /** Fetches the attributes to use when rendering. This is + * implemented to multiplex the attributes specified in the + * model with a StyleSheet. + */ + public AttributeSet getAttributes() + { + return attr; + } + + /** Method tests whether the image within a link + */ + boolean isLink() + { + AttributeSet anchorAttr = (AttributeSet)fElement.getAttributes().getAttribute(HTML.Tag.A); + if(anchorAttr != null) + { + return anchorAttr.isDefined(HTML.Attribute.HREF); + } + return false; + } + + /** Method returns the size of the border to use + */ + int getBorder() + { + return getIntAttr(HTML.Attribute.BORDER, isLink() ? DEFAULT_BORDER : 0); + } + + /** Method returns the amount of extra space to add along an axis + */ + int getSpace(int axis) + { + return getIntAttr((axis == X_AXIS) ? HTML.Attribute.HSPACE : HTML.Attribute.VSPACE, 0); + } + + /** Method returns the border's color, or null if this is not a link + */ + Color getBorderColor() + { + StyledDocument doc = (StyledDocument)getDocument(); + return doc.getForeground(getAttributes()); + } + + /** Method returns the image's vertical alignment + */ + float getVerticalAlignment() + { + String align = (String)fElement.getAttributes().getAttribute(HTML.Attribute.ALIGN); + if(align != null) + { + align = align.toLowerCase(); + if(align.equals(TOP) || align.equals(TEXTTOP)) + { + return 0.0f; + } + else if(align.equals(this.CENTER) || align.equals(MIDDLE) || align.equals(ABSMIDDLE)) + { + return 0.5f; + } + } + return 1.0f; // default alignment is bottom + } + + boolean hasPixels(ImageObserver obs) + { + return ((fImage != null) && (fImage.getHeight(obs) > 0) && (fImage.getWidth(obs) > 0)); + } + + /** Method returns a URL for the image source, or null if it could not be determined + */ + private URL getSourceURL() + { + String src = (String)fElement.getAttributes().getAttribute(HTML.Attribute.SRC); + if(src == null) + { + return null; + } + try + { + URL reference = new URL(BaseUrl); + URL u = new URL(reference,src); + return u; + } + catch(MalformedURLException mue) + { + return null; + } + } + + /** Method looks up an integer-valued attribute (not recursive!) + */ + private int getIntAttr(HTML.Attribute name, int iDefault) + { + AttributeSet attr = fElement.getAttributes(); + if(attr.isDefined(name)) + { + int i; + String val = (String)attr.getAttribute(name); + if(val == null) + { + i = iDefault; + } + else + { + try + { + i = Math.max(0, Integer.parseInt(val)); + } + catch(NumberFormatException nfe) + { + i = iDefault; + } + } + return i; + } + else + { + return iDefault; + } + } + + /** + * Establishes the parent view for this view. + * Seize this moment to cache the AWT Container I'm in. + */ + public void setParent(View parent) + { + super.setParent(parent); + fContainer = ((parent != null) ? getContainer() : null); + if((parent == null) && (fComponent != null)) + { + fComponent.getParent().remove(fComponent); + fComponent = null; + } + } + + /** My attributes may have changed. */ + public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) + { + super.changedUpdate(e, a, f); + float align = getVerticalAlignment(); + + int height = fHeight; + int width = fWidth; + + initialize(getElement()); + + boolean hChanged = fHeight != height; + boolean wChanged = fWidth != width; + if(hChanged || wChanged || getVerticalAlignment() != align) + { + getParent().preferenceChanged(this, hChanged, wChanged); + } + } + + + /** + * Paints the image. + * + * @param g the rendering surface to use + * @param a the allocated region to render into + * @see View#paint + */ + public void paint(Graphics g, Shape a) + { + Color oldColor = g.getColor(); + fBounds = a.getBounds(); + int border = getBorder(); + int x = fBounds.x + border + getSpace(X_AXIS); + int y = fBounds.y + border + getSpace(Y_AXIS); + int width = fWidth; + int height = fHeight; + int sel = getSelectionState(); + + // If no pixels yet, draw gray outline and icon + if(!hasPixels(this)) + { + g.setColor(Color.lightGray); + g.drawRect(x, y, width - 1, height - 1); + g.setColor(oldColor); + loadImageStatusIcons(); + Icon icon = ((fImage == null) ? sMissingImageIcon : sPendingImageIcon); + if(icon != null) + { + icon.paintIcon(getContainer(), g, x, y); + } + } + + // Draw image + if(fImage != null) + { + g.drawImage(fImage, x, y, width, height, this); + } + + // If selected exactly, we need a black border & grow-box + Color bc = getBorderColor(); + if(sel == 2) + { + // Make sure there's room for a border + int delta = 2 - border; + if(delta > 0) + { + x += delta; + y += delta; + width -= delta << 1; + height -= delta << 1; + border = 2; + } + bc = null; + g.setColor(Color.black); + // Draw grow box + g.fillRect(x + width - 5, y + height - 5, 5, 5); + } + + // Draw border + if(border > 0) + { + if(bc != null) + { + g.setColor(bc); + } + // Draw a thick rectangle: + for(int i = 1; i <= border; i++) + { + g.drawRect(x - i, y - i, width - 1 + i + i, height - 1 + i + i); + } + g.setColor(oldColor); + } + } + + /** Request that this view be repainted. Assumes the view is still at its last-drawn location. + */ + protected void repaint(long delay) + { + if((fContainer != null) && (fBounds != null)) + { + fContainer.repaint(delay, fBounds.x, fBounds.y, fBounds.width, fBounds.height); + } + } + + /** + * Determines whether the image is selected, and if it's the only thing selected. + * @return 0 if not selected, 1 if selected, 2 if exclusively selected. + * "Exclusive" selection is only returned when editable. + */ + protected int getSelectionState() + { + int p0 = fElement.getStartOffset(); + int p1 = fElement.getEndOffset(); + if(fContainer instanceof JTextComponent) + { + JTextComponent textComp = (JTextComponent)fContainer; + int start = textComp.getSelectionStart(); + int end = textComp.getSelectionEnd(); + if((start <= p0) && (end >= p1)) + { + if((start == p0) && (end == p1) && isEditable()) + { + return 2; + } + else + { + return 1; + } + } + } + return 0; + } + + protected boolean isEditable() + { + return ((fContainer instanceof JEditorPane) && ((JEditorPane)fContainer).isEditable()); + } + + /** Returns the text editor's highlight color. + */ + protected Color getHighlightColor() + { + JTextComponent textComp = (JTextComponent)fContainer; + return textComp.getSelectionColor(); + } + + // Progressive display ------------------------------------------------- + + // This can come on any thread. If we are in the process of reloading + // the image and determining our state (loading == true) we don't fire + // preference changed, or repaint, we just reset the fWidth/fHeight as + // necessary and return. This is ok as we know when loading finishes + // it will pick up the new height/width, if necessary. + + private static boolean sIsInc = true; + private static int sIncRate = 100; + + public boolean imageUpdate(Image img, int flags, int x, int y, int width, int height) + { + if((fImage == null) || (fImage != img)) + { + return false; + } + + // Bail out if there was an error + if((flags & (ABORT|ERROR)) != 0) + { + fImage = null; + repaint(0); + return false; + } + + // Resize image if necessary + short changed = 0; + if((flags & ImageObserver.HEIGHT) != 0) + { + if(!getElement().getAttributes().isDefined(HTML.Attribute.HEIGHT)) + { + changed |= 1; + } + } + if((flags & ImageObserver.WIDTH) != 0) + { + if(!getElement().getAttributes().isDefined(HTML.Attribute.WIDTH)) + { + changed |= 2; + } + } + + synchronized(this) + { + if((changed & 1) == 1) + { + fWidth = width; + } + if((changed & 2) == 2) + { + fHeight = height; + } + if(bLoading) + { + // No need to resize or repaint, still in the process of loading + return true; + } + } + + if(changed != 0) + { + // May need to resize myself, asynchronously + Document doc = getDocument(); + try + { + if(doc instanceof AbstractDocument) + { + ((AbstractDocument)doc).readLock(); + } + preferenceChanged(this, true, true); + } + finally + { + if(doc instanceof AbstractDocument) + { + ((AbstractDocument)doc).readUnlock(); + } + } + return true; + } + + // Repaint when done or when new pixels arrive + if((flags & (FRAMEBITS|ALLBITS)) != 0) + { + repaint(0); + } + else if((flags & SOMEBITS) != 0) + { + if(sIsInc) + { + repaint(sIncRate); + } + } + return ((flags & ALLBITS) == 0); + } + + // Layout -------------------------------------------------------------- + + /** Determines the preferred span for this view along an axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @returns the span the view would like to be rendered into. + * Typically the view is told to render into the span + * that is returned, although there is no guarantee. + * The parent may choose to resize or break the view. + */ + public float getPreferredSpan(int axis) + { + int extra = 2 * (getBorder() + getSpace(axis)); + switch(axis) + { + case View.X_AXIS: + return fWidth+extra; + case View.Y_AXIS: + return fHeight+extra; + default: + throw new IllegalArgumentException("Invalid axis in getPreferredSpan() : " + axis); + } + } + + /** Determines the desired alignment for this view along an + * axis. This is implemented to give the alignment to the + * bottom of the icon along the y axis, and the default + * along the x axis. + * + * @param axis may be either X_AXIS or Y_AXIS + * @returns the desired alignment. This should be a value + * between 0.0 and 1.0 where 0 indicates alignment at the + * origin and 1.0 indicates alignment to the full span + * away from the origin. An alignment of 0.5 would be the + * center of the view. + */ + public float getAlignment(int axis) + { + switch(axis) + { + case View.Y_AXIS: + return getVerticalAlignment(); + default: + return super.getAlignment(axis); + } + } + + /** Provides a mapping from the document model coordinate space + * to the coordinate space of the view mapped to it. + * + * @param pos the position to convert + * @param a the allocated region to render into + * @return the bounding box of the given position + * @exception BadLocationException if the given position does not represent a + * valid location in the associated document + * @see View#modelToView + */ + public Shape modelToView(int pos, Shape a, Position.Bias b) + throws BadLocationException + { + int p0 = getStartOffset(); + int p1 = getEndOffset(); + if((pos >= p0) && (pos <= p1)) + { + Rectangle r = a.getBounds(); + if(pos == p1) + { + r.x += r.width; + } + r.width = 0; + return r; + } + return null; + } + + /** Provides a mapping from the view coordinate space to the logical + * coordinate space of the model. + * + * @param x the X coordinate + * @param y the Y coordinate + * @param a the allocated region to render into + * @return the location within the model that best represents the + * given point of view + * @see View#viewToModel + */ + public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) + { + Rectangle alloc = (Rectangle) a; + if(x < (alloc.x + alloc.width)) + { + bias[0] = Position.Bias.Forward; + return getStartOffset(); + } + bias[0] = Position.Bias.Backward; + return getEndOffset(); + } + + /** Change the size of this image. This alters the HEIGHT and WIDTH + * attributes of the Element and causes a re-layout. + */ + protected void resize(int width, int height) + { + if((width == fWidth) && (height == fHeight)) + { + return; + } + fWidth = width; + fHeight= height; + // Replace attributes in document + MutableAttributeSet attr = new SimpleAttributeSet(); + attr.addAttribute(HTML.Attribute.WIDTH ,Integer.toString(width)); + attr.addAttribute(HTML.Attribute.HEIGHT,Integer.toString(height)); + ((StyledDocument)getDocument()).setCharacterAttributes(fElement.getStartOffset(), fElement.getEndOffset(), attr, false); + } + + // Mouse event handling ------------------------------------------------ + + /** Select or grow image when clicked. + */ + public void mousePressed(MouseEvent e) + { + Dimension size = fComponent.getSize(); + if((e.getX() >= (size.width - 7)) && (e.getY() >= (size.height - 7)) && (getSelectionState() == 2)) + { + // Click in selected grow-box: + Point loc = fComponent.getLocationOnScreen(); + fGrowBase = new Point(loc.x + e.getX() - fWidth, loc.y + e.getY() - fHeight); + fGrowProportionally = e.isShiftDown(); + } + else + { + // Else select image: + fGrowBase = null; + JTextComponent comp = (JTextComponent)fContainer; + int start = fElement.getStartOffset(); + int end = fElement.getEndOffset(); + int mark = comp.getCaret().getMark(); + int dot = comp.getCaret().getDot(); + if(e.isShiftDown()) + { + // extend selection if shift key down: + if(mark <= start) + { + comp.moveCaretPosition(end); + } + else + { + comp.moveCaretPosition(start); + } + } + else + { + // just select image, without shift: + if(mark != start) + { + comp.setCaretPosition(start); + } + if(dot != end) + { + comp.moveCaretPosition(end); + } + } + } + } + + /** Resize image if initial click was in grow-box: */ + public void mouseDragged(MouseEvent e) + { + if(fGrowBase != null) + { + Point loc = fComponent.getLocationOnScreen(); + int width = Math.max(2, loc.x + e.getX() - fGrowBase.x); + int height= Math.max(2, loc.y + e.getY() - fGrowBase.y); + if(e.isShiftDown() && fImage != null) + { + // Make sure size is proportional to actual image size + float imgWidth = fImage.getWidth(this); + float imgHeight = fImage.getHeight(this); + if((imgWidth > 0) && (imgHeight > 0)) + { + float prop = imgHeight / imgWidth; + float pwidth = height / prop; + float pheight = width * prop; + if(pwidth > width) + { + width = (int)pwidth; + } + else + { + height = (int)pheight; + } + } + } + resize(width,height); + } + } + + public void mouseReleased(MouseEvent me) + { + fGrowBase = null; + //! Should post some command to make the action undo-able + } + + /** On double-click, open image properties dialog. + */ + public void mouseClicked(MouseEvent me) + { + if(me.getClickCount() == 2) + { + //$ IMPLEMENT + } + } + + public void mouseEntered(MouseEvent me) { ; } + public void mouseMoved(MouseEvent me) { ; } + public void mouseExited(MouseEvent me) { ; } + + // Static icon accessors ----------------------------------------------- + + private Icon makeIcon(final String gifFile) + throws IOException + { + /* Copy resource into a byte array. This is + * necessary because several browsers consider + * Class.getResource a security risk because it + * can be used to load additional classes. + * Class.getResourceAsStream just returns raw + * bytes, which we can convert to an image. + */ + InputStream resource = RelativeImageView.class.getResourceAsStream(gifFile); + + if(resource == null) + { + return null; + } + BufferedInputStream in = new BufferedInputStream(resource); + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + byte[] buffer = new byte[1024]; + int n; + while((n = in.read(buffer)) > 0) + { + out.write(buffer, 0, n); + } + in.close(); + out.flush(); + + buffer = out.toByteArray(); + if(buffer.length == 0) + { + System.err.println("WARNING : " + gifFile + " is zero-length"); + return null; + } + return new ImageIcon(buffer); + } + + private void loadImageStatusIcons() + { + try + { + if(sPendingImageIcon == null) + { + sPendingImageIcon = makeIcon(PENDING_IMAGE_SRC); + } + if(sMissingImageIcon == null) + { + sMissingImageIcon = makeIcon(MISSING_IMAGE_SRC); + } + } + catch(Exception e) + { + System.err.println("ImageView : Couldn't load image icons"); + } + } + + protected StyleSheet getStyleSheet() + { + HTMLDocument doc = (HTMLDocument)getDocument(); + return doc.getStyleSheet(); + } + +} diff --git a/ekit/com/hexidec/util/CMUS.proposal.txt b/ekit/com/hexidec/util/CMUS.proposal.txt deleted file mode 100644 index c03b0ae..0000000 --- a/ekit/com/hexidec/util/CMUS.proposal.txt +++ /dev/null @@ -1 +0,0 @@ -Common MUsical Score /* ========================================================================= * CMUS - Common Musical Score An IFF File format for interchanging musical data using Common Music Notation by Talin (David Joiner) Verion 0.4 * ========================================================================= */ #ifndef CMUS_H #define CMUS_H /* ========================================================================= * Note to Non-Amiga users of this document ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CMUS is an IFF (Interchange File Format) file. IFF is a "Meta-standard" for making extensible, self-identifying file formats, and was developed by Commodore and Electronic Arts. In order to understand CMUS you will need to get the IFF Documentation. The quickest way to do this is to get the "Amiga ROM Kernal Manual: Devices" volume and look in the Appendix. * ========================================================================= * Note on Timing: ~~~~~~~~~~~~~~~ Common Music Notation is a symbolic, rather than a literal representation. It is supposed to be interpreted by the player. A note which is listed as "A quarter note", will seldom be played at the exact time or duration as written. These deviations from mathematically perfect time are important; they are part of what musicians call "feel" or "liveliness". Accordingly, FORM CMUS has two different kinds of timing information. _Formal Time_ is represented in symbolic form: Each symbol has a field which indicates it's duration (dotted quarter-note, etc) in symbolic units. The formal start time of an event can be obtained by summing the durations of all the previous times in the measure. In addition, there is also _Casual Time_. Each event has a "start time" which is the number of basic clock units from the start of the measure to the start of that event. Some event types also have "duration" fields of a similar nature. In general, although there will probably be a strong correlation between formal time and casual time, there is no guarantee of this. Certainly this FORM does not enforce any relationship between the two. This means that you cannot, in general, derive one from the other. You can at most make an educated guess, and even that is a non-trivial problem from an algorithmic point of view. * ========================================================================= * Note on Layout Measurements: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In general, I have tried to make all measurements as "device- independent" as possible. Measurements of page dimensions and other page-related information such as margins and indentations are represented in micrometers. Converting from micrometers to inches and "big points" (the definition of points used by Adobe and Apple) can be done with the following formulas: inches = micrometers / 25400; points = micrometers * 72 / 25400; Vertical distances of musical items are all measured in "Levels". A level is one half the distance between the lines of a staff. A note on the centerline of the staff is at level zero. Placing the note just above that line (between the 2nd and 3rd staff line) makes it level 1, while placing it below the centerline makes it level -1. Note that up is positive in this coordinate system. Note positions are recorded as a fraction of the measure width. * ========================================================================= * Rules for clipboard use: ~~~~~~~~~~~~~~~~~~~~~~~~ A CMUS chunk may be copied to the clipboard. In such cases, it is possible that only a subset of the data might be written. Specifically, measures and signatures which occur before the first selection point, or after the last selection point should not be included. Note that the measure containing the first selection point should be written, however, even if it is not in the selection range itself. (As to whether measure-lines are selectable is up to the application). In addition, an initial time signature item for that measure should be written as well; key signature and clef items are optional in this case. The application receiving the clip has the option of whether to use the signature items in the clip, or to ignore them and use the existing signatures in the score. The application can also decide to "insert" the clip into the score (causing existing other events to be shifted later in time), or "merging" the events with the existing items. The application can also choose to respect measure lines in the clip (each new measure line causes the notes to be pasted into the next measure) or to "flow" the notes across measure boundaries. Note that the notes in a clip may be non-contiguous. For example, If the user were to select every second note and copy those to the clipboard, there would be "gaps" in the clipped track. Unfortunately, a reader program would not be able to detect those gaps (since formal time does not have an explicit start time) and thus the formal time and the casual time would get out of sync. To avoid this problem, "filler" events can be inserted into the score to fill up the empty space. Note that the duration of a filler event is formal, unlike all the other events. Notation programs which only support contiguous selection (i.e. can't have a deselected note between two selected notes) can ignore filler items. A filler event at the end of the measure is not neccessary. In fact, there is no requirement in CMUS that a measure be "filled". In addition, certain music programs allow more notes in a measure than would legally fit (only while editing, the extra notes are never played). CMUS readers should handle this case robustly. This allows a reader to make intelligent use of the clip. The clip can be pasted relative to an insertion point, and the relationship of notes to measures can be (optionally) preserved, even if the selection was non-contiguous. * ========================================================================= * Future Directions ~~~~~~~~~~~~~~~~~ A number of musical features are currently mising from the CMUS spec, such as the ability for a track to jump from one staff to another. In addition, there are a number of features which would be desirable on the "page" level, such as seperate margins for each page (currently, there is no representation of individual pages in the spec). All of these things can easily be added by defining new IFF chunks or new event types. I have not done this because I feel that these additional features would best be designed by the person who needs them, in other words someone designing a music product that requires such features and is familiar with the issues inolved. Otherwise, the format might be defined wrongly, missing subtle advantages which */ /* ========================================================================= * General Definitions * ========================================================================= */ typedef long Micrometers; #if CM_MICRO_CONVERSION /* (optional) conversion to / from inches */ #define InchesToMicros(inches) ((inches) * 25400) #define MicrosToInches(micros) (((micros) + 12700) / 25400) #define HundredthsToMicros(inches) ((inches) * 254) #define MicrosToHundredths(micros) (((micros) + 127) / 254) #define PointsToMicros(points) (((points) * 25400 + 36) / 72) #define MicrosToPoints(micros) (((micros) * 72 + 12700) / 25400) #endif /* ========================================================================= * Score Header Chunk (SCHD) * ========================================================================= */ typedef struct { WORD scBarsPerLine; /* preferred bars per line */ WORD scOverallVolume; /* overall volume of score */ Micrometers scPageWidth, /* width of page */ scPageHeight, /* height of page */ scTopMargin, /* top margin of score on page 1*/ scFirstLineIndent, /* left margin indent on line 1 */ scLineIndent; /* left indent on other lines */ } CM_ScoreHeader; /* ========================================================================= * Staff Table Chunk (STAF) This section describes the data structures which are used in the CMUS 'STAF' Chunk. There is one STFF chunk per score, which contains an array of StaffEntry structres (1 per staff in the document). * ========================================================================= */ typedef struct { WORD staffFlags; /* various flags */ /* This defines the vertical size of a measure. Both of the distances are measured from the center line of the staff (in fact all staff- relative distances are represented this way). */ Micrometers staffSpaceAbove, /* space above staff */ staffSpaceBelow; /* space below staff */ Micrometers staffLevelSize; /* distance between staff lines */ } CM_StaffEntry; /* This flag indicates that a formfeed should be done before printing this staff (used when a score has more staffs than will fit on a page. */ #define STAFF_PAGEBREAK (1<<0) /* This indicates that the measure lines for this staff should not be connected to the measure lines for the staff below */ #define STAFF_BAR_BROKEN (1<<1) /* This flag indicates that a set of "curly braces" should connect this staff with the staff below. */ #define STAFF_BRACED (1<<2) /* Staff is "braced" with next */ /* These flags indicate the start and end of a square bracket which can span over several staffs. The brace should start at the staff marked with the "START" bit and continue until a staff with the "END" bit is encountered. */ #define STAFF_BRACKET_START (1<<3) #define STAFF_BRACKET_END (1<<4) /* ========================================================================= * Track Chunk (TRCK) This section describes the data structures which are used in the CMUS 'TRCK' Chunk. A track is a sequence of notes which reside on a staff. In the simplest case, there is one TRCK chunk per melody line in the score. However, a track can contain chords, rests, etc, as well. A more precise definition of a track is that a track is a sequence of chords, where all the notes within each chord have the same start time and the same duration (in formal time of course; in casual time anything is possible). Note that ties can be used to create the illusion of having broken this rule. * ========================================================================= */ /* Track Header structure: Each track begins with a track header structure, followed by any number of score items. (Use the chunk length to determine when to stop reading score items). */ typedef struct { UWORD trkStaff, /* staff number to place this on*/ trkTrack, /* track number within staff */ trkFlags; /* flags for staff header */ /* Sometimes notes on the staff are written transposed from how they should actually be played. This is the number that should be added to the pitch before it is actually played back. */ WORD trkTransposition; /* playback transposition */ } CM_TrackHeader; /* ========================================================================= * Track Item * ========================================================================= */ /* Item Header: Score items are variable in length. The first byte of the item is the length of the item in WORDS. This will allow new item types to be added in the future. All score items are an integer number of WORDS long. Each score item has a standard header structure, followed by a variable amount of item-specific data. The 'itemType' field is used to determine what that data is. 'itemLength' is the length of the item in WORDS. This allows items to be from 2 to 512 bytes long. (The value '0' is reserved as a special case). 'itemXPos' contains the X position of the item in fractions of the measure's width. Note that the area containing the signatures, and the area just before the ending measure line are not considered part of this range. Think of it this way: The value 0 is the first possible note position. The value 0x7fff if the last possible note position. Items placed at these positions should not run into the graphics at either the beginning or the end of the measure. In addition, negative numbers are also allows, which is used for symbols which penetrate into the "signature" area. In this case, 0 represents the first possible note position, and -0x8000 represents the actual barline. This convention is normally only used for lyrics, which can intrude into the signature area. 'itemStart' is used to represent the real starting time of each event. This is recorded as a delta-time, in other words itemStart contains how many clock ticks have elapsed between the current item and the item before it. Note that because of the fact that events can sometimes occur out of order (for example, notes in a chord can be ordered by pitch rather than by time, and they might not all start at exactly the same clock), this value can be negative. In addition, the clock is reset at each measure boundary. In other words, the length of a measure is determined only by it's time signature, and not by the delta between the last note and the next measure line. In fact, the itemStart field for measure line items is ignored and should always be set to zero. An item's start time does NOT have to exactly match the event's "formal" time. For example, an event at the beginning of a measure does not have to start at exactly time zero, but can be offset somewhat. This allows the subtle nuances of a live performance to be preserved, if the notation software allows for that capability. The 'itemStart' field (and the noteDuration field defined later) use a clock standard of 960 clock ticks per whole note. Thus, a quarter note is one/quarter that, or 240. This number is divisible by 3, 5, and several powers of two, making it convenient for representing triplets and quintuplets as well as small note values. */ typedef struct { UBYTE itemLength, /* length of item in WORDS */ itemType; /* type of item */ WORD itemXPos; /* horizontal position of item */ WORD itemStart; /* start time, in ticks */ } CM_ItemHeader; #define WHOLE_NOTE_DURATION 960 /* duration of a whole note */ #define MAX_ITEM_XPOS 0x7fff #define MMIN_ITEM_XPOS -0x8000 /* type codes for chunk item types */ enum notation_item_types { MEASURE_ITEM = 0, /* measure line */ SIGNATURE_ITEM, /* time sig., key sig., or clef */ NOTE_ITEM, /* first note in a chord */ CHORD_ITEM, /* additional notes in a chord */ FILLER_ITEM, /* fills up empty gaps */ DYNAMIC_ITEM, /* dynamic volume item (fff) */ INSTRUMENT_ITEM, /* instrument change item */ TEMPO_ITEM, /* tempo change item */ REPEAT_ITEM, /* for jumping around in score */ BEGIN_GROUP_ITEM, /* begin slur, crescendo, etc. */ END_GROUP_ITEM, /* end slur, crecendo, etc. */ TABLATURE_ITEM, /* guitar or other tablature */ }; /* ========================================================================= * Measure Line Item * ========================================================================= */ /* This item represents the beginning of a new measure. As such, there should be one of these at the beginning of the track, but not at the end. */ typedef struct { CM_ItemHeader measureItem; /* item header */ Micrometers measureWidth; /* width of measure */ UBYTE measureFlags; /* various flags, see below */ /* measureEnding: If non-zero, it means that this measure is part of an ending block. The value indicates which ending this measure is part of. For example, if the value = 1, then this measure is only played the first time through, if the value = 2 then it is only played the second time through. Each "repeat" section in the score can have it's own set of endings. "Endings" can be longer than one measure. Each measure that is encountered that has the same value of measureEnding as the previous measure is considered part of the same ending. */ UBYTE measureEnding; /* "ending" this measure is in */ } CM_Measure; #define MEASURE_FULL_WIDTH 0x7fff /* full width of score */ enum measureFlags { /* Draw a double bar at the end of this measure. The reason for associating the double-bar flag with the next measure line is because double bar can occur at the end of the score but not at the beginning. */ MEASUREF_DOUBLE_BAR = (1<<0), /* This is a "line break", in other words it indicates that this measure should start a new line. */ MEASUREF_NEW_LINE = (1<<1), /* If set, this flags means that the measure width was set by the user. It not set, it means that measure width was calculated on the fly, and can be re-adjusted feely if needed to line things up. Note that this width includes signatures, but does not include any indentation from the left margin of the document. */ MEASUREF_FIXED = (1<<2), }; /* ========================================================================= * Signature Items * ========================================================================= */ /* Signature items are usually placed just after the measure line. Some notators have the ability to change clef in the middle of a measure, but not all notators need support this. If it is not supported, and a clef is encountered in the middle of a measure, it is assumed to apply to the entire measure and therefore is associated with the previous measure line. */ /* Signature types: Each signature has a "sigSubType" field which indicates the type of signature. In addition, the high bit of the signature type field indicates that the signature should not be shown visibly. */ enum SignatureTypes { SIGTYPE_TIMESIG=1, SIGTYPE_CLEF, SIGTYPE_MAJORKEY, SIGTYPE_MINORKEY, SIGTYPE_HIDDEN=(1<<7) }; /* Stores a time signature. 'Beats' is the number above the line, and 'Notes' is the number below the line. For example, '3/4' time is stored as beats = 3, notes = 4. "Common Time" (The "C" symbol) which is equivalent to 4/4 is stored as 4/0 (beats = 4, notes = 0). "Cut Time" ("C" with a slash through it) which is equivalent to 2/4 is stored as 2/0 (beats = 2, notes = 0). In other words, the value "0" in "sigNotes" should always be treated as the value "4" when calculating measure lengths. See the MEASURE_LENGTH macro for an example of this. */ typedef struct { CM_ItemHeader sigItem; /* item header */ UBYTE sigSubType, /* (= SIGTYPE_TIMESIG) */ sigBeats, /* beats per bar */ sigNotes, /* size of each beat */ sigPad; } CM_TimeSignature; /* compute the measure length in clock ticks */ #define MEASURE_LENGTH(beats, notes) \ (WHOLE_NOTE_DURATION * beats / (notes ? notes : 4)) /* stores a Clef */ typedef struct { CM_ItemHeader sigItem; /* item header */ UBYTE sigSubType; /* (= SIGTYPE_CLEF) */ UBYTE sigClef; /* new clef */ } CM_Clef; /* Definitions of clef types. Note clef modifier bits which are used as part of this field as well. */ enum ClefTypes { TREBLE_CLEF = 0, /* G clef in normal position */ BASS_CLEF, /* F clef in normal position */ ALTO_CLEF, /* C clef centered on line 3 */ TENOR_CLEF, /* C clef centered on line 2 */ /* optional clefs, some of which are obselete (in the U.S.) */ SOPRANO_CLEF, /* C clef centered on line 5 */ MEZZO_SOPRANO_CLEF, /* C clef centered on line 4 */ BARITONE_CLEF, /* F clef one line lower */ FRENCH_VIOLIN_CLEF, /* G clef one line lower */ /* For drum scores. One of the things implied by a drum clef is that there might not be a relationship between a note's vertical position and it's MIDI note number, unlike other clef types. (This could be especially handy for work with MIDI drum machines). */ DRUM_CLEF, /* vertical lines or box */ /* clef modifier bits: A clef can be raised or lowered by one or two octaves. This is notated by placing a small "8" or "15" above or below the clef. */ CLEF_DOWN_1_OCTAVE=(1<<4), /* clef 1 oct lower (8 below) */ CLEF_DOWN_2_OCTAVES=(1<<5), /* clef 2 oct lower (15 below) */ CLEF_UP_1_OCTAVE=(1<<6), /* clef 1 oct higher (8 above) */ CLEF_UP_2_OCTAVE=(1<<7), /* clef 2 oct higher (15 above) */ }; /* stores a Key Signature. (used for both major and minor) 'sigKeySig' is a signed BYTE, where '0' is the key of C. Positive numbers represent the number of sharps, so 1=G, 2=D, etc, around the circle of fifths. Negative numbers represent the number of flats, F=-1, B-Flat = -2, etc. For minor keys, 0 is the key of A-minor, which like C has no sharps or flats. Other types of key signatures may be supported in the future, but will probably be done as a different type of signature item. */ typedef struct { CM_ItemHeader sigItem; /* item header */ UBYTE sigSubType; /* (== SIGTYPE_KEYSIG) */ BYTE sigKeySig; /* new key signature */ } CM_KeySignature; /* major key definitions */ #define KEY_OF_C_MAJOR 0 #define KEY_OF_G_MAJOR 1 #define KEY_OF_D_MAJOR 2 #define KEY_OF_A_MAJOR 3 #define KEY_OF_E_MAJOR 4 #define KEY_OF_B_MAJOR 5 #define KEY_OF_F_SHARP_MAJOR 6 #define KEY_OF_C_SHARP_MAJOR 7 #define KEY_OF_F_MAJOR -1 #define KEY_OF_B_FLAT_MAJOR -2 #define KEY_OF_E_FLAT_MAJOR -3 #define KEY_OF_A_FLAT_MAJOR -4 #define KEY_OF_D_FLAT_MAJOR -5 #define KEY_OF_G_FLAT_MAJOR -6 #define KEY_OF_C_FLAT_MAJOR -7 /* minor key definitions */ #define KEY_OF_A_MINOR 0 #define KEY_OF_E_MINOR 1 #define KEY_OF_B_MINOR 2 #define KEY_OF_F_SHARP_MINOR 3 #define KEY_OF_C_SHARP_MINOR 4 #define KEY_OF_G_SHARP_MINOR 5 #define KEY_OF_D_SHARP_MINOR 6 #define KEY_OF_A_SHARP_MINOR 7 #define KEY_OF_D_MINOR -1 #define KEY_OF_G_MINOR -2 #define KEY_OF_C_MINOR -3 #define KEY_OF_F_MINOR -4 #define KEY_OF_B_FLAT_MINOR -5 #define KEY_OF_E_FLAT_MINOR -6 #define KEY_OF_A_FLAT_MINOR -7 /* ========================================================================= * Note or Chord item * ========================================================================= */ /* Note Items. CHORDS: The first note of a chord is always of type "note". Additional notes, or "intervals" are stored using the "chord" type. The itemXPos, noteTuple, noteDots, noteDivision, noteStyle, noteArpeggio, noteTrill and and noteBeamHeight fields are ignored for chord items and are derived from the base note, however score writers should set them to the same as the base note for consistency. RESTS: A rest is a note item with a notePitch of 255. Rests may not be chorded. DRUM HEADS: If the NOTEF_DRUM flag is set, indicating that the note has a "drum head" rather than an ordinary note head, then all of the pitch-modifier fields should be ignored. OPTIONAL FIELDS: For less sophisticated programs, many of the fields in the note structure can be ignored. At a minimum, notation programs should look at noteLevel, noteAccidental, noteDivision and noteDots. When writing, all other fields can be set to zero, with the exception of noteDuration which should be set to the formal duration of the note in clock ticks. Sequencer programs should look at notePitch and noteDuration when loading CMUS scores. Writing is more difficult. It is suggested that unless the program is very sophisticated, that a different FORM, or perhaps a Standard MIDI File, be used for writing out sequencer data, as most of the fields in CMUS have meaning only to a notator-type program. Explanation of Fields: ~~~~~~~~~~~~~~~~~~~~~~ noteDuration -- The casual duration of the note. noteFlags -- various flags which affect either this note. Note that the NOTEF_TIED and NOTEF_TIEDOWN can be different for each note in a chord. noteDots: 0, 1 or 2 depending on the number of "dots" this note has. A dotted note is 50% longer. A double-dotted note is 75% longer. noteDivision: Indicates the base duration of the note: whole note, half note, quarter note, etc. notePitch: The MIDI pitch number for this note. noteArpeggio: Indicates an arpeggiated chord, one where the individual notes are played sequentially (like a harp). noteTrill: Trills are a rapid alternation between two notes. There are vaious kinds, see below. noteAccidental: This includes things like sharps and flats. noteLevel: This is the distance, in levels, from the center line of the staff. noteBeamHeight: The height of a beamed group of notes isn't always related to the height that the stem would be if the note were not beamed. This field is the distance, in levels, from the center line of the staff to the beam's position. This field is only meaningful for the first and last note of a beam. noteStyle: This is a field of flags which indicate things like Staccato, Legato, and other "performance style" modifiers. */ /* What the note structure looks like with bitfields: CM_ItemHeader noteItem; -- item header UWORD noteDuration; -- real duration, in ticks UWORD noteFlags; unsigned int Pad1 : 2 noteDots : 2, -- dotted, double-dotted noteDivision : 4, -- quarter note, etc. UBYTE notePitch; -- MIDI note number unsigned int noteArpeggio : 2, -- arpeggiation noteTrill : 3, -- various trill types noteAccidental : 3; -- sharp, flat, etc. BYTE noteLevel; -- dist from staff centerline BYTE noteBeamHeight; -- Y position of beam UBYTE noteStyle; -- Note Style type */ typedef struct { CM_ItemHeader noteItem; /* item header */ UWORD noteDuration; /* real duration, in ticks */ UWORD noteFlags; /* various note flags */ UBYTE noteDivision; /* formal note length */ UBYTE notePitch; /* MIDI note number */ UBYTE notePitchMods; /* modifications to pitch */ BYTE noteLevel; /* vertical position */ BYTE noteBeamHeight; /* y position of beam */ UBYTE noteStyle; /* Note Style type */ } CM_Note; /* macros to access the various bitfields */ #define CM_NoteDots(f) (((f).noteDivision >> 4) & 0x03) #define CM_NoteDivision(f) ((f).noteDivision & 0x0f) #define CM_NoteAccidental(f) ((f).notePitchMods & 7) #define CM_NoteTrill(f) (((f).notePitchMods >> 3) & 7) #define CM_NoteArpeggiation(f) (((f).notePitchMods >> 6) & 3) #define CM_SetNoteDivision(note, division, dots) \ ((note).noteDivision = (dots << 4) | division) #define CM_SetNotePitchMods(note, arp, trill, accidental) \ ((note).notePitchMods = (arp << 6) | ((trill << 3) & 7) | (accidental & 7)) #define CM_RestPitch 255 enum note_dots { NO_DOT = 0, /* Note is normal duration */ DOTTED_NOTE = 1, /* Note is 50% longer */ DOUBLE_DOTTED = 2 /* note is 75% longer */ }; enum note_divisions { DOUBLE_WHOLE_NOTE = 0, WHOLE_NOTE, HALF_NOTE, QUARTER_NOTE, EIGHTH_NOTE, SIXTEENTH_NOTE, NOTE_32, NOTE_64, NOTE_128, NOTE_256 }; enum note_accidentals { NOTE_ACC_NONE=0, NOTE_ACC_DOUBLE_FLAT, NOTE_ACC_FLAT, NOTE_ACC_HALF_FLAT, NOTE_ACC_NATURAL, NOTE_ACC_HALF_SHARP, NOTE_ACC_SHARP, NOTE_ACC_DOUBLE_SHARP, /* drum styles: used in place of accidental when NOTEF_DRUM is set. Hollow symbols are used in place of "hollow" note heads, such as half notes. Note that the assignment of drum parts to symbols is arbtrary, however the X symbol in jazz notation means "brush", and the triangle symbol is often assigned to the triangle instrument. Note also that normal note heads are often used for many drum instruments. */ NOTE_DRUM_X=0, /* An "x" instead of note head */ NOTE_DRUM_DIAMOND, /* diamond shape */ NOTE_DRUM_SQUARE, /* square box */ NOTE_DRUM_TRIANGLE, /* triangle */ }; /* trills and tremolos and other pitch modulations which can be attached to a note. Note that these apply to the entire chord. */ enum note_trills { NOTE_PMOD_NONE=0, NOTE_PMOD_TRILL, NOTE_PMOD_MORDENT, NOTE_PMOD_INV_MORDENT, NOTE_PMOD_TURN, }; /* Arpeggiation, indicated as a vertical sqiggly line before the chord */ enum note_arp_mods { NOTE_ARPEGGIO = 1, NOTE_REV_ARPEGGIO = 2, }; /* note style flags */ #define NSTYLEF_STACCATO (1<<0) /* Staccatto mark ('.') */ #define NSTYLEF_STACCATISSIMO (1<<1) /* Staccattissimo mark (wedge) */ #define NSTYLEF_LEGATO (1<<2) /* Legato ('-') */ #define NSTYLEF_SFORZANDO (1<<3) /* Szorzando ('^') */ #define NSTYLEF_ACCENT (1<<4) /* Accent ('>') */ #define NSTYLEF_TENUTO (1<<5) /* Tenuto (short '-') */ /* general note flags */ enum noteFlags { NOTEF_STEMDOWN = (1<<0), /* Note's stem is down */ NOTEF_BEAMED = (1<<1), /* Note is beamed with next note*/ NOTEF_TIED = (1<<2), /* Note is tied with next note */ NOTEF_TIEDOWN = (1<<3), /* tie direction is DOWN */ NOTEF_GRACE = (1<<4), /* display as grace note */ NOTEF_CUE = (1<<5), /* display as cue note */ NOTEF_DRUM = (1<<6), /* note has a drum head */ NOTEF_STEMSET = (1<<7), /* Stem direction fixed by user */ NOTEF_RES1 = (1<<12), /* reserved by DMCS for play */ NOTEF_RES2 = (1<<13), /* styles (sorry :-) */ NOTEF_RES3 = (1<<14), NOTEF_RES4 = (1<<15), }; /* ========================================================================= * Filler item * ========================================================================= */ /* This item is used for supporting sparse clips. The fillerDuration field contains the total of the formal durations of the missing items between the previous event and the next one. */ typedef struct { CM_ItemHeader fillerItem; /* item header */ UWORD fillerDuration; /* formal size of items left out*/ } CM_Filler; /* ========================================================================= * Dynamic Item * ========================================================================= */ /* This item specifies a MIDI volume. Note that the relationship between Volume and dynamic markings (such as fff, pp, etc) is defined elsewhere. */ typedef struct { CM_ItemHeader dynItem; /* item header */ BYTE dynLevelPos; /* vertical position in leveks */ UBYTE dynVolume; /* midi pressure number (0..127)*/ BYTE dynSymbol; /* dynamic symbol, see below */ UBYTE dynPad; } CM_Dynamic; /* Dynamic symbols: 0 = symbol not specified, derive from MIDI volume. +1 = mf -1 = mp +2 = f -2 = p +3 = ff -3 = pp +4 = fff -4 = ppp (etc) */ /* ========================================================================= * Instrument item * ========================================================================= */ /* Rather than embedding the instrument names in the actual score, a seperate "instrument table" chunk will be defined. */ typedef struct { CM_ItemHeader instItem; /* instrument item */ UBYTE instNumber; /* instrument number from table */ UBYTE instPad; } CM_Instrument; /* ========================================================================= * Tempo Item * ========================================================================= */ /* For compatibility with Standard MIDI files, tempo is represented as microseconds per quarter note, rather than the more commonly used quarter notes per minute. To convert from one to the other, the following formula works both ways: T = 60,000,000 / t; For accuracy, you may want to round: T = (60,000,000 + t/2) / t; Of course, the user interface of the program should not use units like this. */ typedef struct { CM_ItemHeader tempoItem; /* item header */ ULONG tempoValue; /* new tempo value */ } CM_Tempo; /* ========================================================================= * Repeat Item * ========================================================================= */ /* This is a general category of items for jumping around in the score in a non-sequential fashion. It includes things like begin/end repeat bars, repeat measure, Da Capo, etc. "repeatCount" is the number of times that the jump should occur, not the total number of times a passage should be played. For example, an begin/end repeat which is to play twice (once through, and then prepeated once) should have a repeatCount of "1". In addition, repeatCount should be associated with the jump rather than the label. This imples that the count should go with the "end" of a begin/end block rather than the "begin". */ typedef struct { CM_ItemHeader repeatItem; /* item header */ UBYTE repeatType; /* subtype of group */ UBYTE repeatCount; /* number of times to jump */ } CM_Repeat; enum repeat_types { REPEAT_BLOCK_BEGIN=0, /* defines a repeat block */ REPEAT_BLOCK_END, REPEAT_LAST_MEASURE, /* jumps back 1 measure */ REPEAT_LAST_TWO_MEASURES, /* jumps back 2 measures */ REPEAT_MEASURE_REST, /* rest for N measures */ /* (not really a jump) */ /* labels to go to */ REPEAT_LABEL_SEGNO, /* The "sign" D.S. refers to */ REPEAT_LABEL_CODA, /* The Coda symbol */ /* goto operators */ REPEAT_DC, /* D.C. */ REPEAT_DC_AL_FINE, /* D.C. al fine */ REPEAT_DS, /* D.S. */ REPEAT_DS_AL_FINE, /* D.S. al fine */ REPEAT_DS_AL_CODA, /* D.S. al coda */ }; /* ========================================================================= * Group Item * ========================================================================= */ /* A "Group" Item is defined as a Slur, Crescendo, or Octave Raiser. In general, groups can apply to any contiguous range of notes on a track, and groups of the same type can note overlap. Note that in some cases, such as for example a crecendo, although the modification is technically "attached" to a particular track, it affects all the tracks on that staff. */ typedef struct { CM_ItemHeader groupItem; /* item header */ UBYTE groupType; /* subtype of group */ /* To even out the structure, we'll add an extra byte which means different things based on the group type. Right now it is only defined in the case of a crescendo / decrescendo in which case it means the final volume. For all others, it should be set to zero. */ UBYTE groupVal; } CM_Group; /* Types of group items supported */ enum group_types { GROUPTYPE_SLUR_UP=0, GROUPTYPE_SLUR_DOWN, GROUPTYPE_CRESCENDO, GROUPTYPE_DECRESCENDO, GROUPTYPE_OCTAVE_UP, /* "8va" symbol */ GROUPTYPE_OCTAVE_DOWN, /* "8vb" symbol */ GROUPTYPE_GLISSANDO_UP, GROUPTYPE_GLISSANDO_DOWN, GROUPTYPE_TUPLET, /* see below */ GROUPTYPE_TRILL, /* the one with the wavy line */ GROUPTYPE_TREMOLO, /* Slashes below a beam */ }; /* Tuplets are a subtype of group items, and as such have an extended structure. Unlike other group types, tuplet group items can be nested. Note that for ending a tuplet, the extra fields are not required and a normal "Group" structure can be used. Each tuplet ending item matches the nearest previous unmatched tuplet item. */ typedef struct { CM_ItemHeader tupletItem; /* item header */ UBYTE tupletType; /* subtype of group */ /* tupletNumber indicates how many notes can fit in the space of 'tupletSpace'. For example, a triplet, i.e. "3 in the space of 2", or 2/3 duration, can be represented as tupletNumber = 3, tupletSpace = 2. */ UBYTE tupletNumber, /* How manu items */ tupletSpace; /* in the space of how many */ /* tupletDigits represents the binary number which should be displayed above the tuplet; For example, for a triplet this should be 3. */ UBYTE tupletDigits; /* number to display */ /* tupletFlags is for later use when we want tuplets combined with slurs / brackets. Currently there are no flags defined, so the field should be all zeroes. */ UBYTE tupletFlags, /* various flags */ tupletPad; } CM_Tuplet; /* ========================================================================= * Tablature Item * ========================================================================= */ /* The Tablature item is used for guitar, banjo or other fretted instruments. It's a two-dimensional array of bits, which is drawn as a grid indicating the exact placement of fingers. In addition, most tablatures have the name of the chord placed above the grid. This can be quite complex, looking something like this: 7+6 C min Which means: "C minor, with an added seventh and a raised sixth". */ typedef struct { CM_ItemHeader tabItem; /* item header */ /* tabRoot is used to indicate the name of the chord placed above the tablature. Note that the root can have superscripts, which are defined elsewhere. unsigned int rootLetter : 3, -- A, B, C, etc. rootAccidental : 2, -- accidental of root rootType : 3, -- major, minor, etc. */ UBYTE tabRoot; /* describes root of chord */ /* tabDimensions is a field of two 4 bit values, representing the width and height of the tablature array. A dimension of (0,0) indicates that only the chord symbol should be used. */ UBYTE tabDimensions; /* width/height of tab array */ /* tabIntervals is an optional field -- if it's zero, it means that the writing program wasn't sophisticated enough to set it. (This is generally true for programs that are typographical rather than musical in orientation). The field used to exactly describe the intervals in the chord above the root. Each interval may be: 0 - missing ( no interval) 1 - lowered ( one half-step below major chord position ) 2 - normal ( in the normal position for a major chord ) 3 - raised ( one half-step above major chord position ) unsigned int chordThird : 2, -- (missing, -1, 0, +1) chordFifth : 2, -- (missing, -1, 0, +1) chordSeventh : 2, -- (missing, -1, 0, +1) chordNinth : 2, -- (missing, -1, 0, +1) chordEleventh : 2, -- (missing, -1, 0, +1) chordThirteenth : 2, -- (missing, -1, 0, +1) chordFifteenth : 2, -- (missing, -1, 0, +1) chordValid : 1, -- TRUE if field valid chordPad : 1; */ UWORD tabIntervals; /* describes exact chord intervals*/ /* tabArray is a byte array of finger positions. Each byte represents a string. The value of the byte can be from 0 (representing an open string), or 1-16 (representing a finger placed above the Nth fret). The high 4 bits are reserved for now, but may be used later to indicate special placement of the fingers. Note that the tabArray can be longer or shorter than 6 bytes, up to a maximum of 16 strings. In such cases, the event length stored in the CM_ItemHeader would be adjusted accordingly. */ UBYTE tabArray[6]; /* tablature array */ /* Following the tabArray field is an optional variable-length ASCII string which is the actual text of the superscript, such as "maj6+7". The length of the string can be computed comparing the end of the event with the end of the tab-array. Null termination is not required. */ }; enum chord_accidentals { CHORD_ACC_NONE=0, CHORD_ACC_FLAT, CHORD_ACC_NATURAL, CHORD_ACC_SHARP }; enum chord_types { CHORD_TYPE_MAJOR, CHORD_TYPE_MINOR, CHORD_TYPE_DIMINISHED, CHORD_TYPE_AUGMENTED, CHORD_TYPE_SUSPENDED }; enum chord_letters { CHORD_LETTER_A=0, CHORD_LETTER_B, CHORD_LETTER_C, CHORD_LETTER_D, CHORD_LETTER_E, CHORD_LETTER_F, CHORD_LETTER_G }; /* ========================================================================= * Lyric Font Chunk (LFON) This section describes the data structures which are used in the CMUS 'LFON' Chunk. LFON chunks are used to store the font table for the document. Embedded within the Lyric, Annotation and title chunks are font specifiers which refer to a given font by number. That number is an index into this table. There is one LFON chunk per font. Each LFON chunk consists of the following header, and then the name of the font. The terminating NULL should be included in the font name. * ========================================================================= */ typedef struct { UWORD fontNumber; /* number assigned to font */ UWORD fontHeight; /* height of font in points */ /* fontName follows */ } CM_FontEntry; /* ========================================================================= * Lyric Chunk (LYRC) This section describes the data structures which are used in the CMUS 'LYRC' Chunk. Each lyric is associated with a particular track, and a particular measure within that track. The reason for this is because certain elements within the lyrics can be "attached" to notes within a track, so that syllables of the lyric can properly appear under the notes. Lyrics associated with a particular track are written immediately after that track. In other words, when reading a lyric, it should be associated with the previously read track. * ========================================================================= */ /* This is the header structure for a lyric. It is followed by the actual text of the lyric. No terminating NUL is used. Attaching syllables to notes: This is an optional feature which need not be supported by all readers. Basically, the TAB character is used to specify a block of text to align with the next note. Essentially, each chord on the trackÊacts as a center-justified tab-stop. This is similar to the way tab stops work on medium- to high-end word processors: All the text between a tab, and the next tab (or the end of the line) is "centered" at the tab-stop position. Readers which don't wish to deal with this level of complexity can just treat the tab as a space. */ typedef struct { UWORD lyricMeasure; /* starting measure of lyric */ /* Position of the upper-left coordinate of the lyric. This can be positive or negative, and is interpreted just like the 'itemXPos' field for track events. */ WORD lyricXPos; /* position relative to measure */ /* lyricLevel is the position, in micrometers, of the upper- left corner of the lyric's extent box. lyricHeight is also in micrometers. */ Micrometers lyricLevel, /* distance from center of staff*/ lyricHeight; /* height of lyric extent */ /* Width is in micrometers. */ Micrometers lyricWidth; /* lyric text string follows */ } CM_Lyric; /* Codes for specification of fonts and text styles. The "newfont" code is followed by the font number. If no font is specified, font #0 is the default. */ enum { LSTYLE_BOLD_ON=0x80, LSTYLE_BOLD_OFF, LSTYLE_ITALIC_ON, LSTYLE_ITALIC_OFF, LSTYLE_UNDER_ON, LSTYLE_UNDER_OFF, LSTYLE_NEWFONT, /* font number follows */ }; /* ========================================================================= * Annotation chunks (ANOT) This section describes the data structures which are used in the CMUS 'ANOT' Chunk. Note that there is a standard IFF chunk called 'ANNO' which can be added to any file to annotate the file. The 'ANOT' is used to specify annotations to the music, not to the file. * ========================================================================= */ /* Annotation chunks are specified exactly like Lyric chunks. The only reason for distinguishing between the two is that a "stripper" program might want to strip out one or the other. */ /* ========================================================================= * Title Chunk (TITL) This section describes the data structures which are used in the CMUS 'TITL' Chunk. Unlike Lyrics, Titles are placed at fixed positions on the page (generally at the top) and are are not adjusted based on the positioning of any particular measure. * ========================================================================= */ /* Title chunks are specified exactly like Lyric chunks, except that the lyricMeasure field is ignored and should be set to 0. In particular, the lyricXPos field is no longer based on measure width, but is now a fractional width of the document. Similarly, the lyricLevel field is the number of levels from the top of the page. (Or should that be an absolute measure?) */ /* ========================================================================= * CMUS Instrument Table (FORM INST) The instrument table fo the CMUS form is stored as an embedded FORM called 'INST'. Each instrument in the table is one INST form. Instruments can be configured for MIDI, internal audio, or both. When using internal sounds, samples can be sepcified using an embedded FORM 8SVX, or any other IFF sampled sound FORM that the program wishes to support. Sampled sounds can be embedded in the file, or external sample files can be referenced from within the file, by pathname. Here's a summary of the chunks which can be included in an INST form: 'INHD': This is the instrument header. It contains the instrument number, and the various MIDI-related parameters. The chunk format is defined below. 'FORM 8SVX': This is an embedded sampled sound file. The sampled sound is to be associated with the instrument. 'SFIL': This is a reference to a sampled sound in a different file, and can be used instead of an embedded sample. The chunk format is simply the name of the file. If the file contains more than one sample, only the first is used. (A different chunk can be defined to select the Nth sample, if it turns out that this feature might be desired). 'SHAR': This allows a instrument to share a sampled sound with another instrument. This would be used instead of either and embedded sample or an 'SFIL' chunk. The chunk format is simple a UWORD of the instrument number to share with; This instrument must have been previously loaded. 'ATAK': Identical with the ATAK chunk in FORM 8SVX, this allows the instrument have a different envelope than the one in the sampled sound file. 'RLSE': Identical with the RLSE chunk in FORM 8SVX, this allows the instrument have a different envelope than the one in the sampled sound file. 'NAME': is a standard chunk which can be added to any IFF FORM. In this case, it is used to store the instrument name. Other standard chunks which can be added are "AUTH" (author name), "VERS" (version string), "ANNO" (Annotations) and "(C) " (copyright notice). Note: If there is no sampled sound specified, either through an embedded sample or SFIL or SHAR chunks, then this instrument is a MIDI-only instrument. * ========================================================================= */ /* Instrument header chunk -- INHD */ typedef struct { /* 'instNumber' corresponds to the instrument number used in the "instrument event" in the TRCK chunk. */ UBYTE instNumber; UBYTE instFlags; /* various flags */ WORD instTune; /* tuning, in 1/100 semitones */ WORD instVolume; /* overall volume, 0-0xffff */ /* "Pan" can be used by both MIDI and sampled sound instruments, and indicates a preferences for left or right. It ranges from 0 to 127 (same as MIDI), with 0 being ?? and 127 being ??. */ UBYTE instPan; /* MIDI-related variables */ UBYTE instMidiChannel; /* MIDI channel to use */ UBYTE instMidiPreset; /* MIDI Preset for this channel */ UBYTE instMidiPort; /* Hardware port #, if applies */ } InstrumentHeader; #define INST_MAXVOL 0x0ffff #define INST_MAXPAN 127 /* various flags for instFlags */ #define INSTF_MIDI (1<<0) /* MIDI is enabled */ #define INSTF_MIDIVOL (1<<1) /* use MIDI volume, not velocity*/ #endif \ No newline at end of file diff --git a/ekit/com/hexidec/util/CMUScodec.java b/ekit/com/hexidec/util/CMUScodec.java deleted file mode 100644 index da300fd..0000000 --- a/ekit/com/hexidec/util/CMUScodec.java +++ /dev/null @@ -1 +0,0 @@ -/* GNU General Public License CMUScodec Copyright (C) 2002 Howard A Kistler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.hexidec.util; import java.awt.AWTException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; /** CMUScodec * * Utility for reading (and eventually converting) Deluxe Music * Construction Set (DMCS) files * * @author Howard Kistler * @version 0.1 * * VERSION HISTORY * 0.1 (03/13/2002) - initial creation (03/13/2002) */ public class CMUScodec { // Constants ----------------------------------------------------------------------------> /* CMUS File Structure FILE HEADER ==================== (At the beginning of each file) IFF Form Chunk 4 bytes - FORM 4 bytes - ? 4 bytes - CMUS Score Header Chunk 4 bytes - SCHD 10 bytes - 00 00 00 1A 00 02 00 00 00 02 2 bytes - (significant number[s]?) 2 bytes - 00 04 (# of channels maybe?) 2 bytes - Ch 12 bytes - 00 00 00 00 00 00 0C E6 00 00 0C E6 2 bytes - (2 byte significant number, or 00 and 1 byte significant?) DSCR HEADER BLOCK 4 bytes - DSCR 14 bytes - ? Staff Table Chunk 4 bytes - STAF X bytes - ? (1 entry per staff in score) DSTF HEADER BLOCK 4 bytes - DSTF X bytes - ? LFON HEADER BLOCK 4 bytes - LFON 4 bytes - 00 00 00 0F 4 bytes - 00 00 00 08 X bytes - Font Name 2 bytes - 00 00 SONG BODY ==================== TRCK BLOCKS 4 bytes - TRCK X bytes - track data INSTRUMENT BLOCK ==================== Each Instrument Contains: 4 bytes - FORM 4 bytes - (number?) 8 bytes - INSTINHD 4 bytes - 00 00 00 0A 1 byte - Instrument Number 1 byte - (zero pad?) 8 bytes - 00 00 FF FF 40 00 00 00 4 bytes - NAME 4 bytes - Name Byte Count Y bytes - Name Bytes (Y = value from above) 1 byte - (zero pad?) [Optional Instrument Path] 4 bytes - SFIL 4 bytes - Path Byte Count Y bytes - Path Bytes (Y = value from above) 1 byte - (zero pad) */ // Decoder Vars private static byte[] bData; private static int iPlace; // Public Vartypes ----------------------------------------------------------------------> public final static int FILETYPE_DMCS = 0; public final static int FILETYPE_CMUS = 1; public final static int FILETYPE_SMUS = 2; public final static int FILETYPE_TEXT = 3; public final static int FILETYPE_FINALE = 4; private final static byte NUL = (byte)0; // Constructor --------------------------------------------------------------------------> public CMUScodec() { } // Decode Method ------------------------------------------------------------------------> public static void decode(String sourceFile, String destFile) throws FileNotFoundException, IOException, AWTException { convertToTextFile(sourceFile, destFile); } // Encode Method ------------------------------------------------------------------------> public static void encode(String sourceFile, String destFile, int fileType) throws FileNotFoundException, IOException, AWTException { // String newFormat = convertToFormat(sourceFile, fileType); // writeToDMCSFile(newFormat, destFile); } // CMUS-to-TEXT File Conversion --------------------------------------------------------> protected static void convertToTextFile(String inFile, String outFile) throws IOException { File srcFile = new File(inFile); FileInputStream fis = new FileInputStream(srcFile); int filesize = (int)(srcFile.length()); bData = new byte[filesize]; iPlace = 0; int iCounter = 0; while(iCounter < filesize) { bData[iCounter] = (byte)(fis.read()); iCounter++; } fis.close(); FileWriter fw = new FileWriter(new File(outFile)); String sOutput = "FILE SIZE : " + srcFile.length() + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "BYTES READ : " + iCounter + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "====================\rFILE CONTENTS\r====================\r"; fw.write(sOutput, 0, sOutput.length()); while(iPlace < iCounter) { String chunk = "" + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]); if (chunk.equals("FORM")) { parseFORM(fw); } else if(chunk.equals("SCHD")) { parseSCHD(fw); } else if(chunk.equals("DSCR")) { parseDSCR(fw); } else if(chunk.equals("STAF")) { parseSTAF(fw); } else if(chunk.equals("DSTF")) { parseDSTF(fw); } else if(chunk.equals("LFON")) { parseLFON(fw); } else if(chunk.equals("TRCK")) { parseTRCK(fw); } else if(chunk.equals("INHD")) { parseINHD(fw); } else if(chunk.equals("NAME")) { parseNAME(fw); } else if(chunk.equals("SFIL")) { parseSFIL(fw); } else { sOutput = "UNKNOWN CHUNK : " + chunk + "\r"; fw.write(sOutput, 0, sOutput.length()); iPlace = iCounter; break; } } /* 4 bytes - FORM 4 bytes - (number?) 4 bytes - INST 4 bytes - type : INHD, SFIL, SHAR or others 4 bytes - 00 00 00 0A 1 byte - Instrument Number 1 byte - (zero pad?) 8 bytes - 00 00 FF FF 40 00 00 00 4 bytes - NAME 4 bytes - Name Byte Count Y bytes - Name Bytes (Y = value from above) 1 byte - (zero pad?) [Optional Instrument Path] 4 bytes - SFIL 4 bytes - Path Byte Count Y bytes - Path Bytes (Y = value from above) 1 byte - (zero pad) */ fw.flush(); fw.close(); } // Chunk Parsing Methods----------------------------------------------------------------> private static void parseFORM(FileWriter fw) throws IOException { String sOutput = "--------------------\rFORM CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "FORM BLOCK : FORM\r"; fw.write(sOutput, 0, sOutput.length()); int iSize = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "SIZE BLOCK : " + iSize + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "TYPE BLOCK : " + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + "\r"; fw.write(sOutput, 0, sOutput.length()); } private static void parseSCHD(FileWriter fw) throws IOException { String sOutput = "--------------------\rSCORE HEADER CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "SCHD BLOCK : SCHD\r"; fw.write(sOutput, 0, sOutput.length()); int iData = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "WORD BLOCK 1 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); iData = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "WORD BLOCK 2 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); iData = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "WORD BLOCK 3 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); iData = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "WORD BLOCK 4 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); iData = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "WORD BLOCK 5 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "BYTE 6a: " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "BYTE 6b: " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); iData = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "WORD BLOCK 7+: " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "Ch BLOCK : " + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + "\r"; fw.write(sOutput, 0, sOutput.length()); for(int i = 0; i < 6; i++) { iData = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "SUB WORD " + (i + 1) + ": " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); } iData = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "SUB WORD 7 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); } private static void parseDSCR(FileWriter fw) throws IOException { String sOutput = "--------------------\rDSCR CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "DSCR BLOCK : DSCR\r"; fw.write(sOutput, 0, sOutput.length()); for(int i = 0; i < 7; i++) { int iData = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "DSCR WORD " + i + ": " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); } } private static void parseSTAF(FileWriter fw) throws IOException { String sOutput = "--------------------\rSTAFF TABLE CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "STAF BLOCK : STAF\r"; fw.write(sOutput, 0, sOutput.length()); int iStaffLeadByte = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "STAF Lead Byte : " + iStaffLeadByte + "\r"; fw.write(sOutput, 0, sOutput.length()); int iStaffByteCount = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "STAF Byte Count : " + iStaffByteCount + "\r"; fw.write(sOutput, 0, sOutput.length()); for(int i = 0; i < iStaffByteCount; i++) { sOutput = "STAF Byte " + (i+1) + " : " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); } } private static void parseDSTF(FileWriter fw) throws IOException { String sOutput = "--------------------\rDSTF CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "DSTF BLOCK : DSTF\r"; fw.write(sOutput, 0, sOutput.length()); int iDstaffLeadByte = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "DSTF Lead Byte : " + iDstaffLeadByte + "\r"; fw.write(sOutput, 0, sOutput.length()); int iDstaffByteCount = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "DSTF Byte Count : " + iDstaffByteCount + "\r"; fw.write(sOutput, 0, sOutput.length()); for(int i = 0; i < iDstaffByteCount; i++) { sOutput = "DSTF Byte " + (i+1) + " : " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); } } private static void parseLFON(FileWriter fw) throws IOException { String sOutput = "--------------------\rLYRIC FONT CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "LFON BLOCK : LFON\r"; fw.write(sOutput, 0, sOutput.length()); int iFontNumber = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "FONT NUMBER : " + iFontNumber + "\r"; fw.write(sOutput, 0, sOutput.length()); int iFontHeight = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "FONT HEIGHT : " + iFontHeight + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "Font Name : "; while(bData[iPlace++] != NUL) { sOutput = sOutput + (char)(bData[iPlace-1]); } sOutput = sOutput + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "[TERMNUL] : " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); } private static void parseTRCK(FileWriter fw) throws IOException { String sOutput = "--------------------\rTRACK CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "TRCK BLOCK : TRCK\r"; fw.write(sOutput, 0, sOutput.length()); int iTrackLeadByte = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "TRCK Lead Byte : " + iTrackLeadByte + "\r"; fw.write(sOutput, 0, sOutput.length()); int iTrackByteCount = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "TRCK Byte Count : " + iTrackByteCount + "\r"; fw.write(sOutput, 0, sOutput.length()); int iTrackStaff = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "TRCK Staff : " + iTrackStaff + "\r"; fw.write(sOutput, 0, sOutput.length()); int iTrackTrack = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "TRCK Track : " + iTrackTrack + "\r"; fw.write(sOutput, 0, sOutput.length()); int iTrackFlags = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "TRCK Flags : " + iTrackFlags + "\r"; fw.write(sOutput, 0, sOutput.length()); int iTrackTransposition = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "TRCK Trans : " + iTrackTransposition + "\r"; fw.write(sOutput, 0, sOutput.length()); for(int i = 0; i < iTrackByteCount - 8;) { int iItemLength = (int)(bData[iPlace++]); sOutput = "ITEM LEN : " + iItemLength + "\r"; fw.write(sOutput, 0, sOutput.length()); i++; sOutput = "ITEM TYPE : " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); i++; int iItemXPos = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "ITEM XPOS : " + iItemXPos + "\r"; fw.write(sOutput, 0, sOutput.length()); i++;i++; int iItemStart = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "ITEM START: " + iItemStart + "\r"; fw.write(sOutput, 0, sOutput.length()); i++;i++; for(int j = 0; j < iItemLength - 3; j++) { int iItemData = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "ITEM WORD " + j + " : " + iItemData + "\r"; fw.write(sOutput, 0, sOutput.length()); i++;i++; } } } private static void parseINHD(FileWriter fw) throws IOException { String sOutput = "--------------------\rINSTRUMENT HEADER CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "INHD BLOCK : INHD\r"; fw.write(sOutput, 0, sOutput.length()); int iPadding = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "(HEADPAD) : " + iPadding + "\r"; fw.write(sOutput, 0, sOutput.length()); byte instrumentNumber = bData[iPlace++]; sOutput = "INST # : " + instrumentNumber + "\r"; fw.write(sOutput, 0, sOutput.length()); byte instrumentFlags = bData[iPlace++]; sOutput = "INST FLAGS : " + instrumentFlags + "\r"; fw.write(sOutput, 0, sOutput.length()); int instrumentTune = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "INST TUNE : " + instrumentTune + "\r"; fw.write(sOutput, 0, sOutput.length()); int instrumentVolume = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "INST VOL : " + instrumentVolume + "\r"; fw.write(sOutput, 0, sOutput.length()); byte instrumentPan = bData[iPlace++]; sOutput = "INST PAN : " + instrumentNumber + "\r"; fw.write(sOutput, 0, sOutput.length()); byte instrumentMIDIChannel = bData[iPlace++]; sOutput = "MIDI CHAN : " + instrumentNumber + "\r"; fw.write(sOutput, 0, sOutput.length()); byte instrumentMIDIPreset = bData[iPlace++]; sOutput = "MIDI PRESET : " + instrumentNumber + "\r"; fw.write(sOutput, 0, sOutput.length()); byte instrumentMIDIPort = bData[iPlace++]; sOutput = "MIDI PORT : " + instrumentNumber + "\r"; fw.write(sOutput, 0, sOutput.length()); iPadding = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "(TAILPAD) : " + iPadding + "\r"; fw.write(sOutput, 0, sOutput.length()); } private static void parseNAME(FileWriter fw) throws IOException { String sOutput = "--------------------\rINSTRUMENT NAME CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "NAME BLOCK : NAME\r"; fw.write(sOutput, 0, sOutput.length()); int iNameHeadInt = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "NAME HEAD : " + iNameHeadInt + "\r"; fw.write(sOutput, 0, sOutput.length()); int iNameByteCount = (int)( (bData[iPlace++] << 8) + (bData[iPlace++] ) ); sOutput = "NAME BYTES : " + iNameByteCount + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "Instrument Name : "; for(int i = 1; i < iNameByteCount; i++) { sOutput = sOutput + (char)(bData[iPlace++]); } sOutput = sOutput + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "(PADNUL1) : " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); if((int)(iNameByteCount / 2.0) < (iNameByteCount / 2.0)) { sOutput = "(PADNUL2) : " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); } } private static void parseSFIL(FileWriter fw) throws IOException { String sOutput = "--------------------\rINSTRUMENT SAMPLE FILE CHUNK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "SFIL BLOCK : SFIL\r"; fw.write(sOutput, 0, sOutput.length()); int iSampleFileBytes = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "SFIL BYTES : " + iSampleFileBytes + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "Sample File Name : "; for(int i = 1; i < iSampleFileBytes; i++) { sOutput = sOutput + (char)(bData[iPlace++]); } sOutput = sOutput + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "(PADNUL) : " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); } // File Write Method -------------------------------------------------------------------> protected static void writeToDMCSFile(String fileData, String outFile) throws IOException { /* FileOutputStream fos = new FileOutputStream(new File(outFile)); byte FileSizeByte1 = (byte)(fileData.length() & 0xFF); byte FileSizeByte2 = (byte)((fileData.length() >> 8) & 0xFF); byte FileSizeByte3 = (byte)((fileData.length() >> 16) & 0xFF); byte FileSizeByte4 = (byte)((fileData.length() >> 24) & 0xFF); byte[] DMCSFileHeader = new byte[] { FileHeaderFormWord[0], FileHeaderFormWord[1], FileHeaderFormWord[2], FileHeaderFormWord[3], FileSizeByte1, FileSizeByte2, FileSizeByte3, FileSizeByte4, FileHeaderTypeWord[0], FileHeaderTypeWord[1], FileHeaderTypeWord[2], FileHeaderTypeWord[3], FileHeaderOSAMWord[0], FileHeaderOSAMWord[1], FileHeaderOSAMWord[2], FileHeaderOSAMWord[3], FileHeaderTailWord[0], FileHeaderTailWord[1], FileHeaderTailWord[2], FileHeaderTailWord[3] }; fos.write(DMCSFileHeader); byte BitmapWidthByte1 = (byte)(IntBitmapWidth & 0xFF); byte BitmapWidthByte2 = (byte)((IntBitmapWidth >> 8) & 0xFF); byte BitmapWidthByte3 = (byte)((IntBitmapWidth >> 16) & 0xFF); byte BitmapWidthByte4 = (byte)((IntBitmapWidth >> 24) & 0xFF); byte BitmapHeightByte1 = (byte)(IntBitmapHeight & 0xFF); byte BitmapHeightByte2 = (byte)((IntBitmapHeight >> 8) & 0xFF); byte BitmapHeightByte3 = (byte)((IntBitmapHeight >> 16) & 0xFF); byte BitmapHeightByte4 = (byte)((IntBitmapHeight >> 24) & 0xFF); byte BitmapImageSizeByte1 = (byte)(IntBitmapSize & 0xFF); byte BitmapImageSizeByte2 = (byte)((IntBitmapSize >> 8) & 0xFF); byte BitmapImageSizeByte3 = (byte)((IntBitmapSize >> 16) & 0xFF); byte BitmapImageSizeByte4 = (byte)((IntBitmapSize >> 24) & 0xFF); byte[] BitmapBodyHeader = new byte[] { InfoHeaderSizeByte1, InfoHeaderSizeByte2, InfoHeaderSizeByte3, InfoHeaderSizeByte4, BitmapWidthByte1, BitmapWidthByte2, BitmapWidthByte3, BitmapWidthByte4, BitmapHeightByte1, BitmapHeightByte2, BitmapHeightByte3, BitmapHeightByte4, BitmapPlanesByte1, BitmapPlanesByte2, BitmapBitCountByte1, BitmapBitCountByte2, BitmapCompressionByte1, BitmapCompressionByte2, BitmapCompressionByte3, BitmapCompressionByte4, BitmapImageSizeByte1, BitmapImageSizeByte2, BitmapImageSizeByte3, BitmapImageSizeByte4, BitmapXPixelsPerMByte1, BitmapXPixelsPerMByte2, BitmapXPixelsPerMByte3, BitmapXPixelsPerMByte4, BitmapYPixelsPerMByte1, BitmapYPixelsPerMByte2, BitmapYPixelsPerMByte3, BitmapYPixelsPerMByte4, BitmapColorsUsedByte1, BitmapColorsUsedByte2, BitmapColorsUsedByte3, BitmapColorsUsedByte4, BitmapColorsImportantByte1, BitmapColorsImportantByte2, BitmapColorsImportantByte3, BitmapColorsImportantByte4 }; fos.write(BitmapBodyHeader); int bodySize = IntBitmapWidth * IntBitmapHeight; int bitPad = 4 - ((IntBitmapWidth * 3) % 4); if(bitPad == 4) { bitPad = 0; } int countRow = 1; int indexRow = bodySize - IntBitmapWidth; int indexLastRow = indexRow; byte[] rgbArray = new byte[3]; for(int i = 0; i < bodySize; i++) { int colorValue = PixelMap[indexRow]; rgbArray[0] = (byte)(colorValue & 0xFF); // red rgbArray[1] = (byte)((colorValue >> 8) & 0xFF); // green rgbArray[2] = (byte)((colorValue >> 16) & 0xFF); // blue fos.write(rgbArray); if(countRow == IntBitmapWidth) { // pad row to 4 bits requirement for(int p = 0; p < bitPad; p++) { fos.write(0x00); } countRow = 1; indexRow = indexLastRow - IntBitmapWidth; indexLastRow = indexRow; } else { countRow++; } indexRow++; } fos.flush(); fos.close(); */ } public static void main(String[] args) { if(args.length < 3) { System.out.println("USAGE : CMUScodec -action input output"); } else { if(args[0].equals("-t")) { try { decode(args[1], args[2]); } catch(Exception e) { System.out.println(e); } } else { System.out.println("action " + args[0] + "not yet supported"); } } } } \ No newline at end of file diff --git a/ekit/com/hexidec/util/Cartesian.java b/ekit/com/hexidec/util/Cartesian.java deleted file mode 100644 index 3a5444d..0000000 --- a/ekit/com/hexidec/util/Cartesian.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.hexidec.util; - -public class Cartesian -{ - private int x; - private int y; - - public Cartesian(int x, int y) - { - this.x = x; - this.y = y; - } - - public Cartesian(Cartesian cartesian) - { - this.x = cartesian.getX(); - this.y = cartesian.getY(); - } - - public Cartesian() - { - this(0, 0); - } - - public int getX() { return x; } - public int getY() { return y; } - - public void setX(int i) { x = i; } - public void setY(int i) { y = i; } - - public void mirror(Cartesian cpSource) - { - this.setX(cpSource.getX()); - this.setY(cpSource.getY()); - } - - public void add(Cartesian cpAdd) - { - this.setX(this.getX() + cpAdd.getX()); - this.setY(this.getY() + cpAdd.getY()); - } - - public void subtract(Cartesian cpSub) - { - this.setX(this.getX() - cpSub.getX()); - this.setY(this.getY() - cpSub.getY()); - } - - public void scale(int magnitude) - { - this.setX(this.getX() * magnitude); - this.setY(this.getY() * magnitude); - } - - public Cartesian getScaledInstance(int magnitude) - { - Cartesian cpNew = this.dupe(); - cpNew.scale(magnitude); - return cpNew; - } - - public Cartesian getScaledInstance(Cartesian cpMagnitude) - { - Cartesian cpNew = this.dupe(); - cpNew.setX(cpNew.getX() * cpMagnitude.getX()); - cpNew.setY(cpNew.getY() * cpMagnitude.getY()); - return cpNew; - } - - public Cartesian addToLimit(Cartesian cpBase, Cartesian cpMin, Cartesian cpMax) - { - Cartesian cpNew = this.dupe(); - cpNew.add(cpBase); - if(cpNew.getX() < cpMin.getX()) { cpNew.setX(cpMin.getX()); } - if(cpNew.getY() < cpMin.getY()) { cpNew.setY(cpMin.getY()); } - if(cpNew.getX() > cpMax.getX()) { cpNew.setX(cpMax.getX()); } - if(cpNew.getY() > cpMax.getY()) { cpNew.setY(cpMax.getY()); } - return cpNew; - } - - public Cartesian translate(Cartesian cpOffset, boolean unknown) - { - Cartesian cpNew = this.dupe(); - cpNew.add(cpOffset); - return cpNew; - } - - public Cartesian translate(Cartesian cpOffset) - { - return translate(cpOffset, false); - } - - public double distance(Cartesian cpTarget) - { - // Formula -> SQRT(SQR(x2 - x1) + SQR(y2 - y1)) - int xDiff = cpTarget.getX() - this.getX(); - int yDiff = cpTarget.getY() - this.getY(); - return Math.sqrt((xDiff * xDiff) + (yDiff * yDiff)); - } - - public Cartesian midpoint(Cartesian cpTarget) - { - // Formula -> (x1 + x2) / 2, (y1 + y2) / 2 - int xMid = (this.getX() + cpTarget.getX()) / 2; - int yMid = (this.getY() + cpTarget.getY()) / 2; - return new Cartesian(xMid, yMid); - } - - public double slope(Cartesian cpTarget) - { - // Formula -> (y1 - y2) / (x1 - x2) - int xDiff = this.getX() - cpTarget.getX(); - int yDiff = this.getY() - cpTarget.getY(); - return yDiff / xDiff; - } - - public Cartesian dupe() - { - return new Cartesian(this); - } - - public boolean equals(Cartesian cpCompare) - { - return ((this.getX() == cpCompare.getX()) && (this.getY() == cpCompare.getY())); - } - - public String toString() - { - return "(" + this.getX() + "," + this.getY() + ")"; - } - - public String toKey() - { - return "X" + this.getX() + "Y" + this.getY(); - } - -} \ No newline at end of file diff --git a/ekit/com/hexidec/util/DMCScodec.java b/ekit/com/hexidec/util/DMCScodec.java deleted file mode 100644 index bf797f0..0000000 --- a/ekit/com/hexidec/util/DMCScodec.java +++ /dev/null @@ -1 +0,0 @@ -/* GNU General Public License DMCScodec Copyright (C) 2002 Howard A Kistler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.hexidec.util; import java.awt.AWTException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; /** DMCScodec * * Utility for reading (and eventually converting) Deluxe Music * Construction Set (DMCS) files * * @author Howard Kistler * @version 0.1 * * VERSION HISTORY * 0.1 (03/13/2002) - initial creation (03/13/2002) */ public class DMCScodec { // Constants ----------------------------------------------------------------------------> /* DMCS File Structure FILE HEADER ==================== (At the beginning of each file) FILE HEADER BLOCK 4 bytes - FORM 4 bytes - ? 4 bytes - DMCS 4 bytes - AMGA (for Amiga files) 4 bytes - ? (all zeroes?) SC HEADER BLOCK 4 bytes - USC1 8 bytes - ? 4 bytes - ? 8 bytes - ? 4 x 32 bytes - ? 1 byte - Title Byte count (number of letters in song title) X bytes - Song Title (length stated in previous byte) 16 bytes - 00 05 FE 77 01 01 01 02 00 00 3F 80 00 7F 00 00 SONG BODY ==================== UBR1 BLOCKS 4 bytes - UBR1 INSTRUMENT BLOCK ==================== (At the end of each file) INSTRUMENT HEAD BLOCK 4 bytes - UVLT 22 bytes - 00 00 00 12 00 00 00 0A 00 13 00 1F 00 2F 00 3F 00 51 00 69 00 7F INDIVIDUAL INSTRUMENT BLOCKS 4 bytes - UIID 4 bytes - ? (small int) X bytes - Instrument Name 2 bytes - 00 00 (last instrument may have just one byte of 00) */ // Header Array Sizes private final static byte StartFileHeaderSize = (byte)20; private final static byte InfoHeaderSize = (byte)40; // InfoHeader is always 40 bytes in size for BMP/ICO/CUR format // File Header Fields private final static byte[] FileHeaderFormWord = { 'F', 'O', 'R', 'M', }; private static byte[] FileHeaderSizeWord = new byte[4]; private final static byte[] FileHeaderTypeWord = { 'D', 'M', 'C', 'S' }; private final static byte[] FileHeaderOSAMWord = { 'A', 'M', 'G', 'A' }; // OS Word for Amiga DMCS files private final static byte[] FileHeaderTailWord = { (byte)0, (byte)0, (byte)0, (byte)0 }; // Song Body Fields private final static byte[] SongBlockUnitWord = { 'U', 'B', 'R', '1' }; // Instrument Body Fields private final static byte[] InstrumentBlockHeadWord = { 'U', 'V', 'L', 'T' }; private final static byte[] InstrumentBlockUnitWord = { 'U', 'I', 'I', 'D' }; // Public Vartypes ----------------------------------------------------------------------> public final static int FILETYPE_DMCS = 1; public final static int FILETYPE_TEXT = 2; public final static int FILETYPE_FINALE = 3; // Constructor --------------------------------------------------------------------------> public DMCScodec() { } // Decode Method ------------------------------------------------------------------------> public static void decode(String sourceFile, String destFile) throws FileNotFoundException, IOException, AWTException { convertToTextFile(sourceFile, destFile); } // Encode Method ------------------------------------------------------------------------> public static void encode(String sourceFile, String destFile, int fileType) throws FileNotFoundException, IOException, AWTException { // String newFormat = convertToFormat(sourceFile, fileType); // writeToDMCSFile(newFormat, destFile); } // DMCS-to-TEXT File Conversion --------------------------------------------------------> protected static void convertToTextFile(String inFile, String outFile) throws IOException { File srcFile = new File(inFile); FileInputStream fis = new FileInputStream(srcFile); int filesize = (int)(srcFile.length()); byte[] bData = new byte[filesize]; int iCounter = 0; while(iCounter < filesize) { bData[iCounter] = (byte)(fis.read()); iCounter++; } fis.close(); FileWriter fw = new FileWriter(new File(outFile)); String sOutput = "FILE SIZE : " + srcFile.length() + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "BYTES READ : " + iCounter + "\r"; fw.write(sOutput, 0, sOutput.length()); int iPlace = 0; sOutput = "====================\rFILE HEADER\r====================\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "--------------------\rFILE HEADER BLOCK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "FORM BLOCK : " + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + "\r"; fw.write(sOutput, 0, sOutput.length()); int iSize = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "SIZE BLOCK : " + iSize + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "TYPE BLOCK : " + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "OS BLOCK : " + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "TAIL BLOCK : " + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "--------------------\rSC HEADER BLOCK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "USC1 BLOCK : " + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + (char)(bData[iPlace++]) + "\r"; fw.write(sOutput, 0, sOutput.length()); int iData = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "DATA INT 1 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); iData = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "DATA INT 2 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); iData = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "DATA INT 3 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); iData = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "DATA INT 4 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); iData = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "DATA INT 5 : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); for(int i = 0; i < 32; i++) { iData = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "SUB INT " + (i+1) + " : " + iData + "\r"; fw.write(sOutput, 0, sOutput.length()); } sOutput = "PAD BYTES : " + bData[iPlace++] + bData[iPlace++] + bData[iPlace++] + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); int titleCharCount = (int)(bData[iPlace++]); sOutput = "TITLE BYTE : " + titleCharCount + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "SONG TITLE : "; for(int i = 0; i < titleCharCount; i++) { sOutput = sOutput + (char)(bData[iPlace++]); } sOutput = sOutput + "\r"; fw.write(sOutput, 0, sOutput.length()); for(int i = 0; i < 19; i++) { sOutput = "POST BYTE " + (i+1) + " : " + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); } sOutput = "====================\rSONG BODY\r====================\r"; fw.write(sOutput, 0, sOutput.length()); byte byteA = (byte)0; byte byteB = (byte)0; String blockWord = ""; while(iPlace < filesize - 5) { byteA = bData[iPlace++]; byteB = bData[iPlace++]; blockWord = "" + (char)byteA + (char)byteB; if(blockWord.equals("UB")) { byte byteC = bData[iPlace++]; byte byteD = bData[iPlace++]; String blockWord2 = "" + (char)byteC + (char)byteD; if(blockWord2.equals("R1")) { sOutput = "--------------------\rUBR1 DATA BLOCK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "DATA HEAD : " + blockWord + blockWord2 + "\r"; fw.write(sOutput, 0, sOutput.length()); } else { int iWord = (int)( (byteA << 8) + (byteB ) ); sOutput = "WORD VALUE : " + iWord + "\r"; fw.write(sOutput, 0, sOutput.length()); iWord = (int)( (byteC << 8) + (byteD ) ); sOutput = "WORD VALUE : " + iWord + "\r"; fw.write(sOutput, 0, sOutput.length()); } } else if(blockWord.equals("UV")) { byte byteC = bData[iPlace++]; byte byteD = bData[iPlace++]; String blockWord2 = "" + (char)byteC + (char)byteD; if(blockWord2.equals("LT")) { sOutput = "====================\rINSTRUMENT BLOCK\r====================\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "--------------------\rINSTRUMENT HEAD BLOCK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "GROUP HEAD : " + blockWord + blockWord2 + "\r"; fw.write(sOutput, 0, sOutput.length()); int iWord = 0; for(int i = 0; i < 11; i++) { iWord = (int)( ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "SUB WORD " + (i+1) + " : " + iWord + "\r"; fw.write(sOutput, 0, sOutput.length()); } } else { int iWord = (int)( (byteA << 8) + (byteB ) ); sOutput = "WORD VALUE : " + iWord + "\r"; fw.write(sOutput, 0, sOutput.length()); iWord = (int)( (byteC << 8) + (byteD ) ); sOutput = "WORD VALUE : " + iWord + "\r"; fw.write(sOutput, 0, sOutput.length()); } } else if(blockWord.equals("UI")) { byte byteC = bData[iPlace++]; byte byteD = bData[iPlace++]; String blockWord2 = "" + (char)byteC + (char)byteD; if(blockWord2.equals("ID")) { sOutput = "--------------------\rINDIVIDUAL INSTRUMENT BLOCK\r--------------------\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "INSTR HEAD : " + blockWord + blockWord2 + "\r"; fw.write(sOutput, 0, sOutput.length()); int iVal = (int)( ((bData[iPlace++]) << 24) + ((bData[iPlace++]) << 16) + ((bData[iPlace++]) << 8) + ((bData[iPlace++]) ) ); sOutput = "NAME BYTES : " + iVal + "\r"; fw.write(sOutput, 0, sOutput.length()); sOutput = "INSTR NAME : "; for(int i = 0; i < iVal - 1; i++) { sOutput = sOutput + (char)(bData[iPlace++]); } sOutput = sOutput + "\r"; fw.write(sOutput, 0, sOutput.length()); if(iPlace < filesize - 2) { sOutput = "PAD BYTES : " + bData[iPlace++] + bData[iPlace++] + "\r"; fw.write(sOutput, 0, sOutput.length()); } } else { int iWord = (int)( (byteA << 8) + (byteB ) ); sOutput = "WORD VALUE : " + iWord + "\r"; fw.write(sOutput, 0, sOutput.length()); iWord = (int)( (byteC << 8) + (byteD ) ); sOutput = "WORD VALUE : " + iWord + "\r"; fw.write(sOutput, 0, sOutput.length()); } } else { int iWord = (int)( (byteA << 8) + (byteB ) ); sOutput = "WORD VALUE : " + iWord + "\r"; fw.write(sOutput, 0, sOutput.length()); } } fw.flush(); fw.close(); } // File Write Method -------------------------------------------------------------------> protected static void writeToDMCSFile(String fileData, String outFile) throws IOException { FileOutputStream fos = new FileOutputStream(new File(outFile)); byte FileSizeByte1 = (byte)(fileData.length() & 0xFF); byte FileSizeByte2 = (byte)((fileData.length() >> 8) & 0xFF); byte FileSizeByte3 = (byte)((fileData.length() >> 16) & 0xFF); byte FileSizeByte4 = (byte)((fileData.length() >> 24) & 0xFF); byte[] DMCSFileHeader = new byte[] { FileHeaderFormWord[0], FileHeaderFormWord[1], FileHeaderFormWord[2], FileHeaderFormWord[3], FileSizeByte1, FileSizeByte2, FileSizeByte3, FileSizeByte4, FileHeaderTypeWord[0], FileHeaderTypeWord[1], FileHeaderTypeWord[2], FileHeaderTypeWord[3], FileHeaderOSAMWord[0], FileHeaderOSAMWord[1], FileHeaderOSAMWord[2], FileHeaderOSAMWord[3], FileHeaderTailWord[0], FileHeaderTailWord[1], FileHeaderTailWord[2], FileHeaderTailWord[3] }; fos.write(DMCSFileHeader); /* byte BitmapWidthByte1 = (byte)(IntBitmapWidth & 0xFF); byte BitmapWidthByte2 = (byte)((IntBitmapWidth >> 8) & 0xFF); byte BitmapWidthByte3 = (byte)((IntBitmapWidth >> 16) & 0xFF); byte BitmapWidthByte4 = (byte)((IntBitmapWidth >> 24) & 0xFF); byte BitmapHeightByte1 = (byte)(IntBitmapHeight & 0xFF); byte BitmapHeightByte2 = (byte)((IntBitmapHeight >> 8) & 0xFF); byte BitmapHeightByte3 = (byte)((IntBitmapHeight >> 16) & 0xFF); byte BitmapHeightByte4 = (byte)((IntBitmapHeight >> 24) & 0xFF); byte BitmapImageSizeByte1 = (byte)(IntBitmapSize & 0xFF); byte BitmapImageSizeByte2 = (byte)((IntBitmapSize >> 8) & 0xFF); byte BitmapImageSizeByte3 = (byte)((IntBitmapSize >> 16) & 0xFF); byte BitmapImageSizeByte4 = (byte)((IntBitmapSize >> 24) & 0xFF); byte[] BitmapBodyHeader = new byte[] { InfoHeaderSizeByte1, InfoHeaderSizeByte2, InfoHeaderSizeByte3, InfoHeaderSizeByte4, BitmapWidthByte1, BitmapWidthByte2, BitmapWidthByte3, BitmapWidthByte4, BitmapHeightByte1, BitmapHeightByte2, BitmapHeightByte3, BitmapHeightByte4, BitmapPlanesByte1, BitmapPlanesByte2, BitmapBitCountByte1, BitmapBitCountByte2, BitmapCompressionByte1, BitmapCompressionByte2, BitmapCompressionByte3, BitmapCompressionByte4, BitmapImageSizeByte1, BitmapImageSizeByte2, BitmapImageSizeByte3, BitmapImageSizeByte4, BitmapXPixelsPerMByte1, BitmapXPixelsPerMByte2, BitmapXPixelsPerMByte3, BitmapXPixelsPerMByte4, BitmapYPixelsPerMByte1, BitmapYPixelsPerMByte2, BitmapYPixelsPerMByte3, BitmapYPixelsPerMByte4, BitmapColorsUsedByte1, BitmapColorsUsedByte2, BitmapColorsUsedByte3, BitmapColorsUsedByte4, BitmapColorsImportantByte1, BitmapColorsImportantByte2, BitmapColorsImportantByte3, BitmapColorsImportantByte4 }; fos.write(BitmapBodyHeader); int bodySize = IntBitmapWidth * IntBitmapHeight; int bitPad = 4 - ((IntBitmapWidth * 3) % 4); if(bitPad == 4) { bitPad = 0; } int countRow = 1; int indexRow = bodySize - IntBitmapWidth; int indexLastRow = indexRow; byte[] rgbArray = new byte[3]; for(int i = 0; i < bodySize; i++) { int colorValue = PixelMap[indexRow]; rgbArray[0] = (byte)(colorValue & 0xFF); // red rgbArray[1] = (byte)((colorValue >> 8) & 0xFF); // green rgbArray[2] = (byte)((colorValue >> 16) & 0xFF); // blue fos.write(rgbArray); if(countRow == IntBitmapWidth) { // pad row to 4 bits requirement for(int p = 0; p < bitPad; p++) { fos.write(0x00); } countRow = 1; indexRow = indexLastRow - IntBitmapWidth; indexLastRow = indexRow; } else { countRow++; } indexRow++; } */ fos.flush(); fos.close(); } public static void main(String[] args) { if(args.length < 3) { System.out.println("USAGE : DMCScodec -action input output"); } else { if(args[0].equals("-t")) { try { decode(args[1], args[2]); } catch(Exception e) { System.out.println(e); } } else { System.out.println("action " + args[0] + "not yet supported"); } } } } \ No newline at end of file diff --git a/ekit/com/hexidec/util/MutableFilter.java b/ekit/com/hexidec/util/MutableFilter.java deleted file mode 100644 index bbeb3fc..0000000 --- a/ekit/com/hexidec/util/MutableFilter.java +++ /dev/null @@ -1,77 +0,0 @@ -/* -GNU Lesser General Public License - -MutableFilter -Copyright (C) 2000-2002 Howard A Kistler - -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 -*/ - -package com.hexidec.util; - -import java.io.File; -import javax.swing.filechooser.FileFilter; - -/** Class for providing JFileChooser with a FileFilter - */ -public class MutableFilter extends FileFilter -{ - private String[] acceptableExtensions; - private String descriptor; - - public MutableFilter(String[] exts, String desc) - { - acceptableExtensions = exts; - StringBuffer strbDesc = new StringBuffer(desc + " ("); - for(int i = 0; i < acceptableExtensions.length; i++) - { - if(i > 0) { strbDesc.append(", "); } - strbDesc.append("*." + acceptableExtensions[i]); - } - strbDesc.append(")"); - descriptor = strbDesc.toString(); - } - - public boolean accept(File file) - { - if(file.isDirectory()) - { - return true; - } - String fileName = file.getName(); - String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).toLowerCase(); - if(fileExt != null) - { - for(int i = 0; i < acceptableExtensions.length; i++) - { - if(fileExt.equals(acceptableExtensions[i])) - { - return true; - } - } - return false; - } - else - { - return false; - } - } - - public String getDescription() - { - return descriptor; - } -} - diff --git a/ekit/com/hexidec/util/PatternReplacer.java b/ekit/com/hexidec/util/PatternReplacer.java deleted file mode 100644 index 3fadfb0..0000000 --- a/ekit/com/hexidec/util/PatternReplacer.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.hexidec.util; - -/* -GNU General Public License - -PatternReplacer - Simple Pattern Replacement Class -Copyright (C) 2001 Howard A Kistler - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program 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 General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -import java.util.Hashtable; - -/** PatternReplacer - * Utility class for replacing patterns in script strings - * - * @author Howard Kistler - * @version 0.2 - * - * VERSION HISTORY - * 0.1 (04/04/2001) - initial creation (04/04/2001) - * 0.2 (04/09/2001) - separation into own class (04/09/2001) - */ - -public class PatternReplacer { - - private Hashtable patternTable; - - public PatternReplacer(Hashtable patterns) { - patternTable = patterns; - } - - public PatternReplacer() { - patternTable = new Hashtable(); - } - - public void addPattern(String pattern, String replacement) { - if(patternTable.containsKey(pattern)) { - patternTable.remove(pattern); - } - patternTable.put(pattern, replacement); - } - - public void removePattern(String pattern) { - if(patternTable.containsKey(pattern)) { - patternTable.remove(pattern); - } - } - - public String getPattern(String pattern) { - if(patternTable.containsKey(pattern)) { - return (String)(patternTable.get(pattern)); - } - return null; - } - - public void clearPatterns() { - patternTable.clear(); - } - - public int patternCount() { - return patternTable.size(); - } - -} - diff --git a/ekit/com/hexidec/util/TreeSpider$FileTreeComparator.class b/ekit/com/hexidec/util/TreeSpider$FileTreeComparator.class deleted file mode 100644 index 473353ba05cb35c86c5d1378bbd9ec2227718356..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1055 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 diff --git a/ekit/com/hexidec/util/TreeSpider.java b/ekit/com/hexidec/util/TreeSpider.java deleted file mode 100644 index b738860..0000000 --- a/ekit/com/hexidec/util/TreeSpider.java +++ /dev/null @@ -1 +0,0 @@ -/* GNU General Public License TreeSpider - Directory & File Traversal Utility Class Copyright (C) 2000 Howard A Kistler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.hexidec.util; import java.io.File; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; /** TreeSpider * Utility class for traversing, scanning & describing directory/file structures * * @author Howard Kistler * @version 0.2 * * VERSION HISTORY * 0.1 (09/14/2000) - initial creation (started on 08/09/2000) * 0.2 (09/22/2001) - numerous enhancements */ public class TreeSpider { private File rootFile; private Vector vcFiles; private Vector vcExtensions; private int maxDepth; private boolean bScreenExtensions; private boolean bFileNamesOnly; private boolean bDisplayMode; private FileTreeComparator localComparison; // Constructors ----------------------------------------------------------------------------> /* Main Constructor */ public TreeSpider(String origin, Vector exts, boolean caseSensitive, int mDepth) { rootFile = new File(origin); vcFiles = new Vector(); vcExtensions = exts; maxDepth = mDepth; bScreenExtensions = false; bFileNamesOnly = false; bDisplayMode = false; if(vcExtensions.size() > 0) { bScreenExtensions = true; } localComparison = new FileTreeComparator(caseSensitive); } /* Alternate Constructor */ public TreeSpider(String origin, Vector exts, boolean caseSensitive) { this(origin, new Vector(), caseSensitive, 0); } /* Alternate Constructor */ public TreeSpider(String origin, boolean caseSensitive, int mDepth) { this(origin, new Vector(), caseSensitive, mDepth); } /* Alternate Constructor */ public TreeSpider(String origin, boolean caseSensitive) { this(origin, new Vector(), caseSensitive, 0); } /* Alternate Constructor */ public TreeSpider(String origin, int mDepth) { this(origin, new Vector(), false, mDepth); } /* Alternate Constructor */ public TreeSpider(String origin) { this(origin, new Vector(), false, 0); } /* Alternate Constructor */ public TreeSpider() { this(".", new Vector(), false, 0); } // Public Class Methods --------------------------------------------------------------------> /** Returns a DefaultTreeModel built from the specified root file */ public DefaultTreeModel fetchTree(File newRootFile) { return describeTree(newRootFile); } /** Returns a DefaultTreeModel built from the preset root file */ public DefaultTreeModel fetchTree() { return describeTree(rootFile); } /** Returns an unsorted listing of the file tree */ public Vector fetchMassListing() { return fetchListing(); } /** Returns an index-style listing of the file tree of complete file names and paths */ public Vector sortMassListing() { Vector vcListing = fetchListing(); Collections.sort(vcListing, localComparison); return vcListing; } /** Returns an index-style listing of the file tree of file names only */ public Vector sortFileListing() { Vector vcListing = trimToName(fetchListing()); Collections.sort(vcListing, localComparison); return vcListing; } /** Returns a directory-style listing of the file tree */ public Hashtable collectListing() { Vector vcListing = fetchListing(); Hashtable htCollected = new Hashtable(); for(int i = 0; i < vcListing.size(); i++) { String fullname = (String)(vcListing.elementAt(i)); String filepath = fullname.substring(0, fullname.lastIndexOf(File.separator)); String filename = fullname.substring(fullname.lastIndexOf(File.separator) + 1, fullname.length()); if(htCollected.containsKey(filepath)) { ((Vector)(htCollected.get(filepath))).add(filename); } else { htCollected.put(filepath, new Vector()); ((Vector)(htCollected.get(filepath))).add(filename); } } return htCollected; } // Internal Class Methods ------------------------------------------------------------------> private DefaultTreeModel describeTree(File rootFile) { DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode(rootFile.getName()); DefaultTreeModel treeModel = new DefaultTreeModel(treeRoot); if(!(rootFile.isDirectory())) { return treeModel; } String rootStr = rootFile.getAbsolutePath().trim() + File.separator; Vector vcContents = fetchListing(); while(vcContents.size() > 0) { String nodeName = (String)(vcContents.elementAt(0)); if(nodeName.startsWith(rootStr)) { nodeName = nodeName.substring(rootStr.length()); if(nodeName.indexOf(File.separator) > -1) { String baseNode = nodeName.substring(0, nodeName.indexOf(File.separator)); describeTreeSubNodes(vcContents, treeModel, treeRoot, rootStr, baseNode); } else { treeModel.insertNodeInto(new DefaultMutableTreeNode(nodeName), treeRoot, treeModel.getChildCount(treeRoot)); vcContents.removeElementAt(0); } } else { vcContents.removeElementAt(0); } } return treeModel; } private void describeTreeSubNodes(Vector vcNodes, DefaultTreeModel treeModel, DefaultMutableTreeNode nodeParent, String basePath, String baseNodeName) { DefaultMutableTreeNode thisNode = new DefaultMutableTreeNode(baseNodeName); treeModel.insertNodeInto(thisNode, nodeParent, treeModel.getChildCount(nodeParent)); boolean inSubNode = true; String fullNodePath = basePath + baseNodeName + File.separator; while(inSubNode && vcNodes.size() > 0) { String nodeName = (String)(vcNodes.elementAt(0)); if(nodeName.startsWith(fullNodePath)) { nodeName = nodeName.substring(fullNodePath.length()); if(nodeName.indexOf(File.separator) > -1) { String baseNode = nodeName.substring(0, nodeName.indexOf(File.separator)); describeTreeSubNodes(vcNodes, treeModel, thisNode, fullNodePath, baseNode); } else { treeModel.insertNodeInto(new DefaultMutableTreeNode(nodeName), thisNode, treeModel.getChildCount(thisNode)); vcNodes.removeElementAt(0); } } else { inSubNode = false; } } } private Vector fetchListing() { Vector vcFetch = new Vector(); if(rootFile.isDirectory()) { vcFetch.add(exploreFolder("", rootFile, new Vector(), 1)); } else { String fullFile = rootFile.getAbsolutePath().trim(); if(!bScreenExtensions || (bScreenExtensions && screensOkay(fullFile))) { vcFetch.add(fullFile); } } return describeListing(vcFetch); } private Vector trimToName(Vector fullFiles) { Vector vcReturn = new Vector(fullFiles.size()); for(int i = 0; i < fullFiles.size(); i++) { String trimName = (String)(fullFiles.elementAt(i)); if(trimName.indexOf(File.separator) > -1) { vcReturn.add(trimName.substring(trimName.lastIndexOf(File.separator) + 1, trimName.length())); } else { vcReturn.add(trimName); } } return vcReturn; } /* Recursively explores file folders and subfolders, adding all files which match the filtered type to the returning Vector. */ private Vector exploreFolder(String currentLocal, File folder, Vector vcWorking, int currDepth) { if((maxDepth != 0) && currDepth > maxDepth) { return vcWorking; } String fullDescriptor = currentLocal + File.separator + folder.getName(); File[] contents = folder.listFiles(); for(int i = 0; i < contents.length; i++) { if(contents[i].isDirectory()) { vcWorking.add(exploreFolder(fullDescriptor, contents[i], new Vector(), currDepth + 1)); } else { String completeName = contents[i].getAbsolutePath(); if(bFileNamesOnly) { completeName = contents[i].getName(); } if(!bScreenExtensions || (bScreenExtensions && screensOkay(completeName))) { vcWorking.add(completeName.trim()); } } } Collections.sort(vcWorking, localComparison); return vcWorking; } /* Explores only current file folder, adding all files which match the filtered type to the returning Vector. */ private Vector exploreFolderOnly(String currentLocal, File folder, Vector vcWorking) { String fullDescriptor = currentLocal + File.separator + folder.getName(); File[] contents = folder.listFiles(); for(int i = 0; i < contents.length; i++) { if(contents[i].isDirectory()) { /* ignore */ } else { String completeName = contents[i].getAbsolutePath(); if(bFileNamesOnly) { completeName = contents[i].getName(); } if(!bScreenExtensions || (bScreenExtensions && screensOkay(completeName))) { vcWorking.add(completeName.trim()); } } } Collections.sort(vcWorking, localComparison); return vcWorking; } private Vector describeListing(Vector vcTree) { Collections.sort(vcTree, localComparison); Vector vcDescription = new Vector(); for(int i = 0; i < vcTree.size(); i++) { if(vcTree.elementAt(i) instanceof Vector) { describeNode((Vector)(vcTree.elementAt(i)), vcDescription); } else { vcDescription.add(vcTree.elementAt(i)); } } return vcDescription; } private void describeNode(Vector vcNode, Vector vcStore) { for(int i = 0; i < vcNode.size(); i++) { if(vcNode.elementAt(i) instanceof Vector) { describeNode((Vector)(vcNode.elementAt(i)), vcStore); } else { vcStore.add(vcNode.elementAt(i)); } } } private boolean screensOkay(String filename) { for(int i = 0; i < vcExtensions.size(); i++) { String extension = ((String)(vcExtensions.elementAt(i))).toLowerCase(); if(filename.toLowerCase().endsWith(extension)) { return true; } } return false; } // Accessor Methods ------------------------------------------------------------------------> public File getRootFile() { return rootFile; } public Vector getFiles() { return vcFiles; } public Vector getExtensions() { return vcExtensions; } public int getMaxDepth() { return maxDepth; } public boolean doScreenExtensions() { return bScreenExtensions; } public boolean showFileNamesOnly() { return bFileNamesOnly; } public boolean getDisplayMode() { return bDisplayMode; } public void setRootFile(File f) { rootFile = f; } public void setFiles(Vector vc) { vcFiles = vc; } public void setExtensions(Vector vc) { vcExtensions = vc; if(vcExtensions.size() > 0) { setScreenExtensions(true); } else { setScreenExtensions(false); } } public void setMaxDepth(int i) { maxDepth = i; } public void setScreenExtensions(boolean b) { bScreenExtensions = b; } public void setFileNamesOnly(boolean b) { bFileNamesOnly = b; } public void setDisplayMode(boolean b) { bDisplayMode = b; } // Unit Test Method ------------------------------------------------------------------------> /** Unit test method for testing the code with example settings */ public static void unitTest(String dir) { Vector vcCommonExts = new Vector(); vcCommonExts.add(".java"); vcCommonExts.add(".class"); vcCommonExts.add(".txt"); vcCommonExts.add(".html"); vcCommonExts.add(".htm"); vcCommonExts.add(".gif"); vcCommonExts.add(".jpeg"); vcCommonExts.add(".jpg"); TreeSpider testSpider = new TreeSpider(dir, vcCommonExts, false, 0); System.out.println("UNIT TEST SCAN"); Hashtable htColl = testSpider.collectListing(); Enumeration enumKeys = htColl.keys(); while(enumKeys.hasMoreElements()) { String key = (String)(enumKeys.nextElement()); System.out.println(key); Vector vcValues = (Vector)(htColl.get(key)); for(int i = 0; i < vcValues.size(); i++) { System.out.println(" " + vcValues.elementAt(i)); } } } // Main Method -----------------------------------------------------------------------------> public static void main(String[] args) { if(args.length < 1) { try { TreeSpider.unitTest((new File(".")).getCanonicalPath()); } catch(java.io.IOException ioe) { System.out.println("Unable to resolve current directory. Please specify a directory explicitly."); System.exit(1); } } else { TreeSpider.unitTest(args[0]); } } // Inner Classes ---------------------------------------------------------------------------> /* Class for sorting file listings by directory then name */ class FileTreeComparator implements Comparator { private boolean isCaseSensitive; public FileTreeComparator(boolean caseCheck) { isCaseSensitive = caseCheck; } public FileTreeComparator() { isCaseSensitive = true; } public int compare(Object objA, Object objB) { if(objA instanceof Vector) { if(objB instanceof Vector) { if(((Vector)(objA)).size() < 1) { return -1; } else if(((Vector)(objB)).size() < 1) { return 1; } else { return compare(((Vector)(objA)).elementAt(0), ((Vector)(objB)).elementAt(0)); } } else { return 1; } } else if(objB instanceof Vector) { return -1; } else { String strA = (String)objA; String strB = (String)objB; return (isCaseSensitive ? strA.compareTo(strB) : strA.compareToIgnoreCase(strB)); } } } } \ No newline at end of file diff --git a/ekit/ekit.jar b/ekit/ekit.jar index 01578a2d08b456db160d701e0890acf154b4a9c8..515f7af75b47a917972ddd2ee612639aafeadecb 100644 GIT binary patch delta 82320 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 delta 93569 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�cll0Thi^vTR)pLR)h!ph& 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 F{sUnHemMXD diff --git a/ekit/ekitapplet.jar b/ekit/ekitapplet.jar index ae6111556d4dc5cf1bdb9ca0ebf3575c1010dd30..caff6c1cd28a466b658b27d9f507386c0645e3e6 100644 GIT binary patch delta 81534 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®7nu`KSr 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%0D@3b*!<>VsIS4wP2)lQNi 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