/*=============================================================================# # Copyright (c) 2005-2016 Stephan Wahlbrink (WalWare.de) 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: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.ecommons.ltk.ui.templates; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.databinding.validation.ValidationStatus; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextListener; import org.eclipse.jface.text.TextEvent; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.text.templates.ContextTypeRegistry; import org.eclipse.jface.text.templates.Template; import org.eclipse.jface.text.templates.TemplateContextType; import org.eclipse.jface.text.templates.TemplateException; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; 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.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.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import de.walware.ecommons.templates.TemplateVariableProcessor; import de.walware.ecommons.text.ui.TextViewerAction; import de.walware.ecommons.ui.components.StatusInfo; import de.walware.ecommons.ui.dialogs.ExtStatusDialog; import de.walware.ecommons.ui.util.DialogUtil; import de.walware.ecommons.ui.util.LayoutUtil; import de.walware.ecommons.ui.util.ViewerUtil; import de.walware.ecommons.ltk.internal.ui.EditingMessages; import de.walware.ecommons.ltk.internal.ui.LTKUIPlugin; import de.walware.ecommons.ltk.ui.sourceediting.SnippetEditor; import de.walware.ecommons.ltk.ui.sourceediting.SourceEditorViewerConfigurator; /** * Dialog to edit a template. */ public class EditTemplateDialog extends ExtStatusDialog { public static final int EDITOR_TEMPLATE = 1; public static final int CUSTOM_TEMPLATE = 2; public static final int FIX_TEMPLATE = 3; private final Template fOriginalTemplate; private Template fNewTemplate; private final int fFlags; private final SourceEditorViewerConfigurator fConfigurator; private Text fNameText; private Text fDescriptionText; private ComboViewer fContextCombo; private final SnippetEditor fPatternEditor; private Button fInsertVariableButton; private Button fAutoInsertCheckbox; private IStatus fValidationStatus; private boolean fSuppressError = true; private final ContextTypeRegistry fContextTypeRegistry; private final TemplateVariableProcessor fTemplateProcessor; /** * Creates a new dialog. * * @param parent the shell parent of the dialog * @param template the template to edit * @param edit whether this is a new template or an existing being edited * @param flags edit mode * @param configurator configurator for the source viewer * @param processor the template variable processor * @param registry the context type registry to use */ public EditTemplateDialog(final Shell parent, final Template template, final boolean edit, final int flags, final SourceEditorViewerConfigurator configurator, final TemplateVariableProcessor processor, final ContextTypeRegistry registry) { super(parent); setTitle(edit ? EditingMessages.EditTemplateDialog_title_Edit : EditingMessages.EditTemplateDialog_title_New ); fOriginalTemplate = template; fFlags = flags; fTemplateProcessor = processor; fContextTypeRegistry = registry; final TemplateContextType type = fContextTypeRegistry.getContextType(template.getContextTypeId()); fTemplateProcessor.setContextType(type); fConfigurator = configurator; fPatternEditor = new SnippetEditor(fConfigurator, template.getPattern(), PlatformUI.getWorkbench()); } /** * Returns the created template. * * @return the created template * @since 3.1 */ public Template getTemplate() { return fNewTemplate; } @Override public void create() { super.create(); // update initial OK button to be disabled for new templates final boolean valid= fNameText == null || fNameText.getText().trim().length() != 0; if (!valid) { final StatusInfo status = new StatusInfo(); status.setError(EditingMessages.EditTemplateDialog_error_NoName); updateButtonsEnableState(status); } } @Override protected Control createDialogArea(final Composite parent) { final Composite dialogArea = new Composite(parent, SWT.NONE); dialogArea.setLayout(LayoutUtil.applyDialogDefaults(new GridLayout(), 2)); dialogArea.setLayoutData(new GridData(GridData.FILL_BOTH)); final ModifyListener listener= new ModifyListener() { @Override public void modifyText(final ModifyEvent e) { fSuppressError = false; updateButtons(); } }; if ((fFlags & 0xf) == EDITOR_TEMPLATE) { createLabel(dialogArea, EditingMessages.EditTemplateDialog_Name_label); final Composite composite = new Composite(dialogArea, SWT.NONE); composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); composite.setLayout(LayoutUtil.createCompositeGrid(4)); fNameText = createText(composite); fNameText.addModifyListener(listener); fNameText.addFocusListener(new FocusListener() { @Override public void focusGained(final FocusEvent e) { } @Override public void focusLost(final FocusEvent e) { if (fSuppressError) { fSuppressError = false; updateButtons(); } } }); createLabel(composite, EditingMessages.EditTemplateDialog_Context_label); fContextCombo = new ComboViewer(composite, SWT.BORDER | SWT.READ_ONLY); fContextCombo.setLabelProvider(new LabelProvider() { @Override public String getText(final Object element) { return ((TemplateContextType) element).getName(); } }); fContextCombo.setContentProvider(new ArrayContentProvider()); final List<TemplateContextType> contextTypes = new ArrayList<>(); for (final Iterator<TemplateContextType> iter = fContextTypeRegistry.contextTypes(); iter.hasNext(); ) { contextTypes.add(iter.next()); } fContextCombo.setInput(contextTypes.toArray()); fContextCombo.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(final SelectionChangedEvent event) { final StructuredSelection selection = (StructuredSelection) event.getSelection(); doContextChanged(((TemplateContextType) selection.getFirstElement())); } }); ViewerUtil.setDefaultVisibleItemCount(fContextCombo); fAutoInsertCheckbox= createCheckbox(composite, EditingMessages.EditTemplateDialog_AutoInsert_label); fAutoInsertCheckbox.setSelection(fOriginalTemplate.isAutoInsertable()); } else { configureForContext(getContextType()); } createLabel(dialogArea, EditingMessages.EditTemplateDialog_Description_label); final int descFlags = ((fFlags & 0xf) == FIX_TEMPLATE) ? (SWT.BORDER | SWT.READ_ONLY) : SWT.BORDER; fDescriptionText = new Text(dialogArea, descFlags); fDescriptionText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); fDescriptionText.addModifyListener(listener); final Label patternLabel= createLabel(dialogArea, EditingMessages.EditTemplateDialog_Pattern_label); patternLabel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); createEditor(dialogArea); final Label filler= new Label(dialogArea, SWT.NONE); filler.setLayoutData(new GridData()); final Composite composite= new Composite(dialogArea, SWT.NONE); composite.setLayout(LayoutUtil.createCompositeGrid(1)); composite.setLayoutData(new GridData()); fInsertVariableButton= new Button(composite, SWT.NONE); fInsertVariableButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); fInsertVariableButton.setText(EditingMessages.EditTemplateDialog_InsertVariable); fInsertVariableButton.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(final SelectionEvent e) { insertVariablePressed(); } @Override public void widgetDefaultSelected(final SelectionEvent e) {} }); fDescriptionText.setText(fOriginalTemplate.getDescription()); if (fNameText != null) { fNameText.setText(fOriginalTemplate.getName()); fNameText.addModifyListener(listener); fContextCombo.setSelection(new StructuredSelection(fContextTypeRegistry.getContextType(fOriginalTemplate.getContextTypeId()))); } else { fPatternEditor.getControl().setFocus(); } final TextViewerAction assistAction = new TextViewerAction(fPatternEditor.getSourceViewer(), ISourceViewer.CONTENTASSIST_PROPOSALS); assistAction.setId("ContentAssistProposal"); //$NON-NLS-1$ assistAction.setText(EditingMessages.EditTemplateDialog_ContentAssist); assistAction.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); fPatternEditor.addAction(assistAction); LayoutUtil.addSmallFiller(dialogArea, false); applyDialogFont(dialogArea); return composite; } protected SourceViewer getSourceViewer() { return fPatternEditor.getSourceViewer(); } protected SourceEditorViewerConfigurator getSourceViewerConfigurator() { return fConfigurator; } /* GUI Methods ****************************************************************/ private static Label createLabel(final Composite parent, final String name) { final Label label= new Label(parent, SWT.NULL); label.setText(name); label.setLayoutData(new GridData()); return label; } private static Text createText(final Composite parent) { final Text text= new Text(parent, SWT.BORDER); text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); return text; } private static Button createCheckbox(final Composite parent, final String name) { final Button button= new Button(parent, SWT.CHECK); button.setText(name); button.setLayoutData(new GridData()); return button; } private void createEditor(final Composite parent) { int nLines = fPatternEditor.getDocument().getNumberOfLines(); if (nLines < 6) { nLines= 6; } else if (nLines > 12) { nLines= 12; } fPatternEditor.create(parent, SnippetEditor.DEFAULT_MULTI_LINE_STYLE); final Control control= fPatternEditor.getControl(); final GridData data= new GridData(GridData.FILL_BOTH); data.widthHint= convertWidthInCharsToPixels(80); data.heightHint= convertHeightInCharsToPixels(nLines); control.setLayoutData(data); fPatternEditor.getSourceViewer().addTextListener(new ITextListener() { @Override public void textChanged(final TextEvent event) { if (event.getDocumentEvent() != null) { doSourceChanged(event.getDocumentEvent().getDocument()); } } }); } /* Handlers *******************************************************************/ private void doContextChanged(final TemplateContextType contextType) { fTemplateProcessor.setContextType(contextType); configureForContext(contextType); final Document document = fPatternEditor.getDocument(); doValidate(contextType, document); updateButtons(); } private void doSourceChanged(final IDocument document) { final TemplateContextType contextType = getContextType(); doValidate(contextType, document); updateButtons(); } private void doValidate(final TemplateContextType contextType, final IDocument document) { final String text = document.get(); fValidationStatus = null; if (contextType != null) { final IStatus status = validate(contextType, text); if (status != null && !status.isOK()) { fValidationStatus = status; } } } protected IStatus validate(final TemplateContextType contextType, final String text) { try { contextType.validate(text); return ValidationStatus.ok(); } catch (final TemplateException e) { return ValidationStatus.error(e.getLocalizedMessage()); } } protected void configureForContext(final TemplateContextType contextType) { } protected void insertVariablePressed() { fPatternEditor.getSourceViewer().getTextWidget().setFocus(); fPatternEditor.getSourceViewer().doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); } protected void insertText(final String text) { fPatternEditor.getSourceViewer().getTextWidget().insert(text); } @Override protected void okPressed() { final String name = fNameText == null ? fOriginalTemplate.getName() : fNameText.getText(); final boolean isAutoInsertable= fAutoInsertCheckbox != null && fAutoInsertCheckbox.getSelection(); fNewTemplate = new Template(name, fDescriptionText.getText(), getContextType().getId(), fPatternEditor.getDocument().get(), isAutoInsertable); super.okPressed(); } private void updateButtons() { IStatus status; final boolean valid = (fNameText == null || fNameText.getText().trim().length() != 0); if (!valid) { final StatusInfo info = new StatusInfo(); if (!fSuppressError) { info.setError(EditingMessages.EditTemplateDialog_error_NoName); } status = info; } else if (!isValidPattern(fPatternEditor.getDocument().get())) { final StatusInfo info = new StatusInfo(); if (!fSuppressError) { info.setError(EditingMessages.EditTemplateDialog_error_invalidPattern); } status = info; } else { status = (fValidationStatus != null) ? fValidationStatus : ValidationStatus.ok(); } updateStatus(status); } /** * Validates the pattern. * <p> * The default implementation rejects invalid XML characters. * </p> * * @param pattern the pattern to verify * @return <code>true</code> if the pattern is valid */ protected boolean isValidPattern(final String pattern) { for (int i= 0; i < pattern.length(); i++) { final char ch= pattern.charAt(i); if (!(ch == 9 || ch == 10 || ch == 13 || ch >= 32)) { return false; } } return true; } /* ******/ protected TemplateContextType getContextType() { if (fContextCombo != null) { final StructuredSelection selection = (StructuredSelection) fContextCombo.getSelection(); return ((TemplateContextType) selection.getFirstElement()); } else { return fContextTypeRegistry.getContextType(fOriginalTemplate.getContextTypeId()); } } protected IDialogSettings getDialogSettings() { return DialogUtil.getDialogSettings(LTKUIPlugin.getInstance(), "TemplateEditDialog"); //$NON-NLS-1$ } @Override protected IDialogSettings getDialogBoundsSettings() { return getDialogSettings(); } }