diff --git a/lib/installer-exclude/jmyspell-core.jar b/lib/installer-exclude/jmyspell-core.jar index 0358e35a5..2b31d0ba5 100644 Binary files a/lib/installer-exclude/jmyspell-core.jar and b/lib/installer-exclude/jmyspell-core.jar differ diff --git a/resources/config/spellcheck/de_DE_frami.zip b/resources/config/spellcheck/de_DE_frami.zip new file mode 100644 index 000000000..70c867069 Binary files /dev/null and b/resources/config/spellcheck/de_DE_frami.zip differ diff --git a/resources/config/spellcheck/es_ES.zip b/resources/config/spellcheck/es_ES.zip new file mode 100644 index 000000000..ae59d5d82 Binary files /dev/null and b/resources/config/spellcheck/es_ES.zip differ diff --git a/resources/config/spellcheck/fr_FR.zip b/resources/config/spellcheck/fr_FR.zip new file mode 100644 index 000000000..d0c988115 Binary files /dev/null and b/resources/config/spellcheck/fr_FR.zip differ diff --git a/resources/config/spellcheck/parameters.xml b/resources/config/spellcheck/parameters.xml index 18bf879e9..efae15b2a 100644 --- a/resources/config/spellcheck/parameters.xml +++ b/resources/config/spellcheck/parameters.xml @@ -43,6 +43,12 @@ http://ftp.services.openoffice.org/pub/OpenOffice.org/contrib/dictionaries/ru_RU_yo.zip Slovak (Slovakia, alternative)- http://ftp.services.openoffice.org/pub/OpenOffice.org/contrib/dictionaries/sk_SK.zip Swedish (Sweden, alternative)- http://ftp.services.openoffice.org/pub/OpenOffice.org/contrib/dictionaries/sv_SE.zip + + Some missing dictionaries: + + + + --> - + - - - + - - + - + @@ -84,39 +87,36 @@ - + - + - - - + - + - + - + - - + - - + + @@ -129,11 +129,10 @@ - + - diff --git a/resources/images/images.properties b/resources/images/images.properties index e36515af4..eaafc174c 100644 --- a/resources/images/images.properties +++ b/resources/images/images.properties @@ -532,11 +532,11 @@ plugin.advancedconfig.PLUGIN_ICON=resources/images/plugin/advancedconfig/configI plugin.chatconfig.PLUGIN_ICON=resources/images/plugin/chatconfig/chatIcon.png # spellchecker plugin -plugin.spellcheck.PLUGIN_ICON=resources/images/plugin/spellchecker/pluginIcon.png -plugin.spellcheck.ADD_WORD_ICON=resources/images/plugin/spellchecker/addWord.png -plugin.spellcheck.BLANK_FLAG=resources/images/plugin/spellchecker/blankFlag.png -plugin.spellcheck.PERSONAL_DIR=resources/images/plugin/spellchecker/personalDictionary.png -plugin.spellcheck.WORD_INCLUDE=resources/images/plugin/spellchecker/wordInclude.png -plugin.spellcheck.WORD_EXCLUDE=resources/images/plugin/spellchecker/wordExclude.png -plugin.spellcheck.ENABLE=resources/images/plugin/spellchecker/enableSpellchecker.png -plugin.spellcheck.DISABLE=resources/images/plugin/spellchecker/disableSpellchecker.png +plugin.spellcheck.PLUGIN_ICON=resources/images/plugin/spellchec/pluginIcon.png +plugin.spellcheck.ADD_WORD_ICON=resources/images/plugin/spellcheck/addWord.png +plugin.spellcheck.BLANK_FLAG=resources/images/plugin/spellcheck/blankFlag.png +plugin.spellcheck.PERSONAL_DIR=resources/images/plugin/spellcheck/personalDictionary.png +plugin.spellcheck.WORD_INCLUDE=resources/images/plugin/spellcheck/wordInclude.png +plugin.spellcheck.WORD_EXCLUDE=resources/images/plugin/spellcheck/wordExclude.png +plugin.spellcheck.ENABLE=resources/images/plugin/spellcheck/enableSpellchecker.png +plugin.spellcheck.DISABLE=resources/images/plugin/spellcheck/disableSpellchecker.png diff --git a/resources/images/plugin/spellcheck/flags/xx.png b/resources/images/plugin/spellcheck/flags/xx.png new file mode 100644 index 000000000..10f451fe8 Binary files /dev/null and b/resources/images/plugin/spellcheck/flags/xx.png differ diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 5c9314ee8..a950417a8 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -1225,6 +1225,7 @@ plugin.chatconfig.replacement.REPLACEMENT_SOURCES=Sources: plugin.chatconfig.spellcheck.TITLE=SpellCheck plugin.spellcheck.LANG=Language plugin.spellcheck.EDIT_PERSONAL_DICT=Edit +plugin.spellcheck.ENABLE_SPELL_CHECK=Enable spell check #provisioning plugin plugin.provisioning.PROVISIONING=Provisioning @@ -1269,3 +1270,16 @@ plugin.dnsconfig.lblRedemption.text=Back to primary resolver plugin.dnsconfig.lblRedemption.description=the number of prompt responses that the primary resolver needs to provide before we turn that backup off again plugin.dnsconfig.lblPatience.text=Start backup resolver after plugin.dnsconfig.lblPatience.description=number of ms to wait for a response from primary DNS before starting the backup resolver + +#plugin spellcheck +plugin.spellcheck.TITLE=Spelling and Grammar +plugin.spellcheck.MENU=Show Spelling and Grammar +plugin.spellcheck.LANG=Language +plugin.spellcheck.EDIT_PERSONAL_DICT=Edit +plugin.spellcheck.dialog.FIND=Find Next +plugin.spellcheck.dialog.REPLACE=Replace +plugin.spellcheck.dialog.ADD=Add Word +plugin.spellcheck.DICT_ERROR_TITLE=Error Switching Dictionary +plugin.spellcheck.DICT_ERROR=Cannot switch dictionary +plugin.spellcheck.DICT_RETRIEVE_ERROR=Dictionary cannot be fetched from +plugin.spellcheck.DICT_PROCESS_ERROR=Locale not recognized \ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/ChatWritePanel.java b/src/net/java/sip/communicator/impl/gui/main/chat/ChatWritePanel.java index 3ded2ff4e..3ea9afb17 100755 --- a/src/net/java/sip/communicator/impl/gui/main/chat/ChatWritePanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/chat/ChatWritePanel.java @@ -720,9 +720,26 @@ public void mouseClicked(MouseEvent e) Point p = e.getPoint(); SwingUtilities.convertPointToScreen(p, e.getComponent()); - rightButtonMenu.setInvoker(editorPane); - rightButtonMenu.setLocation(p.x, p.y); - rightButtonMenu.setVisible(true); + //SPELLCHECK + ArrayList contributedMenuEntries + = new ArrayList(); + + for(ChatMenuListener listener : this.menuListeners) + { + contributedMenuEntries.addAll( + listener.getMenuElements(this.chatPanel, e)); + } + + for(JMenuItem item : contributedMenuEntries) + { + rightButtonMenu.add(item); + } + + JPopupMenu rightMenu + = rightButtonMenu.makeMenu(contributedMenuEntries); + rightMenu.setInvoker(editorPane); + rightMenu.setLocation(p.x, p.y); + rightMenu.setVisible(true); } } diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/menus/WritePanelRightButtonMenu.java b/src/net/java/sip/communicator/impl/gui/main/chat/menus/WritePanelRightButtonMenu.java index 5f96820c3..e8ba640f3 100644 --- a/src/net/java/sip/communicator/impl/gui/main/chat/menus/WritePanelRightButtonMenu.java +++ b/src/net/java/sip/communicator/impl/gui/main/chat/menus/WritePanelRightButtonMenu.java @@ -7,6 +7,7 @@ package net.java.sip.communicator.impl.gui.main.chat.menus; import java.awt.event.*; +import java.util.*; import javax.swing.*; @@ -150,4 +151,32 @@ public void loadSkin() closeMenuItem.setIcon(new ImageIcon( ImageLoader.getImage(ImageLoader.CLOSE_ICON))); } + + /** + * Provides a popup menu with custom entries followed by default + * operation entries ( copy, paste ,close) + * + * @param entries custom menu entries to be added + * @return right click menu + */ + public JPopupMenu makeMenu(List entries) { + + JPopupMenu rightMenu = new JPopupMenu(); + + for(JMenuItem entry : entries) { + rightMenu.add(entry); + } + + if(!entries.isEmpty()) rightMenu.addSeparator(); + + rightMenu.add(copyMenuItem); + rightMenu.add(cutMenuItem); + rightMenu.add(pasteMenuItem); + + rightMenu.addSeparator(); + + rightMenu.add(closeMenuItem); + + return rightMenu; + } } diff --git a/src/net/java/sip/communicator/plugin/spellcheck/ChatAttachments.java b/src/net/java/sip/communicator/plugin/spellcheck/ChatAttachments.java index e9f01047c..75f23ff83 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/ChatAttachments.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/ChatAttachments.java @@ -1,8 +1,7 @@ /* * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. + * + * Distributable under LGPL license. See terms of license at gnu.org. */ package net.java.sip.communicator.plugin.spellcheck; @@ -13,33 +12,48 @@ import javax.swing.event.*; import javax.swing.text.*; -import net.java.sip.communicator.service.gui.Chat; -import net.java.sip.communicator.service.gui.event.ChatMenuListener; -import net.java.sip.communicator.util.Logger; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.gui.event.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.util.*; import org.dts.spell.dictionary.*; /** * Wrapper for handling the multiple listeners associated with chats for the * spell checker. - * + * * @author Damian Johnson + * @author Purvesh Sahoo */ class ChatAttachments { - private static final Logger logger - = Logger.getLogger(ChatAttachments.class); - private static final ImageIcon ADD_WORD_ICON - = Resources.getImage(Resources.ADD_WORD_ICON); + private static final Logger logger = Logger + .getLogger(ChatAttachments.class); + + private static final ImageIcon ADD_WORD_ICON = Resources + .getImage(Resources.ADD_WORD_ICON); + private final Chat chat; - private final DocUnderliner docListener; //The red-squibble drawing code + + private final DocUnderliner docListener; // The red-squibble drawing code + private final CaretListener caretListener; + private final ChatMenuListener menuListener; + private boolean isEnabled = true; + private SpellDictionary dict; + private boolean isAttached = false; - ChatAttachments(Chat chat, SpellDictionary dict) + private final ResourceManagementService resources = Resources + .getResources(); + + private SpellCheckerConfigDialog dialog; + + ChatAttachments(Chat chat, final SpellDictionary dict) { this.chat = chat; this.dict = dict; @@ -56,8 +70,7 @@ boolean getFormatting(String word) { // thrown by spell checker API if problem occurs logger.error( - "Spell checker dictionary failed to be accessed", - exc); + "Spell checker dictionary failed to be accessed", exc); return false; } } @@ -77,21 +90,61 @@ void promptRepaint() this.menuListener = new ChatMenuListener() { - public List getMenuElements(Chat chat, MouseEvent event) //Overridden Here + + public List getMenuElements(final Chat chat, + MouseEvent event) { + if (isEnabled && event.getSource() instanceof JTextComponent) { JTextComponent comp = (JTextComponent) event.getSource(); int index = comp.viewToModel(event.getPoint()); + try + { + String compText = + comp.getDocument().getText(0, + comp.getDocument().getLength()); + + if (index != -1 && compText.length() != 0) + { + + return getCorrections(Word.getWord( + comp.getDocument().getText(0, + comp.getDocument().getLength()), index, + false)); + + } - if (index != -1 && comp.getText().length() != 0) + } + catch (BadLocationException e) { - return getCorrections(Word.getWord(comp.getText(), - index, false)); + // TODO Auto-generated catch block + e.printStackTrace(); } } - return new ArrayList (); + JMenuItem spellCheck = + new JMenuItem( + resources.getI18NString("plugin.spellcheck.MENU")); + + ArrayList spellCheckItem = + new ArrayList(); + spellCheckItem.add(spellCheck); + spellCheck.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + if(dialog != null) { + dialog.dispose(); + } + dialog = + new SpellCheckerConfigDialog(chat, null, dict); + dialog.setVisible(true); + } + }); + + return spellCheckItem; } }; } @@ -106,6 +159,7 @@ synchronized void attachListeners() this.chat.addChatEditorDocumentListener(this.docListener); this.chat.addChatEditorCaretListener(this.caretListener); this.chat.addChatEditorMenuListener(this.menuListener); + } } @@ -119,6 +173,7 @@ synchronized void detachListeners() this.chat.removeChatEditorDocumentListener(this.docListener); this.chat.removeChatEditorCaretListener(this.caretListener); this.chat.removeChatEditorMenuListener(this.menuListener); + } } @@ -146,22 +201,23 @@ void setDictionary(SpellDictionary dict) } // provides popup menu entries (mostly separated for readability) - private ArrayList getCorrections(final Word clickedWord) + private ArrayList getCorrections(final Word clickedWord) { - ArrayList correctionEntries = new ArrayList (); + ArrayList correctionEntries = new ArrayList(); synchronized (this.dict) { if (!this.dict.isCorrect(clickedWord.getText())) { - List corrections = - this.dict.getSuggestions(clickedWord.getText()); + List corrections = + this.dict.getSuggestions(clickedWord.getText()); for (String correction : corrections) { JMenuItem newEntry = new JMenuItem(correction); newEntry.addActionListener(new CorrectionListener( - clickedWord, correction)); + clickedWord, correction)); correctionEntries.add(newEntry); + } // entry to add word @@ -181,16 +237,38 @@ public void actionPerformed(ActionEvent event) catch (SpellDictionaryException exc) { String msg = - "Unable to add word to personal dictionary"; + "Unable to add word to personal dictionary"; logger.error(msg, exc); } } }); correctionEntries.add(addWord); + } - } + + JMenuItem spellCheck = + new JMenuItem( + resources.getI18NString("plugin.spellcheck.MENU")); + correctionEntries.add(spellCheck); + spellCheck.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + if(dialog != null) { + dialog.dispose(); + } + + dialog = + new SpellCheckerConfigDialog(chat, clickedWord, + dict); + dialog.setVisible(true); + } + }); + } return correctionEntries; + } // Applies corrections from popup menu to chat @@ -198,6 +276,7 @@ private class CorrectionListener implements ActionListener { private Word clickedWord; + private String correction; CorrectionListener(Word clickedWord, String correction) @@ -209,11 +288,12 @@ private class CorrectionListener public void actionPerformed(ActionEvent event) { StringBuffer newMessage = new StringBuffer(chat.getMessage()); + int endIndex = - this.clickedWord.getStart() - + this.clickedWord.getText().length(); + this.clickedWord.getStart() + + this.clickedWord.getText().length(); newMessage.replace(this.clickedWord.getStart(), endIndex, - this.correction); + this.correction); chat.setMessage(newMessage.toString()); } } diff --git a/src/net/java/sip/communicator/plugin/spellcheck/DocUnderliner.java b/src/net/java/sip/communicator/plugin/spellcheck/DocUnderliner.java index 6935b687f..6ca51c0ad 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/DocUnderliner.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/DocUnderliner.java @@ -1,8 +1,7 @@ /* * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. + * + * Distributable under LGPL license. See terms of license at gnu.org. */ package net.java.sip.communicator.plugin.spellcheck; @@ -18,74 +17,83 @@ * Notifies subclasses when words are changed and lets them decide if text * should be underlined with a red squiggle. Text appended to the end isn't * formatted until the word's completed. + * * @author Damian Johnson */ abstract class DocUnderliner implements DocumentListener { private static final Logger logger = Logger.getLogger(DocUnderliner.class); + private static final Color UNDERLINE_COLOR = new Color(255, 100, 100); + private static final DefaultHighlighter.DefaultHighlightPainter UNDERLINER; + private final Highlighter docHighlighter; + private final CaretListener endChecker; + private boolean isEnabled = true; static { UNDERLINER = - new DefaultHighlighter.DefaultHighlightPainter(UNDERLINE_COLOR) + new DefaultHighlighter.DefaultHighlightPainter(UNDERLINE_COLOR) + { + public Shape paintLayer(Graphics g, int offs0, int offs1, + Shape area, JTextComponent comp, View view) { - public Shape paintLayer(Graphics g, int offs0, int offs1, - Shape area, JTextComponent comp, View view) - { - Color color = getColor(); - if (color == null) g.setColor(comp.getSelectionColor()); - else g.setColor(color); + Color color = getColor(); + if (color == null) + g.setColor(comp.getSelectionColor()); + else + g.setColor(color); - if (offs0 == view.getStartOffset() - && offs1 == view.getEndOffset()) + if (offs0 == view.getStartOffset() + && offs1 == view.getEndOffset()) + { + // contained in view, can just use bounds + drawWavyLine(g, area.getBounds()); + return area; + } + else + { + // should only render part of View + try { - // contained in view, can just use bounds - drawWavyLine(g, area.getBounds()); - return area; + Shape shape = + view.modelToView(offs0, Position.Bias.Forward, + offs1, Position.Bias.Backward, area); + drawWavyLine(g, shape.getBounds()); + return shape.getBounds(); } - else + catch (BadLocationException exc) { - // should only render part of View - try - { - Shape shape = - view.modelToView(offs0, - Position.Bias.Forward, offs1, - Position.Bias.Backward, area); - drawWavyLine(g, shape.getBounds()); - return shape.getBounds(); - } - catch (BadLocationException exc) - { - String msg = - "Bad bounds (programmer error in spell checker)"; - logger.error(msg, exc); - return area; // can't render - } + String msg = + "Bad bounds (programmer error in spell checker)"; + logger.error(msg, exc); + return area; // can't render } } + } - private void drawWavyLine(Graphics g, Rectangle bounds) - { - int y = (int) (bounds.getY() + bounds.getHeight()); - int x1 = (int) bounds.getX(); - int x2 = (int) (bounds.getX() + bounds.getWidth()); + private void drawWavyLine(Graphics g, Rectangle bounds) + { + int y = (int) (bounds.getY() + bounds.getHeight()); + int x1 = (int) bounds.getX(); + int x2 = (int) (bounds.getX() + bounds.getWidth()); - boolean upperCurve = true; - for (int i = x1; i < x2 - 2; i += 3) - { - if (upperCurve) g.drawArc(i, y - 2, 3, 3, 0, 180); - else g.drawArc(i, y - 2, 3, 3, 180, 180); - upperCurve = !upperCurve; - } + boolean upperCurve = true; + for (int i = x1; i < x2 - 2; i += 3) + { + if (upperCurve) + g.drawArc(i, y - 2, 3, 3, 0, 180); + else + g.drawArc(i, y - 2, 3, 3, 180, 180); + upperCurve = !upperCurve; } - }; + } + }; } { @@ -105,7 +113,7 @@ public void caretUpdate(CaretEvent event) { String text = comp.getText(); Word changed = - Word.getWord(text, text.length() - 1, false); + Word.getWord(text, text.length() - 1, false); format(changed); promptRepaint(); } @@ -120,6 +128,7 @@ public void caretUpdate(CaretEvent event) * Queries to see if a word should be underlined. This is called on every * internal change and whenever a word's completed so it should be a * lightweight process. + * * @param word word to be checked * @return true if the word should be underlined, false otherwise */ @@ -127,6 +136,7 @@ public void caretUpdate(CaretEvent event) /** * Provides the index of the character the cursor is in front of. + * * @return index of caret */ abstract int getCaretPosition(); @@ -146,23 +156,23 @@ public static void main(String[] args) editorPane.setPreferredSize(new Dimension(400, 500)); final DocUnderliner formatter = - new DocUnderliner(editorPane.getHighlighter()) + new DocUnderliner(editorPane.getHighlighter()) + { + boolean getFormatting(String word) { - boolean getFormatting(String word) - { - return word.contains("foo"); - } + return word.contains("foo"); + } - int getCaretPosition() - { - return editorPane.getCaretPosition(); - } + int getCaretPosition() + { + return editorPane.getCaretPosition(); + } - void promptRepaint() - { - editorPane.repaint(); - } - }; + void promptRepaint() + { + editorPane.repaint(); + } + }; editorPane.getDocument().addDocumentListener(formatter); editorPane.addCaretListener(formatter.getEndChecker()); @@ -178,7 +188,8 @@ void promptRepaint() public void insertUpdate(DocumentEvent event) { - if (!this.isEnabled) return; + if (!this.isEnabled) + return; try { @@ -201,7 +212,7 @@ public void insertUpdate(DocumentEvent event) // new character at end (ensure it isn't initially // underlined) clearUnderlining(event.getOffset(), - event.getOffset() + 1); + event.getOffset() + 1); } } else @@ -211,9 +222,11 @@ public void insertUpdate(DocumentEvent event) // change within word Word changed; int previousIndex = Math.max(0, event.getOffset() - 1); - if (Character.isLetter(text.charAt(previousIndex))) changed = + if (Character.isLetter(text.charAt(previousIndex))) + changed = Word.getWord(text, event.getOffset(), true); - else changed = + else + changed = Word.getWord(text, event.getOffset(), false); format(changed); } @@ -221,11 +234,9 @@ public void insertUpdate(DocumentEvent event) { // dividing a word - need to check both sides Word firstWord = - Word.getWord(text, event.getOffset(), true); + Word.getWord(text, event.getOffset(), true); Word secondWord = - Word - .getWord(text, event.getOffset() + 1, - false); + Word.getWord(text, event.getOffset() + 1, false); format(firstWord); format(secondWord); } @@ -241,9 +252,8 @@ public void insertUpdate(DocumentEvent event) { format(changed); int end = - Math.min(changed.getStart() - + changed.getText().length() + 1, text - .length()); + Math.min(changed.getStart() + + changed.getText().length() + 1, text.length()); changed = Word.getWord(text, end, false); wordStart = end; } @@ -260,7 +270,8 @@ public void insertUpdate(DocumentEvent event) public void removeUpdate(DocumentEvent event) { - if (!this.isEnabled) return; + if (!this.isEnabled) + return; try { @@ -270,8 +281,7 @@ public void removeUpdate(DocumentEvent event) { Word changed; if (event.getOffset() == 0 - || !Character.isLetter(text - .charAt(event.getOffset() - 1))) + || !Character.isLetter(text.charAt(event.getOffset() - 1))) { changed = Word.getWord(text, event.getOffset(), false); } @@ -293,11 +303,13 @@ public void removeUpdate(DocumentEvent event) } public void changedUpdate(DocumentEvent e) - {} + { + } /** * Provides a listener that prompts the last word to be checked when the * cursor moves away from it. + * * @return listener for caret position that formats last word when * appropriate */ @@ -308,24 +320,26 @@ public CaretListener getEndChecker() /** * Formats the word with the appropriate underlining (or lack thereof). + * * @param word word to be formatted */ public void format(Word word) { - if (!this.isEnabled) return; + if (!this.isEnabled) + return; String text = word.getText(); if (text.length() > 0) { clearUnderlining(word.getStart(), word.getStart() + text.length()); - if (getFormatting(text)) underlineRange(word.getStart(), word - .getStart() - + text.length()); + if (getFormatting(text)) + underlineRange(word.getStart(), word.getStart() + text.length()); } } /** * Sets a range in the editor to be underlined. + * * @param start start of range to be underlined * @param end end of range to be underlined */ @@ -335,8 +349,8 @@ private void underlineRange(int start, int end) { try { - if (this.isEnabled) this.docHighlighter.addHighlight(start, - end, UNDERLINER); + if (this.isEnabled) + this.docHighlighter.addHighlight(start, end, UNDERLINER); } catch (BadLocationException exc) { @@ -350,6 +364,7 @@ private void underlineRange(int start, int end) * Clears any underlining that spans to include the given range. Since * formatting is defined by ranges this will likely clear more than the * defined range. + * * @param start start of range in which to clear underlining * @param end end of range in which to clear underlining */ @@ -361,12 +376,12 @@ private void clearUnderlining(int start, int end) if (this.isEnabled) { for (Highlighter.Highlight highlight : this.docHighlighter - .getHighlights()) + .getHighlights()) { if ((highlight.getStartOffset() <= start && highlight - .getEndOffset() > start) - || (highlight.getStartOffset() < end && highlight - .getEndOffset() >= end)) + .getEndOffset() > start) + || (highlight.getStartOffset() < end && highlight + .getEndOffset() >= end)) { this.docHighlighter.removeHighlight(highlight); } @@ -380,18 +395,23 @@ public void setEnabled(boolean enable, String message) if (this.isEnabled != enable) { this.isEnabled = enable; - if (this.isEnabled) reset(message); - else this.docHighlighter.removeAllHighlights(); + if (this.isEnabled) + reset(message); + else + this.docHighlighter.removeAllHighlights(); promptRepaint(); } } /** * Clears underlining and re-evaluates message's contents + * * @param message textual contents of document */ - public void reset(String message) { - if (!this.isEnabled) return; + public void reset(String message) + { + if (!this.isEnabled) + return; // clears previous underlined sections this.docHighlighter.removeAllHighlights(); @@ -405,9 +425,8 @@ public void reset(String message) { { format(changed); int end = - Math.min(changed.getStart() - + changed.getText().length() + 1, message - .length()); + Math.min(changed.getStart() + changed.getText().length() + + 1, message.length()); changed = Word.getWord(message, end, false); wordStart = end; } diff --git a/src/net/java/sip/communicator/plugin/spellcheck/LanguageSelectionField.java b/src/net/java/sip/communicator/plugin/spellcheck/LanguageSelectionField.java new file mode 100644 index 000000000..2986ed2af --- /dev/null +++ b/src/net/java/sip/communicator/plugin/spellcheck/LanguageSelectionField.java @@ -0,0 +1,508 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.spellcheck; + +import java.awt.*; +import java.awt.image.*; + +import java.io.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.*; + +import net.java.sip.communicator.plugin.spellcheck.Parameters.Default; +import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.gui.Container; +import net.java.sip.communicator.service.protocol.Contact; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.swing.*; +import net.java.sip.communicator.util.swing.SwingWorker; + +/** + * Combo box providing a listing of all available locales with corresponding + * country flags. Selecting a new field causes that locale's dictionary to be + * downloaded, if not available. The spell checker then use the selected + * language for further checking. + * + * @author Damian Johnson + * @author Yana Stamcheva + */ +public class LanguageSelectionField + extends SIPCommMenuBar + implements PluginComponent +{ + private static final HashMap + CLASS_INSTANCES = + new HashMap(); + + // parallel maps containing cached instances of country flags + private static final HashMap + AVAILABLE_FLAGS = new HashMap(); + + private static final HashMap + UNAVAILABLE_FLAGS = new HashMap(); + + private static final Logger logger = Logger + .getLogger(LanguageSelectionField.class); + + private static final ImageIcon BLANK_FLAG_ICON = Resources + .getImage("blankFlag"); + + private final ListCellRenderer languageSelectionRenderer; + + private final HashMap localeAvailabilityCache = + new HashMap(); + + private final SpellChecker spellChecker; + + private final SIPCommMenu menu = new SelectorMenu(); + + private final ArrayList localeList = + new ArrayList(); + + /** + * Provides instance of this class associated with a spell checker. If ones + * already been created then this instance is used. + * + * @param checker spell checker field is to be associated with + * @return spell checker locale selection field + */ + public synchronized static LanguageSelectionField makeSelectionField( + SpellChecker checker) + { + // singleton constructor to ensure only one combo box is associated with + // each checker + if (CLASS_INSTANCES.containsKey(checker)) + return CLASS_INSTANCES.get(checker); + else + { + LanguageSelectionField instance = + new LanguageSelectionField(checker); + CLASS_INSTANCES.put(checker, instance); + return instance; + } + } + + private LanguageSelectionField(SpellChecker checker) + { + this.spellChecker = checker; + + setPreferredSize(new Dimension(30, 28)); + setMaximumSize(new Dimension(30, 28)); + setMinimumSize(new Dimension(30, 28)); + + this.menu.setPreferredSize(new Dimension(30, 45)); + this.menu.setMaximumSize(new Dimension(30, 45)); + + this.add(menu); + + this.setBorder(null); + this.menu.add(createEnableCheckBox()); + this.menu.addSeparator(); + this.menu.setBorder(null); + this.menu.setOpaque(false); + this.setOpaque(false); + + DefaultListModel model = new DefaultListModel(); + final JList list = new JList(model); + + this.languageSelectionRenderer = new LanguageListRenderer(); + + for (Parameters.Locale locale : Parameters.getLocales()) + { + + if (!localeAvailabilityCache.containsKey(locale)) + { + localeAvailabilityCache.put(locale, + spellChecker.isLocaleAvailable(locale)); + } + + model.addElement(locale); + localeList.add(locale); + } + + JScrollPane scroll = new JScrollPane(list); + scroll.setBorder(null); + + String localeIso = Parameters.getDefault(Default.LOCALE); + Parameters.Locale loc = Parameters.getLocale(localeIso); + + list.setCellRenderer(languageSelectionRenderer); + list.setSelectedIndex(localeList.indexOf(loc) + 1); + + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + list.addListSelectionListener(new ListSelectionListener() + { + + public void valueChanged(ListSelectionEvent e) + { + + if (!e.getValueIsAdjusting()) + { + final JList source = (JList) e.getSource(); + final Parameters.Locale locale = + (Parameters.Locale) source.getSelectedValue(); + + source.setEnabled(false); + + // Indicate to the user that the language is currently + // loading. + locale.setLoading(true); + + new SetSpellChecker(locale, source).start(); + } + } + }); + + menu.add(scroll); + + ImageIcon flagIcon = + getLocaleIcon(checker.getLocale(), + localeAvailabilityCache.get(checker.getLocale())); + SelectedObject selectedObject = + new SelectedObject(flagIcon, checker.getLocale()); + menu.setSelected(selectedObject); + } + + /** + * Clears any cached data used by the field so it reflects the current state + * of its associated spell checker. + */ + // public void revalidate() + // { + // this.localeAvailabilityCache.clear(); + // this.field.setSelectedItem(this.spellChecker.getLocale()); + // } + + public String getConstraints() + { + return Container.RIGHT; + } + + public Container getContainer() + { + return Container.CONTAINER_CHAT_TOOL_BAR; + } + + public String getName() + { + return "Spell Checker Toggle"; + } + + public int getPositionIndex() + { + return -1; + } + + public boolean isNativeComponent() + { + return false; + } + + public void setCurrentContact(MetaContact metaContact) + { + + } + + public void setCurrentContactGroup(MetaContactGroup metaGroup) + { + + } + + private static ImageIcon getLocaleIcon(Parameters.Locale locale, + boolean isAvailable) + { + if (isAvailable && AVAILABLE_FLAGS.containsKey(locale)) + return AVAILABLE_FLAGS.get(locale); + else if (!isAvailable && UNAVAILABLE_FLAGS.containsKey(locale)) + return UNAVAILABLE_FLAGS.get(locale); + else + { + // load resource + ImageIcon localeFlag; + + try + { + int commaIndex = locale.getIsoCode().indexOf(","); + String countryCode = + locale.getIsoCode().substring(commaIndex + 1); + localeFlag = Resources.getFlagImage(countryCode); + + BufferedImage flagBuffer = copy(localeFlag.getImage()); + setFaded(flagBuffer); + ImageIcon unavailableLocaleFlag = new ImageIcon(flagBuffer); + + AVAILABLE_FLAGS.put(locale, localeFlag); + UNAVAILABLE_FLAGS.put(locale, unavailableLocaleFlag); + return isAvailable ? localeFlag : unavailableLocaleFlag; + } + catch (IOException exc) + { + AVAILABLE_FLAGS.put(locale, BLANK_FLAG_ICON); + UNAVAILABLE_FLAGS.put(locale, BLANK_FLAG_ICON); + return BLANK_FLAG_ICON; + } + } + } + + /** + * Creates a deep copy of an image. + * + * @param image picture to be processed + * @return copy of the image + */ + private static BufferedImage copy(Image image) + { + int width = image.getWidth(null); + int height = image.getHeight(null); + + BufferedImage copy; + try + { + PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); + pg.grabPixels(); + ColorModel cm = pg.getColorModel(); + + WritableRaster raster = + cm.createCompatibleWritableRaster(width, height); + boolean isRasterPremultiplied = cm.isAlphaPremultiplied(); + copy = new BufferedImage(cm, raster, isRasterPremultiplied, null); + } + catch (InterruptedException e) + { + copy = + new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + } + + Graphics2D g2 = copy.createGraphics(); + g2.setComposite(AlphaComposite.Src); // Preserves color of + // transparent pixels + g2.drawImage(image, 0, 0, null); + g2.dispose(); + return copy; + } + + /** + * Removes all color from an image and makes it partly translucent. Original + * grayscale method written by Marty Stepp. + * + * @param image picture to be processed + */ + private static void setFaded(BufferedImage image) + { + int width = image.getWidth(); + int height = image.getHeight(); + + for (int row = 0; row < width; ++row) + { + for (int col = 0; col < height; ++col) + { + int c = image.getRGB(row, col); + + int r = + (((c >> 16) & 0xff) + ((c >> 8) & 0xff) + (c & 0xff)) / 3; + + int newRgb = (0xff << 24) | (r << 16) | (r << 8) | r; + newRgb &= (1 << 24) - 1; // Blanks alpha value + newRgb |= 128 << 24; // Resets it to the alpha of 128 + image.setRGB(row, col, newRgb); + } + } + } + + public void setCurrentContact(Contact contact) + { + // TODO Auto-generated method stub + + } + + private class SelectorMenu + extends SIPCommMenu + { + Image image = Resources.getImage("service.gui.icons.DOWN_ARROW_ICON") + .getImage(); + + public void paintComponent(Graphics g) + { + super.paintComponent(g); + + g.drawImage(image, getWidth() - image.getWidth(this) - 1, + (getHeight() - image.getHeight(this) - 1) / 2, this); + } + } + + /** + * Returns the enable spell check checkbox. + * + * @return the created checkbox + */ + private JCheckBox createEnableCheckBox() + { + final JCheckBox checkBox = new SIPCommCheckBox( + Resources.getString("plugin.spellcheck.ENABLE_SPELL_CHECK")); + + checkBox.setSelected(spellChecker.isEnabled()); + checkBox.setIconTextGap(0); + checkBox.addChangeListener(new ChangeListener() + { + public void stateChanged(ChangeEvent evt) + { + spellChecker.setEnabled(checkBox.isSelected()); + } + }); + + return checkBox; + } + + private class SetSpellChecker extends SwingWorker + { + private final Parameters.Locale locale; + + private final JList sourceList; + + private boolean skipFiring = false; + + public SetSpellChecker( Parameters.Locale locale, + JList sourceList) + { + this.locale = locale; + this.sourceList = sourceList; + } + + /** + * Called on the event dispatching thread (not on the worker thread) + * after the construct method has returned. + */ + public void finished() + { + if (getValue() != null) + { + sourceList.setEnabled(true); + + localeAvailabilityCache.put(locale, true); + + ImageIcon flagIcon = getLocaleIcon(locale, + localeAvailabilityCache.get(locale)); + + SelectedObject selectedObject = + new SelectedObject(flagIcon, locale); + + menu.setSelected(selectedObject); + } + else + { + // reverts selection + skipFiring = true; + + // source.setSelectedItem(spellChecker.getLocale()); + ImageIcon flagIcon = + getLocaleIcon(locale, + localeAvailabilityCache.get(locale)); + + SelectedObject selectedObject = + new SelectedObject(flagIcon, locale); + + menu.setSelected(selectedObject); + + skipFiring = false; + + sourceList.setEnabled(true); + } + + // Indicate to the user that the language is currently + // loading. + locale.setLoading(false); + } + + /** + * Download the dictionary. + */ + public Object construct() throws Exception + { + try + { + // prevents potential infinite loop during errors + if (this.skipFiring) + return null; + + spellChecker.setLocale(locale); + + return locale; + } + catch (Exception exc) + { + logger.warn( + "Unable to retrieve dictionary for " + locale, exc); + + // warns that it didn't work + PopupDialog dialog = + SpellCheckActivator.getUIService() + .getPopupDialog(); + String message + = Resources.getString( + "plugin.spellcheck.DICT_ERROR"); + if (exc instanceof IOException) + { + message = Resources.getString( + "plugin.spellcheck.DICT_RETRIEVE_ERROR") + + ":\n" + locale.getDictUrl(); + } + else if (exc instanceof IllegalArgumentException) + { + message = Resources.getString( + "plugin.spellcheck.DICT_PROCESS_ERROR"); + } + + dialog.showMessagePopupDialog( + message, + Resources + .getString("plugin.spellcheck.DICT_ERROR_TITLE"), + PopupDialog.WARNING_MESSAGE); + } + + return null; + } + } + + /** + * A custom renderer for languages list, transforming a Locale to a row + * with an icon and text. + */ + private class LanguageListRenderer + extends DefaultListCellRenderer + { + public Component getListCellRendererComponent(JList list, + Object value, int index, boolean isSelected, + boolean cellHasFocus) + { + Parameters.Locale locale = (Parameters.Locale) value; + + if (!localeAvailabilityCache.containsKey(locale)) + { + localeAvailabilityCache.put(locale, + spellChecker.isLocaleAvailable(locale)); + } + + ImageIcon flagIcon = + getLocaleIcon(locale, localeAvailabilityCache.get(locale)); + + String localeLabel = locale.getLabel(); + + if (locale.isLoading()) + setText("" + localeLabel + + " loading..."); + else + setText(localeLabel); + + setIcon(flagIcon); + + return this; + } + } +} diff --git a/src/net/java/sip/communicator/plugin/spellcheck/Parameters.java b/src/net/java/sip/communicator/plugin/spellcheck/Parameters.java index 7c3db386c..2dd458a46 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/Parameters.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/Parameters.java @@ -1,8 +1,7 @@ /* * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. + * + * Distributable under LGPL license. See terms of license at gnu.org. */ package net.java.sip.communicator.plugin.spellcheck; @@ -20,27 +19,32 @@ /** * Information provided via the spellchecer's xml parameters. - * + * * @author Damian Johnson */ class Parameters { private static final Logger logger = Logger.getLogger(Parameters.class); - private static final String RESOURCE_LOC - = "/resources/config/spellcheck/parameters.xml"; + private static final String RESOURCE_LOC = + "resources/config/spellcheck/parameters.xml"; + private static final String NODE_DEFAULTS = "defaults"; + private static final String NODE_LOCALES = "locales"; - private static final HashMap DEFAULTS - = new HashMap (); - private static final ArrayList LOCALES = new ArrayList (); + + private static final HashMap DEFAULTS = + new HashMap(); + + private static final ArrayList LOCALES = new ArrayList(); static { try { - URL url = SpellCheckActivator.bundleContext - .getBundle().getResource(RESOURCE_LOC); + URL url = + SpellCheckActivator.bundleContext.getBundle().getResource( + RESOURCE_LOC); InputStream stream = url.openStream(); @@ -48,10 +52,10 @@ class Parameters throw new IOException(); // strict parsing options - DocumentBuilderFactory factory - = DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); - factory.setValidating(true); + factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); @@ -103,7 +107,7 @@ else if (node.getNodeName().equals(NODE_LOCALES)) /** * Retrieves default values from xml. - * + * * @param list the configuration list */ private static void parseDefaults(NodeList list) @@ -128,6 +132,7 @@ private static void parseDefaults(NodeList list) /** * Populates LOCALES list with contents of xml. + * * @param list the configuration list */ private static void parseLocales(NodeList list) @@ -138,10 +143,9 @@ private static void parseLocales(NodeList list) NamedNodeMap attributes = node.getAttributes(); String label = ((Attr) attributes.getNamedItem("label")).getValue(); String code = - ((Attr) attributes.getNamedItem("isoCode")).getValue(); + ((Attr) attributes.getNamedItem("isoCode")).getValue(); String dictLocation = - ((Attr) attributes.getNamedItem("dictionaryUrl")) - .getValue(); + ((Attr) attributes.getNamedItem("dictionaryUrl")).getValue(); try { LOCALES.add(new Locale(label, code, new URL(dictLocation))); @@ -149,13 +153,14 @@ private static void parseLocales(NodeList list) catch (MalformedURLException exc) { logger.warn("Unable to parse dictionary location of " + label - + " (" + dictLocation + ")", exc); + + " (" + dictLocation + ")", exc); } } } /** * Provides the value of a particular default field, null if undefined. + * * @param field default field to retrieve * @return value corresponding to default field */ @@ -166,6 +171,7 @@ public static String getDefault(Default field) /** * Provides locale with a given iso code. Null if undefined. + * * @param isoCode iso code of locale to be retrieved * @return locale with corresponding iso code */ @@ -173,7 +179,8 @@ public static Locale getLocale(String isoCode) { for (Locale locale : LOCALES) { - if (locale.getIsoCode().equals(isoCode)) return locale; + if (locale.getIsoCode().equals(isoCode)) + return locale; } return null; @@ -181,11 +188,12 @@ public static Locale getLocale(String isoCode) /** * Provides locales in which dictionary resources are available. + * * @return locations with dictionary resources */ - public static ArrayList getLocales() + public static ArrayList getLocales() { - return new ArrayList (LOCALES); + return new ArrayList(LOCALES); } /** @@ -194,9 +202,13 @@ public static ArrayList getLocales() public static class Locale { private final String label; + private final String isoCode; + private final URL dictLocation; + private boolean isLoading = false; + private Locale(String label, String isoCode, URL dictLocation) { this.label = label; @@ -206,6 +218,7 @@ private Locale(String label, String isoCode, URL dictLocation) /** * Provides user readable name of language. + * * @return name of language presented to user */ public String getLabel() @@ -216,6 +229,7 @@ public String getLabel() /** * Provides ISO code as defined by:
* http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + * * @return iso code */ public String getIsoCode() @@ -226,6 +240,7 @@ public String getIsoCode() /** * Provides the url where the dictionary resource can be found for this * language. + * * @return url of dictionary resource */ public URL getDictUrl() @@ -233,6 +248,29 @@ public URL getDictUrl() return this.dictLocation; } + /** + * Sets the loading property. Indicates if this locale is currently + * loaded in the list. + * + * @param loading indicates if this locale is currently loading in the + * locales list + */ + public void setLoading(boolean loading) + { + this.isLoading = loading; + } + + /** + * Indicates if this locale is currenly loading in the list of locales. + * + * @return true if the locale is loading, false - + * otherwise + */ + public boolean isLoading() + { + return isLoading; + } + @Override public String toString() { @@ -256,6 +294,7 @@ public enum Default /** * Returns the enum representation of a string. This is case sensitive. + * * @param str toString representation of a default field * @return default field associated with a string * @throws IllegalArgumentException if argument is not represented by a diff --git a/src/net/java/sip/communicator/plugin/spellcheck/Resources.java b/src/net/java/sip/communicator/plugin/spellcheck/Resources.java index 61da50e16..e7a5cc2b5 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/Resources.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/Resources.java @@ -1,8 +1,7 @@ /* * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. + * + * Distributable under LGPL license. See terms of license at gnu.org. */ package net.java.sip.communicator.plugin.spellcheck; @@ -20,7 +19,7 @@ /** * The Resources class manages the access to the internationalization * properties files and the image resources used in this plugin. - * + * * @author Damian Johnson * @author Yana Stamcheva */ @@ -34,40 +33,38 @@ public class Resources * Location of flag resources. */ private static final String FLAG_PATH = - "resources/images/plugin/spellchecker/flags/"; + "resources/images/plugin/spellcheck/flags/"; /** * The spell check plugin icon, shown in the configuration form. */ - public static final String PLUGIN_ICON - = "plugin.spellcheck.PLUGIN_ICON"; + public static final String PLUGIN_ICON = "plugin.spellcheck.PLUGIN_ICON"; /** * The add word icon. */ - public static final String ADD_WORD_ICON - = "plugin.spellcheck.ADD_WORD_ICON"; + public static final String ADD_WORD_ICON = + "plugin.spellcheck.ADD_WORD_ICON"; /** * The personal dictionary icon. */ - public static final String PERSONAL_DICTIONARY - = "plugin.spellcheck.PERSONAL_DIR"; + public static final String PERSONAL_DICTIONARY = + "plugin.spellcheck.PERSONAL_DIR"; /** * The word include icon. */ - public static final String WORD_INCLUDE - = "plugin.spellcheck.WORD_INCLUDE"; + public static final String WORD_INCLUDE = "plugin.spellcheck.WORD_INCLUDE"; /** * The word exclude icon. */ - public static final String WORD_EXCLUDE - = "plugin.spellcheck.WORD_EXCLUDE"; + public static final String WORD_EXCLUDE = "plugin.spellcheck.WORD_EXCLUDE"; /** * Returns an internationalized string corresponding to the given key. + * * @param key The key of the string. * @return An internationalized string corresponding to the given key. */ @@ -78,6 +75,7 @@ public static String getString(String key) /** * Loads an image from a given image identifier. + * * @param imageID The identifier of the image. * @return The image for the given identifier. */ @@ -88,7 +86,7 @@ public static ImageIcon getImage(String imageID) /** * Loads a flag image from a given image identifier. - * + * * @param resource iso code for flag to be retrieved. * @return icon reflecting iso code * @throws IOException if no such resource is available @@ -108,6 +106,7 @@ public static ImageIcon getFlagImage(String resource) throws IOException /** * Loads an image from a given image identifier. + * * @param imageID The identifier of the image. * @return The image for the given identifier. */ @@ -119,21 +118,21 @@ public static byte[] getImageInBytes(String imageID) /** * Returns the ResourceManagementService through which we obtain * resources like images and localized texts. - * + * * @return the ResourceManagementService */ - private static ResourceManagementService getResources() + public static ResourceManagementService getResources() { if (resourceService != null) return resourceService; - ServiceReference configServiceRef - = SpellCheckActivator.bundleContext.getServiceReference( - ResourceManagementService.class.getName()); + ServiceReference configServiceRef = + SpellCheckActivator.bundleContext + .getServiceReference(ResourceManagementService.class.getName()); - resourceService - = (ResourceManagementService) SpellCheckActivator.bundleContext - .getService(configServiceRef); + resourceService = + (ResourceManagementService) SpellCheckActivator.bundleContext + .getService(configServiceRef); return resourceService; } diff --git a/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckActivator.java b/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckActivator.java index dd0b4140f..f4427b895 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckActivator.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckActivator.java @@ -1,11 +1,12 @@ /* * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. + * + * Distributable under LGPL license. See terms of license at gnu.org. */ package net.java.sip.communicator.plugin.spellcheck; +import java.util.*; + import net.java.sip.communicator.service.configuration.*; import net.java.sip.communicator.service.fileaccess.*; import net.java.sip.communicator.service.gui.*; @@ -14,7 +15,7 @@ /** * Enabling and disabling osgi functionality for the spell checker. - * + * * @author Damian Johnson */ public class SpellCheckActivator @@ -32,38 +33,30 @@ public class SpellCheckActivator /** * Called when this bundle is started. + * * @param context The execution context of the bundle being started. */ public void start(BundleContext context) throws Exception { bundleContext = context; -// Disables spell checker until we fix also the configuration form in -// order to be able to change the language. -// -// this.checker.start(context); - -// SpellCheckerConfigForm checkerManager = -// new SpellCheckerConfigForm(this.checker); -// context.registerService(ConfigurationForm.class.getName(), -// checkerManager, null); -// -// // adds button to toggle spell checker -// Hashtable containerFilter = -// new Hashtable (); -// containerFilter.put(Container.CONTAINER_ID, -// Container.CONTAINER_CHAT_TOOL_BAR.getID()); -// context.registerService(PluginComponent.class.getName(), -// new CheckerToggleButton(this.checker), containerFilter); -// -// // adds field to change language -// context.registerService(PluginComponent.class.getName(), -// LanguageSelectionField.makeSelectionField(this.checker), -// containerFilter); + this.checker.start(context); + + // adds button to toggle spell checker + Hashtable containerFilter = + new Hashtable(); + containerFilter.put(Container.CONTAINER_ID, + Container.CONTAINER_CHAT_TOOL_BAR.getID()); + + // adds field to change language + context.registerService(PluginComponent.class.getName(), + LanguageSelectionField.makeSelectionField(this.checker), + containerFilter); } /** * Returns the UIService. + * * @return the UIService */ public static UIService getUIService() @@ -72,8 +65,8 @@ public static UIService getUIService() return uiService; // retrieves needed services - ServiceReference uiServiceRef - = bundleContext.getServiceReference(UIService.class.getName()); + ServiceReference uiServiceRef = + bundleContext.getServiceReference(UIService.class.getName()); uiService = (UIService) bundleContext.getService(uiServiceRef); @@ -82,6 +75,7 @@ public static UIService getUIService() /** * Returns the FileAccessService. + * * @return the FileAccessService */ public static FileAccessService getFileAccessService() @@ -89,17 +83,18 @@ public static FileAccessService getFileAccessService() if (faService != null) return faService; - ServiceReference faServiceReference - = bundleContext.getServiceReference( - FileAccessService.class.getName()); - faService - = (FileAccessService) bundleContext.getService(faServiceReference); + ServiceReference faServiceReference = + bundleContext + .getServiceReference(FileAccessService.class.getName()); + faService = + (FileAccessService) bundleContext.getService(faServiceReference); return faService; } /** * Returns the ConfigurationService. + * * @return the ConfigurationService */ public static ConfigurationService getConfigService() @@ -107,12 +102,12 @@ public static ConfigurationService getConfigService() if (configService != null) return configService; - ServiceReference configServiceRef - = bundleContext.getServiceReference( - ConfigurationService.class.getName()); + ServiceReference configServiceRef = + bundleContext.getServiceReference(ConfigurationService.class + .getName()); - configService = (ConfigurationService) bundleContext - .getService(configServiceRef); + configService = + (ConfigurationService) bundleContext.getService(configServiceRef); return configService; } diff --git a/src/net/java/sip/communicator/plugin/spellcheck/SpellChecker.java b/src/net/java/sip/communicator/plugin/spellcheck/SpellChecker.java index 813880625..24f253e66 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/SpellChecker.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/SpellChecker.java @@ -1,8 +1,7 @@ /* * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. + * + * Distributable under LGPL license. See terms of license at gnu.org. */ package net.java.sip.communicator.plugin.spellcheck; @@ -22,7 +21,7 @@ * Model for spell checking capabilities. This allows for the on-demand * retrieval of dictionaries in other languages which are cached with the user's * configurations. - * + * * @author Damian Johnson */ class SpellChecker @@ -30,12 +29,12 @@ class SpellChecker { private static final Logger logger = Logger.getLogger(SpellChecker.class); - private static final String LOCALE_CONFIG_PARAM - = "net.java.sip.communicator.plugin.spellchecker.LOCALE"; + private static final String LOCALE_CONFIG_PARAM = + "net.java.sip.communicator.plugin.spellchecker.LOCALE"; // default bundled dictionary - private static final String DEFAULT_DICT_PATH - = "/resources/config/spellcheck/en_US.zip"; + private static final String DEFAULT_DICT_PATH = + "/resources/config/spellcheck/"; // location where dictionaries are stored private static final String DICT_DIR = "spellingDictionaries/"; @@ -50,24 +49,29 @@ class SpellChecker * will cause an internal NullPointerException in the spell checker. */ private File personalDictLocation; + private File dictLocation; + private SpellDictionary dict; + private Parameters.Locale locale; // dictionary locale // chat instances the spell checker is currently attached to - private ArrayList attachedChats = - new ArrayList (); + private ArrayList attachedChats = + new ArrayList(); + private boolean isEnabled = true; /** * Associates spell checking capabilities with all chats. This doesn't do * anything if this is already running. + * * @param bc execution context of the bundle */ synchronized void start(BundleContext bc) throws Exception { - FileAccessService faService - = SpellCheckActivator.getFileAccessService(); + FileAccessService faService = + SpellCheckActivator.getFileAccessService(); // checks if DICT_DIR exists to see if this is the first run File dictionaryDir = faService.getPrivatePersistentFile(DICT_DIR); @@ -76,33 +80,44 @@ synchronized void start(BundleContext bc) throws Exception { dictionaryDir.mkdir(); - // copy default dictionary so it doesn't need to be downloaded - URL dictURL = SpellCheckActivator.bundleContext - .getBundle().getResource(DEFAULT_DICT_PATH); + // copy default dictionaries so they don't need to be downloaded + @SuppressWarnings ("unchecked") + Enumeration dictUrls + = SpellCheckActivator.bundleContext.getBundle() + .findEntries(DEFAULT_DICT_PATH, + "*.zip", + false); - if (dictURL != null) + if (dictUrls != null) { - InputStream source = dictURL.openStream(); + while (dictUrls.hasMoreElements()) + { + URL dictUrl = dictUrls.nextElement(); - int filenameStart = DEFAULT_DICT_PATH.lastIndexOf('/') + 1; - String filename = DEFAULT_DICT_PATH.substring(filenameStart); - File dictLocation - = faService.getPrivatePersistentFile(DICT_DIR + filename); - copyDictionary(source, dictLocation); + InputStream source = dictUrl.openStream(); + + int filenameStart = dictUrl.getPath().lastIndexOf('/') + 1; + String filename = dictUrl.getPath().substring(filenameStart); + + File dictLocation = + faService.getPrivatePersistentFile(DICT_DIR + filename); + + copyDictionary(source, dictLocation); + } } } // gets resource for personal dictionary - this.personalDictLocation - = faService.getPrivatePersistentFile(DICT_DIR + PERSONAL_DICT_NAME); + this.personalDictLocation = + faService.getPrivatePersistentFile(DICT_DIR + PERSONAL_DICT_NAME); if (!personalDictLocation.exists()) personalDictLocation.createNewFile(); // gets dictionary locale - String localeIso - = SpellCheckActivator.getConfigService() - .getString(LOCALE_CONFIG_PARAM); + String localeIso = + SpellCheckActivator.getConfigService().getString( + LOCALE_CONFIG_PARAM); if (localeIso == null) { @@ -114,8 +129,9 @@ synchronized void start(BundleContext bc) throws Exception } Parameters.Locale tmp = Parameters.getLocale(localeIso); - if (tmp == null) throw new Exception( - "No dictionary resources defined for locale: " + localeIso); + if (tmp == null) + throw new Exception("No dictionary resources defined for locale: " + + localeIso); this.locale = tmp; // needed for synchronization lock setLocale(tmp); // initializes dictionary and saves locale config @@ -169,18 +185,19 @@ public void chatCreated(Chat chat) /** * Provides the user's list of words to be ignored by the spell checker. + * * @return user's word list */ - ArrayList getPersonalWords() + ArrayList getPersonalWords() { synchronized (this.personalDictLocation) { try { // Retrieves contents of the custom dictionary - ArrayList customWords = new ArrayList (); + ArrayList customWords = new ArrayList(); Scanner customDictScanner = - new Scanner(this.personalDictLocation); + new Scanner(this.personalDictLocation); while (customDictScanner.hasNextLine()) { customWords.add(customDictScanner.nextLine()); @@ -191,7 +208,7 @@ ArrayList getPersonalWords() catch (FileNotFoundException exc) { logger.error("Unable to read custom dictionary", exc); - return new ArrayList (); + return new ArrayList(); } } } @@ -199,9 +216,10 @@ ArrayList getPersonalWords() /** * Writes custom dictionary and updates spell checker to utilize new * listing. + * * @param words words to be ignored by the spell checker */ - void setPersonalWords(List words) + void setPersonalWords(List words) { synchronized (this.personalDictLocation) { @@ -209,8 +227,8 @@ void setPersonalWords(List words) { // writes new word list BufferedWriter writer = - new BufferedWriter(new FileWriter( - this.personalDictLocation)); + new BufferedWriter( + new FileWriter(this.personalDictLocation)); for (String customWord : words) { @@ -224,10 +242,10 @@ void setPersonalWords(List words) synchronized (this.attachedChats) { InputStream dictInput = - new FileInputStream(this.dictLocation); + new FileInputStream(this.dictLocation); this.dict = - new OpenOfficeSpellDictionary(dictInput, - this.personalDictLocation); + new OpenOfficeSpellDictionary(dictInput, + this.personalDictLocation); // updates chats for (ChatAttachments chat : this.attachedChats) @@ -239,7 +257,7 @@ void setPersonalWords(List words) catch (IOException exc) { logger.error("Unable to access personal spelling dictionary", - exc); + exc); } } } @@ -247,6 +265,7 @@ void setPersonalWords(List words) /** * Provides the locale of the dictionary currently being used by the spell * checker. + * * @return locale of current dictionary */ Parameters.Locale getLocale() @@ -261,6 +280,7 @@ Parameters.Locale getLocale() * Resets spell checker to use a different locale's dictionary. This uses * the local copy of the dictionary if available, otherwise it's downloaded * and saved for future use. + * * @param locale locale of dictionary to be used * @throws Exception problem occurring in utilizing locale's dictionary */ @@ -273,31 +293,30 @@ void setLocale(Parameters.Locale locale) throws Exception int filenameStart = path.lastIndexOf('/') + 1; String filename = path.substring(filenameStart); - File dictLocation - = SpellCheckActivator.getFileAccessService() + File dictLocation = + SpellCheckActivator.getFileAccessService() .getPrivatePersistentFile(DICT_DIR + filename); // downloads dictionary if unavailable (not cached) if (!dictLocation.exists()) - copyDictionary(locale.getDictUrl() - .openStream(), dictLocation); + copyDictionary(locale.getDictUrl().openStream(), dictLocation); // resets dictionary being used to include changes synchronized (this.attachedChats) { InputStream dictInput = new FileInputStream(dictLocation); - SpellDictionary dict - = new OpenOfficeSpellDictionary(dictInput, - this.personalDictLocation); + SpellDictionary dict = + new OpenOfficeSpellDictionary(dictInput, + this.personalDictLocation); this.dict = dict; this.dictLocation = dictLocation; this.locale = locale; // saves locale choice to configuration properties - SpellCheckActivator.getConfigService() - .setProperty(LOCALE_CONFIG_PARAM, locale.getIsoCode()); + SpellCheckActivator.getConfigService().setProperty( + LOCALE_CONFIG_PARAM, locale.getIsoCode()); // updates chats for (ChatAttachments chat : this.attachedChats) @@ -310,6 +329,7 @@ void setLocale(Parameters.Locale locale) throws Exception /** * Determines if locale's dictionary is locally available or not. + * * @param locale locale to be checked * @return true if local resources for dictionary are available and * accessible, false otherwise @@ -321,8 +341,8 @@ boolean isLocaleAvailable(Parameters.Locale locale) String filename = path.substring(filenameStart); try { - File dictLocation - = SpellCheckActivator.getFileAccessService() + File dictLocation = + SpellCheckActivator.getFileAccessService() .getPrivatePersistentFile(DICT_DIR + filename); return dictLocation.exists(); @@ -358,7 +378,8 @@ void setEnabled(boolean enable) // copies dictionary to appropriate location, closing the stream afterward private void copyDictionary(InputStream input, File dest) - throws IOException, FileNotFoundException + throws IOException, + FileNotFoundException { byte[] buf = new byte[1024]; FileOutputStream output = new FileOutputStream(dest); @@ -377,6 +398,7 @@ private void copyDictionary(InputStream input, File dest) * Determines if spell checker dictionary works. Backend API often fails * when used so this tests that the current dictionary is able to process * words. + * * @return true if current dictionary can check words, false otherwise */ private boolean isDictionaryValid(SpellDictionary dict) diff --git a/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckerConfigDialog.java b/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckerConfigDialog.java new file mode 100644 index 000000000..266be20b5 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/spellcheck/SpellCheckerConfigDialog.java @@ -0,0 +1,411 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. See terms of license at gnu.org. + */ +package net.java.sip.communicator.plugin.spellcheck; + +import java.awt.*; +import java.awt.event.*; +import java.util.List; + +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +import org.dts.spell.dictionary.*; + +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.util.Logger; +import net.java.sip.communicator.util.swing.*; + +/** + * The spell check dialog that would be opened from the right click menu in the + * chat window. + * + * @author Purvesh Sahoo + */ +public class SpellCheckerConfigDialog + extends SIPCommDialog + implements ActionListener +{ + private static final Logger logger = Logger + .getLogger(SpellCheckerConfigDialog.class); + + /** + * UI Components + */ + private JTextComponent currentWord; + + private JList suggestionList; + + private JScrollPane suggestionScroll; + + private JButton changeButton; + + private JButton nextButton; + + private JButton addButton; + + private JPanel checkPanel; + + private JPanel buttonsPanel; + + private JPanel topPanel; + + private JPanel suggestionPanel; + + private SpellDictionary dict; + + private Chat chat; + + private final ResourceManagementService resources = Resources + .getResources(); + + private String word; + + private int index; + + private Word clickedWord; + + public SpellCheckerConfigDialog(Chat chat, Word clickedWord, + SpellDictionary dict) + { + + super(false); + + this.dict = dict; + this.chat = chat; + + initComponents(clickedWord); + + this.setTitle(resources.getI18NString("plugin.spellcheck.TITLE")); + this.setMinimumSize(new Dimension(450, 320)); + this.setPreferredSize(new Dimension(450, 320)); + this.setResizable(false); + + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + mainPanel.add(topPanel); + + this.getContentPane().add(mainPanel); + + this.pack(); + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Dimension screenSize = toolkit.getScreenSize(); + + int x = (screenSize.width - this.getWidth()) / 2; + int y = (screenSize.height - this.getHeight()) / 2; + + this.setLocation(x, y); + + if (!currentWord.getText().equals(" ") + && this.dict.isCorrect(currentWord.getText())) + { + nextButton.doClick(); + } + } + + /** + * Initialises the UI components. + */ + private void initComponents(final Word clickWord) + { + + clickedWord = + (clickWord == null) ? Word.getWord(" ", 1, false) : clickWord; + + topPanel = new TransparentPanel(new BorderLayout()); + topPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8)); + topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS)); + + checkPanel = new TransparentPanel(new BorderLayout(10, 10)); + checkPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8)); + checkPanel.setLayout(new BoxLayout(checkPanel, BoxLayout.X_AXIS)); + + currentWord = new JTextField(clickedWord.getText()); + + currentWord.setAlignmentX(LEFT_ALIGNMENT); + currentWord.setMaximumSize(new Dimension(550, 30)); + + currentWord.setText(clickedWord.getText()); + currentWord.selectAll(); + + // JPanel wordPanel = new TransparentPanel(new BorderLayout()); + // wordPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8)); + // wordPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 5)); + // wordPanel.add(currentWord); + + buttonsPanel = + new TransparentPanel(new FlowLayout(FlowLayout.RIGHT, 0, 10)); + changeButton = + new JButton( + resources.getI18NString("plugin.spellcheck.dialog.REPLACE")); + changeButton.setMnemonic(resources + .getI18nMnemonic("plugin.spellcheck.dialog.REPLACE")); + + changeButton.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + if (suggestionList.getSelectedValue() != null) + { + + StringBuffer newMessage = + new StringBuffer(chat.getMessage()); + int endIndex; + + if (word != null) + { + endIndex = index + currentWord.getText().length(); + newMessage.replace(index, endIndex, + (String) suggestionList.getSelectedValue()); + word = (String) suggestionList.getSelectedValue(); + } + else + { + endIndex = + clickedWord.getStart() + + clickedWord.getText().length(); + newMessage.replace(clickedWord.getStart(), endIndex, + (String) suggestionList.getSelectedValue()); + } + currentWord.setText((String) suggestionList + .getSelectedValue()); + chat.setMessage(newMessage.toString()); + + } + } + }); + changeButton.setEnabled(false); + + nextButton = + new JButton( + resources.getI18NString("plugin.spellcheck.dialog.FIND")); + nextButton.setMnemonic(resources + .getI18nMnemonic("plugin.spellcheck.dialog.FIND")); + + nextButton.addActionListener(new ActionListener() + { + + public Word getNextWord() + { + + Word nextWord; + int wordIndex; + + if (word == null) + { + if (currentWord.getText().equals(" ")) + { + String words[] = chat.getMessage().split(" "); + currentWord.setText(words[0]); + + } + + wordIndex = + chat.getMessage().indexOf(currentWord.getText()); + if (dict.isCorrect(currentWord.getText())) + currentWord.setText(""); + } + else + { + wordIndex = chat.getMessage().indexOf(word, index); + } + + Word presentWord = + Word.getWord(chat.getMessage(), wordIndex, false); + + if (presentWord.getEnd() == chat.getMessage().length()) + { + nextWord = Word.getWord(chat.getMessage(), 1, false); + + } + else + { + nextWord = + Word.getWord(chat.getMessage(), + presentWord.getEnd() + 1, false); + } + + index = nextWord.getStart(); + word = nextWord.getText(); + + return nextWord; + } + + public void actionPerformed(ActionEvent e) + { + Word nextWord = getNextWord(); + int breakIndex = nextWord.getStart(); + + while (dict.isCorrect(nextWord.getText()) + && nextWord.getEnd() + 1 != breakIndex) + { + nextWord = getNextWord(); + + } + + if (!dict.isCorrect(nextWord.getText())) + { + word = nextWord.getText(); + currentWord.setText(nextWord.getText()); + + String clickedWord = currentWord.getText(); + setSuggestionModel(clickedWord); + } + + } + }); + + buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.Y_AXIS)); + buttonsPanel.add(changeButton); + buttonsPanel.add(nextButton); + + checkPanel.add(currentWord, BorderLayout.NORTH); + checkPanel.add(Box.createHorizontalStrut(10)); + checkPanel.add(buttonsPanel, BorderLayout.EAST); + + topPanel.add(checkPanel, BorderLayout.NORTH); + topPanel.add(Box.createVerticalStrut(10)); + + DefaultListModel dataModel = new DefaultListModel(); + suggestionList = new JList(dataModel); + + suggestionScroll = new JScrollPane(suggestionList); + suggestionScroll.setAlignmentX(LEFT_ALIGNMENT); + + if (!dict.isCorrect(clickedWord.getText())) + setSuggestionModel(clickedWord.getText()); + + suggestionList.addListSelectionListener(new ListSelectionListener() + { + + public void valueChanged(ListSelectionEvent e) + { + + if (!e.getValueIsAdjusting()) + { + changeButton.setEnabled(true); + } + } + }); + + MouseListener clickListener = new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + if (e.getClickCount() == 2) + { + + StringBuffer newMessage = + new StringBuffer(chat.getMessage()); + int endIndex; + + if (word != null) + { + endIndex = index + currentWord.getText().length(); + newMessage.replace(index, endIndex, + (String) suggestionList.getSelectedValue()); + word = (String) suggestionList.getSelectedValue(); + } + else + { + endIndex = + clickedWord.getStart() + + clickedWord.getText().length(); + newMessage.replace(clickedWord.getStart(), endIndex, + (String) suggestionList.getSelectedValue()); + } + currentWord.setText((String) suggestionList + .getSelectedValue()); + chat.setMessage(newMessage.toString()); + + } + } + }; + + suggestionList.addMouseListener(clickListener); + + addButton = + new JButton(resources.getI18NString("plugin.spellcheck.dialog.ADD")); + addButton.setMnemonic(resources + .getI18nMnemonic("plugin.spellcheck.dialog.ADD")); + + addButton.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + + try + { + dict.addWord(currentWord.getText()); + chat.promptRepaint(); + } + catch (SpellDictionaryException exc) + { + String msg = "Unable to add word to personal dictionary"; + logger.error(msg, exc); + } + } + }); + + suggestionPanel = new TransparentPanel(new BorderLayout(10, 10)); + suggestionPanel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8)); + suggestionPanel.setLayout(new BoxLayout(suggestionPanel, + BoxLayout.X_AXIS)); + suggestionPanel.add(suggestionScroll); + suggestionPanel.add(Box.createHorizontalStrut(10)); + suggestionPanel.add(addButton); + + topPanel.add(suggestionPanel, BorderLayout.SOUTH); + + } + + /** + * Sets the model for the suggestion list + * + * @param clickedWord + */ + private void setSuggestionModel(String clickedWord) + { + + DefaultListModel dataModel = new DefaultListModel(); + List corrections = this.dict.getSuggestions(clickedWord); + for (String correction : corrections) + { + dataModel.addElement(correction); + } + + suggestionList.setModel(dataModel); + } + + /** + * Returns the selected correction value + * + * @return selected value from suggestion list + */ + public Object getCorrection() + { + + return suggestionList.getSelectedValue(); + } + + public void actionPerformed(ActionEvent e) + { + // TODO Auto-generated method stub + + } + + @Override + protected void close(boolean escaped) + { + // TODO Auto-generated method stub + + } + +} diff --git a/src/net/java/sip/communicator/plugin/spellcheck/Word.java b/src/net/java/sip/communicator/plugin/spellcheck/Word.java index e7a882aea..b2e2b40dc 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/Word.java +++ b/src/net/java/sip/communicator/plugin/spellcheck/Word.java @@ -1,35 +1,40 @@ /* * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. - * - * Distributable under LGPL license. - * See terms of license at gnu.org. + * + * Distributable under LGPL license. See terms of license at gnu.org. */ package net.java.sip.communicator.plugin.spellcheck; import java.text.BreakIterator; /** - * Immutable representation of a word in the context of a document, bundling - * the bounds with the text. + * Immutable representation of a word in the context of a document, bundling the + * bounds with the text. + * * @author Damian Johnson */ class Word { - private static final BreakIterator WORD_ITR - = BreakIterator.getWordInstance(); + private static final BreakIterator WORD_ITR = BreakIterator + .getWordInstance(); + private final int start; + private final String text; + + private final int end; /** * Provides the word before or after a given index. No bounds checking is * performed. + * * @param text text to be checked * @param index index in which to begin search (inclusive) * @param before search is before index if true, after otherwise * @return index of word boundary */ public static synchronized Word getWord(String text, int index, - boolean before) + boolean before) { int start, end; WORD_ITR.setText(text); @@ -38,21 +43,24 @@ public static synchronized Word getWord(String text, int index, { start = WORD_ITR.preceding(index); end = WORD_ITR.next(); - if (start == BreakIterator.DONE) start = 0; + if (start == BreakIterator.DONE) + start = 0; } else { end = WORD_ITR.following(index); start = WORD_ITR.previous(); - if (end == BreakIterator.DONE) end = text.length() - 1; + if (end == BreakIterator.DONE) + end = text.length() - 1; } - return new Word(start, text.substring(start, end)); + return new Word(start, end, text.substring(start, end)); } - private Word(int start, String text) + private Word(int start, int end, String text) { this.start = start; + this.end = end; this.text = text; } @@ -61,6 +69,11 @@ public int getStart() return this.start; } + public int getEnd() + { + return this.end; + } + public String getText() { return this.text; diff --git a/src/net/java/sip/communicator/plugin/spellcheck/spellCheck.manifest.mf b/src/net/java/sip/communicator/plugin/spellcheck/spellCheck.manifest.mf index e06daecf8..cf41de6f2 100644 --- a/src/net/java/sip/communicator/plugin/spellcheck/spellCheck.manifest.mf +++ b/src/net/java/sip/communicator/plugin/spellcheck/spellCheck.manifest.mf @@ -10,9 +10,13 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.gui, net.java.sip.communicator.service.gui.event, net.java.sip.communicator.service.resources, + net.java.sip.communicator.util.swing, + net.java.sip.communicator.service.contactlist, + net.java.sip.communicator.service.protocol, javax.swing, javax.swing.event, javax.swing.text, + javax.swing.text.html, javax.imageio, javax.xml.parsers, org.w3c.dom,