[425633] Machine translation suggestions for Babel editor
diff --git a/org.eclipse.babel.editor/.classpath b/org.eclipse.babel.editor/.classpath
index 0b1bcf9..c72d35a 100644
--- a/org.eclipse.babel.editor/.classpath
+++ b/org.eclipse.babel.editor/.classpath
@@ -2,6 +2,6 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src/"/>
+ <classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>
diff --git a/org.eclipse.babel.editor/META-INF/MANIFEST.MF b/org.eclipse.babel.editor/META-INF/MANIFEST.MF
index 50521ea..d273d9a 100644
--- a/org.eclipse.babel.editor/META-INF/MANIFEST.MF
+++ b/org.eclipse.babel.editor/META-INF/MANIFEST.MF
@@ -36,6 +36,11 @@
org.eclipse.babel.editor.api,
org.eclipse.babel.editor.util,
org.eclipse.babel.editor.widgets,
+ org.eclipse.babel.editor.widgets.suggestion,
+ org.eclipse.babel.editor.widgets.suggestion.exception,
+ org.eclipse.babel.editor.widgets.suggestion.model,
+ org.eclipse.babel.editor.widgets.suggestion.provider,
org.eclipse.babel.editor.wizards
Import-Package: org.eclipse.ui.forms.widgets
Bundle-ActivationPolicy: lazy
+Bundle-ClassPath: .
diff --git a/org.eclipse.babel.editor/build.properties b/org.eclipse.babel.editor/build.properties
index fdad6ae..2b0454a 100644
--- a/org.eclipse.babel.editor/build.properties
+++ b/org.eclipse.babel.editor/build.properties
@@ -3,11 +3,9 @@
bin.includes = META-INF/,\
.,\
plugin.xml,\
- icons/,\
plugin.properties,\
messages.properties,\
- CHANGES
+ CHANGES,\
+ icons/
src.includes = icons/,\
- messages.properties,\
- plugin.properties,\
- plugin.xml
+ messages.properties
diff --git a/org.eclipse.babel.editor/icons/ajax-loader.gif b/org.eclipse.babel.editor/icons/ajax-loader.gif
new file mode 100644
index 0000000..4d5b583
--- /dev/null
+++ b/org.eclipse.babel.editor/icons/ajax-loader.gif
Binary files differ
diff --git a/org.eclipse.babel.editor/icons/sample.gif b/org.eclipse.babel.editor/icons/sample.gif
new file mode 100644
index 0000000..34fb3c9
--- /dev/null
+++ b/org.eclipse.babel.editor/icons/sample.gif
Binary files differ
diff --git a/org.eclipse.babel.editor/plugin.xml b/org.eclipse.babel.editor/plugin.xml
index 5d6d349..4aae8f4 100644
--- a/org.eclipse.babel.editor/plugin.xml
+++ b/org.eclipse.babel.editor/plugin.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
+ <extension-point id="org.eclipse.babel.editor.suggestion" name="Suggestion Provider Extension Point" schema="schema/org.eclipse.babel.editor.suggestion.exsd"/>
<extension
point="org.eclipse.ui.editors">
<editor
diff --git a/org.eclipse.babel.editor/schema/org.eclipse.babel.editor.suggestion.exsd b/org.eclipse.babel.editor/schema/org.eclipse.babel.editor.suggestion.exsd
new file mode 100644
index 0000000..08b72c3
--- /dev/null
+++ b/org.eclipse.babel.editor/schema/org.eclipse.babel.editor.suggestion.exsd
@@ -0,0 +1,102 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.babel.editor" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.babel.editor" id="org.eclipse.babel.editor.suggestion" name="Suggestion Provider Extension Point"/>
+ </appInfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <choice minOccurs="1" maxOccurs="unbounded">
+ <element ref="client"/>
+ </choice>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="client">
+ <complexType>
+ <attribute name="class" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.babel.editor.widgets.suggestion.provider.ISuggestionProvider"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiinfo"/>
+ </appInfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/i18n/AbstractI18NEntry.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/i18n/AbstractI18NEntry.java
index f05d8b5..38b2bc5 100755
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/i18n/AbstractI18NEntry.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/i18n/AbstractI18NEntry.java
@@ -8,6 +8,7 @@
* Contributors:
* Pascal Essiembre - initial API and implementation
* Alexej Strelzow - updateKey
+ * Samir Soyer - passing Locale to NullableText
******************************************************************************/
package org.eclipse.babel.editor.i18n;
@@ -32,6 +33,8 @@
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.layout.GridData;
@@ -182,7 +185,7 @@
*/
private void createTextbox() {
textBox = new NullableText(this, SWT.MULTI | SWT.WRAP | SWT.H_SCROLL
- | SWT.V_SCROLL | SWT.BORDER);
+ | SWT.V_SCROLL | SWT.BORDER, locale);
textBox.setEnabled(false);
textBox.setOrientation(UIUtils.getOrientation(locale));
@@ -212,8 +215,17 @@
}
});
- // Handle dirtyness
- textBox.addKeyListener(getKeyListener());
+ // Handle dirtyness
+ textBox.addKeyListener(getKeyListener());
+ textBox.getTextBox().addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ if (textBox.isDirty()) {
+ updateModel();
+ textBox.setDirty(false);
+ }
+ }
+ });
editor.addChangeListener(msgEditorUpdateKey);
}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/plugin/Startup.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/plugin/Startup.java
index 2d43da3..fa99b05 100644
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/plugin/Startup.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/plugin/Startup.java
@@ -10,6 +10,7 @@
******************************************************************************/
package org.eclipse.babel.editor.plugin;
+import org.eclipse.babel.editor.widgets.suggestion.lookup.SuggestionProviderLoader;
import org.eclipse.ui.IStartup;
/**
@@ -22,6 +23,7 @@
* @see org.eclipse.ui.IStartup#earlyStartup()
*/
public void earlyStartup() {
+ SuggestionProviderLoader.registerProviders();
// done.
// System.out.println("Starting up. "
// + "TODO: Register nature with every project and listen for new "
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/NullableText.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/NullableText.java
index f500469..4f96de3 100644
--- a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/NullableText.java
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/NullableText.java
@@ -7,17 +7,23 @@
*
* Contributors:
* Pascal Essiembre - initial API and implementation
+ * Samir Soyer - Suggestion Bubble
******************************************************************************/
package org.eclipse.babel.editor.widgets;
+import java.util.Locale;
import java.util.Stack;
import org.eclipse.babel.editor.util.UIUtils;
+import org.eclipse.babel.editor.widgets.suggestion.SuggestionBubble;
+import org.eclipse.babel.editor.widgets.suggestion.provider.SuggestionProviderUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
@@ -38,6 +44,9 @@
private final Text text;
private final Color defaultColor;
private final Color nullColor;
+ private Locale locale;
+ private boolean dirty;
+ private boolean suggestionBubbleOn;
private boolean isnull;
@@ -60,7 +69,7 @@
/**
* Constructor.
*/
- public NullableText(Composite parent, int style) {
+ public NullableText(Composite parent, int style, Locale locale) {
super(parent, SWT.NONE);
text = new Text(this, style);
text.setData("UNDO", new Stack<String>());
@@ -78,7 +87,28 @@
setLayoutData(gd);
initComponents();
- }
+ this.locale = locale;
+
+ suggestionBubbleOn = !SuggestionProviderUtils.getSuggetionProviders()
+ .isEmpty();
+
+ if (suggestionBubbleOn) {
+ if (locale != null) {
+ new SuggestionBubble(text, locale.getLanguage());
+ } else {
+ text.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ SuggestionBubble.setDefaultText(text.getText());
+ }
+ });
+ }
+ }
+ }
+
+ public Text getTextBox() {
+ return this.text;
+ }
public void setOrientation(int orientation) {
text.setOrientation(orientation);
@@ -86,17 +116,59 @@
public void setText(String text) {
isnull = text == null;
+
+ if (locale == null && suggestionBubbleOn) {
+ SuggestionBubble.setDefaultText(text);
+ }
+
if (isnull) {
this.text.setText(""); //$NON-NLS-1$x
renderNull();
} else {
this.text.setText(text);
+
renderNormal();
}
+
Stack<String> undoCache = (Stack<String>) this.text.getData("UNDO");
undoCache.push(this.text.getText());
}
+ /**
+ * Sets this <code>NullableText</code> to dirty or vice versa
+ *
+ * @param dirty
+ */
+ public void setDirty(boolean dirty) {
+ this.dirty = dirty;
+ }
+
+ /**
+ * This method returns whether the content of this
+ * <code> NullableText</code> have changed since the last save.
+ *
+ * @return <code>true</code> if this NullableText is dirty;
+ * <code>false</code> otherwise.
+ */
+ public boolean isDirty() {
+ return dirty;
+ }
+
+ /**
+ * Applies the string to <code>NullableText</code> and makes it dirty,
+ * depending on the value of <code> dirty </code>
+ *
+ * @param text
+ * is the string to be applied to <code> NullableText </code>
+ * @param dirty
+ * whether setting text should make this
+ * <code>NullableText</code> dirty.
+ */
+ public void setText(String text, boolean dirty) {
+ this.dirty = dirty;
+ setText(text);
+ }
+
public String getText() {
if (isnull) {
return null;
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/PartialTranslationDialog.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/PartialTranslationDialog.java
new file mode 100644
index 0000000..568644c
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/PartialTranslationDialog.java
@@ -0,0 +1,312 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion;
+
+import org.eclipse.babel.editor.widgets.NullableText;
+import org.eclipse.babel.editor.widgets.suggestion.exception.SuggestionErrors;
+import org.eclipse.jface.dialogs.PopupDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Child dialog of {@link SuggestionBubble}, which lets user to mark a part of
+ * the suggestion and apply it to {@link Text}
+ *
+ * @author Samir Soyer
+ *
+ */
+public class PartialTranslationDialog {
+
+ private PopupDialog dialog;
+ private Shell shell;
+ private SuggestionBubble parent;
+ private Composite composite;
+ private Text textField;
+ private final String FOOT_NOTE_1 = "Click for focus";
+ private final String FOOT_NOTE_2 = "Mark the text, which will be used as translation, then click on 'Apply' button";
+ private String infoText;
+ private String text;
+ private int orientation;
+ private boolean win;
+ private static int SHELL_STYLE;
+
+ /**
+ * The constructor
+ *
+ * @param shell
+ * is the shell of the SuggestionBubble that is parent of this
+ * dialog
+ * @param parent
+ * is the parent of this dialog.
+ */
+ public PartialTranslationDialog(Shell shell, SuggestionBubble parent) {
+ this.parent = parent;
+ this.shell = shell;
+
+ if (System.getProperty("os.name").toLowerCase().contains("windows")) {
+ SHELL_STYLE = PopupDialog.INFOPOPUP_SHELLSTYLE;
+ win = true;
+ } else {
+ SHELL_STYLE = PopupDialog.HOVER_SHELLSTYLE;
+ win = false;
+ }
+
+ }
+
+ private void createDialog(final int shellStyle) {
+ // int shellStyle = PopupDialog.INFOPOPUPRESIZE_SHELLSTYLE;
+ boolean takeFocusOnOpen = false;
+ boolean persistSize = false;
+ boolean persistLocation = false;
+ boolean showDialogMenu = false;
+ boolean showPersistActions = false;
+ String titleText = null;
+ dialog = new PopupDialog(shell, shellStyle, takeFocusOnOpen,
+ persistSize, persistLocation, showDialogMenu,
+ showPersistActions, titleText, infoText) {
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ composite = (Composite) super.createDialogArea(parent);
+
+ composite.setLayout(new GridLayout(2, false));
+
+ final Button button = new Button(composite, SWT.PUSH);
+ button.setText("Apply");
+ button.setEnabled(false);
+ button.addSelectionListener(new SelectionListener() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ NullableText nText = (NullableText) PartialTranslationDialog.this.parent
+ .getTextField().getParent();
+
+ nText.setText(
+ PartialTranslationDialog.this.parent
+ .getTextField().getText()
+ + textField.getSelectionText(), true);
+
+ PartialTranslationDialog.this.parent.dispose();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ });
+
+ Label label = new Label(composite, SWT.NONE);
+ label.setText("Selected translation");
+
+ FontData fontData = label.getFont().getFontData()[0];
+ Font font = new Font(label.getDisplay(), new FontData(
+ fontData.getName(), fontData.getHeight(), SWT.BOLD));
+ label.setFont(font);
+
+ // Invisible separator
+ new Label(composite, SWT.NONE);
+
+ textField = new Text(composite, SWT.V_SCROLL | SWT.WRAP
+ | SWT.MULTI | SWT.READ_ONLY | orientation);
+ textField.setText(text);
+ textField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
+ true, 2, 1));
+ textField.addListener(SWT.MouseUp, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ Text text = (Text) event.widget;
+
+ String selection = text.getSelectionText();
+
+ if (selection.length() > 0
+ && !SuggestionErrors.contains(textField
+ .getText())) {
+ button.setEnabled(true);
+ } else {
+ button.setEnabled(false);
+ }
+ }
+ });
+
+ Listener scrollBarListener = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ Text t = (Text) event.widget;
+ Rectangle r1 = t.getClientArea();
+ Rectangle r2 = t.computeTrim(r1.x, r1.y, r1.width,
+ r1.height);
+ Point p = t.computeSize(composite.getSize().x,
+ SWT.DEFAULT, true);
+ t.getVerticalBar().setVisible(r2.height <= p.y);
+ }
+ };
+
+ textField.addListener(SWT.Resize, scrollBarListener);
+
+ return composite;
+ }
+
+ @Override
+ protected void adjustBounds() {
+ super.adjustBounds();
+
+ Point start = parent.getCurrentLocation();
+ Point size = parent.getSize();
+
+ int x = start.x + size.x;
+ int y = start.y;
+ int screenWidth = Display.getCurrent().getBounds().width;
+
+ if (screenWidth - x <= 200) {
+ x = start.x - 450;
+ }
+
+ getShell().setLocation(x, y);
+
+ if (screenWidth - x < 450) {
+ getShell().setSize(screenWidth - x, 200);
+ } else {
+ getShell().setSize(450, 200);
+ }
+ }
+
+ @Override
+ protected void configureShell(Shell shell) {
+ super.configureShell(shell);
+
+ if (win) {
+ shell.addFocusListener(new FocusListener() {
+
+ @Override
+ public void focusGained(FocusEvent e) {
+ if (shellStyle == INFOPOPUPRESIZE_SHELLSTYLE
+ || dialog == null) {
+ return;
+ }
+ dialog.close();
+ infoText = FOOT_NOTE_2;
+ createDialog(PopupDialog.INFOPOPUPRESIZE_SHELLSTYLE);
+ dialog.open();
+ dialog.getShell().setFocus();
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ }
+ });
+ }
+ }
+ };
+ }
+
+ /**
+ * @return location of this dialog relative to display
+ */
+ public Point getLocation() {
+ return dialog.getShell().toDisplay(1, 1);
+ }
+
+ /**
+ * @return size if this dialog
+ */
+ public Point getSize() {
+ return dialog.getShell().getSize();
+ }
+
+ /**
+ * Creates a new dialog. If it is already created, it updates its text.
+ *
+ * @param text
+ * is the string to displayed in the dialog
+ * @param orientation
+ * is the text alignment for the specific language/locale, which
+ * should be either <code>SWT.LEFT_TO_RIGHT</code> or
+ * <code>SWT.RIGHT_TO_LEFT</code>.
+ */
+ public void openDialog(String text, int orientation) {
+ if (SuggestionErrors.contains(text)) {
+ return;
+ }
+
+ this.text = text;
+ this.orientation = orientation;
+ if (dialog != null && dialog.getShell() != null) {
+ textField.setText(text);
+ } else {
+ infoText = FOOT_NOTE_1;
+ createDialog(SHELL_STYLE);
+ dialog.open();
+ }
+
+ }
+
+ /**
+ * Disposes this dialog.
+ */
+ public void dispose() {
+ if (dialog != null) {
+ dialog.close();
+ }
+ }
+
+ /**
+ * @return true if mouse cursor is in the bounds of dialog, otherwise false.
+ */
+ public boolean isCursorInsideDialog() {
+ if (dialog == null || dialog.getShell() == null
+ || dialog.getShell().isDisposed()) {
+ return false;
+ }
+
+ Display d = Display.getCurrent();
+ if (d == null) {
+ d = Display.getDefault();
+ }
+
+ Point start = dialog.getShell().getLocation();
+ Point size = dialog.getShell().getSize();
+ Point end = new Point(size.x + start.x, size.y + start.y);
+
+ if ((d.getCursorLocation().x > end.x || d.getCursorLocation().x < start.x)
+ || (d.getCursorLocation().y > end.y || d.getCursorLocation().y < start.y)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return true if dialog is already created and visible, otherwise false.
+ */
+ public boolean isVisible() {
+ if (dialog != null && dialog.getShell() != null) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/SuggestionBubble.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/SuggestionBubble.java
new file mode 100644
index 0000000..7dad126
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/SuggestionBubble.java
@@ -0,0 +1,824 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion;
+
+import java.awt.Frame;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+
+import org.eclipse.babel.editor.widgets.NullableText;
+import org.eclipse.babel.editor.widgets.suggestion.exception.SuggestionErrors;
+import org.eclipse.babel.editor.widgets.suggestion.filter.SuggestionFilter;
+import org.eclipse.babel.editor.widgets.suggestion.model.Suggestion;
+import org.eclipse.babel.editor.widgets.suggestion.provider.ISuggestionProvider;
+import org.eclipse.babel.editor.widgets.suggestion.provider.ISuggestionProviderListener;
+import org.eclipse.babel.editor.widgets.suggestion.provider.SuggestionProviderUtils;
+import org.eclipse.jface.bindings.keys.KeySequence;
+import org.eclipse.jface.bindings.keys.KeyStroke;
+import org.eclipse.jface.bindings.keys.SWTKeySupport;
+import org.eclipse.jface.dialogs.PopupDialog;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.awt.SWT_AWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbenchCommandConstants;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.keys.IBindingService;
+
+/**
+ * Auto complete pop-up dialog that displays translation suggestions from a
+ * given text to a target language. Detecting the source language depends on the
+ * implementation of {@link ISuggestionProvider}.
+ *
+ * @author Samir Soyer
+ */
+public class SuggestionBubble implements ISuggestionProviderListener {
+
+ private PopupDialog dialog;
+ private TableViewer tableViewer;
+ private Text text;
+ private Shell shell;
+ private Point caret;
+ private SuggestionFilter suggestionFilter;
+ private Composite composite;
+ private ScrolledComposite scrollComposite;
+ private Label noSug;
+ private PartialTranslationDialog partialTranslationDialog;
+ private ArrayList<Suggestion> suggestions;
+ // private static ArrayList<ISuggestionProvider> suggestionProviders;
+ private String targetLanguage;
+ private String oldDefaultText = "";
+ private static String defaultText;
+ private static boolean win;
+ private String SRC_LANG = "EN";
+ private static int SHELL_STYLE;
+ private final String CONTENT_ASSIST;
+ private final Level LOG_LEVEL = Level.INFO;
+
+ private static final Logger LOGGER = Logger
+ .getLogger(SuggestionBubble.class.getName());
+
+ /**
+ * Constructor
+ *
+ * @param parent
+ * is the parent {@link Text} object, to which SuggestionBubble
+ * will be added.
+ * @param targetLanguage
+ * is the language, to which the
+ * {@link SuggestionBubble.defaultText} will be translated
+ */
+ public SuggestionBubble(Text parent, String targetLanguage) {
+ shell = parent.getShell();
+ text = parent;
+ this.targetLanguage = targetLanguage;
+
+ suggestionFilter = new SuggestionFilter();
+ suggestions = new ArrayList<Suggestion>();
+
+ String srcLang = System
+ .getProperty("tapiji.translator.default.language");
+ if (srcLang != null) {
+ SRC_LANG = srcLang.substring(0, 2).toUpperCase();
+ }
+
+ if (System.getProperty("os.name").toLowerCase().contains("windows")) {
+ SHELL_STYLE = PopupDialog.INFOPOPUPRESIZE_SHELLSTYLE;
+ win = true;
+ } else {
+ SHELL_STYLE = PopupDialog.HOVER_SHELLSTYLE;
+ win = false;
+ }
+
+ // MessagesEditorPlugin.getDefault().getBundle().
+ // getEntry("glossary.xml").getPath()
+ // System.out.println("install path "+MessagesEditorPlugin.getDefault().getBundle().getEntry("/").getPath()+"glossary.xml");
+
+ SuggestionProviderUtils.addSuggestionProviderUpdateListener(this);
+
+ /*
+ * Read shortcut of content assist (code completion) directly from
+ * org.eclipse.ui.IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST and
+ * save it to CONTENT_ASSIST final variable
+ */
+ IBindingService bindingService = (IBindingService) PlatformUI
+ .getWorkbench().getAdapter(IBindingService.class);
+
+ CONTENT_ASSIST = bindingService
+ .getBestActiveBindingFormattedFor(IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST);
+
+ init();
+ }
+
+ /**
+ * @return default text i.e source text that is being localized
+ */
+ public static String getDefaultText() {
+ return defaultText;
+ }
+
+ /**
+ * @param defaultText
+ * is the source text that is being localized.
+ */
+ public static void setDefaultText(String defaultText) {
+ SuggestionBubble.defaultText = defaultText;
+ }
+
+ private void updateSuggestions() {
+ if (!oldDefaultText.equals(defaultText)) {
+
+ ArrayList<ISuggestionProvider> providers = SuggestionProviderUtils
+ .getSuggetionProviders();
+
+ LOGGER.log(LOG_LEVEL, "size of suggestions: " + suggestions.size()
+ + ", size of providers: " + providers.size());
+
+ suggestions.clear();
+
+ final Display d = Display.getCurrent();
+ for (final ISuggestionProvider provider : providers) {
+
+ Thread fetch = new Thread() {
+ Composite loadingCircle;
+
+ @Override
+ public void run() {
+ if (!d.isDisposed()) {
+ d.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+
+ // Show circle
+ if (!composite.isDisposed()) {
+ loadingCircle = createLoadingCircle();
+ }
+ }
+ });
+ }
+
+ // Do the work
+ suggestions.add(provider.getSuggestion(defaultText,
+ targetLanguage));
+
+ if (!d.isDisposed()) {
+ d.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+
+ // remove laoding circle
+ if (!composite.isDisposed()) {
+ loadingCircle.dispose();
+ tableViewer.setInput(suggestions
+ .toArray());
+ pack();
+ composite.layout();
+ }
+ }
+ });
+ }
+ }
+ };
+ fetch.start();
+ }
+ oldDefaultText = defaultText;
+ } else {
+ tableViewer.setInput(suggestions.toArray());
+ pack();
+ composite.layout();
+ }
+ }
+
+ /**
+ * @return true if this SuggestionBubble is created, i.e if it is visible,
+ * false otherwise.
+ */
+ public boolean isCreated() {
+ if (dialog != null && dialog.getShell() != null) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Disposes this SuggestionBubble.
+ */
+ public void dispose() {
+ if (dialog != null)
+ dialog.close();
+ }
+
+ private void init() {
+ // Focus handling simulation for non-Windows systems
+ if (!win) {
+ shell.getDisplay().addFilter(SWT.MouseDown, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+
+ if (isCursorInsideTextField()
+ && text.getText().length() == 0 && !isCreated()
+ && text.isFocusControl()) {
+ suggestionFilter.setSearchText("");
+ createDialog();
+ tableViewer.refresh();
+ } else {
+ if (partialTranslationDialog != null) {
+ if (!partialTranslationDialog
+ .isCursorInsideDialog()
+ && !isCursorInsideDialog()) {
+ dispose();
+ }
+ } else {
+ if (!isCursorInsideDialog()) {
+ dispose();
+ }
+ }
+ }
+ }
+ });
+ }
+
+ // shell resize listener to dispose suggestion bubble
+ shell.addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event e) {
+ if (dialog != null && dialog.getShell() != null) {
+ dialog.close();
+ }
+ }
+ });
+
+ // shell move listener
+ shell.addListener(SWT.Move, new Listener() {
+ public void handleEvent(Event e) {
+ if (dialog != null && dialog.getShell() != null) {
+ dialog.close();
+ }
+ }
+ });
+
+ // get ScrolledComposite
+ ScrolledComposite scrolledComposite = (ScrolledComposite) text
+ .getParent().getParent().getParent().getParent();
+ // scroll listener
+ scrolledComposite.getVerticalBar().addSelectionListener(
+ new SelectionListener() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (dialog != null && dialog.getShell() != null) {
+ dialog.close();
+ }
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ });
+
+ // ModifyListener
+ text.addModifyListener(new ModifyListener() {
+
+ @Override
+ public void modifyText(ModifyEvent e) {
+
+ recalculatePosition();
+
+ if (dialog != null && dialog.getShell() != null
+ && !tableViewer.getControl().isDisposed()) {
+ suggestionFilter.setSearchText(text.getText().trim());
+ tableViewer.refresh();
+
+ if (tableViewer.getTable().getItemCount() == 0) {
+ if (noSug == null || noSug.isDisposed()) {
+ noSug = new Label(composite, SWT.NONE);
+ noSug.setText("No suggestions available");
+ noSug.moveAbove(tableViewer.getControl());
+ noSug.setBackground(new Color(shell.getDisplay(),
+ 255, 255, 225));
+ composite.layout();
+ }
+ } else {
+ if (noSug != null && !noSug.isDisposed()) {
+ tableViewer.getTable().setSelection(0);
+ noSug.dispose();
+ composite.layout();
+ }
+ }
+
+ suggestionFilter.setSearchText("");
+ }
+ }
+
+ });
+
+ // KeyListener
+ text.addKeyListener(new KeyListener() {
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if ((e.keyCode == SWT.CR || e.keyCode == SWT.LF)
+ && (dialog != null && dialog.getShell() != null)
+ && tableViewer.getTable().getSelectionIndex() != -1) {
+ e.doit = false;
+ }
+
+ int accelerator = SWTKeySupport
+ .convertEventToUnmodifiedAccelerator(e);
+ KeyStroke keyStroke = SWTKeySupport
+ .convertAcceleratorToKeyStroke(accelerator);
+ KeySequence sequence = KeySequence.getInstance(keyStroke);
+
+ if (sequence.format().equals(CONTENT_ASSIST)) {
+
+ if (isCreated()) {
+ if (noSug != null && !noSug.isDisposed()) {
+ noSug.dispose();
+ composite.layout();
+ }
+ suggestionFilter.setSearchText("");
+ tableViewer.refresh();
+ tableViewer.getTable().setSelection(0);
+ } else {
+ createDialog();
+ suggestionFilter.setSearchText(text.getText().trim());
+ tableViewer.refresh();
+ }
+ e.doit = false;
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+
+ if (dialog == null || dialog.getShell() == null) {
+ return;
+ }
+
+ if (e.keyCode == SWT.ESC) {
+ dialog.close();
+ return;
+ }
+
+ // Changing selection with keyboard arrows and applying
+ // translation with enter
+ int currentSelectionIndex = tableViewer.getTable()
+ .getSelectionIndex();
+
+ if (e.keyCode == SWT.ARROW_DOWN) {
+ if (currentSelectionIndex >= tableViewer.getTable()
+ .getItemCount() - 1) {
+ tableViewer.getTable().setSelection(0);
+ } else {
+ tableViewer.getTable().setSelection(
+ currentSelectionIndex + 1);
+ }
+ }
+
+ if (e.keyCode == SWT.ARROW_UP) {
+ if (currentSelectionIndex <= 0) {
+ tableViewer.getTable().setSelection(
+ tableViewer.getTable().getItemCount() - 1);
+ } else {
+ tableViewer.getTable().setSelection(
+ currentSelectionIndex - 1);
+ }
+ }
+
+ if (e.keyCode == SWT.CR || e.keyCode == SWT.LF) {
+ applySuggestion(text);
+ }
+ }
+ });
+
+ // FocusListener
+ text.addFocusListener(new FocusListener() {
+
+ @Override
+ public void focusGained(FocusEvent e) {
+
+ if (win && !isCreated() && text.getText().length() == 0) {
+ suggestionFilter.setSearchText("");
+ createDialog();
+ tableViewer.refresh();
+ }
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ if (win && dialog != null && !isCursorInsideDialog()) {
+ dialog.close();
+ }
+ }
+ });
+
+ //MouseListener for Windows systems
+ if (win) {
+ text.addMouseListener(new MouseListener() {
+
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ // Nothing to do
+ }
+
+ @Override
+ public void mouseDown(MouseEvent e) {
+ // Nothing to do
+ }
+
+ @Override
+ public void mouseUp(MouseEvent e) {
+
+ if (caret != null) {
+ if (dialog != null
+ && !caret.equals(text.getCaretLocation())) {
+ dialog.close();
+ caret = text.getCaretLocation();
+ }
+ } else {
+ caret = text.getCaretLocation();
+ }
+
+ if (partialTranslationDialog != null
+ && !partialTranslationDialog.isCursorInsideDialog()) {
+ partialTranslationDialog.dispose();
+ }
+ }
+ });
+ }
+ }
+
+ private void createDialog() {
+ boolean takeFocusOnOpen = false;
+ boolean persistSize = false;
+ boolean persistLocation = false;
+ boolean showDialogMenu = false;
+ boolean showPersistActions = false;
+ String titleText = "Suggestions (" + SRC_LANG + " > "
+ + targetLanguage.toUpperCase() + ")";
+ String infoText = "Ctrl+Space to display all suggestions";
+ dialog = new PopupDialog(shell, SHELL_STYLE, takeFocusOnOpen,
+ persistSize, persistLocation, showDialogMenu,
+ showPersistActions, titleText, infoText) {
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ scrollComposite = new ScrolledComposite(
+ (Composite) super.createDialogArea(parent),
+ SWT.V_SCROLL | SWT.H_SCROLL);
+ scrollComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ scrollComposite.setExpandVertical(true);
+ scrollComposite.setExpandHorizontal(true);
+
+ GridLayout gl = new GridLayout(1, true);
+ gl.verticalSpacing = 0;
+ composite = new Composite(scrollComposite, SWT.NONE);
+ composite.setLayout(gl);
+ scrollComposite.setContent(composite);
+
+ tableViewer = new TableViewer(composite, SWT.NO_SCROLL);
+ tableViewer.getTable().setLayoutData(
+ new GridData(GridData.FILL, SWT.TOP, true, false));
+
+ tableViewer.setContentProvider(new ArrayContentProvider());
+ tableViewer.setLabelProvider(new ITableLabelProvider() {
+
+ @Override
+ public Image getColumnImage(Object arg0, int arg1) {
+ Suggestion s = (Suggestion) arg0;
+ return s.getIcon();
+ }
+
+ @Override
+ public String getColumnText(Object element, int index) {
+ return ((Suggestion) element).getText();
+
+ }
+
+ @Override
+ public void addListener(ILabelProviderListener listener) {
+ // nothing to do
+ }
+
+ @Override
+ public void dispose() {
+ // nothing to do
+ }
+
+ @Override
+ public boolean isLabelProperty(Object arg0, String arg1) {
+ return true;
+ }
+
+ @Override
+ public void removeListener(ILabelProviderListener arg0) {
+ // nothing to do
+ }
+ });
+
+ tableViewer.addFilter(suggestionFilter);
+
+ tableViewer.addDoubleClickListener(new DoubleClickListener() {
+
+ @Override
+ public void doubleClick(DoubleClickEvent event) {
+ applySuggestion(text);
+ }
+
+ });
+
+ tableViewer
+ .addSelectionChangedListener(new ISelectionChangedListener() {
+
+ @Override
+ public void selectionChanged(
+ SelectionChangedEvent event) {
+ if (tableViewer.getTable().getSelection().length > 0) {
+ partialTranslationDialog.openDialog(
+ tableViewer.getTable()
+ .getSelection()[0]
+ .getText(), text
+ .getOrientation());
+ }
+ }
+ });
+
+ // For Windows 7
+ // Set background color of column line
+ // tableViewer.getTable().addListener(SWT.EraseItem, new
+ // Listener() {
+ // @Override
+ // public void handleEvent(Event event) {
+ // event.gc.setBackground(new Color(shell.getDisplay(), 255,
+ // 255, 225));
+ // event.gc.fillRectangle(event.getBounds());
+ // }
+ // });
+
+ tableViewer.getTable().setSelection(0);
+ return scrollComposite;
+ }
+
+ @Override
+ protected void adjustBounds() {
+ super.adjustBounds();
+
+ Point point = text.getCaretLocation();
+
+ getShell().setLocation(text.toDisplay(1, 1).x + point.x + 5,
+ text.toDisplay(1, 1).y + point.y + 20);
+
+ getShell().setSize(450, 200);
+ }
+
+ };
+ dialog.open();
+
+ partialTranslationDialog = new PartialTranslationDialog(
+ dialog.getShell(), this);
+
+ dialog.getShell().addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event e) {
+ partialTranslationDialog.dispose();
+ }
+ });
+
+ updateSuggestions();
+ }
+
+ private void pack() {
+ Point temp = new Point(0, 0);
+ Point max = new Point(0, 0);
+
+ for (TableItem item : tableViewer.getTable().getItems()) {
+ temp.x = item.getBounds().width;
+ temp.y = item.getBounds().height;
+ if (temp.x > max.x) {
+ max.x = temp.x;
+ }
+ max.y = max.y + temp.y;
+ }
+ scrollComposite.setMinSize(max);
+ }
+
+ private boolean isCursorInsideTextField() {
+ if (text.isDisposed()) {
+ return false;
+ }
+
+ Display d = Display.getCurrent();
+ if (d == null) {
+ d = Display.getDefault();
+ }
+
+ Point start = text.getLocation();
+ start = text.toDisplay(start.x, start.y);
+ Point size = text.getSize();
+ Point end = new Point(size.x + start.x, size.y + start.y);
+
+ if ((d.getCursorLocation().x > end.x || d.getCursorLocation().x < start.x)
+ || (d.getCursorLocation().y > end.y || d.getCursorLocation().y < start.y)) {
+ return false;
+ }
+ return true;
+ }
+
+ private Composite createLoadingCircle() {
+ // Create loading cicle
+ Composite loadingCircle = new Composite(composite, SWT.EMBEDDED
+ | SWT.NO_BACKGROUND);
+ Frame frame = SWT_AWT.new_Frame(loadingCircle);
+ ImageIcon imageIcon = new ImageIcon(this.getClass().getResource(
+ "/icons/ajax-loader.gif"));
+ JLabel label = new JLabel(imageIcon);
+ frame.add(label);
+ frame.setBackground(new java.awt.Color(255, 255, 225));
+
+ GridData gd = new GridData(GridData.BEGINNING, SWT.TOP, false, false);
+ gd.heightHint = 16;
+ gd.widthHint = 16;
+ loadingCircle.setLayoutData(gd);
+ return loadingCircle;
+ }
+
+ /**
+ * @return parent {@link Text} of this SuggestionBubble.
+ */
+ public Text getTextField() {
+ return text;
+ }
+
+ /**
+ * @return language of this SuggestionBubble, to which default text is being
+ * translated
+ */
+ public String getTargetLanguage() {
+ return targetLanguage;
+ }
+
+ private void recalculatePosition() {
+ caret = text.getCaretLocation();
+
+ if (dialog != null && dialog.getShell() != null) {
+
+ int oldCaretX = getCurrentLocation().x - (text.toDisplay(1, 1).x)
+ - 5;
+ int oldCaretY = getCurrentLocation().y - (text.toDisplay(1, 1).y)
+ - 20;
+
+ int newCaretX = caret.x;
+ int newCaretY = caret.y;
+
+ setLocation(getCurrentLocation().x + (newCaretX - oldCaretX),
+ getCurrentLocation().y + (newCaretY - oldCaretY));
+
+ }
+ }
+
+ /**
+ * @return current location of the SuggestionBubble on the screen.
+ */
+ public Point getCurrentLocation() {
+ if (dialog != null && dialog.getShell() != null) {
+ return dialog.getShell().getLocation();
+ // return dialog.getShell().toDisplay(1, 1);
+ }
+
+ return new Point(0, 0);
+ }
+
+ /**
+ * @return size of the SuggestionBubble
+ */
+ public Point getSize() {
+ return dialog.getShell().getSize();
+ }
+
+ private void setLocation(int x, int y) {
+ if (dialog != null && dialog.getShell() != null)
+ dialog.getShell().setLocation(new Point(x, y));
+ }
+
+ private void applySuggestion(Text text) {
+ if (tableViewer.getTable().getSelectionIndex() == -1) {
+ return;
+ }
+ IStructuredSelection selection = (IStructuredSelection) tableViewer
+ .getSelection();
+ Suggestion suggestion = (Suggestion) selection.getFirstElement();
+
+ String s = suggestion.getText();
+
+ // Filter out [(].*[% match)]
+ if (s.lastIndexOf("(") != -1) {
+ s = s.substring(0, s.lastIndexOf("(") - 1);
+ }
+
+ if (SuggestionErrors.contains(s)) {
+ // Ignore call
+ return;
+ }
+
+ ((NullableText) text.getParent()).setText(s, true);
+
+ dialog.close();
+ }
+
+ private boolean isCursorInsideDialog() {
+ if (dialog == null || dialog.getShell() == null) {
+ return false;
+ }
+
+ Display d = Display.getCurrent();
+ if (d == null) {
+ d = Display.getDefault();
+ }
+
+ Point start = dialog.getShell().getLocation();
+ Point size = dialog.getShell().getSize();
+ Point end = new Point(size.x + start.x, size.y + start.y);
+
+ if ((d.getCursorLocation().x > end.x || d.getCursorLocation().x < start.x)
+ || (d.getCursorLocation().y > end.y || d.getCursorLocation().y < start.y)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @see org.eclipse.babel.editor.widgets.suggestion.provider.
+ * ISuggestionProviderListener#suggestionProviderUpdated(org.eclipse.babel
+ * .editor.widgets.suggestion.provider.ISuggestionProvider)
+ */
+ @Override
+ public void suggestionProviderUpdated(ISuggestionProvider provider) {
+ LOGGER.log(LOG_LEVEL, "provider :"
+ + provider.getClass().getSimpleName()
+ + ", size of suggestions: " + suggestions.size());
+
+ for (int i = 0; i < suggestions.size(); i++) {
+ Suggestion sug = suggestions.get(i);
+ if (sug.getProvider().equals(provider)) {
+ suggestions.set(i,
+ provider.getSuggestion(defaultText, targetLanguage));
+ }
+ }
+
+ if (tableViewer != null && !tableViewer.getTable().isDisposed()) {
+ tableViewer.setInput(suggestions);
+ }
+ }
+}
+
+/**
+ * Implements {@link IDoubleClickListener}
+ *
+ * @author Samir Soyer
+ *
+ */
+abstract class DoubleClickListener implements IDoubleClickListener {
+}
\ No newline at end of file
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/exception/InvalidConfigurationSetting.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/exception/InvalidConfigurationSetting.java
new file mode 100644
index 0000000..7d622ab
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/exception/InvalidConfigurationSetting.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.exception;
+
+public class InvalidConfigurationSetting extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public InvalidConfigurationSetting(String message) {
+ super(message);
+ }
+
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/exception/SuggestionErrors.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/exception/SuggestionErrors.java
new file mode 100644
index 0000000..32eef52
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/exception/SuggestionErrors.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.exception;
+
+/**
+ * This class contains all the error messages that can be used by
+ * {@link org.eclipse.babel.editor.widgets.suggestion.provider.ISuggestionProvider}
+ *
+ * @author Samir Soyer
+ *
+ */
+public class SuggestionErrors {
+
+ /**
+ * to be used, in case suggestion provider can't provide a suggestion
+ */
+ public final static String NO_SUGESTION_ERR = "No suggestions available";
+ /**
+ * to be used, in case an error occurs that is related to Internet
+ * (Connection / protocol error, etc.)
+ */
+ public final static String CONNECTION_ERR = "Connection error, check your internet connection";
+ /**
+ * to be used, in case suggestion provider doesn't support a specific
+ * language
+ */
+ public final static String LANG_NOT_SUPPORT_ERR = "Language not supported";
+ /**
+ * to be used, in case quota for maximum allowed translations were exceeded.
+ */
+ public final static String QUOTA_EXCEEDED = "Translation quota has been exceeded";
+ /**
+ * to be used, in case selected glossary file can not be read.
+ */
+ public final static String INVALID_GLOSSARY = "Invalid glossary file";
+ /**
+ * to be used, in case no glossary file is selected.
+ */
+ public final static String NO_GLOSSARY_FILE = "Open a glossary to see suggestions";
+
+ /**
+ * Checks whether a string is contained in this class, i.e whether the
+ * string is an error message.
+ *
+ * @param s
+ * is the string to check.
+ * @return true if string is a error message, otherwise false.
+ */
+ public static boolean contains(String s) {
+ if (s.equals(SuggestionErrors.LANG_NOT_SUPPORT_ERR)
+ || s.equals(SuggestionErrors.CONNECTION_ERR)
+ || s.equals(SuggestionErrors.NO_SUGESTION_ERR)
+ || s.equals(SuggestionErrors.QUOTA_EXCEEDED)
+ || s.equals(SuggestionErrors.NO_GLOSSARY_FILE)) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/filter/SuggestionFilter.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/filter/SuggestionFilter.java
new file mode 100644
index 0000000..0719e22
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/filter/SuggestionFilter.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.filter;
+
+import org.eclipse.babel.editor.widgets.suggestion.exception.SuggestionErrors;
+import org.eclipse.babel.editor.widgets.suggestion.model.Suggestion;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+/**
+ * Filter class for {@link org.eclipse.jface.viewers.TableViewer.TableViewer} in
+ * {@link org.eclipse.babel.editor.widgets.suggestion.SuggestionBubble}
+ *
+ * @author Samir Soyer
+ *
+ */
+public class SuggestionFilter extends ViewerFilter {
+
+ private String searchString;
+
+ /**
+ * Sets the text that is going to be checked, whether it matches suggestions
+ * in the tableviewer
+ *
+ * @param s
+ * is the text to be searched for
+ */
+ public void setSearchText(String s) {
+
+ this.searchString = ".*" + s + ".*";
+
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer,
+ * java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ if (searchString == null || searchString.length() == 0) {
+ return true;
+ }
+
+ Suggestion s = (Suggestion) element;
+ if (s.getText().toLowerCase().matches(searchString.toLowerCase())
+ || SuggestionErrors.contains(s.getText())) {
+ return true;
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/lookup/SuggestionProviderLoader.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/lookup/SuggestionProviderLoader.java
new file mode 100644
index 0000000..a74ae1c
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/lookup/SuggestionProviderLoader.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.lookup;
+
+import org.eclipse.babel.editor.widgets.suggestion.provider.ISuggestionProvider;
+import org.eclipse.babel.editor.widgets.suggestion.provider.SuggestionProviderUtils;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.SafeRunner;
+
+/**
+ * Look up for suggestion providers, which implements the extension point
+ * {@literal "org.eclipselabs.tapiji.translator.suggestion"}.
+ * @author Samir Soyer
+ *
+ */
+public class SuggestionProviderLoader {
+ private static final String ISUGGESTIONPROVIDER_ID =
+ "org.eclipse.babel.editor.suggestion";
+
+ /**
+ * Finds all the suggestion providers resp. extensions that implement
+ * {@code org.eclipselabs.tapiji.translator.suggestion} extension point.
+ * Then registers them at
+ * {@link org.eclipse.babel.editor.widgets.
+ * suggestion.provider.SuggestionProviderUtils}
+ */
+ public static void registerProviders() {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+
+ IConfigurationElement[] config =
+ registry.getConfigurationElementsFor(ISUGGESTIONPROVIDER_ID);
+
+ try {
+ for (IConfigurationElement e : config) {
+ final Object o =
+ e.createExecutableExtension("class");
+ if (o instanceof ISuggestionProvider) {
+ executeExtension(o);
+ }
+ }
+ } catch (CoreException ex) {
+ //TODO logging
+ // System.out.println(ex.getMessage());
+ }
+ }
+
+ private static void executeExtension(final Object o) {
+ ISafeRunnable runnable = new ISafeRunnable() {
+ @Override
+ public void handleException(Throwable e) {
+ //TODO logging
+ // System.out.println("Exception in extension");
+ }
+
+ @Override
+ public void run() throws Exception {
+ ISuggestionProvider provider = ((ISuggestionProvider) o);
+ SuggestionProviderUtils.addSuggestionProvider(provider);
+ }
+ };
+ SafeRunner.run(runnable);
+ }
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/model/Suggestion.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/model/Suggestion.java
new file mode 100644
index 0000000..a79bc98
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/model/Suggestion.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.model;
+
+import org.eclipse.babel.editor.widgets.suggestion.provider.ISuggestionProvider;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Encapsulates text of the suggestion and icon of the suggestion provider,
+ * which provides the respective translation.
+ *
+ * @author Samir Soyer
+ *
+ */
+public class Suggestion {
+
+ private Image icon;
+ private String text;
+ private ISuggestionProvider provider;
+
+ /**
+ * @param icon
+ * is the image of suggestion provider which provides the
+ * translation of the text
+ * @param text
+ * is the translated suggestion
+ */
+ public Suggestion(Image icon, String text, ISuggestionProvider provider) {
+ this.icon = icon;
+ this.text = text;
+ this.provider = provider;
+ }
+
+ /**
+ * @return Image object of the suggestion provider which provides the
+ * suggestion
+ */
+ public Image getIcon() {
+ return icon;
+ }
+
+ /**
+ * @param icon
+ * Image object of the suggestion provider which provides the
+ * suggestion
+ */
+ public void setIcon(Image icon) {
+ this.icon = icon;
+ }
+
+ /**
+ * @return translated text, i.e suggestion
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * @param text
+ * is the translated text, i.e suggestion
+ */
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ /**
+ * @return {@link ISuggestionProvider} that provides this suggestion
+ */
+ public ISuggestionProvider getProvider() {
+ return provider;
+ }
+
+ /**
+ * @param provider
+ * is the {@link ISuggestionProvider} that provides this
+ * suggestion
+ */
+ public void setProvider(ISuggestionProvider provider) {
+ this.provider = provider;
+ }
+
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/ISuggestionProvider.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/ISuggestionProvider.java
new file mode 100644
index 0000000..be0e339
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/ISuggestionProvider.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.provider;
+
+import java.util.Map;
+
+import org.eclipse.babel.editor.widgets.suggestion.exception.InvalidConfigurationSetting;
+import org.eclipse.babel.editor.widgets.suggestion.model.Suggestion;
+
+/**
+ * Interface for the suggestion providers which should implement {@link
+ * ISuggestionProvider.getSuggestion()} method to return provided suggestion
+ *
+ * @author Samir Soyer
+ * @author Martin Reiterer - Added suggestion provider configuration methods
+ *
+ */
+public interface ISuggestionProvider {
+
+ /**
+ * Returns translation of the original text to a given language
+ *
+ * @param original
+ * is the untranslated string
+ * @param targetLanguage
+ * is the language, to which the original text will be translated
+ * @return translation of original text
+ */
+ Suggestion getSuggestion(String original, String targetLanguage);
+
+ /**
+ * Returns a list of all configuration settings of the suggestion provider
+ *
+ * @return The list of active configuration settings
+ */
+ Map<String, ISuggestionProviderConfigurationSetting> getAllConfigurationSettings();
+
+ /**
+ * Allows to update one particular configuration setting
+ *
+ * @param setting
+ * The configuration Setting of type
+ * {@link ISuggestionProviderConfigurationSetting}
+ */
+ void updateConfigurationSetting(String configurationId,
+ ISuggestionProviderConfigurationSetting setting)
+ throws InvalidConfigurationSetting;
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/ISuggestionProviderConfigurationSetting.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/ISuggestionProviderConfigurationSetting.java
new file mode 100644
index 0000000..73c1175
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/ISuggestionProviderConfigurationSetting.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.provider;
+
+/**
+ * Interface for suggestion provider configuration settings. Classes that
+ * implements this interface can represent the configuration as any type of
+ * object, which depends on the implementation of
+ * {@code updateConfigurationSetting()} method in {@link ISuggestionProvider}.
+ *
+ * @author Samir Soyer
+ *
+ */
+public interface ISuggestionProviderConfigurationSetting {
+
+ /**
+ * @return configuration setting in any type of object. Note: implementation
+ * of {@code updateConfigurationSetting()} method in
+ * {@link ISuggestionProvider} should be able to handle the returned
+ * object from this method.
+ */
+ Object getConfigurationSetting();
+
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/ISuggestionProviderListener.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/ISuggestionProviderListener.java
new file mode 100644
index 0000000..653c31c
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/ISuggestionProviderListener.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.provider;
+
+/**
+ * Listener interface for {@link ISuggestionProvider}s. Defines the method to
+ * call when an update event occurs.
+ *
+ * @author Samir Soyer
+ *
+ */
+public interface ISuggestionProviderListener {
+
+ /**
+ * This method will be called after a {@link ISuggestionProvider} is
+ * updated. e.q if resource of a suggestion provider is updated after
+ * creating the object.
+ *
+ * @param provider
+ * is the suggestion provider which was updated
+ */
+ public void suggestionProviderUpdated(ISuggestionProvider provider);
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/StringConfigurationSetting.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/StringConfigurationSetting.java
new file mode 100644
index 0000000..ee8c36f
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/StringConfigurationSetting.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.provider;
+
+/**
+ * This class contains configuration setting for {@link ISuggestionProvider} in
+ * string format, i.e object that contains the configuration setting is a
+ * string.
+ *
+ * @author Samir Soyer
+ *
+ */
+public class StringConfigurationSetting implements
+ ISuggestionProviderConfigurationSetting {
+
+ private String config;
+
+ /**
+ * Constructor
+ *
+ * @param config
+ * is the string that contains the configuration, e.g
+ * {@code "/home/xyz/file.xml"}
+ */
+ public StringConfigurationSetting(String config) {
+ super();
+ this.config = config;
+ }
+
+ /**
+ * @return configuration as string
+ */
+ public String getConfig() {
+ return config;
+ }
+
+ /**
+ * @param config
+ * is the string that contains the configuration, e.g
+ * {@code "/home/xyz/file.xml"
+ */
+ public void setConfig(String config) {
+ this.config = config;
+ }
+
+ /**
+ * @see org.eclipse.babel.editor.widgets .suggestion.provider
+ * .ISuggestionProviderConfigurationSetting#getConfigurationSetting()
+ */
+ @Override
+ public Object getConfigurationSetting() {
+ return config;
+ }
+
+}
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/SuggestionProviderUtils.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/SuggestionProviderUtils.java
new file mode 100644
index 0000000..a17d748
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/widgets/suggestion/provider/SuggestionProviderUtils.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2013 Samir Soyer.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Samir Soyer - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.babel.editor.widgets.suggestion.provider;
+
+import java.util.ArrayList;
+
+import org.eclipse.babel.editor.widgets.suggestion.exception.InvalidConfigurationSetting;
+
+/**
+ * This class contains a list of all suggestion providers.
+ * {@link org.eclipse.babel.editor.widgets.suggestion.SuggestionBubble} gets its
+ * suggestion providers from this class, therefore the ones that should be used
+ * in SuggestionBubble must be registered in this class by calling {@link
+ * SuggestionProviderUtils.addSuggestionProvider()} method
+ *
+ * @author Samir Soyer
+ *
+ */
+public class SuggestionProviderUtils {
+ private static ArrayList<ISuggestionProvider> providers = new ArrayList<ISuggestionProvider>();
+ private static ArrayList<ISuggestionProviderListener> listeners = new ArrayList<ISuggestionProviderListener>();
+
+ /**
+ * Adds suggestion provider object to the list
+ *
+ * @param provider
+ * is the suggestion provider to be registered
+ */
+ public static void addSuggestionProvider(ISuggestionProvider provider) {
+ if (!providers.contains(provider)) {
+ providers.add(provider);
+ }
+ }
+
+ /**
+ * Removes a specific {@link ISuggestionProvider} from the list of
+ * suggestion providers.
+ *
+ * @param provider
+ * is the {@link ISuggestionProvider} to be removed from the list
+ */
+ public static void removeSuggestionProvider(ISuggestionProvider provider) {
+ providers.remove(provider);
+ }
+
+ /**
+ * @return all the registered suggestion providers
+ */
+ public static ArrayList<ISuggestionProvider> getSuggetionProviders() {
+ return providers;
+ }
+
+ /**
+ * Adds a new suggestion provider listener, which calls {@link
+ * ISuggestionProviderListener.suggestionProviderUpdated()} method, when a
+ * suggestion provider object is updated
+ *
+ * @param listener
+ * is the object, whose {@link
+ * ISuggestionProviderListener.suggestionProviderUpdated()} will
+ * be called, when the suggestion provider is updated
+ */
+ public static void addSuggestionProviderUpdateListener(
+ ISuggestionProviderListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * This method is to call after updating a provider
+ */
+ public static void fireSuggestionProviderUpdated(
+ ISuggestionProvider provider) {
+ for (ISuggestionProviderListener listener : listeners) {
+ listener.suggestionProviderUpdated(provider);
+ }
+ }
+
+ /**
+ * Allows to update one particular configuration setting
+ *
+ * @param setting
+ * The configuration Setting of type
+ * {@link ISuggestionProviderConfigurationSetting}
+ */
+ public static void updateConfigurationSetting(String configurationId,
+ ISuggestionProviderConfigurationSetting setting)
+ throws InvalidConfigurationSetting {
+
+ for (ISuggestionProvider provider : providers) {
+ if (provider.getAllConfigurationSettings().containsKey(
+ configurationId)) {
+ provider.updateConfigurationSetting(configurationId, setting);
+ fireSuggestionProviderUpdated(provider);
+ }
+ }
+
+ }
+}