/*
* $Id$
*
* Copyright (c) 2004-2005 by the TeXlapse Team.
* 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
*/
package net.sourceforge.texlipse.properties.editor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.sourceforge.texlipse.TexlipsePlugin;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
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.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
/**
* A FieldEditor that holds a list of key-value-pairs as its value.
*
* The list of strings is converted into a single String object by
* putting a separator character between the items while concatenating.
*
* @author Kimmo Karlsson
* @author Markus Maus
* @author Reza Esmaeili Soumeeh
* @author Ehsan Baghi
*/
public class StyleListFieldEditor extends FieldEditor implements IInputValidator {
// separator character for strings
public static final String SEPARATOR = ",";
private static final String SK_TABLE_KEY = "key";
private static final String SK_TABLE_VALUE = "value";
// component holding smart key variables table and label
private Composite table;
// smart key variable table component
private TableViewer smartKeyTable;
// for adding new variables
private Button skAddButton;
// for removing variables
private Button skRemoveButton;
// for editing existing variables
private Button skEditButton;
// the smartKeys variables
private Map smartKeys;
/**
* Smart key variable for the smart key table.
*/
class SmartKeyVariable {
// The name of the smartKeys variable
private String name;
// The value of the smartKeys variable
private String value;
/**
* Create new key/value -pair.
* @param name key
* @param value value
*/
public SmartKeyVariable(String name, String value) {
this.name = name;
this.value = value;
}
/**
* Returns this variable's name, which serves as the key in the key/value
* pair this variable represents
*
* @return this variable's name
*/
public String getName() {
return name;
}
/**
* Returns this variables value.
* @return this variable's value
*/
public String getValue() {
return value;
}
/**
* Sets this variable's name (key)
* @param name name
*/
public void setName(String name) {
this.name = name;
}
/**
* Sets this variable's value
* @param value value
*/
public void setValue(String value) {
this.value = value;
}
/**
* Returns this variable's name.
* @return this variable's name
*/
public String toString() {
return getName();
}
/**
* Compare names.
*/
public boolean equals(Object obj) {
boolean equal = false;
if (obj instanceof SmartKeyVariable) {
SmartKeyVariable var = (SmartKeyVariable)obj;
equal = var.getName().equals(name);
}
return equal;
}
/**
* @return name.hashCode()
*/
public int hashCode() {
return name.hashCode();
}
}
/**
* Content provider for the smartKeys table.
* @see org.eclipse.debug.ui.EnvironmentTab.EnvironmentVariableContentProvider
*/
class SmartKeyVariableContentProvider implements IStructuredContentProvider {
public Object[] getElements(Object inputElement) {
SmartKeyVariable[] elements = new SmartKeyVariable[0];
Map m = (Map) inputElement;
if (m != null && !m.isEmpty()) {
elements = new SmartKeyVariable[m.size()];
String[] varNames = new String[m.size()];
m.keySet().toArray(varNames);
for (int i = 0; i < m.size(); i++) {
elements[i] = new SmartKeyVariable(varNames[i], (String) m.get(varNames[i]));
}
}
return elements;
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (newInput == null){
return;
}
if (viewer instanceof TableViewer){
TableViewer tableViewer = (TableViewer) viewer;
if (tableViewer.getTable().isDisposed()) {
return;
}
tableViewer.setSorter(new ViewerSorter() {
public int compare(Viewer iviewer, Object e1, Object e2) {
if (e1 == null) {
return -1;
} else if (e2 == null) {
return 1;
} else {
return ((SmartKeyVariable)e1).getName().compareToIgnoreCase(((SmartKeyVariable)e2).getName());
}
}
});
}
}
}
/**
* Label provider for the smartKeys table.
* @see org.eclipse.debug.ui.EnvironmentTab.EnvironmentVariableLabelProvider
*/
class SmartKeyVariableLabelProvider extends LabelProvider implements ITableLabelProvider {
public String getColumnText(Object element, int columnIndex) {
String result = null;
if (element != null) {
SmartKeyVariable var = (SmartKeyVariable) element;
switch (columnIndex) {
case 0: // variable
result = var.getName();
break;
case 1: // value
result = var.getValue();
break;
}
}
return result;
}
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
}
/**
* Dialog to input variable name and value.
*/
class KeyValueInputDialog extends MessageDialog {
private IInputValidator validator;
private Text keyTextField;
protected String key;
private Text valueTextField;
protected String value;
/**
* Default constructor from super class. Do not use.
*
* @param parentShell
* @param dialogTitle
* @param dialogTitleImage
* @param dialogMessage
* @param dialogImageType
* @param dialogButtonLabels
* @param defaultIndex
*/
public KeyValueInputDialog(Shell parentShell, String dialogTitle, Image dialogTitleImage, String dialogMessage, int dialogImageType, String[] dialogButtonLabels, int defaultIndex) {
super(parentShell, dialogTitle, dialogTitleImage, dialogMessage,
dialogImageType, dialogButtonLabels, defaultIndex);
key = "";
value = "";
}
/**
* Constructor with default values for many fields.
* This is the preferred constructor.
*
* @param shell parent shell
* @param title dialog title
* @param message dialog message
* @param vali validator for the text
*/
public KeyValueInputDialog(Shell shell, String title, String message, IInputValidator vali) {
this(shell, title, null, message, QUESTION, new String[] { IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL }, 0);
validator = vali;
}
/**
* @return key
*/
public String getKey() {
return key;
}
/**
* @param name key
*/
public void setKey(String name) {
key = name;
}
/**
* @return value
*/
public String getValue() {
return value;
}
/**
* @param val value
*/
public void setValue(String val) {
value = val;
}
/**
* Creates and returns the contents of an area of the dialog which appears
* below the message and above the button bar.
*
* This implementation creates two labels and two textfields.
*
* @param parent parent composite to contain the custom area
* @return the custom area control, or <code>null</code>
*/
protected Control createCustomArea(Composite parent) {
// create the top level composite for the dialog area
Composite composite = new Composite(parent, SWT.NULL);
GridLayout layout = new GridLayout();
layout.numColumns = 2;
composite.setLayout(layout);
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
Label keyLabel = new Label(composite, SWT.LEFT);
keyLabel.setLayoutData(new GridData());
keyLabel.setText("key:");
keyTextField = new Text(composite, SWT.SINGLE | SWT.BORDER);
keyTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
keyTextField.setText(key);
keyTextField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
if (validator != null) {
key = keyTextField.getText();
if (key != null) {
key = key.trim();
}
String error = validator.isValid(key);
Button ok = getButton(IDialogConstants.OK_ID);
ok.setEnabled(error == null);
} else {
key = keyTextField.getText();
}
}});
Label valueLabel = new Label(composite, SWT.LEFT);
valueLabel.setLayoutData(new GridData());
valueLabel.setText("value:");
valueTextField = new Text(composite, SWT.SINGLE | SWT.BORDER);
valueTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
valueTextField.setText(value);
valueTextField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
value = valueTextField.getText();
}});
return composite;
}
}
/**
* Dialog to input variables from the current smartKeys.
*/
class SmartKeyVariableInputDialog extends MessageDialog {
private List skVarList;
protected int[] selections;
protected String[] items;
/**
* Default constructor from super class. Do not use.
*
* @param parentShell
* @param dialogTitle
* @param dialogTitleImage
* @param dialogMessage
* @param dialogImageType
* @param dialogButtonLabels
* @param defaultIndex
*/
public SmartKeyVariableInputDialog(Shell parentShell, String dialogTitle, Image dialogTitleImage, String dialogMessage, int dialogImageType, String[] dialogButtonLabels, int defaultIndex) {
super(parentShell, dialogTitle, dialogTitleImage, dialogMessage,
dialogImageType, dialogButtonLabels, defaultIndex);
selections = new int[0];
items = new String[0];
setShellStyle(getShellStyle() | SWT.RESIZE);
}
/**
* Constructor with default values for many fields.
* This is the preferred way to construct this class.
*
* @param shell parent shell
* @param title dialog title
* @param message dialog message
* @param items items for the list
*/
public SmartKeyVariableInputDialog(Shell shell, String title, String message, String[] items) {
this(shell, title, null, message, QUESTION, new String[] { IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL }, 0);
this.items = items;
}
/**
* Prevents the dialog from appearing too wide and narrow.
*/
protected Point getInitialSize() {
Point p = super.getInitialSize();
int sum = 0;
int maxLen = 0;
for (int i = 0; i < items.length; i++) {
int l = items[i].length();
sum += l;
if (l > maxLen) {
maxLen = l;
}
}
sum /= items.length;
FontData[] fd = skVarList.getFont().getFontData();
p.x = (p.x-100) * sum / maxLen + 100; // scale the list size down by the average length of an item
p.y += fd[0].getHeight() * 8 * 2; // add 8 lines to the default height (*2 to get it right. don't know why this is needed)
p.x *= 2; // don't know why this is needed
return p;
}
/**
* @return indexes of selected items
*/
public int[] getSelections() {
return selections;
}
/**
* Creates and returns the contents of an area of the dialog which appears
* below the message and above the button bar.
*
* This implementation creates two labels and two textfields.
*
* @param parent parent composite to contain the custom area
* @return the custom area control, or <code>null</code>
*/
protected Control createCustomArea(Composite parent) {
Composite composite = new Composite(parent, SWT.NULL);
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
composite.setLayout(new GridLayout());
skVarList = new List(composite, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
skVarList.setLayoutData(new GridData(GridData.FILL_BOTH));
skVarList.setItems(items);
skVarList.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
selections = skVarList.getSelectionIndices();
}});
return composite;
}
}
/**
* Creates a new string list field editor.
*
* @param name preference name
* @param labelText text for description label
* @param parent parent component
*/
public StyleListFieldEditor(String name, String labelText, Composite parent) {
super(name, labelText, parent);
smartKeys = new HashMap();
}
/**
* Validates the input of the dialog.
* @param newText the contents of the dialog's text field
* @return error message, or null if text is valid
*/
/**
* @return 2
*/
public int getNumberOfControls() {
return 2;
}
/**
*
* @param numColumns number of columns in the page layout
*/
protected void adjustForNumColumns(int numColumns) {
((GridData)table.getLayoutData()).horizontalSpan = numColumns - 1;
}
/**
*
* @param parent parent component
* @param numColumns number of columns in the page layout
*/
protected void doFillIntoGrid(Composite parent, int numColumns) {
table = createTable(parent);
((GridData)table.getLayoutData()).horizontalSpan = numColumns - 1;
createButtons(parent);
}
/**
* @see org.eclipse.debug.ui.EnvironmentTab#createEnvironmentTable(org.eclipse.swt.widgets.Composite)
* @param parent parent component
* @return table container
*/
protected Composite createTable(Composite parent) {
Font font = parent.getFont();
// Create table composite
Composite tableComposite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
GridData gridData = new GridData(GridData.FILL_BOTH);
gridData.heightHint = 150;
tableComposite.setLayout(layout);
tableComposite.setLayoutData(gridData);
tableComposite.setFont(font);
// Create label
getLabelControl(tableComposite);
// Create table
smartKeyTable = new TableViewer(tableComposite, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
Table table = smartKeyTable.getTable();
TableLayout tableLayout = new TableLayout();
table.setLayout(tableLayout);
table.setHeaderVisible(true);
table.setFont(font);
gridData = new GridData(GridData.FILL_BOTH);
smartKeyTable.getControl().setLayoutData(gridData);
smartKeyTable.setContentProvider(new SmartKeyVariableContentProvider());
smartKeyTable.setLabelProvider(new SmartKeyVariableLabelProvider());
smartKeyTable.setColumnProperties(new String[] { SK_TABLE_KEY, SK_TABLE_VALUE });
smartKeyTable.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
handleTableSelectionChanged(event);
}
});
smartKeyTable.addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
if (!smartKeyTable.getSelection().isEmpty()) {
handleSkEditButtonSelected();
}
}
});
// Create columns
ColumnWeightData columnLayout = new ColumnWeightData(50);
tableLayout.addColumnData(columnLayout);
TableColumn tc = new TableColumn(table, SWT.NONE, 0);
tc.setResizable(columnLayout.resizable);
tc.setText(TexlipsePlugin.getResourceString("preferenceKeyValueTableColumn1"));
columnLayout = new ColumnWeightData(50);
tableLayout.addColumnData(columnLayout);
tc = new TableColumn(table, SWT.NONE, 1);
tc.setResizable(columnLayout.resizable);
tc.setText(TexlipsePlugin.getResourceString("preferenceKeyValueTableColumn2"));
return tableComposite;
}
/**
*
* @param parent parent component
* @return button container
*/
protected Composite createButtons(Composite parent) {
Composite buttonComposite = new Composite(parent, SWT.NULL);
buttonComposite.setLayoutData(new GridData(GridData.FILL_VERTICAL));
buttonComposite.setLayout(new GridLayout());
Label empty = new Label(buttonComposite, SWT.NONE);
empty.setLayoutData(new GridData());
skAddButton = new Button(buttonComposite, SWT.PUSH);
skAddButton.setText(TexlipsePlugin.getResourceString("preferenceKeyValueAddButton"));
skAddButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
skAddButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
handleSkAddButtonSelected();
}});
skRemoveButton = new Button(buttonComposite, SWT.PUSH);
skRemoveButton.setText(TexlipsePlugin.getResourceString("preferenceKeyValueRemoveButton"));
skRemoveButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
skRemoveButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
handleSkRemoveButtonSelected();
}});
skRemoveButton.setEnabled(false);
skEditButton = new Button(buttonComposite, SWT.PUSH);
skEditButton.setText(TexlipsePlugin.getResourceString("preferenceKeyValueEditButton"));
skEditButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
skEditButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
handleSkEditButtonSelected();
}});
skEditButton.setEnabled(false);
Label filler = new Label(buttonComposite, SWT.NONE);
filler.setLayoutData(new GridData(GridData.FILL_VERTICAL));
return buttonComposite;
}
/**
* Handle add-button presses.
*/
private void handleSkAddButtonSelected() {
KeyValueInputDialog dialog =
new KeyValueInputDialog(getLabelControl().getShell(),
TexlipsePlugin.getResourceString("preferenceKeyValueAddDialogTitle"),
TexlipsePlugin.getResourceString("preferenceKeyValueAddDialogLabel"), this);
int code = dialog.open();
if (code == KeyValueInputDialog.OK) {
String key = dialog.getKey();
if (key == null || key.length() == 0) {
return;
}
String value = dialog.getValue();
smartKeys.put(key, value);
smartKeyTable.add(new SmartKeyVariable(key, value));
}
}
/**
* Handle remove-button presses.
*/
private void handleSkRemoveButtonSelected() {
IStructuredSelection sel = (IStructuredSelection) smartKeyTable.getSelection();
smartKeyTable.getControl().setRedraw(false);
for (Iterator i = sel.iterator(); i.hasNext(); ) {
SmartKeyVariable var = (SmartKeyVariable) i.next();
smartKeyTable.remove(var);
smartKeys.remove(var.getName());
}
smartKeyTable.getControl().setRedraw(true);
}
/**
* Handle edit-button presses.
*/
private void handleSkEditButtonSelected() {
IStructuredSelection sel = (IStructuredSelection) smartKeyTable.getSelection();
SmartKeyVariable var = (SmartKeyVariable) sel.iterator().next();
if (var == null) {
return;
}
KeyValueInputDialog dialog =
new KeyValueInputDialog(getLabelControl().getShell(),
TexlipsePlugin.getResourceString("preferenceKeyValueEditDialogTitle"),
TexlipsePlugin.getResourceString("preferenceKeyValueEditDialogLabel"), this);
dialog.setKey(var.getName());
dialog.setValue(var.getValue());
int code = dialog.open();
if (code == KeyValueInputDialog.OK) {
String key = dialog.getKey();
if (key == null || key.length() == 0) {
return;
}
String value = dialog.getValue();
smartKeyTable.remove(var);
smartKeys.remove(var.getName());
smartKeys.put(key, value);
smartKeyTable.add(new SmartKeyVariable(key, value));
}
}
/**
* Responds to a selection changed event in the smartKeys table
* @param event the selection change event
*/
protected void handleTableSelectionChanged(SelectionChangedEvent event) {
int size = ((IStructuredSelection)event.getSelection()).size();
skEditButton.setEnabled(size == 1);
skRemoveButton.setEnabled(size > 0);
}
/**
* Loads the smartKeys variables from preference store.
*/
protected void doLoad() {
doLoadFrom(getPreferenceStore().getString(getPreferenceName()));
}
/**
* Loads the smartKeys variables from the given string.
* @param str
*/
protected void doLoadFrom(String str) {
smartKeys.clear();
if (str == null) {
return;
}
String[] binds = str.split(SEPARATOR);
if (binds == null) {
return;
}
for (int i = 0; i < binds.length; i++) {
int index = binds[i].indexOf('=');
if (index <= 0) {
continue;
}
smartKeys.put(binds[i].substring(0, index),
binds[i].substring(index+1));
}
smartKeyTable.setInput(smartKeys);
}
/**
* Loads the default smartKeys variables from preference store.
*/
protected void doLoadDefault() {
doLoadFrom(getPreferenceStore().getDefaultString(getPreferenceName()));
}
/**
* Stores the smartKeys variables to single string.
*/
protected void doStore() {
StringBuffer sb = new StringBuffer();
String[] keys = (String[]) smartKeys.keySet().toArray(new String[0]);
for (int i = 0; i < keys.length; i++) {
sb.append(keys[i]);
sb.append('=');
sb.append(smartKeys.get(keys[i]));
if (i < keys.length-1) {
sb.append(',');
}
}
getPreferenceStore().setValue(getPreferenceName(), sb.toString());
}
public String isValid(String newText) {
return null;
}
}