| /*=============================================================================# |
| # Copyright (c) 2005, 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.ltk.ui.templates.config; |
| |
| 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.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.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.text.templates.ContextTypeRegistry; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; |
| |
| import org.eclipse.statet.ecommons.templates.TemplateVariableProcessor; |
| import org.eclipse.statet.ecommons.text.ui.TextViewerAction; |
| import org.eclipse.statet.ecommons.ui.components.StatusInfo; |
| import org.eclipse.statet.ecommons.ui.dialogs.DialogUtils; |
| import org.eclipse.statet.ecommons.ui.dialogs.ExtStatusDialog; |
| import org.eclipse.statet.ecommons.ui.util.LayoutUtils; |
| import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils; |
| |
| import org.eclipse.statet.internal.ltk.ui.EditingMessages; |
| import org.eclipse.statet.internal.ltk.ui.LtkUIPlugin; |
| import org.eclipse.statet.ltk.ui.sourceediting.SnippetEditor; |
| import org.eclipse.statet.ltk.ui.sourceediting.SnippetEditor1; |
| import org.eclipse.statet.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 originalTemplate; |
| private Template newTemplate; |
| |
| private final int flags; |
| |
| private final SourceEditorViewerConfigurator configurator; |
| |
| private Text nameText; |
| private Text descriptionText; |
| private ComboViewer contextCombo; |
| private final SnippetEditor1 patternEditor; |
| private Button insertVariableButton; |
| private Button autoInsertCheckbox; |
| |
| |
| private IStatus validationStatus; |
| private boolean suppressError= true; |
| |
| private final ContextTypeRegistry contextTypeRegistry; |
| private final TemplateVariableProcessor templateProcessor; |
| |
| |
| /** |
| * 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, |
| final String prefQualifier) { |
| super(parent); |
| |
| setTitle(edit ? |
| EditingMessages.EditTemplateDialog_title_Edit : |
| EditingMessages.EditTemplateDialog_title_New ); |
| |
| this.originalTemplate= template; |
| this.flags= flags; |
| |
| this.templateProcessor= processor; |
| this.contextTypeRegistry= registry; |
| |
| final TemplateContextType type= this.contextTypeRegistry.getContextType(template.getContextTypeId()); |
| this.templateProcessor.setContextType(type); |
| this.configurator= configurator; |
| this.patternEditor= new SnippetEditor1(this.configurator, template.getPattern(), |
| PlatformUI.getWorkbench(), prefQualifier ); |
| } |
| |
| |
| /** |
| * Returns the created template. |
| * |
| * @return the created template |
| * @since 3.1 |
| */ |
| public Template getTemplate() { |
| return this.newTemplate; |
| } |
| |
| @Override |
| public void create() { |
| super.create(); |
| // update initial OK button to be disabled for new templates |
| final boolean valid= this.nameText == null || this.nameText.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(LayoutUtils.newDialogGrid(2)); |
| dialogArea.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| |
| final ModifyListener listener= new ModifyListener() { |
| @Override |
| public void modifyText(final ModifyEvent e) { |
| EditTemplateDialog.this.suppressError= false; |
| updateButtons(); |
| } |
| }; |
| |
| if ((this.flags & 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(LayoutUtils.newCompositeGrid(4)); |
| |
| this.nameText= createText(composite); |
| this.nameText.addModifyListener(listener); |
| this.nameText.addFocusListener(new FocusListener() { |
| |
| @Override |
| public void focusGained(final FocusEvent e) { |
| } |
| |
| @Override |
| public void focusLost(final FocusEvent e) { |
| if (EditTemplateDialog.this.suppressError) { |
| EditTemplateDialog.this.suppressError= false; |
| updateButtons(); |
| } |
| } |
| }); |
| |
| createLabel(composite, EditingMessages.EditTemplateDialog_Context_label); |
| this.contextCombo= new ComboViewer(composite, SWT.BORDER | SWT.READ_ONLY); |
| this.contextCombo.setLabelProvider(new LabelProvider() { |
| @Override |
| public String getText(final Object element) { |
| return ((TemplateContextType) element).getName(); |
| } |
| }); |
| this.contextCombo.setContentProvider(new ArrayContentProvider()); |
| final List<TemplateContextType> contextTypes= new ArrayList<>(); |
| for (final Iterator<TemplateContextType> iter= this.contextTypeRegistry.contextTypes(); iter.hasNext(); ) { |
| contextTypes.add(iter.next()); |
| } |
| this.contextCombo.setInput(contextTypes.toArray()); |
| |
| this.contextCombo.addSelectionChangedListener(new ISelectionChangedListener() { |
| @Override |
| public void selectionChanged(final SelectionChangedEvent event) { |
| final StructuredSelection selection= (StructuredSelection) event.getSelection(); |
| doContextChanged(((TemplateContextType) selection.getFirstElement())); |
| } |
| }); |
| ViewerUtils.setDefaultVisibleItemCount(this.contextCombo); |
| |
| this.autoInsertCheckbox= createCheckbox(composite, EditingMessages.EditTemplateDialog_AutoInsert_label); |
| this.autoInsertCheckbox.setSelection(this.originalTemplate.isAutoInsertable()); |
| } |
| else { |
| configureForContext(getContextType()); |
| } |
| |
| createLabel(dialogArea, EditingMessages.EditTemplateDialog_Description_label); |
| |
| final int descFlags= ((this.flags & 0xf) == FIX_TEMPLATE) ? (SWT.BORDER | SWT.READ_ONLY) : SWT.BORDER; |
| this.descriptionText= new Text(dialogArea, descFlags); |
| this.descriptionText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| |
| this.descriptionText.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(LayoutUtils.newCompositeGrid(1)); |
| composite.setLayoutData(new GridData()); |
| |
| this.insertVariableButton= new Button(composite, SWT.NONE); |
| this.insertVariableButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| this.insertVariableButton.setText(EditingMessages.EditTemplateDialog_InsertVariable); |
| this.insertVariableButton.addSelectionListener(new SelectionListener() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| insertVariablePressed(); |
| } |
| @Override |
| public void widgetDefaultSelected(final SelectionEvent e) {} |
| }); |
| |
| this.descriptionText.setText(this.originalTemplate.getDescription()); |
| if (this.nameText != null) { |
| this.nameText.setText(this.originalTemplate.getName()); |
| this.nameText.addModifyListener(listener); |
| this.contextCombo.setSelection(new StructuredSelection(this.contextTypeRegistry.getContextType(this.originalTemplate.getContextTypeId()))); |
| } |
| else { |
| this.patternEditor.getControl().setFocus(); |
| } |
| |
| final TextViewerAction assistAction= new TextViewerAction(this.patternEditor.getSourceViewer(), ISourceViewer.CONTENTASSIST_PROPOSALS); |
| assistAction.setId("ContentAssistProposal"); //$NON-NLS-1$ |
| assistAction.setText(EditingMessages.EditTemplateDialog_ContentAssist); |
| assistAction.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); |
| this.patternEditor.addAction(assistAction); |
| |
| LayoutUtils.addSmallFiller(dialogArea, false); |
| applyDialogFont(dialogArea); |
| return composite; |
| } |
| |
| protected SourceViewer getSourceViewer() { |
| return this.patternEditor.getSourceViewer(); |
| } |
| |
| protected SourceEditorViewerConfigurator getSourceViewerConfigurator() { |
| return this.configurator; |
| } |
| |
| |
| /* 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= this.patternEditor.getDocument().getNumberOfLines(); |
| if (nLines < 6) { |
| nLines= 6; |
| } else if (nLines > 12) { |
| nLines= 12; |
| } |
| |
| this.patternEditor.create(parent, SnippetEditor.DEFAULT_MULTI_LINE_STYLE); |
| final Control control= this.patternEditor.getControl(); |
| final GridData data= new GridData(GridData.FILL_BOTH); |
| data.widthHint= convertWidthInCharsToPixels(80); |
| data.heightHint= convertHeightInCharsToPixels(nLines); |
| control.setLayoutData(data); |
| |
| this.patternEditor.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) { |
| this.templateProcessor.setContextType(contextType); |
| configureForContext(contextType); |
| final Document document= this.patternEditor.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(); |
| this.validationStatus= null; |
| if (contextType != null) { |
| final IStatus status= validate(contextType, text); |
| if (status != null && !status.isOK()) { |
| this.validationStatus= 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() { |
| this.patternEditor.getSourceViewer().getTextWidget().setFocus(); |
| this.patternEditor.getSourceViewer().doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); |
| } |
| |
| protected void insertText(final String text) { |
| this.patternEditor.getSourceViewer().getTextWidget().insert(text); |
| } |
| |
| @Override |
| protected void okPressed() { |
| final String name= this.nameText == null ? this.originalTemplate.getName() : this.nameText.getText(); |
| final boolean isAutoInsertable= this.autoInsertCheckbox != null && this.autoInsertCheckbox.getSelection(); |
| this.newTemplate= new Template(name, this.descriptionText.getText(), getContextType().getId(), this.patternEditor.getDocument().get(), isAutoInsertable); |
| super.okPressed(); |
| } |
| |
| private void updateButtons() { |
| IStatus status; |
| |
| final boolean valid= (this.nameText == null || this.nameText.getText().trim().length() != 0); |
| if (!valid) { |
| final StatusInfo info= new StatusInfo(); |
| if (!this.suppressError) { |
| info.setError(EditingMessages.EditTemplateDialog_error_NoName); |
| } |
| status= info; |
| } else if (!isValidPattern(this.patternEditor.getDocument().get())) { |
| final StatusInfo info= new StatusInfo(); |
| if (!this.suppressError) { |
| info.setError(EditingMessages.EditTemplateDialog_error_invalidPattern); |
| } |
| status= info; |
| } else { |
| status= (this.validationStatus != null) ? this.validationStatus : 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 (this.contextCombo != null) { |
| final StructuredSelection selection= (StructuredSelection) this.contextCombo.getSelection(); |
| return ((TemplateContextType) selection.getFirstElement()); |
| } |
| else { |
| return this.contextTypeRegistry.getContextType(this.originalTemplate.getContextTypeId()); |
| } |
| } |
| |
| |
| protected IDialogSettings getDialogSettings() { |
| return DialogUtils.getDialogSettings(LtkUIPlugin.getInstance(), "TemplateEditDialog"); //$NON-NLS-1$ |
| } |
| |
| @Override |
| protected IDialogSettings getDialogBoundsSettings() { |
| return getDialogSettings(); |
| } |
| |
| } |