/*
* Copyright (c) 2008 Stiftung Deutsches Elektronen-Synchrotron,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.csstudio.sds.ui.internal.properties;
import java.util.LinkedList;
import java.util.List;
import org.csstudio.sds.ui.SdsUiPlugin;
import org.csstudio.sds.util.DialogFontUtil;
import org.csstudio.ui.util.CustomMediaFactory;
import org.eclipse.core.runtime.Assert;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.ToolBar;
/**
* A table cell editor for values of type PointList.
*
* @author Kai Meyer
*/
public final class PointListCellEditor extends AbstractDialogCellEditor {
/**
* The current PointList value.
*/
private List<Point> _pointList;
/**
* A copy of the current PointList value.
*/
private List<Point> _orgPointList;
/**
* Creates a new string cell editor parented under the given control. The
* cell editor value is a PointList.
*
* @param parent
* The parent table.
*/
public PointListCellEditor(final Composite parent) {
super(parent, "Points");
}
/**
* {@inheritDoc}
*/
@Override
protected void openDialog(final Shell parentShell, final String dialogTitle) {
PointListInputDialog dialog = new PointListInputDialog(parentShell,dialogTitle,"Add or remove Points");
if (dialog.open()==Window.CANCEL) {
_pointList = _orgPointList;
}
}
/**
* {@inheritDoc}
*/
@Override
protected boolean shouldFireChanges() {
if (_pointList.size()==_orgPointList.size()) {
for (int i=0;i<_pointList.size();i++) {
Point p1 = _pointList.get(i);
Point p2 = _orgPointList.get(i);
if (p1.x!=p2.x || p1.y!=p2.y) {
return true;
}
}
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected Object doGetValue() {
return this.listToPointList(_pointList);
}
/**
* Creates a new PointList with the Points of the given List.
* @param list
* A {@link List} which contains the Points
* @return PointList
* The new {@link PointList}
*/
private PointList listToPointList(final List<Point> list) {
PointList result = new PointList();
for (Point p : list) {
result.addPoint(p);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
protected void doSetValue(final Object value) {
Assert.isTrue(value instanceof PointList);
PointList list = (PointList) value;
_orgPointList = new LinkedList<Point>();
_pointList = new LinkedList<Point>();
for (int i=0;i<list.size();i++) {
_pointList.add(list.getPoint(i));
_orgPointList.add(list.getPoint(i));
}
}
/**
* This class represents a Dialog to add, edit and remove Points of a PointList.
*
* @author Kai Meyer
*/
private final class PointListInputDialog extends Dialog {
/**
* The title of the dialog.
*/
private String _title;
/**
* The message to display, or <code>null</code> if none.
*/
private String _message;
/**
* The List-Widget.
*/
private ListViewer _viewer;
/**
* Adds new entries to the List.
*/
private Action _addAction;
/**
* Edits the selected entry.
*/
private Action _editAction;
/**
* Removes the selected entries from the List.
*/
private Action _removeAction;
/**
* Edits the selected entry.
*/
private Action _upAction;
/**
* Removes the selected entries from the List.
*/
private Action _downAction;
/**
* Creates an input dialog with OK and Cancel buttons. Note that the dialog
* will have no visual representation (no widgets) until it is told to open.
* <p>
* Note that the <code>open</code> method blocks for input dialogs.
* </p>
*
* @param parentShell
* the parent shell, or <code>null</code> to create a top-level
* shell
* @param dialogTitle
* the dialog title, or <code>null</code> if none
* @param dialogMessage
* the dialog message, or <code>null</code> if none
*/
public PointListInputDialog(final Shell parentShell, final String dialogTitle,
final String dialogMessage) {
super(parentShell);
_title = dialogTitle;
_message = dialogMessage;
}
/**
* {@inheritDoc}
*/
@Override
protected void configureShell(final Shell shell) {
super.configureShell(shell);
if (_title != null) {
shell.setText(_title);
}
}
/**
* {@inheritDoc}
*/
@Override
protected Control createDialogArea(final Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
composite.setLayout(new GridLayout(1, false));
if (_message != null) {
Label label = new Label(composite, SWT.WRAP);
label.setText(_message);
GridData data = new GridData(GridData.GRAB_HORIZONTAL
| GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL
| GridData.VERTICAL_ALIGN_CENTER);
data.horizontalSpan = 2;
data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
label.setLayoutData(data);
}
Composite toolBarComposite = new Composite(composite,SWT.BORDER);
GridLayout gridLayout = new GridLayout(1,false);
gridLayout.marginLeft = 0;
gridLayout.marginRight = 0;
gridLayout.marginBottom = 0;
gridLayout.marginTop = 0;
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
toolBarComposite.setLayout(gridLayout);
GridData grid = new GridData(SWT.FILL,SWT.FILL,true,true);
toolBarComposite.setLayoutData(grid);
ToolBarManager toolbarManager = new ToolBarManager(SWT.FLAT);
ToolBar toolBar = toolbarManager.createControl(toolBarComposite);
GridData gid = new GridData();
gid.horizontalAlignment = GridData.FILL;
gid.verticalAlignment = GridData.BEGINNING;
toolBar.setLayoutData(gid);
this.createActions(toolbarManager);
_viewer = this.createListViewer(toolBarComposite);
this.hookPopupMenu(_viewer);
this.hookDoubleClick(_viewer);
DialogFontUtil.setDialogFont(composite);
//applyDialogFont(composite);
return composite;
}
/**
* Creates Actions and adds them to the given {@link ToolBarManager}.
* @param manager
* The ToolBarManager, which should contain the actions
*/
private void createActions(final ToolBarManager manager) {
_addAction = new Action() {
@Override
public void run() {
openPointDialog(true);
}
};
_addAction.setText("Add "+_title);
_addAction.setToolTipText("Adds a new "+_title+" to the list");
_addAction.setImageDescriptor(CustomMediaFactory.getInstance()
.getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID,
"icons/add.gif"));
manager.add(_addAction);
_editAction = new Action() {
@Override
public void run() {
openPointDialog(false);
refreshActions();
}
};
_editAction.setText("Edit "+_title);
_editAction.setToolTipText("Edits the selected "+_title);
_editAction.setImageDescriptor(CustomMediaFactory.getInstance()
.getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID,
"icons/edit.gif"));
_editAction.setEnabled(false);
manager.add(_editAction);
_removeAction = new Action() {
@Override
public void run() {
removePoint();
refreshActions();
}
};
_removeAction.setText("Remove "+_title);
_removeAction.setToolTipText("Removes the selected "+_title+" from the list");
_removeAction.setImageDescriptor(CustomMediaFactory.getInstance()
.getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID,
"icons/delete.gif"));
_removeAction.setEnabled(false);
manager.add(_removeAction);
manager.add(new Separator());
_upAction = new Action() {
@Override
public void run() {
movePoint(true);
}
};
_upAction.setText("Move up");
_upAction.setToolTipText("Increases the index of the selected Point");
_upAction.setImageDescriptor(CustomMediaFactory.getInstance()
.getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID,
"icons/pos_up.gif"));
_upAction.setEnabled(false);
manager.add(_upAction);
_downAction = new Action() {
@Override
public void run() {
movePoint(false);
}
};
_downAction.setText("Move down");
_downAction.setToolTipText("Decreases the index of the selected Point");
_downAction.setImageDescriptor(CustomMediaFactory.getInstance()
.getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID,
"icons/pos_down.gif"));
_downAction.setEnabled(false);
manager.add(_downAction);
manager.update(true);
}
/**
* Creates the viewer for the List.
* @param parent
* The parent composite for the viewer
* @return ListViewer
* The ListViewer
*/
private ListViewer createListViewer(final Composite parent) {
final ListViewer viewer = new ListViewer(parent);
viewer.setContentProvider(new ArrayContentProvider());
viewer.setLabelProvider(new LabelProvider() {
@Override
public String getText(final Object element) {
if (element instanceof Point) {
Point p = (Point) element;
return p.toString();//p.x+","+p.y;
}
return element.toString();
}
});
viewer.setInput(_pointList.toArray(new Point[_pointList.size()]));
GridData gridData = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
gridData.verticalSpan = 6;
gridData.heightHint = 150;
viewer.getList().setLayoutData(gridData);
viewer.getList().addSelectionListener(new SelectionAdapter() {
/**
* {@inheritDoc}
*/
@Override
public void widgetSelected(final SelectionEvent e) {
refreshActions();
}
});
viewer.getList().setFocus();
return viewer;
}
/**
* Adds a Popup menu to the given ListViewer.
* @param viewer
* The ListViewer
*/
private void hookPopupMenu(final ListViewer viewer) {
MenuManager popupMenu = new MenuManager();
popupMenu.add(_addAction);
popupMenu.add(_editAction);
popupMenu.add(new Separator());
popupMenu.add(_removeAction);
popupMenu.add(new Separator());
popupMenu.add(_upAction);
popupMenu.add(_downAction);
Menu menu = popupMenu.createContextMenu(viewer.getList());
viewer.getList().setMenu(menu);
}
/**
* Adds doubleclick support to the given ListViewer.
* @param viewer
* The Listviewer
*/
private void hookDoubleClick(final ListViewer viewer) {
viewer.getControl().addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(final MouseEvent e) {
if (_viewer.getList().getSelectionCount()==1) {
_editAction.run();
} else {
_addAction.run();
}
}
});
}
/**
* Opens a Dialog for adding a new Point.
* @param isNew
* True, if a new Point should be created, false otherwise
*/
private void openPointDialog(final boolean isNew) {
int index = _viewer.getList().getItemCount();
int[] selectedIndices = _viewer.getList().getSelectionIndices();
if (selectedIndices.length>0) {
index = selectedIndices[0];
}
PointDialog dialog = new PointDialog(this.getParentShell(),"Point", null, index, isNew);
if (dialog.open()==Window.OK) {
this.setInput();
}
_viewer.getList().setSelection(index);
this.refreshActions();
}
/**
* Removes the current selected Points from the List.
*/
private void removePoint() {
if (_viewer.getList().getSelectionIndices().length>0) {
for (int i : _viewer.getList().getSelectionIndices()) {
_pointList.remove(i);
}
this.setInput();
}
refreshActions();
}
/**
* Enables or disables the RemoveButton.
*/
private void refreshActions() {
if (_viewer.getList().getItemCount()>2) {
_removeAction.setEnabled(_viewer.getList().getSelectionIndices().length>0);
} else {
_removeAction.setEnabled(false);
}
_editAction.setEnabled(_viewer.getList().getSelectionIndices().length==1);
_upAction.setEnabled(_viewer.getList().getSelectionIndices().length==1 && _viewer.getList().getSelectionIndex()>0);
_downAction.setEnabled(_viewer.getList().getSelectionIndices().length==1 && _viewer.getList().getSelectionIndex()<_viewer.getList().getItemCount()-1);
}
/**
* Moves the current selected Point one step up or down, depending on the given boolean.
* @param up
* True, if the Point should be moved up, false otherwise
*/
private void movePoint(final boolean up) {
int newIndex = _viewer.getList().getSelectionIndex();
Point point = _pointList.get(newIndex);
_pointList.remove(newIndex);
if (up) {
newIndex = newIndex-1;
} else {
newIndex = newIndex+1;
}
if (newIndex<0) {
newIndex = 0;
}
if (newIndex>_pointList.size()) {
newIndex=_pointList.size();
}
_pointList.add(newIndex, point);
this.setInput();
_viewer.getList().setSelection(newIndex);
this.refreshActions();
}
/**
* Sets the input on the viewer and refreshes it.
*/
private void setInput() {
_viewer.setInput(_pointList.toArray(new Point[_pointList.size()]));
_viewer.refresh();
}
}
/**
* This class represents a Dialog for editing a Point.
* @author Kai Meyer
*/
private final class PointDialog extends Dialog {
/**
* The title of the dialog.
*/
private String _title;
/**
* The message to display, or <code>null</code> if none.
*/
private String _message;
/**
* The input value; the empty string by default.
*/
private int _index = -1;
/**
* The Spinner for the x-value of the Point.
*/
private Spinner _xSpinner;
/**
* The Spinner for the y-value of the Point.
*/
private Spinner _ySpinner;
/**
* A boolean, which indicates if the Point is new.
*/
private boolean _isNew;
/**
* Creates an input dialog with OK and Cancel buttons. Note that the dialog
* will have no visual representation (no widgets) until it is told to open.
* <p>
* Note that the <code>open</code> method blocks for input dialogs.
* </p>
*
* @param parentShell
* the parent shell, or <code>null</code> to create a top-level
* shell
* @param dialogTitle
* the dialog title, or <code>null</code> if none
* @param dialogMessage
* the dialog message, or <code>null</code> if none
* @param initialValue
* the initial input value, or <code>null</code> if none
* @param isNew
* true id the Point is new, false otherwise
*/
public PointDialog(final Shell parentShell, final String dialogTitle,
final String dialogMessage, final int initialValue, final boolean isNew) {
super(parentShell);
_title = dialogTitle;
_message = dialogMessage;
_isNew = isNew;
if (initialValue>=0) {
_index = initialValue;
}
}
/**
* {@inheritDoc}
*/
@Override
protected void configureShell(final Shell shell) {
super.configureShell(shell);
if (_title != null) {
shell.setText(_title);
}
}
/**
* {@inheritDoc}
*/
@Override
protected Control createDialogArea(final Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
composite.setLayout(new GridLayout(2, false));
if (_message != null) {
Label label = new Label(composite, SWT.WRAP);
label.setText(_message);
GridData data = new GridData(GridData.GRAB_HORIZONTAL
| GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL
| GridData.VERTICAL_ALIGN_CENTER);
data.horizontalSpan = 2;
data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
label.setLayoutData(data);
}
Label label = new Label(composite, SWT.NONE);
label.setText("x:");
_xSpinner = new Spinner(composite, SWT.BORDER);
_xSpinner.setMaximum(10000);
_xSpinner.setMinimum(-10000);
if (_index<0 || _index >=_pointList.size()) {
_xSpinner.setSelection(0);
} else {
_xSpinner.setSelection(_pointList.get(_index).x);
}
label = new Label(composite, SWT.NONE);
label.setText("y:");
_ySpinner = new Spinner(composite, SWT.BORDER);
_ySpinner.setMaximum(10000);
_ySpinner.setMinimum(-10000);
if (_index<0 || _index >=_pointList.size()) {
_ySpinner.setSelection(0);
} else {
_ySpinner.setSelection(_pointList.get(_index).y);
}
DialogFontUtil.setDialogFont(composite);
//applyDialogFont(composite);
return composite;
}
/**
* {@inheritDoc}
*/
@Override
protected void okPressed() {
this.getButton(IDialogConstants.OK_ID).setFocus();
if (_index>=0) {
if (!_isNew) {
_pointList.remove(_index);
}
_pointList.add(_index, new Point(_xSpinner.getSelection(), _ySpinner.getSelection()));
}
//_pointList.add(new Point(_xSpinner.getSelection(), _ySpinner.getSelection()));
super.okPressed();
}
}
}