blob: 53f24ee682eaa1c282e2924f62d4f90337fa124c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.xsd.ui.internal.dialogs.types.common;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
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.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.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.wst.xsd.ui.internal.XSDEditorPlugin;
public class ComponentSelectionDialog extends Dialog {
private Display display = Display.getCurrent();
private String dialogTitle;
protected IComponentSelectionProvider provider;
private List componentTreeViewerInput;
private List masterComponentList;
// widgets
protected Composite topComposite;
protected Composite bottomComposite;
private String filterTextLabel = "";
private String componentListLabel = XSDEditorPlugin.getXSDString("_UI_LABEL_COMPONENTS");
private Text textFilter;
protected TreeViewer componentTreeViewer;
private org.eclipse.swt.widgets.List qualifierList;
protected Object componentSelection;
protected Object qualifierTextSelection;
public ComponentSelectionDialog(Shell shell, String dialogTitle, IComponentSelectionProvider provider) {
super(shell);
setShellStyle(getShellStyle() | SWT.RESIZE);
this.dialogTitle = dialogTitle;
this.provider = provider;
componentTreeViewerInput = new ArrayList();
masterComponentList = new ArrayList();
}
/*
* This method should be called before createDialogArea(Composite)
*/
public void setComponentTreeLabel(String string) {
componentListLabel = string;
}
/*
* This method should be called before createDialogArea(Composite)
*/
public void setFilterLabel(String string) {
filterTextLabel = string;
}
public void create() {
super.create();
setTextFilterFocus();
}
protected void setTextFilterFocus() {
textFilter.setFocus();
}
public Control createDialogArea(Composite parent) {
getShell().setText(dialogTitle);
Composite mainComposite = (Composite) super.createDialogArea(parent);
GridData gData = (GridData) mainComposite.getLayoutData();
gData.heightHint = 500;
gData.widthHint = 400;
// Subclasses may use this Composite to add desired widgets
//topComposite = new Composite(mainComposite, SWT.NONE);
//topComposite.setLayoutData(new GridData());
//topComposite.setLayout(new GridLayout());
// do we need to introduce a method here to contain this
// so we can add different parent other than 'topComposite'
Composite filterLabelAndTree = new Composite(mainComposite, SWT.NONE);
GridData gd = new GridData(GridData.FILL_BOTH);
GridLayout layout = new GridLayout();
layout.marginWidth = 0;
filterLabelAndTree.setLayoutData(new GridData(GridData.FILL_BOTH));
filterLabelAndTree.setLayout(layout);
// Create Text textFilter
Label filterLabel = new Label(filterLabelAndTree, SWT.NONE);
filterLabel.setText(filterTextLabel);// + "(? = any character, * = any string):"); // TODO: Externalize String
textFilter = new Text(filterLabelAndTree, SWT.SINGLE | SWT.BORDER);
textFilter.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
textFilter.addModifyListener(new TextFilterModifyAdapter());
GridData textFilterData = new GridData();
textFilterData.horizontalAlignment = GridData.FILL;
textFilterData.grabExcessHorizontalSpace = true;
textFilter.setLayoutData(textFilterData);
//textFilter.setSelection(0);
//textFilter.setf
// Create Component TreeViewer
createComponentTreeViewer(filterLabelAndTree);
// Create Qualifier List widget
Label qualifierLabel = new Label(mainComposite, SWT.NONE);
qualifierLabel.setText(XSDEditorPlugin.getXSDString("_UI_LABEL_QUALIFIER"));
qualifierList = new org.eclipse.swt.widgets.List(mainComposite, SWT.BORDER | SWT.SINGLE);
GridData data = new GridData();
data.horizontalAlignment = GridData.FILL;
data.grabExcessHorizontalSpace = true;
data.heightHint = 45;
qualifierList.setLayoutData(data);
bottomComposite = new Composite(mainComposite, SWT.NONE);
bottomComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
bottomComposite.setLayout(new GridLayout());
// Populate the Component TreeViewer via the provider
// TODO: Is this the right way to set/get the ContentProvider?
componentTreeViewer.setContentProvider(new ComponentTreeContentProvider());
componentTreeViewer.setLabelProvider(provider.getLabelProvider());
componentTreeViewer.setSorter(new ViewerSorter());
componentTreeViewer.setInput(componentTreeViewerInput);
populateMasterComponentList();
refreshTreeViewer("");
return mainComposite;
}
protected TreeViewer createTreeViewer(Composite comp, String title) {
Label label = new Label(comp, SWT.NONE);
label.setText(title);
TreeViewer treeViewer = new TreeViewer(new Tree(comp, SWT.SINGLE | SWT.BORDER));
Control treeWidget = treeViewer.getTree();
GridData gd = new GridData(GridData.FILL_BOTH);
treeWidget.setLayoutData(gd);
return treeViewer;
}
/*
* Creates the Component TreeViewer.
*/
private void createComponentTreeViewer(Composite base) {
componentTreeViewer = createTreeViewer(base, componentListLabel);
componentTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection structuredSelection = (IStructuredSelection) event.getSelection();
List qualifiers = provider.getQualifiers(structuredSelection.getFirstElement());
updateQualifierList(qualifiers);
updateCanFinish();
}
});
}
private void updateQualifierList(List qualifiers) {
qualifierList.removeAll();
Iterator it = qualifiers.iterator();
while (it.hasNext()) {
qualifierList.add(it.next().toString());
}
}
/*
* Returns the processed filter text for the Text field. Inserts a "."
* before each supported meta-character.
*/
protected String getProcessedFilterString() {
return processFilterString(textFilter.getText());
}
/*
* If supported metacharacters are used in the filter string, we need to
* insert a "." before each metacharacter.
*/
private String processFilterString(String inputString) {
if (!(inputString.equals(""))) {
inputString = insertString("*", ".", inputString);
inputString = insertString("?", ".", inputString);
inputString = inputString + ".*";
} else {
inputString = ".*";
}
return inputString.toLowerCase();
}
/*
* Helper method to insert a "." before each metacharacter in the
* search/filter string.
*/
private String insertString(String target, String newString, String string) {
ArrayList list = new ArrayList();
StringBuffer stringBuffer = new StringBuffer(string);
int index = stringBuffer.indexOf(target);
while (index != -1) {
stringBuffer = stringBuffer.insert(index, newString);
index = stringBuffer.indexOf(target, index + newString.length() + target.length());
}
return stringBuffer.toString();
}
/*
* Listens to changes made in the text filter widget
*/
private class TextFilterModifyAdapter implements ModifyListener {
public void modifyText(ModifyEvent e) {
if (e.widget == textFilter) {
if (delayedEvent != null) {
delayedEvent.CANCEL = true;
}
delayedEvent = new DelayedEvent();
Display.getCurrent().timerExec(400, delayedEvent);
}
}
}
//TODO... do we really need one instance?
private DelayedEvent delayedEvent;
/*
* Update the component TreeViewer when the text filter is modified.
* Use a DelayedEvent so we don't update on every keystroke.
*/
private class DelayedEvent implements Runnable {
public boolean CANCEL = false;
public void run() {
if (!CANCEL) {
refreshTreeViewer(getProcessedFilterString());
// Select first match
if (componentTreeViewer.getTree().getItemCount() > 0) {
TreeItem item = componentTreeViewer.getTree().getItems()[0];
TreeItem items[] = new TreeItem[1];
items[0] = item;
componentTreeViewer.getTree().setSelection(items);
}
// Update qualifierList
IStructuredSelection structuredSelection = (IStructuredSelection) componentTreeViewer.getSelection();
List qualifiers = provider.getQualifiers(structuredSelection.getFirstElement());
updateQualifierList(qualifiers);
updateCanFinish();
}
}
}
class ComponentList implements IComponentList {
private Vector objectVector = new Vector();
private long currentChangeCounter = 0;
public void addComponent(Object o) {
objectVector.add(o);
currentChangeCounter++;
doViewerUpdate();
}
private void doViewerUpdate() {
// TODO: Investigate if we should also add a timer condition??
if (currentChangeCounter == 10) {
currentChangeCounter = 0;
fireUpdateList(this);
}
}
public int size() {
return objectVector.size();
}
public List subList(int startIndex, int endIndex) {
return objectVector.subList(startIndex, endIndex);
}
public Iterator iterator() {
return objectVector.iterator();
}
}
// this method gets called from a non-ui thread so needs to call
// asyncExec to ensure the UI updates happen on the UI thread
//
protected void fireUpdateList(final ComponentList list) {
Runnable runnable = new Runnable() {
public void run(){
// add new objects
int growingListSize = list.size();
int currentSize = masterComponentList.size();
if (growingListSize > currentSize) {
masterComponentList.addAll(list.subList(currentSize, growingListSize));
}
refreshTreeViewer(getProcessedFilterString());
}
};
display.asyncExec(runnable);
}
/*
* Populate the Component TreeViewer with items.
*/
protected void populateMasterComponentList() {
masterComponentList.clear();
ILabelProvider labelProvider = provider.getLabelProvider();
final ComponentList componentList = new ComponentList();
provider.getComponents(componentList, true);
Job job = new Job("read components") {
protected IStatus run(IProgressMonitor monitor) {
try {
// this stuff gets executed on a non-UI thread
//
provider.getComponents(componentList, false);
// Do a final update of our Input for the component tree viewer.
fireUpdateList(componentList);
}
catch (Exception e) {
}
return Status.OK_STATUS;
}
};
job.schedule();
}
protected void refreshTreeViewer(String filterText) {
componentTreeViewerInput.clear();
ILabelProvider labelProvider = provider.getLabelProvider();
Pattern regex = Pattern.compile(filterText);
Iterator it = masterComponentList.iterator();
while (it.hasNext()) {
Object item = it.next();
String itemString = labelProvider.getText(item);
Matcher m = regex.matcher(itemString.toLowerCase());
if (itemString.toLowerCase().startsWith(filterText) || m.matches()) {
componentTreeViewerInput.add(item);
}
}
componentTreeViewer.refresh();
}
/*
* If there is a selection in the ComponentTreeViewer, enable OK
*/
protected void updateCanFinish() {
IStructuredSelection selection = (IStructuredSelection) componentTreeViewer.getSelection();
if (selection.getFirstElement() != null) {
getButton(IDialogConstants.OK_ID).setEnabled(true);
}
else {
getButton(IDialogConstants.OK_ID).setEnabled(false);
}
}
protected void okPressed() {
IStructuredSelection selection = (IStructuredSelection) componentTreeViewer.getSelection();
componentSelection = selection.getFirstElement();
int qualifierIndex = qualifierList.getSelectionIndex();
if (qualifierIndex < 0) {
qualifierIndex = 0;
}
qualifierTextSelection = qualifierList.getItem(qualifierIndex);
super.okPressed();
}
private class ComponentTreeContentProvider implements ITreeContentProvider {
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof List) {
return ((List) parentElement).toArray();
}
return new Object[0];
}
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
public Object getParent(Object element) {
return null;
}
public boolean hasChildren(Object element) {
if (getChildren(element).length > 0) {
return true;
}
return false;
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
public void dispose() {
}
}
}