/*******************************************************************************
* Copyright (c) 2004, 2010 BREDEX GmbH.
* 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:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.client.ui.rcp.widgets;
import java.util.Arrays;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.eclipse.jface.bindings.keys.KeySequenceText;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jubula.client.ui.rcp.i18n.Messages;
import org.eclipse.jubula.client.ui.rcp.widgets.ModifiableListObservable.IContentAddedListener;
import org.eclipse.jubula.client.ui.rcp.widgets.ModifiableListObservable.IContentChangedListener;
import org.eclipse.jubula.client.ui.rcp.widgets.ModifiableListObservable.IContentRemovedListener;
import org.eclipse.jubula.client.ui.rcp.widgets.ModifiableListObservable.IOptionalButtonSelectedListener;
import org.eclipse.jubula.client.ui.rcp.widgets.ModifiableListObservable.ISelectionChangedListener;
import org.eclipse.jubula.client.ui.utils.LayoutUtil;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
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.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author BREDEX GmbH
* @created Aug 29, 2008
*/
public class ModifiableTriggerList extends Composite implements
IModifiableListObservable {
/** for log messages */
private static Logger log =
LoggerFactory.getLogger(ModifiableTriggerList.class);
/** GUI component */
private Text m_editor;
/** GUI component */
private Button m_addButton;
/** GUI component */
private List m_list;
/** GUI component */
private Button m_changeButton;
/** GUI component */
private Button m_removeButton;
/** optional button */
private Button m_optionalButton;
/** object for execution of all operations regarding to observation of
* this class
*/
private IModifiableListObservable m_observable;
/** determines, if the list might be empty or not */
private boolean m_isEmptyListAllowed;
/**
* The manager for the text widget that traps incoming key events. This
* manager should be used to access the widget, rather than accessing the
* widget directly.
*/
private KeySequenceText m_textTriggerSequenceManager;
/**
* @param parent see Composite
* @param style see Composite
* @param headerText the text to display
* and change operations
* @param values initial values to display
* @param emptyListAllowed allows, that the list is empty or not
*/
public ModifiableTriggerList(
Composite parent,
int style,
String headerText,
java.util.Set<String> values,
boolean emptyListAllowed) {
super(parent, style);
if (!emptyListAllowed && values != null && values.isEmpty()) {
String msg = Messages.ValuesForListMustNotBeEmpty
+ StringConstants.DOT;
log.error(msg);
throw new IllegalArgumentException(msg);
}
m_observable = new ModifiableListObservable();
m_isEmptyListAllowed = emptyListAllowed;
initControls(headerText, values);
enableButtons();
addListeners();
}
// code for delegation of all operations regarding to observation
// --------------------------------------------------------------
/**
* @param listener listener for add events in the container
*/
public void addContentAddedListener(IContentAddedListener listener) {
m_observable.addContentAddedListener(listener);
}
/**
* @param listener listener for modification of an item in the container
*/
public void addContentChangedListener(IContentChangedListener listener) {
m_observable.addContentChangedListener(listener);
}
/**
* @param listener listener for removal of an item in the container
*/
public void addContentRemovedListener(IContentRemovedListener listener) {
m_observable.addContentRemovedListener(listener);
}
/**
* @param listener listener for change of selection in the container
*/
public void addSelectionChangedListener(
ISelectionChangedListener listener) {
m_observable.addSelectionChangedListener(listener);
}
/**
* @param listener listener for selection of optional button
*/
public void addOptionalButtonSelectedListener(
IOptionalButtonSelectedListener listener) {
m_observable.addOptionalButtonSelectedListener(listener);
}
/**
* @param listener listener for add events in the container
*/
public void removeContentAddedListener(IContentAddedListener listener) {
m_observable.removeContentAddedListener(listener);
}
/**
* @param listener listener for modification of an item in the container
*/
public void removeContentChangedListener(IContentChangedListener listener) {
m_observable.removeContentChangedListener(listener);
}
/**
* @param listener listener for removal of an item in the container
*/
public void removeContentRemovedListener(IContentRemovedListener listener) {
m_observable.removeContentRemovedListener(listener);
}
/**
* @param listener listener for change of selection in the container
*/
public void removeSelectionChangedListener(
ISelectionChangedListener listener) {
m_observable.removeSelectionChangedListener(listener);
}
/**
* @param listener listener for selection of optional button
*/
public void removeOptionalButtonSelectedListener(
IOptionalButtonSelectedListener listener) {
m_observable.removeOptionalButtonSelectedListener(listener);
}
/**
* @param newValue newly added content
*/
public void fireContentAdded(String newValue) {
m_observable.fireContentAdded(newValue);
}
/**
* @param oldValue this value was just changed
* @param newValue this is the new value
*/
public void fireContentChanged(String oldValue, String newValue) {
m_observable.fireContentChanged(oldValue, newValue);
}
/**
* @param oldValue the value which was just removed from the list
*/
public void fireContentRemoved(String oldValue) {
m_observable.fireContentRemoved(oldValue);
}
/**
* @param value which value is selected
*/
public void fireSelectionChanged(String value) {
m_observable.fireSelectionChanged(value);
}
/**
*
*/
public void fireOptionalButtonSelected() {
m_observable.fireOptionalButtonSelected();
}
// end code for delegation of all operations regarding to observation
// -------------------------------------------------------------------
/**
* tooltip Listener;
*/
private abstract class ToolTipListener implements Listener {
/***/
private Control m_toolTipOwner;
/***/
private Text m_toolTipContent;
/***/
private Shell m_tip;
/***/
private Listener m_labelListener = new LabelListener();
/**
* constructor
* @param c Control
*/
public ToolTipListener(Control c) {
m_toolTipOwner = c;
c.addListener(SWT.MouseHover, this);
c.addListener(SWT.Dispose, this);
c.addListener(SWT.KeyDown, this);
c.addListener(SWT.MouseMove, this);
c.addListener(SWT.Selection, this);
c.addListener(SWT.FocusOut, this);
}
/**
* abstract getText
* @param event
* Event
* @return
* String
*/
public abstract String getText(Event event);
/**
* @return
* Owner of Tooltip
*/
public Control getToolTipOwner() {
return m_toolTipOwner;
}
/**
* switch event
* @param event Event
*/
public void handleEvent(Event event) {
switch (event.type) {
case SWT.Dispose:
case SWT.KeyDown:
case SWT.FocusOut:
case SWT.Selection:
case SWT.MouseMove: {
if (m_tip == null) {
break;
}
m_tip.dispose();
m_tip = null;
m_toolTipContent = null;
break;
}
case SWT.MouseHover: {
if (m_tip != null) {
m_tip.dispose();
}
Point point = toDisplay(m_toolTipOwner.getLocation());
m_tip = new Shell(m_toolTipOwner.getShell(),
SWT.ON_TOP | SWT.TOOL);
FillLayout layout = new FillLayout();
layout.marginHeight = 2;
layout.marginWidth = 2;
m_tip.setLayout(layout);
m_toolTipContent = new Text(m_tip, LayoutUtil.MULTI_TEXT
| SWT.READ_ONLY);
m_tip.setForeground(m_toolTipOwner.getDisplay()
.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
m_tip.setBackground(m_toolTipOwner.getDisplay()
.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
m_toolTipContent.setForeground(m_toolTipOwner.getDisplay()
.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
m_toolTipContent.setBackground(m_toolTipOwner.getDisplay()
.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
String text = getText(event);
if (text.length() == 0) {
return;
}
m_toolTipContent.setText(text);
m_toolTipContent.addListener(SWT.MouseExit,
m_labelListener);
m_toolTipContent.addListener(SWT.MouseDown,
m_labelListener);
m_toolTipContent.addListener(SWT.FocusOut,
m_labelListener);
m_tip.setBounds(m_toolTipOwner.getBounds());
int height = Dialog.convertHeightInCharsToPixels(
LayoutUtil.getFontMetrics(m_toolTipContent),
m_toolTipContent.getLineCount() + 1);
Rectangle itemBounds = m_toolTipOwner.getBounds();
m_tip.setBounds(point.x + event.x,
point.y + event.y + 20,
itemBounds.width, height);
m_tip.setVisible(true);
}
default : {
break;
}
}
}
/**
*
* @author BREDEX GmbH
* @created 13.02.2006
*/
private class LabelListener implements Listener {
/**
* {@inheritDoc}
*/
public void handleEvent(Event event) {
Text label = (Text) event.widget;
Shell shell = label.getShell();
switch (event.type) {
case SWT.FocusOut:
case SWT.MouseExit:
shell.dispose();
break;
default: {
break;
}
}
}
}
}
/**
* @author BREDEX GmbH
* @created 12.06.2006
*/
public class EditorModified implements ModifyListener {
/**
* {@inheritDoc}
* @param e
*/
public void modifyText(ModifyEvent e) {
handleEditorChanged();
}
/**
*
*/
private void handleEditorChanged() {
enableButtons();
}
}
/**
* @author BREDEX GmbH
* @created 12.06.2006
*/
public class ItemSelected implements SelectionListener {
/**
* {@inheritDoc}
* @param e
*/
public void widgetSelected(SelectionEvent e) {
handleListSelection();
}
/**
* {@inheritDoc}
* @param e
*/
public void widgetDefaultSelected(SelectionEvent e) {
// nothing to be done, this component doesn't have a default button.
}
/**
*
*/
@SuppressWarnings("synthetic-access")
public void handleListSelection() {
if (m_list.getSelectionCount() > 0) {
final String selection = m_list.getSelection()[0];
m_editor.setText(selection);
fireSelectionChanged(selection);
}
enableButtons();
}
}
/**
* @author BREDEX GmbH
* @created 12.06.2006
*/
public class AddSelected implements SelectionListener {
/**
* {@inheritDoc}
* @param e
*/
public void widgetSelected(SelectionEvent e) {
handleAdd();
}
/**
* {@inheritDoc}
* @param e
*/
public void widgetDefaultSelected(SelectionEvent e) {
// nothing to be done, this component doesn't have a default button.
}
/**
* handle the add button
*/
@SuppressWarnings("synthetic-access")
private void handleAdd() {
String newValue = m_editor.getText();
if (!StringUtils.isEmpty(newValue)) {
final java.util.List<String> listContent =
Arrays.asList(m_list.getItems());
if (!(listContent.contains(newValue))) {
SortedSet<String> content =
new TreeSet<String>(listContent);
content.add(newValue);
setValues(content);
setSelection(m_list.indexOf(newValue));
m_list.showSelection();
fireContentAdded(newValue);
}
}
}
}
/**
* @author BREDEX GmbH
* @created 12.06.2006
*/
public class RemoveSelected implements SelectionListener {
/**
* {@inheritDoc}
* @param e
*/
public void widgetSelected(SelectionEvent e) {
handleRemove();
}
/**
* {@inheritDoc}
* @param e
*/
public void widgetDefaultSelected(SelectionEvent e) {
// nothing to be done, this component doesn't have a default button.
}
/**
* handle the remove button
*
*/
private void handleRemove() {
final int selectionIndex = m_list.getSelectionIndex();
if (selectionIndex != -1) {
String oldValue = m_list.getItem(selectionIndex);
m_list.remove(selectionIndex);
enableButtons();
fireContentRemoved(oldValue);
}
}
}
/**
* @author BREDEX GmbH
* @created 12.06.2006
*/
@SuppressWarnings("synthetic-access")
public class ChangeSelected implements SelectionListener {
/**
* {@inheritDoc}
* @param e
*/
public void widgetSelected(SelectionEvent e) {
handleChange();
}
/**
* {@inheritDoc}
* @param e
*/
public void widgetDefaultSelected(SelectionEvent e) {
// nothing to be done, this component doesn't have a default button.
}
/**
* handle the change button
*/
private void handleChange() {
final int selectionIndex = m_list.getSelectionIndex();
String oldValue = m_list.getItem(selectionIndex);
if (selectionIndex != -1) {
final String newValue = m_editor.getText();
final java.util.List<String> listContent =
Arrays.asList(m_list.getItems());
if (!(listContent.contains(newValue))) {
SortedSet<String> content =
new TreeSet<String>(listContent);
content.remove(oldValue);
content.add(newValue);
setValues(content);
m_list.select(m_list.indexOf(newValue));
m_list.showSelection();
m_editor.setText(newValue);
fireContentChanged(oldValue, newValue);
}
}
}
}
/**
* @author BREDEX GmbH
* @created 19.06.2006
*/
public class OptionalButtonSelected implements SelectionListener {
/**
* {@inheritDoc}
*/
public void widgetSelected(SelectionEvent e) {
handleOptionalButtonSelected();
}
/**
* callback method
*/
private void handleOptionalButtonSelected() {
fireOptionalButtonSelected();
}
/**
* {@inheritDoc}
*/
public void widgetDefaultSelected(SelectionEvent e) {
// nothing
}
}
/**
*
*/
private void enableButtons() {
boolean validSelection = m_list.getSelectionIndex() != -1;
boolean emptyEditor = StringConstants.EMPTY.equals(m_editor.getText());
if (!m_isEmptyListAllowed && m_list.getItemCount() <= 1) {
m_removeButton.setEnabled(false);
} else {
m_removeButton.setEnabled(validSelection);
}
m_changeButton.setEnabled(validSelection && !emptyEditor);
m_addButton.setEnabled(!emptyEditor);
}
/**
* set the internal listeners
*/
private void addListeners() {
m_addButton.addSelectionListener(new AddSelected());
m_removeButton.addSelectionListener(new RemoveSelected());
m_changeButton.addSelectionListener(new ChangeSelected());
m_list.addSelectionListener(new ItemSelected());
m_editor.addModifyListener(new EditorModified());
if (m_optionalButton != null) {
m_optionalButton.addSelectionListener(new OptionalButtonSelected());
}
}
/**
* create all controls for this component
* @param headerText the text to display
* @param values initial values to display
*/
private void initControls(String headerText, Set <String> values) {
FormLayout layout = new FormLayout();
layout.marginHeight = 5;
layout.marginWidth = 10;
this.setLayout(layout);
Label header = new Label(this, SWT.NONE);
header.setText(headerText);
FormData headerData = new FormData();
headerData.left = new FormAttachment(0, 0);
headerData.top = new FormAttachment(0, 0);
headerData.right = new FormAttachment(100, 0);
header.setLayoutData(headerData);
m_editor = new Text(this, SWT.BORDER);
FormData editorData = new FormData();
editorData.left = new FormAttachment(0, 0);
editorData.top = new FormAttachment(header, 5, SWT.BOTTOM);
m_editor.setLayoutData(editorData);
m_textTriggerSequenceManager = new KeySequenceText(m_editor);
m_textTriggerSequenceManager.setKeyStrokeLimit(1);
m_addButton = new Button(this, SWT.PUSH);
m_addButton.setText(Messages.ModifiableListAdd);
FormData addButtonData = new FormData();
m_addButton.setLayoutData(addButtonData);
editorData.right = new FormAttachment(74, 0);
addButtonData.top = new FormAttachment(m_editor, -2, SWT.TOP);
addButtonData.left = new FormAttachment(76, 0);
addButtonData.right = new FormAttachment(100, 0);
createButtonGroup();
m_list = new List(this, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER);
addToolTipListener();
FormData listData = new FormData();
listData.left = new FormAttachment(0, 0);
listData.top = new FormAttachment(m_addButton, 10, SWT.BOTTOM);
listData.right = new FormAttachment(m_addButton, 0, SWT.RIGHT);
listData.bottom = new FormAttachment(m_changeButton, -10, SWT.TOP);
m_list.setLayoutData(listData);
setValues(values);
// pack(true);
}
/**
* create a group with edit and remove button
*/
private void createButtonGroup() {
m_changeButton = new Button(this, SWT.NONE);
m_changeButton.setText(Messages.ModifiableListEdit);
FormData changeBtData = new FormData();
changeBtData.left = new FormAttachment(0, 0);
changeBtData.top = new FormAttachment(100, -30);
changeBtData.right = new FormAttachment(49, 0);
m_changeButton.setLayoutData(changeBtData);
m_removeButton = new Button(this, SWT.NONE);
m_removeButton.setText(Messages.ModifiableListRemove);
FormData removeBtData = new FormData();
removeBtData.left = new FormAttachment(51, 0);
removeBtData.top = new FormAttachment(m_changeButton, 0, SWT.TOP);
removeBtData.right = new FormAttachment(100, 0);
m_removeButton.setLayoutData(removeBtData);
}
/**
* adds a tooltip listener
*
*/
private void addToolTipListener() {
new ToolTipListener(m_list) {
@Override
public String getText(Event event) {
List list = ((List)getToolTipOwner());
int start = list.getTopIndex();
int heightPerItem = list.getItemHeight();
int itemIndex = start + event.getBounds().y / heightPerItem;
if (itemIndex >= list.getItemCount()) {
return StringConstants.EMPTY;
}
String item = list.getItem(itemIndex);
return item;
}
};
}
/**
* @param values values to set
*/
public void setValues(Set <String> values) {
m_list.removeAll();
m_editor.setText(StringConstants.EMPTY);
if (values != null) {
for (String value : values) {
m_list.add(value);
}
}
enableButtons();
}
/**
* @return values values of List
*/
public String[] getValues() {
return m_list.getItems();
}
/**
* @param pos index for selection to set
*/
public void setSelection(int pos) {
Validate.isTrue(pos >= 0, "Invalid index for selection."); //$NON-NLS-1$
if (m_list.getItemCount() > pos) {
m_list.setSelection(pos);
fireSelectionChanged(m_list.getSelection()[0]);
enableButtons();
}
}
/**
* @param isEnabled flag to set the enabled status
*/
public void setOptionalButtonEnabled(boolean isEnabled) {
if (m_optionalButton != null) {
m_optionalButton.setEnabled(isEnabled);
}
}
/**
* @param value value to set in textfield
*/
public void setEditorText(String value) {
m_editor.setText(value);
}
/**
* @return number of items in list
*/
public int getItemCount() {
return m_list.getItemCount();
}
}