1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2004 IBM Corporation and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v1.0
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v10.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  *******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.ui.preferences;
 
  13 import java.util.ArrayList;
 
  14 import java.util.HashMap;
 
  15 import java.util.Iterator;
 
  16 import java.util.List;
 
  19 import net.sourceforge.phpdt.internal.ui.IJavaHelpContextIds;
 
  20 import net.sourceforge.phpdt.internal.ui.dialogs.StatusDialog;
 
  21 import net.sourceforge.phpdt.internal.ui.dialogs.StatusInfo;
 
  22 import net.sourceforge.phpdt.internal.ui.text.IPHPPartitions;
 
  23 import net.sourceforge.phpdt.internal.ui.text.template.preferences.TemplateVariableProcessor;
 
  24 import net.sourceforge.phpdt.internal.ui.util.SWTUtil;
 
  25 import net.sourceforge.phpdt.ui.IContextMenuConstants;
 
  26 import net.sourceforge.phpdt.ui.PreferenceConstants;
 
  27 import net.sourceforge.phpdt.ui.text.JavaTextTools;
 
  28 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
 
  29 import net.sourceforge.phpeclipse.phpeditor.JavaSourceViewer;
 
  31 import org.eclipse.jface.action.Action;
 
  32 import org.eclipse.jface.action.GroupMarker;
 
  33 import org.eclipse.jface.action.IAction;
 
  34 import org.eclipse.jface.action.IMenuListener;
 
  35 import org.eclipse.jface.action.IMenuManager;
 
  36 import org.eclipse.jface.action.MenuManager;
 
  37 import org.eclipse.jface.action.Separator;
 
  38 import org.eclipse.jface.preference.IPreferenceStore;
 
  39 import org.eclipse.jface.resource.JFaceResources;
 
  40 import org.eclipse.jface.text.Document;
 
  41 import org.eclipse.jface.text.IDocument;
 
  42 import org.eclipse.jface.text.ITextListener;
 
  43 import org.eclipse.jface.text.ITextOperationTarget;
 
  44 import org.eclipse.jface.text.ITextViewer;
 
  45 import org.eclipse.jface.text.TextEvent;
 
  46 import org.eclipse.jface.text.source.ISourceViewer;
 
  47 import org.eclipse.jface.text.source.SourceViewer;
 
  48 import org.eclipse.jface.text.templates.ContextTypeRegistry;
 
  49 import org.eclipse.jface.text.templates.Template;
 
  50 import org.eclipse.jface.text.templates.TemplateContextType;
 
  51 import org.eclipse.jface.text.templates.TemplateException;
 
  52 import org.eclipse.jface.viewers.ISelectionChangedListener;
 
  53 import org.eclipse.jface.viewers.SelectionChangedEvent;
 
  54 import org.eclipse.swt.SWT;
 
  55 import org.eclipse.swt.custom.StyledText;
 
  56 import org.eclipse.swt.custom.VerifyKeyListener;
 
  57 import org.eclipse.swt.events.FocusEvent;
 
  58 import org.eclipse.swt.events.FocusListener;
 
  59 import org.eclipse.swt.events.ModifyEvent;
 
  60 import org.eclipse.swt.events.ModifyListener;
 
  61 import org.eclipse.swt.events.SelectionEvent;
 
  62 import org.eclipse.swt.events.SelectionListener;
 
  63 import org.eclipse.swt.events.VerifyEvent;
 
  64 import org.eclipse.swt.graphics.Font;
 
  65 import org.eclipse.swt.layout.GridData;
 
  66 import org.eclipse.swt.layout.GridLayout;
 
  67 import org.eclipse.swt.widgets.Button;
 
  68 import org.eclipse.swt.widgets.Combo;
 
  69 import org.eclipse.swt.widgets.Composite;
 
  70 import org.eclipse.swt.widgets.Control;
 
  71 import org.eclipse.swt.widgets.Label;
 
  72 import org.eclipse.swt.widgets.Menu;
 
  73 import org.eclipse.swt.widgets.Shell;
 
  74 import org.eclipse.swt.widgets.Text;
 
  75 import org.eclipse.swt.widgets.Widget;
 
  76 import org.eclipse.ui.help.WorkbenchHelp;
 
  77 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
 
  78 import org.eclipse.ui.texteditor.IUpdate;
 
  81  * Dialog to edit a template.
 
  83 public class EditTemplateDialog extends StatusDialog {
 
  85         private static class TextViewerAction extends Action implements IUpdate {
 
  87                 private int fOperationCode= -1;
 
  88                 private ITextOperationTarget fOperationTarget;
 
  91                  * Creates a new action.
 
  93                  * @param viewer the viewer
 
  94                  * @param operationCode the opcode
 
  96                 public TextViewerAction(ITextViewer viewer, int operationCode) {
 
  97                         fOperationCode= operationCode;
 
  98                         fOperationTarget= viewer.getTextOperationTarget();
 
 103                  * Updates the enabled state of the action.
 
 104                  * Fires a property change if the enabled state changes.
 
 106                  * @see Action#firePropertyChange(String, Object, Object)
 
 108                 public void update() {
 
 110                         boolean wasEnabled= isEnabled();
 
 111                         boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode));
 
 112                         setEnabled(isEnabled);
 
 114                         if (wasEnabled != isEnabled) {
 
 115                                 firePropertyChange(ENABLED, wasEnabled ? Boolean.TRUE : Boolean.FALSE, isEnabled ? Boolean.TRUE : Boolean.FALSE);
 
 123                         if (fOperationCode != -1 && fOperationTarget != null) {
 
 124                                 fOperationTarget.doOperation(fOperationCode);
 
 129         private final Template fTemplate;
 
 131         private Text fNameText;
 
 132         private Text fDescriptionText;
 
 133         private Combo fContextCombo;
 
 134         private SourceViewer fPatternEditor;    
 
 135         private Button fInsertVariableButton;
 
 136         private boolean fIsNameModifiable;
 
 138         private StatusInfo fValidationStatus;
 
 139         private boolean fSuppressError= true; // #4354  
 
 140         private Map fGlobalActions= new HashMap(10);
 
 141         private List fSelectionActions = new ArrayList(3);      
 
 142         private String[][] fContextTypes;
 
 144         private ContextTypeRegistry fContextTypeRegistry; 
 
 146         private final TemplateVariableProcessor fTemplateProcessor= new TemplateVariableProcessor();
 
 149          * Creates a new dialog.
 
 151          * @param parent the shell parent of the dialog
 
 152          * @param template the template to edit
 
 153          * @param edit whether this is a new template or an existing being edited
 
 154          * @param isNameModifiable whether the name of the template may be modified
 
 155          * @param registry the context type registry to use
 
 157         public EditTemplateDialog(Shell parent, Template template, boolean edit, boolean isNameModifiable, ContextTypeRegistry registry) {
 
 160                 setShellStyle(getShellStyle() | SWT.MAX | SWT.RESIZE);
 
 163                         ? PreferencesMessages.getString("EditTemplateDialog.title.edit") //$NON-NLS-1$
 
 164                         : PreferencesMessages.getString("EditTemplateDialog.title.new"); //$NON-NLS-1$
 
 168                 fIsNameModifiable= isNameModifiable;
 
 170                 // XXX workaround for bug 63313 - disabling prefix until fixed.
 
 171 //              String delim= new Document().getLegalLineDelimiters()[0];
 
 173                 List contexts= new ArrayList();
 
 174                 for (Iterator it= registry.contextTypes(); it.hasNext();) {
 
 175                         TemplateContextType type= (TemplateContextType) it.next();
 
 176 //                      if (type.getId().equals("javadoc")) //$NON-NLS-1$
 
 177 //                              contexts.add(new String[] { type.getId(), type.getName(), "/**" + delim }); //$NON-NLS-1$
 
 179                                 contexts.add(new String[] { type.getId(), type.getName(), "" }); //$NON-NLS-1$
 
 181                 fContextTypes= (String[][]) contexts.toArray(new String[contexts.size()][]);
 
 183                 fValidationStatus= new StatusInfo();
 
 185                 fContextTypeRegistry= registry;
 
 187                 TemplateContextType type= fContextTypeRegistry.getContextType(template.getContextTypeId());
 
 188                 fTemplateProcessor.setContextType(type);
 
 192          * @see net.sourceforge.phpdt.internal.ui.dialogs.StatusDialog#create()
 
 194         public void create() {
 
 196                 // update initial ok button to be disabled for new templates 
 
 197                 boolean valid= fNameText == null || fNameText.getText().trim().length() != 0;
 
 199                         StatusInfo status = new StatusInfo();
 
 200                         status.setError(PreferencesMessages.getString("EditTemplateDialog.error.noname")); //$NON-NLS-1$
 
 201                         updateButtonsEnableState(status);
 
 206          * @see Dialog#createDialogArea(Composite)
 
 208         protected Control createDialogArea(Composite ancestor) {
 
 209                 Composite parent= new Composite(ancestor, SWT.NONE);
 
 210                 GridLayout layout= new GridLayout();
 
 211                 layout.numColumns= 2;
 
 212                 parent.setLayout(layout);
 
 213                 parent.setLayoutData(new GridData(GridData.FILL_BOTH));
 
 215                 ModifyListener listener= new ModifyListener() {
 
 216                         public void modifyText(ModifyEvent e) {
 
 217                                 doTextWidgetChanged(e.widget);
 
 221                 if (fIsNameModifiable) {
 
 222                         createLabel(parent, PreferencesMessages.getString("EditTemplateDialog.name")); //$NON-NLS-1$    
 
 224                         Composite composite= new Composite(parent, SWT.NONE);
 
 225                         composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 
 226                         layout= new GridLayout();               
 
 227                         layout.numColumns= 3;
 
 228                         layout.marginWidth= 0;
 
 229                         layout.marginHeight= 0;
 
 230                         composite.setLayout(layout);
 
 232                         fNameText= createText(composite);
 
 233                         fNameText.addFocusListener(new FocusListener() {
 
 235                                 public void focusGained(FocusEvent e) {
 
 238                                 public void focusLost(FocusEvent e) {
 
 239                                         if (fSuppressError) {
 
 240                                                 fSuppressError= false;
 
 246                         createLabel(composite, PreferencesMessages.getString("EditTemplateDialog.context")); //$NON-NLS-1$              
 
 247                         fContextCombo= new Combo(composite, SWT.READ_ONLY);
 
 249                         for (int i= 0; i < fContextTypes.length; i++) {
 
 250                                 fContextCombo.add(fContextTypes[i][1]);
 
 253                         fContextCombo.addModifyListener(listener);
 
 256                 createLabel(parent, PreferencesMessages.getString("EditTemplateDialog.description")); //$NON-NLS-1$             
 
 258                 int descFlags= fIsNameModifiable ? SWT.BORDER : SWT.BORDER | SWT.READ_ONLY;
 
 259                 fDescriptionText= new Text(parent, descFlags );
 
 260                 fDescriptionText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 
 
 262                 fDescriptionText.addModifyListener(listener);
 
 264                 Label patternLabel= createLabel(parent, PreferencesMessages.getString("EditTemplateDialog.pattern")); //$NON-NLS-1$
 
 265                 patternLabel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
 
 266                 fPatternEditor= createEditor(parent);
 
 268                 Label filler= new Label(parent, SWT.NONE);              
 
 269                 filler.setLayoutData(new GridData());
 
 271                 Composite composite= new Composite(parent, SWT.NONE);
 
 272                 layout= new GridLayout();               
 
 273                 layout.marginWidth= 0;
 
 274                 layout.marginHeight= 0;
 
 275                 composite.setLayout(layout);            
 
 276                 composite.setLayoutData(new GridData());
 
 278                 fInsertVariableButton= new Button(composite, SWT.NONE);
 
 279                 fInsertVariableButton.setLayoutData(getButtonGridData(fInsertVariableButton));
 
 280                 fInsertVariableButton.setText(PreferencesMessages.getString("EditTemplateDialog.insert.variable")); //$NON-NLS-1$
 
 281                 fInsertVariableButton.addSelectionListener(new SelectionListener() {
 
 282                         public void widgetSelected(SelectionEvent e) {
 
 283                                 fPatternEditor.getTextWidget().setFocus();
 
 284                                 fPatternEditor.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS);                      
 
 287                         public void widgetDefaultSelected(SelectionEvent e) {}
 
 290                 fDescriptionText.setText(fTemplate.getDescription());
 
 291                 if (fIsNameModifiable) {
 
 292                         fNameText.setText(fTemplate.getName());
 
 293                         fNameText.addModifyListener(listener);
 
 294                         fContextCombo.select(getIndex(fTemplate.getContextTypeId()));
 
 296                         fPatternEditor.getControl().setFocus();
 
 300                 applyDialogFont(parent);
 
 304         protected void doTextWidgetChanged(Widget w) {
 
 305                 if (w == fNameText) {
 
 306                         fSuppressError= false;
 
 307                         String name= fNameText.getText();
 
 308                         fTemplate.setName(name);
 
 310                 } else if (w == fContextCombo) {
 
 311                         String name= fContextCombo.getText();
 
 312                         String contextId= getContextId(name);
 
 313                         fTemplate.setContextTypeId(contextId);
 
 314                         fTemplateProcessor.setContextType(fContextTypeRegistry.getContextType(contextId));
 
 315                 } else if (w == fDescriptionText) {
 
 316                         String desc= fDescriptionText.getText();
 
 317                         fTemplate.setDescription(desc);
 
 321         private String getContextId(String name) {
 
 325                 for (int i= 0; i < fContextTypes.length; i++) {
 
 326                         if (name.equals(fContextTypes[i][1])) {
 
 327                                 return fContextTypes[i][0];     
 
 333         protected void doSourceChanged(IDocument document) {
 
 334                 String text= document.get();
 
 335                 String prefix= getPrefix();
 
 336                 fTemplate.setPattern(text.substring(prefix.length(), text.length()));
 
 337                 fValidationStatus.setOK();
 
 338                 TemplateContextType contextType= fContextTypeRegistry.getContextType(fTemplate.getContextTypeId());
 
 339                 if (contextType != null) {
 
 341                                 contextType.validate(text);
 
 342                         } catch (TemplateException e) {
 
 343                                 fValidationStatus.setError(e.getLocalizedMessage());
 
 351         private static GridData getButtonGridData(Button button) {
 
 352                 GridData data= new GridData(GridData.FILL_HORIZONTAL);
 
 353                 data.heightHint= SWTUtil.getButtonHeightHint(button);
 
 358         private static Label createLabel(Composite parent, String name) {
 
 359                 Label label= new Label(parent, SWT.NULL);
 
 361                 label.setLayoutData(new GridData());
 
 366         private static Text createText(Composite parent) {
 
 367                 Text text= new Text(parent, SWT.BORDER);
 
 368                 text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));             
 
 373         private SourceViewer createEditor(Composite parent) {
 
 374                 String prefix= getPrefix();
 
 375                 IDocument document= new Document(prefix + fTemplate.getPattern());
 
 376                 JavaTextTools tools= PHPeclipsePlugin.getDefault().getJavaTextTools();
 
 377                 tools.setupJavaDocumentPartitioner(document, IPHPPartitions.PHP_PARTITIONING);
 
 378                 IPreferenceStore store= PHPeclipsePlugin.getDefault().getCombinedPreferenceStore();
 
 379                 SourceViewer viewer= new JavaSourceViewer(parent, null, null, false, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL, store);
 
 380                 TemplateEditorSourceViewerConfiguration configuration= new TemplateEditorSourceViewerConfiguration(tools.getColorManager(), store, null, fTemplateProcessor);
 
 381                 viewer.configure(configuration);
 
 382                 viewer.setEditable(true);
 
 383                 // XXX workaround for bug 63313 - disabling prefix until fixed.
 
 384 //              viewer.setDocument(document, prefix.length(), document.getLength() - prefix.length());
 
 385                 viewer.setDocument(document);
 
 387                 Font font= JFaceResources.getFont(PreferenceConstants.EDITOR_TEXT_FONT);
 
 388                 viewer.getTextWidget().setFont(font);
 
 389                 new JavaSourcePreviewerUpdater(viewer, configuration, store);
 
 391                 int nLines= document.getNumberOfLines();
 
 394                 } else if (nLines > 12) {
 
 398                 Control control= viewer.getControl();
 
 399                 GridData data= new GridData(GridData.FILL_BOTH);
 
 400                 data.widthHint= convertWidthInCharsToPixels(80);
 
 401                 data.heightHint= convertHeightInCharsToPixels(nLines);
 
 402                 control.setLayoutData(data);
 
 404                 viewer.addTextListener(new ITextListener() {
 
 405                         public void textChanged(TextEvent event) {
 
 406                                 if (event .getDocumentEvent() != null)
 
 407                                         doSourceChanged(event.getDocumentEvent().getDocument());
 
 411                 viewer.addSelectionChangedListener(new ISelectionChangedListener() {                    
 
 412                         public void selectionChanged(SelectionChangedEvent event) {
 
 413                                 updateSelectionDependentActions();
 
 417                 viewer.prependVerifyKeyListener(new VerifyKeyListener() {
 
 418                         public void verifyKey(VerifyEvent event) {
 
 419                                 handleVerifyKeyPressed(event);
 
 426         private String getPrefix() {
 
 428                 int idx= getIndex(fTemplate.getContextTypeId());
 
 430                         prefix= fContextTypes[idx][2];
 
 432                         prefix= ""; //$NON-NLS-1$
 
 437         private void handleVerifyKeyPressed(VerifyEvent event) {
 
 441                 if (event.stateMask != SWT.MOD1)
 
 444                 switch (event.character) {
 
 446                                 fPatternEditor.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS);
 
 452                                 fPatternEditor.doOperation(ITextOperationTarget.UNDO);
 
 458         private void initializeActions() {
 
 459                 TextViewerAction action= new TextViewerAction(fPatternEditor, SourceViewer.UNDO);
 
 460                 action.setText(PreferencesMessages.getString("EditTemplateDialog.undo")); //$NON-NLS-1$
 
 461                 fGlobalActions.put(ITextEditorActionConstants.UNDO, action);
 
 463                 action= new TextViewerAction(fPatternEditor, SourceViewer.CUT);
 
 464                 action.setText(PreferencesMessages.getString("EditTemplateDialog.cut")); //$NON-NLS-1$
 
 465                 fGlobalActions.put(ITextEditorActionConstants.CUT, action);
 
 467                 action= new TextViewerAction(fPatternEditor, SourceViewer.COPY);
 
 468                 action.setText(PreferencesMessages.getString("EditTemplateDialog.copy")); //$NON-NLS-1$
 
 469                 fGlobalActions.put(ITextEditorActionConstants.COPY, action);
 
 471                 action= new TextViewerAction(fPatternEditor, SourceViewer.PASTE);
 
 472                 action.setText(PreferencesMessages.getString("EditTemplateDialog.paste")); //$NON-NLS-1$
 
 473                 fGlobalActions.put(ITextEditorActionConstants.PASTE, action);
 
 475                 action= new TextViewerAction(fPatternEditor, SourceViewer.SELECT_ALL);
 
 476                 action.setText(PreferencesMessages.getString("EditTemplateDialog.select.all")); //$NON-NLS-1$
 
 477                 fGlobalActions.put(ITextEditorActionConstants.SELECT_ALL, action);
 
 479                 action= new TextViewerAction(fPatternEditor, SourceViewer.CONTENTASSIST_PROPOSALS);
 
 480                 action.setText(PreferencesMessages.getString("EditTemplateDialog.content.assist")); //$NON-NLS-1$
 
 481                 fGlobalActions.put("ContentAssistProposal", action); //$NON-NLS-1$
 
 483                 fSelectionActions.add(ITextEditorActionConstants.CUT);
 
 484                 fSelectionActions.add(ITextEditorActionConstants.COPY);
 
 485                 fSelectionActions.add(ITextEditorActionConstants.PASTE);
 
 487                 // create context menu
 
 488                 MenuManager manager= new MenuManager(null, null);
 
 489                 manager.setRemoveAllWhenShown(true);
 
 490                 manager.addMenuListener(new IMenuListener() {
 
 491                         public void menuAboutToShow(IMenuManager mgr) {
 
 492                                 fillContextMenu(mgr);
 
 496                 StyledText text= fPatternEditor.getTextWidget();                
 
 497                 Menu menu= manager.createContextMenu(text);
 
 501         private void fillContextMenu(IMenuManager menu) {
 
 502                 menu.add(new GroupMarker(ITextEditorActionConstants.GROUP_UNDO));
 
 503                 menu.appendToGroup(ITextEditorActionConstants.GROUP_UNDO, (IAction) fGlobalActions.get(ITextEditorActionConstants.UNDO));
 
 505                 menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));         
 
 506                 menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, (IAction) fGlobalActions.get(ITextEditorActionConstants.CUT));
 
 507                 menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, (IAction) fGlobalActions.get(ITextEditorActionConstants.COPY));
 
 508                 menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, (IAction) fGlobalActions.get(ITextEditorActionConstants.PASTE));
 
 509                 menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, (IAction) fGlobalActions.get(ITextEditorActionConstants.SELECT_ALL));
 
 511                 menu.add(new Separator(IContextMenuConstants.GROUP_GENERATE));
 
 512                 menu.appendToGroup(IContextMenuConstants.GROUP_GENERATE, (IAction) fGlobalActions.get("ContentAssistProposal")); //$NON-NLS-1$
 
 515         protected void updateSelectionDependentActions() {
 
 516                 Iterator iterator= fSelectionActions.iterator();
 
 517                 while (iterator.hasNext())
 
 518                         updateAction((String)iterator.next());          
 
 521         protected void updateUndoAction() {
 
 522                 IAction action= (IAction) fGlobalActions.get(ITextEditorActionConstants.UNDO);
 
 523                 if (action instanceof IUpdate)
 
 524                         ((IUpdate) action).update();
 
 527         protected void updateAction(String actionId) {
 
 528                 IAction action= (IAction) fGlobalActions.get(actionId);
 
 529                 if (action instanceof IUpdate)
 
 530                         ((IUpdate) action).update();
 
 533         private int getIndex(String contextid) {
 
 535                 if (contextid == null)
 
 538                 for (int i= 0; i < fContextTypes.length; i++) {
 
 539                         if (contextid.equals(fContextTypes[i][0])) {
 
 546         protected void okPressed() {
 
 550         private void updateButtons() {          
 
 553                 boolean valid= fNameText == null || fNameText.getText().trim().length() != 0;
 
 555                         status = new StatusInfo();
 
 556                         if (!fSuppressError) {
 
 557                                 status.setError(PreferencesMessages.getString("EditTemplateDialog.error.noname")); //$NON-NLS-1$
 
 560                         status= fValidationStatus; 
 
 562                 updateStatus(status);
 
 566          * @see org.eclipse.jface.window.Window#configureShell(Shell)
 
 568         protected void configureShell(Shell newShell) {
 
 569                 super.configureShell(newShell);
 
 570                 WorkbenchHelp.setHelp(newShell, IJavaHelpContextIds.EDIT_TEMPLATE_DIALOG);