Bug 474962 - it should be possible to support Radio Group for data
collection or enumeration, like ComboViewer 
diff --git a/org.eclipse.xwt/src/org/eclipse/xwt/jface/JFacesHelper.java b/org.eclipse.xwt/src/org/eclipse/xwt/jface/JFacesHelper.java
index be7031f..ae2ddc1 100644
--- a/org.eclipse.xwt/src/org/eclipse/xwt/jface/JFacesHelper.java
+++ b/org.eclipse.xwt/src/org/eclipse/xwt/jface/JFacesHelper.java
@@ -66,6 +66,7 @@
 

 			JFACES_VIEWER = Class.forName("org.eclipse.jface.viewers.Viewer");

 			String[] jfaceClasses = new String[] {

+					"org.eclipse.xwt.jface.RadioGroupViewer",

 					"org.eclipse.jface.viewers.ComboViewer",

 					"org.eclipse.jface.viewers.ListViewer",

 					"org.eclipse.jface.viewers.TreeViewer",

diff --git a/org.eclipse.xwt/src/org/eclipse/xwt/jface/RadioGroup.java b/org.eclipse.xwt/src/org/eclipse/xwt/jface/RadioGroup.java
new file mode 100644
index 0000000..1686451
--- /dev/null
+++ b/org.eclipse.xwt/src/org/eclipse/xwt/jface/RadioGroup.java
@@ -0,0 +1,291 @@
+package org.eclipse.xwt.jface;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.TypedListener;
+
+public class RadioGroup extends Composite {
+	private final int cardinality;
+	private final int buttonStyle;
+
+	private RadioItem[] items = {};
+
+	private RadioItem selection = null;
+
+	public RadioGroup(Composite parent, int style) {
+		super(parent, checkCompositeStyle(style));
+		this.cardinality = checkCardinality(style);
+		this.buttonStyle = checkButtonStyle(style);
+
+		super.setLayout(new RowLayout(cardinality));
+
+		setBackgroundMode(SWT.INHERIT_DEFAULT);
+
+		addListener(SWT.Dispose, new Listener() {
+			public void handleEvent(Event event) {
+				handleDispose(event);
+			}
+		});
+	}
+
+	private static int checkCompositeStyle(int style) {
+		int result = style & SWT.BORDER;
+		if ((style & SWT.LEFT_TO_RIGHT) != 0)
+			result |= SWT.LEFT_TO_RIGHT;
+		else if ((style & SWT.RIGHT_TO_LEFT) != 0)
+			result |= SWT.RIGHT_TO_LEFT;
+		return result;
+	}
+
+	private int checkCardinality(int style) {
+		if ((style & SWT.VERTICAL) != 0)
+			return SWT.VERTICAL;
+		return SWT.HORIZONTAL;
+	}
+
+	private static int checkButtonStyle(int style) {
+		int result = 0;
+		if ((style & SWT.FLAT) != 0)
+			result |= SWT.FLAT;
+		if ((style & SWT.LEFT) != 0)
+			result |= SWT.LEFT;
+		else if ((style & SWT.CENTER) != 0)
+			result |= SWT.CENTER;
+		else if ((style & SWT.RIGHT) != 0)
+			result |= SWT.RIGHT;
+		else
+			result |= SWT.LEFT;
+		return result;
+	}
+
+	private void handleDispose(Event event) {
+		RadioItem[] items = getItems();
+		for (int i = 0; i < items.length; i++)
+			items[i].dispose();
+	}
+
+	public int getStyle() {
+		return super.getStyle() | buttonStyle | cardinality;
+	}
+
+	public void setLayout(Layout layout) {
+		checkWidget();
+		return;
+	}
+
+	public void clear(int position) {
+		checkWidget();
+		checkExistingPosition(position);
+		items[position].clear();
+	}
+
+	private void checkExistingPosition(int position) {
+		if (position < 0 || position >= getItemCount())
+			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+	}
+
+	public void remove(RadioItem item) {
+		checkWidget();
+		if (item.isDisposed() || item.getParent() != this)
+			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+		item.dispose();
+	}
+
+	public void remove(int position) {
+		checkWidget();
+		checkExistingPosition(position);
+		items[position].dispose();
+	}
+
+	public void remove(int start, int end) {
+		checkWidget();
+		if (start > end)
+			return;
+		if (start < 0 || end >= items.length)
+			SWT.error(SWT.ERROR_INVALID_RANGE);
+
+		setLayoutDeferred(true);
+		try {
+			Item[] items = (Item[]) this.items.clone();
+			for (int i = start; i <= end; i++) {
+				items[i].dispose();
+			}
+		} finally {
+			setLayoutDeferred(false);
+		}
+	}
+
+	public void removeAll() {
+		checkWidget();
+		remove(0, items.length - 1);
+	}
+	public int getItemCount() {
+		checkWidget();
+		if (items == null)
+			return 0;
+		return items.length;
+	}
+
+	public void addSelectionListener(SelectionListener listener) {
+		checkWidget();
+		if (listener == null)
+			SWT.error(SWT.ERROR_NULL_ARGUMENT);
+		TypedListener typedListener = new TypedListener(listener);
+		addListener(SWT.Selection, typedListener);
+		addListener(SWT.DefaultSelection, typedListener);
+	}
+
+	public void removeSelectionListener(SelectionListener listener) {
+		checkWidget();
+		removeListener(SWT.Selection, listener);
+		removeListener(SWT.DefaultSelection, listener);
+	}
+
+	public RadioItem[] getItems() {
+		checkWidget();
+		if (items == null)
+			return new RadioItem[0];
+		RadioItem[] result = new RadioItem[items.length];
+		System.arraycopy(items, 0, result, 0, items.length);
+		return result;
+	}
+
+	public int indexOf(RadioItem item) {
+		checkWidget();
+		if (items == null)
+			return -1;
+		if (item == null)
+			return -1;
+		for (int i = 0; i < items.length; i++) {
+			if (items[i] == item)
+				return i;
+		}
+		return -1;
+	}
+
+	public RadioItem getSelection() {
+		checkWidget();
+		return selection;
+	}
+
+	public int getSelectionIndex() {
+		checkWidget();
+		return indexOf(selection);
+	}
+
+	public void setSelection(RadioItem item) {
+		checkWidget();
+		if (selection == item)
+			return;
+		if (selection != null)
+			selection.deselect();
+		if (item != null) {
+			if (item.getParent() != this)
+				SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+			item.select();
+		}
+	}
+
+	public void select(int index) {
+		checkWidget();
+		checkExistingPosition(index);
+		setSelection(items[index]);
+	}
+
+	public void deselectAll() {
+		checkWidget();
+		setSelection(null);
+	}
+
+	Button createButton(int itemStyle, int position) {
+		// Check add position (which may throw exception) before creating button
+		position = checkAddPosition(position);
+
+		Button button = new Button(this, computeButtonStyle(itemStyle));
+
+		if (position < items.length)
+			button.moveAbove(items[position].getButton());
+
+		layout(new Control[] { button });
+
+		return button;
+	}
+
+	private int computeButtonStyle(int itemStyle) {
+		int buttonStyle = SWT.RADIO | this.buttonStyle;
+
+		int itemStyleMask = SWT.LEFT | SWT.CENTER | SWT.RIGHT;
+		if ((itemStyle & itemStyleMask) != 0) {
+			buttonStyle &= ~itemStyleMask;
+			buttonStyle |= itemStyle;
+		}
+
+		return buttonStyle;
+	}
+
+	void addItem(RadioItem item, int position) {
+		position = checkAddPosition(position);
+		RadioItem[] newItems = new RadioItem[items == null ? 1
+				: items.length + 1];
+
+		if (items == null) {
+			items = new RadioItem[] { item };
+		} else {
+			System.arraycopy(items, 0, newItems, 0, position);
+			newItems[position] = item;
+			System.arraycopy(items, position, newItems, position + 1,
+					items.length - position);
+			items = newItems;
+		}
+	}
+
+	private int checkAddPosition(int position) {
+		if (position == -1)
+			position = getItemCount();
+		else if (position < 0 || position > getItemCount())
+			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+		return position;
+	}
+
+	void removeItem(RadioItem item) {
+		checkWidget();
+
+		int position = indexOf(item);
+		if (position != -1) {
+			RadioItem[] newItems = new RadioItem[items.length - 1];
+			System.arraycopy(items, 0, newItems, 0, position);
+			System.arraycopy(items, position + 1, newItems, position,
+					newItems.length - position);
+			items = newItems;
+		}
+
+		if (selection == item) {
+			selection = null;
+			notifyListeners(SWT.Selection, null);
+		}
+	}
+
+	void itemSelected(RadioItem item) {
+		RadioItem oldSelection = selection;
+		RadioItem newSelection = item.isSelected() ? item : null;
+		if (oldSelection == newSelection)
+			return;
+
+		selection = newSelection;
+
+		Event event = new Event();
+		event.item = selection;
+		event.index = indexOf(selection);
+
+		notifyListeners(SWT.Selection, event);
+	}
+}
diff --git a/org.eclipse.xwt/src/org/eclipse/xwt/jface/RadioGroupViewer.java b/org.eclipse.xwt/src/org/eclipse/xwt/jface/RadioGroupViewer.java
new file mode 100644
index 0000000..a2fdddd
--- /dev/null
+++ b/org.eclipse.xwt/src/org/eclipse/xwt/jface/RadioGroupViewer.java
@@ -0,0 +1,118 @@
+package org.eclipse.xwt.jface;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.viewers.AbstractListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+public class RadioGroupViewer extends AbstractListViewer {
+	private RadioGroup radioGroup;
+
+	public RadioGroupViewer(Composite parent) {
+		this(parent, SWT.NONE);
+	}
+
+	public RadioGroupViewer(Composite parent, int style) {
+		this(new RadioGroup(parent, style));
+	}
+
+	public RadioGroupViewer(RadioGroup group) {
+		Assert.isNotNull(group);
+		this.radioGroup = group;
+		hookControl(group);
+	}
+
+	public Control getControl() {
+		return radioGroup;
+	}
+
+	public RadioGroup getRadioGroup() {
+		return radioGroup;
+	}
+
+	public void reveal(Object element) {
+	}
+
+	protected void listShowSelection() {
+	}
+
+	protected void listSetSelection(int[] ixs) {
+		for (int idx = 0; idx < ixs.length; idx++) {
+			radioGroup.select(ixs[idx]);
+		}
+	}
+
+	protected int[] listGetSelectionIndices() {
+		return new int[] { radioGroup.getSelectionIndex() };
+	}
+
+	protected void listAdd(String string, int index) {
+		radioGroup.setLayoutDeferred(true);
+		try {
+			RadioItem item = new RadioItem(radioGroup, SWT.NONE, index);
+			item.setText(string);
+		} finally {
+			radioGroup.setLayoutDeferred(false);
+		}
+	}
+	protected void listDeselectAll() {
+		radioGroup.deselectAll();
+	}
+
+	protected int listGetItemCount() {
+		return radioGroup.getItemCount();
+	}
+	protected void listRemove(int index) {
+		radioGroup.remove(index);
+	}
+
+	protected void listRemoveAll() {
+		radioGroup.removeAll();
+	}
+	protected void listSetItem(int index, String string) {
+		RadioItem item = radioGroup.getItems()[index];
+		item.setText(string);
+	}
+	protected void listSetItems(String[] labels) {
+		radioGroup.removeAll();
+
+		for (int i = 0; i < labels.length; i++) {
+			RadioItem item = new RadioItem(radioGroup, SWT.NONE);
+			item.setText(labels[i]);
+		}
+	}
+
+	public void add(Object element) {
+		super.add(element);
+	}
+
+	public void add(Object[] elements) {
+		super.add(elements);
+	}
+	public void insert(Object element, int position) {
+		super.insert(element, position);
+	}
+	public Object getElementAt(int index) {
+		return super.getElementAt(index);
+	}
+	protected int indexForElement(Object element) {
+		return super.indexForElement(element);
+	}
+
+	protected int listGetTopIndex() {
+		return super.listGetTopIndex();
+	}
+
+	protected void listSetTopIndex(int index) {
+		super.listSetTopIndex(index);
+	}
+
+	public void remove(Object element) {
+		super.remove(element);
+	}
+
+	public void remove(Object[] elements) {
+		super.remove(elements);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.xwt/src/org/eclipse/xwt/jface/RadioItem.java b/org.eclipse.xwt/src/org/eclipse/xwt/jface/RadioItem.java
new file mode 100644
index 0000000..4ca28f2
--- /dev/null
+++ b/org.eclipse.xwt/src/org/eclipse/xwt/jface/RadioItem.java
@@ -0,0 +1,163 @@
+package org.eclipse.xwt.jface;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Listener;
+
+public class RadioItem extends Item {
+	private RadioGroup parent;
+	private Button button;
+
+	public RadioItem(RadioGroup parent, int style) {
+		this(parent, style, -1);
+	}
+
+	public RadioItem(final RadioGroup parent, int style, int index) {
+		super(parent, checkStyle(style), checkIndex(parent, index));
+		this.parent = parent;
+		this.button = parent.createButton(getStyle(), index);
+
+		Listener listener = new Listener() {
+			public void handleEvent(Event event) {
+				if (event.type == SWT.Selection)
+					handleSelection(event);
+				else if (event.type == SWT.Dispose)
+					handleDispose(event);
+			}
+		};
+		button.addListener(SWT.Selection, listener);
+		addListener(SWT.Dispose, listener);
+
+		parent.addItem(this, index);
+	}
+
+	private static int checkStyle(int style) {
+		int result = 0;
+		if ((style & SWT.LEFT) != 0)
+			result |= SWT.LEFT;
+		else if ((style & SWT.CENTER) != 0)
+			result |= SWT.CENTER;
+		else if ((style & SWT.RIGHT) != 0)
+			result |= SWT.RIGHT;
+		return result;
+	}
+
+	private static int checkIndex(RadioGroup parent, int position) {
+		if (position == -1)
+			return parent.getItemCount();
+		if (position < 0 || position > parent.getItemCount())
+			SWT.error(SWT.ERROR_INVALID_ARGUMENT);
+		return position;
+	}
+
+	private void handleSelection(Event event) {
+		parent.itemSelected(this);
+	}
+
+	private void handleDispose(Event event) {
+		if (parent != null) {
+			parent.removeItem(RadioItem.this);
+		}
+		if (button != null) {
+			button.dispose();
+			if (parent != null && !parent.isDisposed())
+				parent.layout(false);
+		}
+		RadioItem.this.parent = null;
+		button = null;
+	}
+
+	public RadioGroup getParent() {
+		return parent;
+	}
+
+	public String getText() {
+		checkWidget();
+		return button.getText();
+	}
+
+	public void setText(String string) {
+		checkWidget();
+		button.setText(string);
+		parent.layout(new Control[] { button });
+	}
+
+	public Image getImage() {
+		checkWidget();
+		return button.getImage();
+	}
+
+	public void setImage(Image image) {
+		checkWidget();
+		button.setImage(image);
+		parent.layout(new Control[] { button });
+	}
+
+	public Color getForeground() {
+		checkWidget();
+		return button.getForeground();
+	}
+
+	public void setForeground(Color foreground) {
+		checkWidget();
+		if (foreground == null)
+			SWT.error(SWT.ERROR_NULL_ARGUMENT);
+		button.setForeground(foreground);
+	}
+
+	public Color getBackground() {
+		checkWidget();
+		return button.getBackground();
+	}
+
+	public void setBackground(Color background) {
+		checkWidget();
+		if (background == null)
+			SWT.error(SWT.ERROR_NULL_ARGUMENT);
+		button.setBackground(background);
+	}
+
+	public Font getFont() {
+		checkWidget();
+		return button.getFont();
+	}
+
+	public void setFont(Font font) {
+		checkWidget();
+		if (font == null)
+			SWT.error(SWT.ERROR_NULL_ARGUMENT);
+		button.setFont(font);
+	}
+
+	public Button getButton() {
+		return button;
+	}
+
+	public boolean isSelected() {
+		return button.getSelection();
+	}
+
+	void select() {
+		button.setSelection(true);
+		parent.itemSelected(this);
+	}
+
+	void deselect() {
+		button.setSelection(false);
+		parent.itemSelected(this);
+	}
+
+	void clear() {
+		setText("");
+		setImage(null);
+		setFont(parent.getFont());
+		setForeground(parent.getForeground());
+		setBackground(parent.getBackground());
+	}
+}
\ No newline at end of file