Announcement Announcement Module
Collapse
No announcement yet.
How To: Spell check for all TextComponents Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How To: Spell check for all TextComponents

    So, who wants spell check. Well, I did and here's how, in case anyone is interested. Also, I have no affiliation with any of these projects if anyone cares about that either.

    First download JOrtho from here: http://sourceforge.net/projects/jortho/

    JOrtho works with any language you can provide a dictionary for. Looks like wiktionary has dozens of langauges available. So this should work well for tons of people.

    Now you can add the jortho.jar and your chosen dictionary/s to your project. I created a /dictionaries directory in my Resource Packages along side my /images, /ctx, etc.

    Create your dictionary:

    The dictionaries are based on the Wiktionary dictionaries. This is a step by step description how you can generate a new dictionary version.

    1. Download the data from Wiktionary. The file for the English language is http://download.wikimedia.org/enwikt...ticles.xml.bz2 Replace the two letters language code (en in this case) before wiktionary (2 places) with your language. Some languages also require the data of the English Wiktionary (currently Polish and Arabic)
    2. Extract the XML file from the archive
    3. Download or check out the sources of JOrtho and compile it.
    4. Execute the command
    java -Xmx256M com.inet.jorthodictionaries.BookGenerator en <wiktionary folder>
    Replace the "en" with your language.

    Then I dropped the dictionary file in my /dictionaries directory. And created a dictionaries.properties file to specify what languages I have provided dictionaries for that for me looks like this:

    Code:
    # dictionaries.properties
    # 
    # Created on Dec 8, 2010, 2:29:47 PM
    # 
    # To change this template, choose Tools | Templates
    # and open the template in the editor.
    
    languages=en
    Just add your list of comma separated language code you wan't to provide spell checking for in place of the en.

    Now for the fun part, you can create an interceptor factory and interceptor to automatically add spell check to all your text components like this:

    Note: I created my own AbstractTextComponentInterceptor, as the default one doesn't work right.

    Code:
    /*
     * Copyright 2002-2007 the original author or authors.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
     * use this file except in compliance with the License. You may obtain a copy of
     * the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     * License for the specific language governing permissions and limitations under
     * the License.
     */
    package org.chd.hydra.form.interceptor;
    
    import javax.swing.JComponent;
    import javax.swing.JSpinner;
    import javax.swing.JSpinner.DefaultEditor;
    import javax.swing.JTextField;
    import javax.swing.text.JTextComponent;
    import org.springframework.binding.form.FormModel;
    import org.springframework.richclient.form.builder.support.AbstractFormComponentInterceptor;
    
    /**
     * A <code>AbstractTextComponentInterceptor</code> is an abstract base class for
     * FormComponentInterceptors that work on JTextComponents.
     * 
     * @author Peter De Bruycker
     * @author aarmistead
     */
    public abstract class AbstractTextComponentInterceptor extends AbstractFormComponentInterceptor {
    
      /**
       * Creates a new instance of <code>AbstractTextComponentInterceptor</code> with no form model.
       */
      public AbstractTextComponentInterceptor() {
        super();
      }
      
      /**
       * Creates a new instance of <code>AbstractTextComponentInterceptor</code> with the specified
       * <code>formModel</code>.
       * 
       * @param formModel The form model for this form component interceptor.
       */
      public AbstractTextComponentInterceptor(FormModel formModel) {
        super(formModel);
      }
    
      /**
       * Process the specified <code>component</code> by getting an appropriate text component
       * from it and processing the text component.
       *
       * @param propertyName The property name for which to process a component.
       * @param component The component to process.
       */
      @Override
      public final void processComponent(String propertyName, JComponent component) {
        JTextComponent textComponent = getTextComponent(getInnerComponent(component));
        if(textComponent != null) {
          processComponent(propertyName, textComponent);
        }
      }
    
      /**
       * Process the specified <code>textComponent</code> with the specified
       * <code>propertyName</code>.
       *
       * @param propertyName The name of the property with which to process a component.
       * @param textComponent The text component to process.
       */
      protected abstract void processComponent(String propertyName, JTextComponent textComponent);
    
      /**
       * Converts the given component to a <code>JTextComponent</code>. This can be a
       * simple cast if the component is already a text component, or an embedded component
       * (for example a JSpinner).
       * <p>
       * This method is protected, and can be overridden when necessary.
       *
       * @param component The component from which to get a text component.
       * @return a <code>JTextComponent</code>, or <code>null</code>
       */
      protected JTextComponent getTextComponent(JComponent component) {
        if(component instanceof JTextComponent) {
          return (JTextComponent)component;
        }
    
        if(component instanceof JSpinner) {
          JSpinner spinner = (JSpinner)component;
          if(spinner.getEditor() instanceof JSpinner.DefaultEditor) {
            return ((DefaultEditor)spinner.getEditor()).getTextField();
          }
          if(spinner.getEditor() instanceof JTextField) {
            return (JTextField)spinner.getEditor();
          }
        }
    
        return null;
      }
    
    }

  • #2
    Here are the Interceptors and InterceptorFactory classes.

    Code:
    /*
     * SpellCheckTextComponentInterceptor.java
     * 
     * Created on Dec 8, 2010, 12:49:33 PM
     * 
     * To change this template, choose Tools | Templates
     * and open the template in the editor.
     */
    package org.chd.hydra.form.interceptor;
    
    import com.inet.jortho.SpellChecker;
    import javax.swing.text.JTextComponent;
    import org.springframework.binding.form.FormModel;
    
    /**
     * <p>
     * A <code>SpellCheckTextComponentInterceptor</code> is a TextComponentInterceptor that
     * registers text components with the JOrtho spell checker.
     * </p>
     *
     * @author aarmistead
     */
    public class SpellCheckTextComponentInterceptor extends AbstractTextComponentInterceptor {
    
      /**
       * The flag indicating whether or not to enable auto spell for the text component.
       */
      private boolean hasAutoSpell;
    
      /**
       * The flag indicating whether or not to add a spell check popup to the text component.
       */
      private boolean hasPopup;
    
      /**
       * The flag that indicates whether or not to configure the F7 shortcut key to perform a spell check and
       * display the spell check dialog for the text component.
       */
      private boolean hasShortcutKey;
    
      /**
       * Creates a new instance of <code>SpellCheckTextComponentInterceptor</code> with no
       * form model.
       */
      public SpellCheckTextComponentInterceptor() {
        super();
      }
    
      /**
       * Creates a new instance of <code>SpellCheckTextComponentInterceptor</code> with the specified
       * <code>formModel</code> auto spell, popup, and shortcut key flags.
       *
       * @param formModel The form model for this interceptor.
       * @param hasAutoSpell True if auto spell is enables for the text component.
       * @param hasPopup True if a spell check popup should be added to the component.
       * @param hasShortcutKey True if the F7 key should be configure to spell check the text component.
       */
      public SpellCheckTextComponentInterceptor(FormModel formModel, boolean hasAutoSpell, boolean hasPopup, boolean hasShortcutKey) {
        super(formModel);
        this.hasAutoSpell = hasAutoSpell;
        this.hasPopup = hasPopup;
        this.hasShortcutKey = hasShortcutKey;
      }
    
      /**
       * Processes the specified <code>textComponent</code> by registering it with the spell checker.
       *
       * @param propertyName The property name for which to process the text component.
       * @param textComponent The text component to process.
       */
      @Override
      protected void processComponent(String propertyName, JTextComponent textComponent) {
        SpellChecker.register(textComponent, hasPopup, hasShortcutKey, hasAutoSpell);
      }
    
    }
    Code:
    /*
     * SpellCheckTextComponentInterceptorFactory.java
     * 
     * Created on Dec 8, 2010, 1:10:41 PM
     * 
     * To change this template, choose Tools | Templates
     * and open the template in the editor.
     */
    package org.chd.hydra.form.interceptor;
    
    import com.inet.jortho.FileUserDictionary;
    import com.inet.jortho.SpellChecker;
    import java.util.Locale;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.binding.form.FormModel;
    import org.springframework.core.io.Resource;
    import org.springframework.richclient.form.builder.FormComponentInterceptor;
    import org.springframework.richclient.form.builder.FormComponentInterceptorFactory;
    import org.springframework.richclient.util.Assert;
    
    /**
     * <p>
     * A <code>SpellCheckTextComponentInterceptorFactory</code> is a FormComponentInterceptorFactory
     * that initialized the JOrtho spell checker and creates SpellCheckTextComponentInterceptors to register
     * text component with the spell checker.  Allows enabling and disabling of auto spell, popup menu, and
     * shortcut (F7) key.
     * </p>
     *
     * @author aarmistead
     */
    public class SpellCheckTextComponentInterceptorFactory implements FormComponentInterceptorFactory, InitializingBean {
    
      /**
       * The flag indicating whether or not to enable auto spell for the intercepted text components.
       */
      private boolean hasAutoSpell;
    
      /**
       * The flag indicating whether or not to add a spell check popup to the intercepted text components.
       */
      private boolean hasPopup;
    
      /**
       * The flag that indicates whether or not to configure the F7 shortcut key to perform a spell check and
       * display the spell check dialog for the intercepted text components.
       */
      private boolean hasShortcutKey;
    
      /**
       * The resource indicating the location of the dictionaries to be loaded.
       */
      private Resource dictionaries;
    
      /**
       * The path to the user dictionary file.
       */
      private String userDictionaryPath;
    
      /**
       * Gets the state of the flag indicating whether or not to enable auto spell for the intercepted text components.
       *
       * @return True if auto spell is enabled for intercepted text components.
       */
      public boolean hasAutoSpell() {
        return hasAutoSpell;
      }
    
      /**
       * Sets the state of the flag indicating whether or not to enable auto spell for the intercepted
       * text components to <code>hasAutoSpell</code>.
       *
       * @param hasAutoSpell The new auto spell state for intercepted text components.
       */
      public void setHasAutoSpell(boolean hasAutoSpell) {
        this.hasAutoSpell = hasAutoSpell;
      }
    
      /**
       * Gets the state of the flag indicating whether or not to add a spell check popup to the intercepted text components.
       *
       * @return True if a spell check popup is added to intercepted text components.
       */
      public boolean hasPopup() {
        return hasPopup;
      }
    
      /**
       * Sets the state of the flag indicating whether or not to add a spell check popup to the intercepted
       * text components to <code>hasPopup</code>.
       *
       * @param hasPopup The new popup state for the intercepted text components.
       */
      public void setHasPopup(boolean hasPopup) {
        this.hasPopup = hasPopup;
      }
    
      /**
       * Gets the flag that indicates whether or not to configure the F7 shortcut key to perform a spell check and
       * display the spell check dialog for the intercepted text components.
       *
       * @return True if the F7 shortcut key should be configured for the intercepted text components.
       */
      public boolean hasShortcutKey() {
        return hasShortcutKey;
      }
    
      /**
       * Sets the flag that indicates whether or not to configure the F7 shortcut key to perform a spell check and
       * display the spell check dialog for the intercepted text components to <code>hasShortcutKey</code>.
       *
       * @param hasShortcutKey The new state of the shortcut flag for the intercepted text components.
       */
      public void setHasShortcutKey(boolean hasShortcutKey) {
        this.hasShortcutKey = hasShortcutKey;
      }
    
      /**
       * Sets the resource indicating the location of the dictionaries to be loaded to <code>dictionaries</code>.
       *
       * @param dictionaries The new resource location of the dictionaries to be loaded.
       */
      public void setDictionaries(Resource dictionaries) {
        this.dictionaries = dictionaries;
      }
    
      /**
       * Sets the path to the user dictionary file to <code>userDictionaryPath</code>.
       *
       * @param userDictionaryPath The new path to the user dictionary file.
       */
      public void setUserDictionaryPath(String userDictionaryPath) {
        this.userDictionaryPath = userDictionaryPath;
      }
    
      /**
       * Creates a new instance of <code>SpellCheckTextComponentInterceptorFactory</code>.
       * Enables auto spell checking, popup menu, and shortcut (F7) key by default.
       */
      public SpellCheckTextComponentInterceptorFactory() {
        hasAutoSpell = true;
        hasPopup = true;
        hasShortcutKey = true;
      }
    
      /**
       * Ensures that a dictionaries resource and user dictionary path have been set.
       * Configures the spell checker to use the specified dictionaries and sets the locale to the
       * default locale.  The user dictionary path is configured with a FileUserDictionary and
       * added to the spell checker
       *
       * @throws Exception If a dictionaries resource was not specified.
       */
      @Override
      public void afterPropertiesSet() throws Exception {
        Assert.notNull(dictionaries, "You must specify a dictionaries resource.");
        Assert.notNull(userDictionaryPath, "You must specify a userDictionaryPath.");
    
        SpellChecker.setUserDictionaryProvider(new FileUserDictionary(userDictionaryPath)); // Must be called first.
        SpellChecker.registerDictionaries(dictionaries.getURL(), Locale.getDefault().getLanguage());
      }
    
      /**
       * Gets a new interceptor for the specified <code>formModel</code>.
       *
       * @param formModel The form model for which to get an interceptor.
       * @return The new interceptor for the specified form model.
       */
      @Override
      public FormComponentInterceptor getInterceptor(FormModel formModel) {
        return new SpellCheckTextComponentInterceptor(formModel, hasAutoSpell, hasPopup, hasShortcutKey);
      }
    
    }

    Comment


    • #3
      If you want to add a popup with the spell check menus added on your text components you can create a PopupInterceptorFactory and a SpellCheckTextComponentPopup that work just like the org.springframework.richclient.text.TextComponentP opupInterceptorFactory
      and its associated TextComponentPopup and add in the spell check menus like this (in my SpellCheckTextComponentPopup class):

      Code:
      private final SpellCheckExecutor spellCheck = new SpellCheckExecutor();
      
      protected CommandGroup getEditableCommandGroup() {
          CommandGroup editGroup = getCommandManager().getCommandGroup("textEditMenu");
          if(editGroup == null) {
            JMenu checkerMenu = SpellChecker.createCheckerMenu();
            IconSource iconSource = (IconSource)Application.services().getService(IconSource.class);
            checkerMenu.setIcon(iconSource.getIcon("spellingCommand.icon"));
            JMenu languagesMenu = SpellChecker.createLanguagesMenu();
            languagesMenu.setIcon(iconSource.getIcon("languagesCommand.icon"));
      
            editGroup = getCommandManager().createCommandGroup(
                "textEditMenu",
                new Object[] {checkerMenu, languagesMenu, "separator", GlobalCommandIds.UNDO, GlobalCommandIds.REDO, "separator", GlobalCommandIds.CUT,
                  GlobalCommandIds.COPY, GlobalCommandIds.PASTE, "separator", GlobalCommandIds.SELECT_ALL});
          }
          return editGroup;
        }
      
      private void registerCommandExecutors() {
          CommandManager commandManager = getCommandManager();
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.UNDO, undo);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.REDO, redo);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.CUT, cut);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.COPY, copy);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.PASTE, paste);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.SELECT_ALL, selectAll);
          commandManager.setTargetableActionCommandExecutor("spellCheckCommand", spellCheck);
        }
      
        private void unregisterCommandExecutors() {
          CommandManager commandManager = getCommandManager();
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.UNDO, null);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.REDO, null);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.CUT, null);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.COPY, null);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.PASTE, null);
          commandManager.setTargetableActionCommandExecutor(GlobalCommandIds.SELECT_ALL, null);
          commandManager.setTargetableActionCommandExecutor("spellCheckCommand", null);
        }
      
        private class SpellCheckExecutor extends AbstractActionCommandExecutor {
      
          public SpellCheckExecutor() {
            super();
            setEnabled(true);
          }
      
          @Override
          public void execute() {
            textComponent.getActionMap().get("spell-checking").actionPerformed(new ActionEvent(textComponent, ActionEvent.ACTION_PERFORMED, "spell-checking"));
          }
      
        }
      Now I can just register my interceptors with my component interceptor factory:

      Code:
      <bean id="formComponentInterceptorFactory"
              class="org.springframework.richclient.form.builder.support.ChainedInterceptorFactory">
          <property name="interceptorFactories">
            <list>
              <bean class="org.springframework.richclient.form.builder.support.ColorValidationInterceptorFactory">
                <property name="errorColor" value="255,245,245"/>
              </bean>
              <bean class="org.springframework.richclient.list.ComboBoxAutoCompletionInterceptorFactory"/>
              <bean class="org.springframework.richclient.form.builder.support.OverlayValidationInterceptorFactory"/>
              <bean class="org.chd.hydra.form.interceptor.PromptTextFieldFormComponentInterceptorFactory"/>
              <bean class="org.chd.hydra.form.interceptor.ShowDescriptionInStatusBarInterceptorFactory"/>
              <bean class="org.chd.hydra.form.interceptor.SpellCheckTextComponentInterceptorFactory">
                <property name="dictionaries">
                  <value>classpath:dictionaries/</value>
                </property>
                <property name="hasPopup" value="false"/>
                <property name="userDictionaryPath" value="${user.home}/Application Data/CHD/${application.title}"/>
              </bean>
              <bean class="org.chd.hydra.form.interceptor.SpellCheckTextComponentPopupInterceptorFactory"/>
              <!--<bean class="org.springframework.richclient.text.TextComponentPopupInterceptorFactory"/>-->
            </list>
          </property>
        </bean>
      add the spellCheckCommand to my windowCommandManager and viewMenu, and configure the spellCheckCommand label,caption and icon, as well as provide icons for the spellingCommand and languagesCommand that will be loded by the getEditableCommandGroup method shown above if desired.

      Comment


      • #4
        thanks

        Hi,

        I had the same query. Thanks for the great post.

        Comment


        • #5
          hey

          waiting for reply

          Comment


          • #6
            Very interesting, thanks indeed!

            Comment


            • #7
              Great one Adam! I'll be integrating this one into Valkyrie .

              Comment


              • #8
                Ha, excellent, I'm keeping an eye out for more solutions like this. This was just too generically useful for me NOT to post about it.

                Comment

                Working...
                X