[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);
+			}
+		}
+
+	}
+}