blob: 2057faf96591f62727040d35114ea35b5e685109 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 John Krasnay 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:
* John Krasnay - initial implementation
* Holger Voormann
*******************************************************************************/
package org.eclipse.wst.xml.vex.ui.internal.swt;
import java.text.MessageFormat;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.PopupDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.StyledString.Styler;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
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.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.TextStyle;
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.Text;
import org.eclipse.wst.xml.vex.core.internal.dom.Element;
import org.eclipse.wst.xml.vex.core.internal.provisional.dom.I.VEXElement;
import org.eclipse.wst.xml.vex.ui.internal.Icon;
import org.eclipse.wst.xml.vex.ui.internal.VexPlugin;
import org.eclipse.wst.xml.vex.ui.internal.editor.Messages;
/**
* Content assist dialog that is popped up to show a list of actions to select
* from. The input field at the top above the list could be used to filter the
* content.
*/
public class ContentAssist extends PopupDialog {
private static final String SETTINGS_SECTION =
"contentAssistant"; //$NON-NLS-1$
private final VexWidget vexWidget;
private final AbstractVexAction[] actions;
private final boolean autoExecute;
private final Point location;
private Text textWidget;
private TableViewer viewer;
private Font boldFont;
/**
* Constructs a new content assist dialog which can be opened by
* {@link #open()}.
*
* @param vexWidget the vex widget this content assist belongs to
* @param actions list of actions to select from
* @param autoExecute if {@code true} and if there is only one action then
* {@link #open()} does not show dialog but executes the
* only action
*/
private ContentAssist(VexWidget vexWidget,
AbstractVexAction[] actions,
boolean autoExecute) {
super(vexWidget.getShell(),
SWT.RESIZE,
true, // take focus on open
true, // persist size
false, // persist location
false, // show dialog menu
false, // show persist actions
null, // title
null); // footer line
this.vexWidget = vexWidget;
this.actions = actions;
this.autoExecute = autoExecute;
location = vexWidget.toDisplay(vexWidget.getLocationForContentAssist());
}
public int open() {
if (autoExecute && actions.length == 1) {
actions[0].execute(vexWidget);
return Window.OK;
}
return super.open();
}
@Override
protected IDialogSettings getDialogSettings() {
IDialogSettings root = VexPlugin.getInstance().getDialogSettings();
IDialogSettings settings = root.getSection(SETTINGS_SECTION);
if (settings == null) {
settings = root.addNewSection(SETTINGS_SECTION);
}
return settings;
}
@Override
protected Color getBackground() {
String colorId = JFacePreferences.CONTENT_ASSIST_BACKGROUND_COLOR;
return JFaceResources.getColorRegistry().get(colorId);
}
@Override
protected Control createDialogArea(Composite parent) {
// dialog area panel
Composite composite = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults()
.extendedMargins(0, 0, 4, 0)
.applyTo(composite);
// 1. input field
textWidget = new Text(composite, SWT.SINGLE);
GridDataFactory.fillDefaults().grab(true, false).applyTo(textWidget);
textWidget.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
repopulateList();
}
});
textWidget.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.CR) {
doAction();
} else if ( e.widget == textWidget
&& e.keyCode == SWT.ARROW_DOWN) {
viewer.getControl().setFocus();
}
}
});
// 2. separator
int separatorStyle = SWT.SEPARATOR | SWT.HORIZONTAL | SWT.LINE_DOT;
Label separator = new Label(composite, separatorStyle);
GridDataFactory.fillDefaults().grab(true, false).applyTo(separator);
// 3. list of proposals
viewer = new TableViewer(composite, SWT.H_SCROLL | SWT.V_SCROLL);
Control viewerControl = viewer.getControl();
GridDataFactory.fillDefaults().grab(true, true).applyTo(viewerControl);
boldFont = getModifiedFont(viewerControl.getFont(), SWT.BOLD);
viewer.setLabelProvider(new MyLabelProvider());
viewer.setContentProvider(new ArrayContentProvider());
viewer.getTable().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
doAction();
}
});
viewer.getTable().addMouseListener(new MouseAdapter() {
@Override
public void mouseUp(MouseEvent e) {
if (e.button == 1) {
doAction();
}
}
});
// fill with content
repopulateList();
return composite;
}
@Override
protected Point getDefaultLocation(Point initialSize) {
return location;
}
@Override
public boolean close() {
if (boldFont != null) {
boldFont.dispose();
}
return super.close();
}
/**
* Perform the action that is currently selected in the tree view, if any,
* and close the dialog.
*/
private void doAction() {
ISelection selection = viewer.getSelection();
if (selection instanceof StructuredSelection) {
Object first = ((StructuredSelection) selection).getFirstElement();
if (first instanceof AbstractVexAction) {
((AbstractVexAction) first).execute(vexWidget);
}
}
close();
}
private void repopulateList() {
String filterText = textWidget.getText();
List<AbstractVexAction> actionList = new LinkedList<AbstractVexAction>();
for (int i = 0; i < actions.length; i++) {
AbstractVexAction action = actions[i];
if (action.getText().contains(filterText)) {
actionList.add(action);
}
}
viewer.setInput(actionList.toArray(new AbstractVexAction[actionList.size()]));
viewer.getTable().setSelection(0);
}
private class MyLabelProvider extends StyledCellLabelProvider {
private final Styler boldStyler = new Styler() {
public void applyStyles(TextStyle textStyle) {
textStyle.font = boldFont;
}
};
@Override
public void update(ViewerCell cell) {
AbstractVexAction action = (AbstractVexAction) cell.getElement();
String filterText = textWidget.getText();
String text = action.getText();
StyledString styledString = new StyledString(action.getText());
// show matching text in bold
if (text.contains(filterText)) {
int start = text.indexOf(filterText);
int end = start + filterText.length();
styledString = new StyledString(text.substring(0, start));
styledString.append(text.substring(start, end), boldStyler);
styledString.append(text.substring(end));
}
cell.setText(styledString.toString());
cell.setStyleRanges(styledString.getStyleRanges());
cell.setImage(Icon.get(action.getImage()));
super.update(cell);
}
}
private static abstract class AbstractVexAction {
private final VexWidget widget;
private final String text;
private final String parameter;
private final Icon image;
public AbstractVexAction(VexWidget widget,
String text,
Icon image) {
this(widget, text, null, image);
}
public AbstractVexAction(VexWidget widget,
String text,
String parameter,
Icon image) {
this.widget = widget;
this.text = text;
this.parameter = parameter;
this.image = image;
}
abstract void execute(VexWidget vexWidget);
public VexWidget getWidget() {
return widget;
}
public String getText() {
return text;
}
public String getParameter() {
return parameter;
}
public Icon getImage() {
return image;
}
}
/**
* Shows the content assist to add a new element.
*
* @param widget the VexWidget which hosts the content assist
*/
public static void openAddElementsContentAssist(VexWidget widget) {
AbstractVexAction[] addActions = computeAddElementsActions(widget);
ContentAssist assist = new ContentAssist(widget,
addActions,
true);
assist.open();
}
/**
* Shows the content assist to convert current element.
*
* @param widget the VexWidget which hosts the content assist
*/
public static void openQuickFixContentAssist(VexWidget widget) {
AbstractVexAction[] quickFixActions = computeQuickFixActions(widget);
ContentAssist assist = new ContentAssist(widget,
quickFixActions,
true);
assist.open();
}
private static AbstractVexAction[] computeAddElementsActions(VexWidget widget) {
String[] names = widget.getValidInsertElements();
AbstractVexAction[] actions = new AbstractVexAction[names.length];
for (int i = 0; i < names.length; i++) {
actions[i] = new AbstractVexAction(widget, names[i], Icon.ELEMENT) {
public void execute(VexWidget vexWidget) {
getWidget().insertElement(new Element(getText()));
}
};
}
return actions;
}
private static AbstractVexAction[] computeQuickFixActions(VexWidget widget) {
String[] names = widget.getValidMorphElements();
AbstractVexAction[] actions = new AbstractVexAction[names.length];
int caretOffset = widget.getCaretOffset();
VEXElement element = widget.getDocument().getElementAt(caretOffset);
String sourceName = element.getName();
for (int i = 0; i < names.length; i++) {
String message = Messages.getString(
"command.convertElement.dynamicCommandName"); //$NON-NLS-1$
String text = MessageFormat.format(message, sourceName, names[i]);
Icon icon = Icon.CONVERT;
actions[i] = new AbstractVexAction(widget, text, names[i], icon) {
public void execute(VexWidget vexWidget) {
getWidget().morph(getParameter());
}
};
}
return actions;
}
private static Font getModifiedFont(Font baseFont, int additionalStyle) {
FontData[] baseData = baseFont.getFontData();
FontData[] styleData = new FontData[baseData.length];
for (int i = 0; i < styleData.length; i++) {
FontData data = baseData[i];
styleData[i] = new FontData(data.getName(),
data.getHeight(),
data.getStyle() | additionalStyle);
}
return new Font(Display.getCurrent(), styleData);
}
}