1 /******************************************************************************* 2 * Copyright (c) 2000, 2018 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Nicolaj Hoess <nicohoess@gmail.com> - Editor templates pref page: Allow to sort by column - https://bugs.eclipse.org/203722 14 *******************************************************************************/ 15 package org.eclipse.ui.texteditor.templates; 16 17 import java.io.BufferedInputStream; 18 import java.io.BufferedOutputStream; 19 import java.io.File; 20 import java.io.FileInputStream; 21 import java.io.FileNotFoundException; 22 import java.io.FileOutputStream; 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.io.OutputStream; 26 import java.text.Collator; 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import java.util.Collections; 30 import java.util.Comparator; 31 import java.util.HashMap; 32 import java.util.Iterator; 33 import java.util.List; 34 import java.util.Map; 35 36 import org.eclipse.swt.SWT; 37 import org.eclipse.swt.custom.StyledText; 38 import org.eclipse.swt.events.FocusEvent; 39 import org.eclipse.swt.events.FocusListener; 40 import org.eclipse.swt.events.ModifyListener; 41 import org.eclipse.swt.events.SelectionAdapter; 42 import org.eclipse.swt.events.SelectionEvent; 43 import org.eclipse.swt.events.SelectionListener; 44 import org.eclipse.swt.graphics.Cursor; 45 import org.eclipse.swt.graphics.GC; 46 import org.eclipse.swt.graphics.Image; 47 import org.eclipse.swt.layout.GridData; 48 import org.eclipse.swt.layout.GridLayout; 49 import org.eclipse.swt.widgets.Button; 50 import org.eclipse.swt.widgets.Combo; 51 import org.eclipse.swt.widgets.Composite; 52 import org.eclipse.swt.widgets.Control; 53 import org.eclipse.swt.widgets.FileDialog; 54 import org.eclipse.swt.widgets.Label; 55 import org.eclipse.swt.widgets.Menu; 56 import org.eclipse.swt.widgets.Shell; 57 import org.eclipse.swt.widgets.Table; 58 import org.eclipse.swt.widgets.TableColumn; 59 import org.eclipse.swt.widgets.Text; 60 import org.eclipse.swt.widgets.Widget; 61 62 import org.eclipse.core.expressions.Expression; 63 64 import org.eclipse.core.runtime.IStatus; 65 import org.eclipse.core.runtime.Status; 66 67 import org.eclipse.text.templates.TemplatePersistenceData; 68 import org.eclipse.text.templates.TemplateReaderWriter; 69 70 import org.eclipse.jface.action.Action; 71 import org.eclipse.jface.action.GroupMarker; 72 import org.eclipse.jface.action.IAction; 73 import org.eclipse.jface.action.IMenuManager; 74 import org.eclipse.jface.action.MenuManager; 75 import org.eclipse.jface.action.Separator; 76 import org.eclipse.jface.commands.ActionHandler; 77 import org.eclipse.jface.dialogs.Dialog; 78 import org.eclipse.jface.dialogs.IDialogConstants; 79 import org.eclipse.jface.dialogs.IDialogSettings; 80 import org.eclipse.jface.dialogs.MessageDialog; 81 import org.eclipse.jface.dialogs.StatusDialog; 82 import org.eclipse.jface.preference.IPreferenceStore; 83 import org.eclipse.jface.preference.PreferencePage; 84 import org.eclipse.jface.resource.JFaceResources; 85 import org.eclipse.jface.util.BidiUtils; 86 import org.eclipse.jface.viewers.CheckboxTableViewer; 87 import org.eclipse.jface.viewers.ColumnPixelData; 88 import org.eclipse.jface.viewers.ColumnWeightData; 89 import org.eclipse.jface.viewers.IBaseLabelProvider; 90 import org.eclipse.jface.viewers.IStructuredSelection; 91 import org.eclipse.jface.viewers.ITableLabelProvider; 92 import org.eclipse.jface.viewers.LabelProvider; 93 import org.eclipse.jface.viewers.StructuredSelection; 94 import org.eclipse.jface.viewers.TableViewer; 95 import org.eclipse.jface.viewers.Viewer; 96 import org.eclipse.jface.viewers.ViewerComparator; 97 import org.eclipse.jface.window.Window; 98 99 import org.eclipse.jface.text.Document; 100 import org.eclipse.jface.text.IDocument; 101 import org.eclipse.jface.text.ITextOperationTarget; 102 import org.eclipse.jface.text.ITextViewer; 103 import org.eclipse.jface.text.contentassist.ContentAssistant; 104 import org.eclipse.jface.text.contentassist.IContentAssistProcessor; 105 import org.eclipse.jface.text.contentassist.IContentAssistant; 106 import org.eclipse.jface.text.source.ISourceViewer; 107 import org.eclipse.jface.text.source.SourceViewer; 108 import org.eclipse.jface.text.source.SourceViewerConfiguration; 109 import org.eclipse.jface.text.templates.ContextTypeRegistry; 110 import org.eclipse.jface.text.templates.Template; 111 import org.eclipse.jface.text.templates.TemplateContextType; 112 import org.eclipse.jface.text.templates.TemplateException; 113 import org.eclipse.jface.text.templates.persistence.TemplateStore; 114 115 import org.eclipse.ui.ActiveShellExpression; 116 import org.eclipse.ui.IWorkbench; 117 import org.eclipse.ui.IWorkbenchCommandConstants; 118 import org.eclipse.ui.IWorkbenchPreferencePage; 119 import org.eclipse.ui.PlatformUI; 120 import org.eclipse.ui.handlers.IHandlerActivation; 121 import org.eclipse.ui.handlers.IHandlerService; 122 import org.eclipse.ui.internal.texteditor.NLSUtility; 123 import org.eclipse.ui.internal.texteditor.SWTUtil; 124 import org.eclipse.ui.internal.texteditor.TextEditorPlugin; 125 126 import org.eclipse.ui.texteditor.ITextEditorActionConstants; 127 import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; 128 import org.eclipse.ui.texteditor.IUpdate; 129 130 131 /** 132 * A template preference page allows configuration of the templates for an 133 * editor. It provides controls for adding, removing and changing templates as 134 * well as enablement, default management and an optional formatter preference. 135 * <p> 136 * Subclasses need to provide a {@link TemplateStore} and a 137 * {@link ContextTypeRegistry} and should set the preference store. They may 138 * optionally override {@link #isShowFormatterSetting()}. 139 * </p> 140 * 141 * @since 3.0 142 */ 143 public abstract class TemplatePreferencePage extends PreferencePage implements IWorkbenchPreferencePage { 144 145 146 /** 147 * Dialog to edit a template. Clients will usually instantiate, but 148 * may also extend. 149 * 150 * @since 3.3 151 */ 152 protected static class EditTemplateDialog extends StatusDialog { 153 154 private class TextViewerAction extends Action implements IUpdate { 155 156 private int fOperationCode= -1; 157 private ITextOperationTarget fOperationTarget; 158 159 /** 160 * Creates a new action. 161 * 162 * @param viewer the viewer 163 * @param operationCode the opcode 164 */ TextViewerAction(ITextViewer viewer, int operationCode)165 public TextViewerAction(ITextViewer viewer, int operationCode) { 166 fOperationCode= operationCode; 167 fOperationTarget= viewer.getTextOperationTarget(); 168 update(); 169 } 170 171 /** 172 * Updates the enabled state of the action. 173 * Fires a property change if the enabled state changes. 174 * 175 * @see Action#firePropertyChange(String, Object, Object) 176 */ 177 @Override update()178 public void update() { 179 // XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=206111 180 if (fOperationCode == ITextOperationTarget.REDO || fOperationCode == ITextOperationTarget.REDO) 181 return; 182 183 boolean wasEnabled= isEnabled(); 184 boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode)); 185 setEnabled(isEnabled); 186 187 if (wasEnabled != isEnabled) { 188 firePropertyChange(ENABLED, wasEnabled ? Boolean.TRUE : Boolean.FALSE, isEnabled ? Boolean.TRUE : Boolean.FALSE); 189 } 190 } 191 192 /** 193 * @see Action#run() 194 */ 195 @Override run()196 public void run() { 197 if (fOperationCode != -1 && fOperationTarget != null) { 198 fOperationTarget.doOperation(fOperationCode); 199 } 200 } 201 } 202 203 private final Template fOriginalTemplate; 204 205 private Text fNameText; 206 private Text fDescriptionText; 207 private Combo fContextCombo; 208 private SourceViewer fPatternEditor; 209 private Button fInsertVariableButton; 210 private Button fAutoInsertCheckbox; 211 private boolean fIsNameModifiable; 212 213 private StatusInfo fValidationStatus; 214 private boolean fSuppressError= true; // #4354 215 private Map<String, TextViewerAction> fGlobalActions= new HashMap<>(10); 216 private List<String> fSelectionActions = new ArrayList<>(3); 217 private String[][] fContextTypes; 218 219 private ContextTypeRegistry fContextTypeRegistry; 220 221 private final TemplateVariableProcessor fTemplateProcessor= new TemplateVariableProcessor(); 222 223 private Template fNewTemplate; 224 225 226 /** 227 * Creates a new dialog. 228 * 229 * @param parent the shell parent of the dialog 230 * @param template the template to edit 231 * @param edit whether this is a new template or an existing being edited 232 * @param isNameModifiable whether the name of the template may be modified 233 * @param registry the context type registry to use 234 */ EditTemplateDialog(Shell parent, Template template, boolean edit, boolean isNameModifiable, ContextTypeRegistry registry)235 public EditTemplateDialog(Shell parent, Template template, boolean edit, boolean isNameModifiable, ContextTypeRegistry registry) { 236 super(parent); 237 238 String title= edit 239 ? TemplatesMessages.EditTemplateDialog_title_edit 240 : TemplatesMessages.EditTemplateDialog_title_new; 241 setTitle(title); 242 243 fOriginalTemplate= template; 244 fIsNameModifiable= isNameModifiable; 245 246 List<String[]> contexts= new ArrayList<>(); 247 for (Iterator<TemplateContextType> it= registry.contextTypes(); it.hasNext();) { 248 TemplateContextType type= it.next(); 249 contexts.add(new String[] { type.getId(), type.getName() }); 250 } 251 Collections.sort(contexts, new Comparator<String[]>() { 252 Collator fCollator= Collator.getInstance(); 253 @Override 254 public int compare(String[] o1, String[] o2) { 255 return fCollator.compare(o1[1], o2[1]); 256 } 257 }); 258 fContextTypes= contexts.toArray(new String[contexts.size()][]); 259 260 fValidationStatus= new StatusInfo(); 261 262 fContextTypeRegistry= registry; 263 264 TemplateContextType type= fContextTypeRegistry.getContextType(template.getContextTypeId()); 265 fTemplateProcessor.setContextType(type); 266 } 267 268 @Override isResizable()269 protected boolean isResizable() { 270 return true; 271 } 272 273 @Override create()274 public void create() { 275 super.create(); 276 // update initial OK button to be disabled for new templates 277 boolean valid= fNameText == null || !fNameText.getText().trim().isEmpty(); 278 if (!valid) { 279 StatusInfo status = new StatusInfo(); 280 status.setError(TemplatesMessages.EditTemplateDialog_error_noname); 281 updateButtonsEnableState(status); 282 } 283 } 284 285 @Override createDialogArea(Composite ancestor)286 protected Control createDialogArea(Composite ancestor) { 287 Composite parent= new Composite(ancestor, SWT.NONE); 288 GridLayout layout= new GridLayout(); 289 layout.numColumns= 2; 290 layout.marginHeight= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); 291 layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); 292 layout.verticalSpacing= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); 293 layout.horizontalSpacing= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); 294 parent.setLayout(layout); 295 parent.setLayoutData(new GridData(GridData.FILL_BOTH)); 296 297 ModifyListener listener = e -> doTextWidgetChanged(e.widget); 298 299 if (fIsNameModifiable) { 300 createLabel(parent, TemplatesMessages.EditTemplateDialog_name); 301 302 Composite composite= new Composite(parent, SWT.NONE); 303 composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 304 layout= new GridLayout(); 305 layout.numColumns= 4; 306 layout.marginWidth= 0; 307 layout.marginHeight= 0; 308 composite.setLayout(layout); 309 310 fNameText= createText(composite); 311 fNameText.addModifyListener(listener); 312 fNameText.addFocusListener(new FocusListener() { 313 314 @Override 315 public void focusGained(FocusEvent e) { 316 } 317 318 @Override 319 public void focusLost(FocusEvent e) { 320 if (fSuppressError) { 321 fSuppressError= false; 322 updateButtons(); 323 } 324 } 325 }); 326 BidiUtils.applyBidiProcessing(fNameText, BidiUtils.BTD_DEFAULT); 327 328 createLabel(composite, TemplatesMessages.EditTemplateDialog_context); 329 fContextCombo= new Combo(composite, SWT.READ_ONLY); 330 331 for (String[] fContextType : fContextTypes) { 332 fContextCombo.add(fContextType[1]); 333 } 334 335 fContextCombo.addModifyListener(listener); 336 SWTUtil.setDefaultVisibleItemCount(fContextCombo); 337 338 fAutoInsertCheckbox= createCheckbox(composite, TemplatesMessages.EditTemplateDialog_autoinsert); 339 fAutoInsertCheckbox.setSelection(fOriginalTemplate.isAutoInsertable()); 340 } 341 342 createLabel(parent, TemplatesMessages.EditTemplateDialog_description); 343 344 int descFlags= fIsNameModifiable ? SWT.BORDER : SWT.BORDER | SWT.READ_ONLY; 345 fDescriptionText= new Text(parent, descFlags ); 346 fDescriptionText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 347 348 fDescriptionText.addModifyListener(listener); 349 BidiUtils.applyBidiProcessing(fDescriptionText, BidiUtils.BTD_DEFAULT); 350 351 Label patternLabel= createLabel(parent, TemplatesMessages.EditTemplateDialog_pattern); 352 patternLabel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); 353 fPatternEditor= createEditor(parent, fOriginalTemplate.getPattern()); 354 355 Label filler= new Label(parent, SWT.NONE); 356 filler.setLayoutData(new GridData()); 357 358 Composite composite= new Composite(parent, SWT.NONE); 359 layout= new GridLayout(); 360 layout.marginWidth= 0; 361 layout.marginHeight= 0; 362 composite.setLayout(layout); 363 composite.setLayoutData(new GridData()); 364 365 fInsertVariableButton= new Button(composite, SWT.NONE); 366 fInsertVariableButton.setLayoutData(getButtonGridData(fInsertVariableButton)); 367 fInsertVariableButton.setText(TemplatesMessages.EditTemplateDialog_insert_variable); 368 fInsertVariableButton.addSelectionListener(new SelectionListener() { 369 @Override 370 public void widgetSelected(SelectionEvent e) { 371 fPatternEditor.getTextWidget().setFocus(); 372 fPatternEditor.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); 373 } 374 375 @Override 376 public void widgetDefaultSelected(SelectionEvent e) {} 377 }); 378 379 fDescriptionText.setText(fOriginalTemplate.getDescription()); 380 if (fIsNameModifiable) { 381 fNameText.setText(fOriginalTemplate.getName()); 382 fNameText.addModifyListener(listener); 383 fContextCombo.select(getIndex(fOriginalTemplate.getContextTypeId())); 384 } else { 385 fPatternEditor.getControl().setFocus(); 386 } 387 initializeActions(); 388 389 applyDialogFont(parent); 390 return composite; 391 } 392 doTextWidgetChanged(Widget w)393 private void doTextWidgetChanged(Widget w) { 394 if (w == fNameText) { 395 fSuppressError= false; 396 updateButtons(); 397 } else if (w == fContextCombo) { 398 String contextId= getContextId(); 399 fTemplateProcessor.setContextType(fContextTypeRegistry.getContextType(contextId)); 400 } else if (w == fDescriptionText) { 401 // oh, nothing 402 } 403 } 404 getContextId()405 private String getContextId() { 406 if (fContextCombo != null && !fContextCombo.isDisposed()) { 407 String name= fContextCombo.getText(); 408 for (String[] fContextType : fContextTypes) { 409 if (name.equals(fContextType[1])) { 410 return fContextType[0]; 411 } 412 } 413 } 414 415 return fOriginalTemplate.getContextTypeId(); 416 } 417 doSourceChanged(IDocument document)418 private void doSourceChanged(IDocument document) { 419 String text= document.get(); 420 fValidationStatus.setOK(); 421 TemplateContextType contextType= fContextTypeRegistry.getContextType(getContextId()); 422 if (contextType != null) { 423 try { 424 contextType.validate(text); 425 } catch (TemplateException e) { 426 fValidationStatus.setError(e.getLocalizedMessage()); 427 } 428 } 429 430 updateAction(ITextEditorActionConstants.UNDO); 431 updateButtons(); 432 } 433 434 /** 435 * Return the grid data for the button. 436 * 437 * @param button the button 438 * @return the grid data 439 */ getButtonGridData(Button button)440 private static GridData getButtonGridData(Button button) { 441 GridData data= new GridData(GridData.FILL_HORIZONTAL); 442 // TODO get some button hints. 443 // data.heightHint= SWTUtil.getButtonHeightHint(button); 444 445 return data; 446 } 447 createLabel(Composite parent, String name)448 private static Label createLabel(Composite parent, String name) { 449 Label label= new Label(parent, SWT.NULL); 450 label.setText(name); 451 label.setLayoutData(new GridData()); 452 453 return label; 454 } 455 createText(Composite parent)456 private static Text createText(Composite parent) { 457 Text text= new Text(parent, SWT.BORDER); 458 text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 459 460 return text; 461 } 462 createCheckbox(Composite parent, String name)463 private static Button createCheckbox(Composite parent, String name) { 464 Button button= new Button(parent, SWT.CHECK); 465 button.setText(name); 466 button.setLayoutData(new GridData()); 467 468 return button; 469 } 470 createEditor(Composite parent, String pattern)471 private SourceViewer createEditor(Composite parent, String pattern) { 472 SourceViewer viewer= createViewer(parent); 473 viewer.setEditable(true); 474 475 IDocument document= viewer.getDocument(); 476 if (document != null) 477 document.set(pattern); 478 else { 479 document= new Document(pattern); 480 viewer.setDocument(document); 481 } 482 483 int nLines= document.getNumberOfLines(); 484 if (nLines < 5) { 485 nLines= 5; 486 } else if (nLines > 12) { 487 nLines= 12; 488 } 489 490 Control control= viewer.getControl(); 491 GridData data= new GridData(GridData.FILL_BOTH); 492 data.widthHint= convertWidthInCharsToPixels(80); 493 data.heightHint= convertHeightInCharsToPixels(nLines); 494 control.setLayoutData(data); 495 496 viewer.addTextListener(event -> { 497 if (event.getDocumentEvent() != null) 498 doSourceChanged(event.getDocumentEvent().getDocument()); 499 }); 500 501 viewer.addSelectionChangedListener(event -> updateSelectionDependentActions()); 502 503 return viewer; 504 } 505 506 /** 507 * Creates the viewer to be used to display the pattern. Subclasses may override. 508 * 509 * @param parent the parent composite of the viewer 510 * @return a configured <code>SourceViewer</code> 511 */ createViewer(Composite parent)512 protected SourceViewer createViewer(Composite parent) { 513 SourceViewer viewer= new SourceViewer(parent, null, null, false, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); 514 SourceViewerConfiguration configuration= new SourceViewerConfiguration() { 515 @Override 516 public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { 517 518 ContentAssistant assistant= new ContentAssistant(); 519 assistant.enableAutoActivation(true); 520 assistant.enableAutoInsert(true); 521 assistant.setContentAssistProcessor(fTemplateProcessor, IDocument.DEFAULT_CONTENT_TYPE); 522 return assistant; 523 } 524 }; 525 viewer.configure(configuration); 526 return viewer; 527 } 528 initializeActions()529 private void initializeActions() { 530 final ArrayList<IHandlerActivation> handlerActivations= new ArrayList<>(3); 531 final IHandlerService handlerService= PlatformUI.getWorkbench().getAdapter(IHandlerService.class); 532 final Expression expression= new ActiveShellExpression(fPatternEditor.getControl().getShell()); 533 534 getShell().addDisposeListener(e -> handlerService.deactivateHandlers(handlerActivations)); 535 536 fPatternEditor.getTextWidget().addFocusListener(new FocusListener() { 537 @Override 538 public void focusLost(FocusEvent e) { 539 handlerService.deactivateHandlers(handlerActivations); 540 } 541 542 @Override 543 public void focusGained(FocusEvent e) { 544 IAction action= fGlobalActions.get(ITextEditorActionConstants.REDO); 545 handlerActivations.add(handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_REDO, new ActionHandler(action), expression)); 546 action= fGlobalActions.get(ITextEditorActionConstants.UNDO); 547 handlerActivations.add(handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_UNDO, new ActionHandler(action), expression)); 548 action= fGlobalActions.get(ITextEditorActionConstants.CONTENT_ASSIST); 549 handlerActivations.add(handlerService.activateHandler(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, new ActionHandler(action), expression)); 550 } 551 }); 552 553 TextViewerAction action= new TextViewerAction(fPatternEditor, ITextOperationTarget.UNDO); 554 action.setText(TemplatesMessages.EditTemplateDialog_undo); 555 fGlobalActions.put(ITextEditorActionConstants.UNDO, action); 556 557 action= new TextViewerAction(fPatternEditor, ITextOperationTarget.REDO); 558 action.setText(TemplatesMessages.EditTemplateDialog_redo); 559 fGlobalActions.put(ITextEditorActionConstants.REDO, action); 560 561 action= new TextViewerAction(fPatternEditor, ITextOperationTarget.CUT); 562 action.setText(TemplatesMessages.EditTemplateDialog_cut); 563 fGlobalActions.put(ITextEditorActionConstants.CUT, action); 564 565 action= new TextViewerAction(fPatternEditor, ITextOperationTarget.COPY); 566 action.setText(TemplatesMessages.EditTemplateDialog_copy); 567 fGlobalActions.put(ITextEditorActionConstants.COPY, action); 568 569 action= new TextViewerAction(fPatternEditor, ITextOperationTarget.PASTE); 570 action.setText(TemplatesMessages.EditTemplateDialog_paste); 571 fGlobalActions.put(ITextEditorActionConstants.PASTE, action); 572 573 action= new TextViewerAction(fPatternEditor, ITextOperationTarget.SELECT_ALL); 574 action.setText(TemplatesMessages.EditTemplateDialog_select_all); 575 fGlobalActions.put(ITextEditorActionConstants.SELECT_ALL, action); 576 577 action= new TextViewerAction(fPatternEditor, ISourceViewer.CONTENTASSIST_PROPOSALS); 578 action.setText(TemplatesMessages.EditTemplateDialog_content_assist); 579 fGlobalActions.put(ITextEditorActionConstants.CONTENT_ASSIST, action); 580 581 fSelectionActions.add(ITextEditorActionConstants.CUT); 582 fSelectionActions.add(ITextEditorActionConstants.COPY); 583 fSelectionActions.add(ITextEditorActionConstants.PASTE); 584 585 // create context menu 586 MenuManager manager= new MenuManager(null, null); 587 manager.setRemoveAllWhenShown(true); 588 manager.addMenuListener(this::fillContextMenu); 589 590 StyledText text= fPatternEditor.getTextWidget(); 591 Menu menu= manager.createContextMenu(text); 592 text.setMenu(menu); 593 } 594 fillContextMenu(IMenuManager menu)595 private void fillContextMenu(IMenuManager menu) { 596 menu.add(new GroupMarker(ITextEditorActionConstants.GROUP_UNDO)); 597 menu.appendToGroup(ITextEditorActionConstants.GROUP_UNDO, fGlobalActions.get(ITextEditorActionConstants.UNDO)); 598 menu.appendToGroup(ITextEditorActionConstants.GROUP_UNDO, fGlobalActions.get(ITextEditorActionConstants.REDO)); 599 600 menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT)); 601 menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.CUT)); 602 menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.COPY)); 603 menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.PASTE)); 604 menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.SELECT_ALL)); 605 606 menu.add(new Separator("templates")); //$NON-NLS-1$ 607 menu.appendToGroup("templates", fGlobalActions.get("ContentAssistProposal")); //$NON-NLS-1$ //$NON-NLS-2$ 608 } 609 updateSelectionDependentActions()610 private void updateSelectionDependentActions() { 611 Iterator<String> iterator= fSelectionActions.iterator(); 612 while (iterator.hasNext()) 613 updateAction(iterator.next()); 614 } 615 updateAction(String actionId)616 private void updateAction(String actionId) { 617 IAction action= fGlobalActions.get(actionId); 618 if (action instanceof IUpdate) 619 ((IUpdate) action).update(); 620 } 621 getIndex(String contextid)622 private int getIndex(String contextid) { 623 624 if (contextid == null) 625 return -1; 626 627 for (int i= 0; i < fContextTypes.length; i++) { 628 if (contextid.equals(fContextTypes[i][0])) { 629 return i; 630 } 631 } 632 return -1; 633 } 634 updateButtons()635 private void updateButtons() { 636 StatusInfo status; 637 638 boolean valid= fNameText == null || !fNameText.getText().trim().isEmpty(); 639 if (!valid) { 640 status = new StatusInfo(); 641 if (!fSuppressError) 642 status.setError(TemplatesMessages.EditTemplateDialog_error_noname); 643 } else if (!isValidPattern(fPatternEditor.getDocument().get())) { 644 status = new StatusInfo(); 645 if (!fSuppressError) 646 status.setError(TemplatesMessages.EditTemplateDialog_error_invalidPattern); 647 } else { 648 status= fValidationStatus; 649 } 650 updateStatus(status); 651 } 652 653 /** 654 * Validates the pattern. 655 * <p> 656 * The default implementation rejects invalid XML characters. 657 * </p> 658 * 659 * @param pattern the pattern to verify 660 * @return <code>true</code> if the pattern is valid 661 * @since 3.7 protected, before it was private 662 */ isValidPattern(String pattern)663 protected boolean isValidPattern(String pattern) { 664 for (int i= 0; i < pattern.length(); i++) { 665 char ch= pattern.charAt(i); 666 if (!(ch == 9 || ch == 10 || ch == 13 || ch >= 32)) 667 return false; 668 } 669 return true; 670 } 671 672 /* 673 * @since 3.1 674 */ 675 @Override okPressed()676 protected void okPressed() { 677 String name= fNameText == null ? fOriginalTemplate.getName() : fNameText.getText(); 678 boolean isAutoInsertable= fAutoInsertCheckbox != null && fAutoInsertCheckbox.getSelection(); 679 fNewTemplate= new Template(name, fDescriptionText.getText(), getContextId(), fPatternEditor.getDocument().get(), isAutoInsertable); 680 super.okPressed(); 681 } 682 683 /** 684 * Returns the created template. 685 * 686 * @return the created template 687 * @since 3.1 688 */ getTemplate()689 public Template getTemplate() { 690 return fNewTemplate; 691 } 692 693 /** 694 * Returns the content assist processor that 695 * suggests template variables. 696 * 697 * @return the processor to suggest variables 698 * @since 3.3 699 */ getTemplateProcessor()700 protected IContentAssistProcessor getTemplateProcessor() { 701 return fTemplateProcessor; 702 } 703 704 @Override getDialogBoundsSettings()705 protected IDialogSettings getDialogBoundsSettings() { 706 String sectionName= getClass().getName() + "_dialogBounds"; //$NON-NLS-1$ 707 IDialogSettings settings= TextEditorPlugin.getDefault().getDialogSettings(); 708 IDialogSettings section= settings.getSection(sectionName); 709 if (section == null) 710 section= settings.addNewSection(sectionName); 711 return section; 712 } 713 714 } 715 716 717 /** 718 * Label provider for templates. 719 */ 720 private class TemplateLabelProvider extends LabelProvider implements ITableLabelProvider { 721 722 @Override getColumnImage(Object element, int columnIndex)723 public Image getColumnImage(Object element, int columnIndex) { 724 return null; 725 } 726 727 @Override getColumnText(Object element, int columnIndex)728 public String getColumnText(Object element, int columnIndex) { 729 TemplatePersistenceData data = (TemplatePersistenceData) element; 730 Template template= data.getTemplate(); 731 732 switch (columnIndex) { 733 case 0: 734 return template.getName(); 735 case 1: 736 TemplateContextType type= fContextTypeRegistry.getContextType(template.getContextTypeId()); 737 if (type != null) 738 return type.getName(); 739 return template.getContextTypeId(); 740 case 2: 741 return template.getDescription(); 742 case 3: 743 return template.isAutoInsertable() ? TemplatesMessages.TemplatePreferencePage_on : ""; //$NON-NLS-1$ 744 default: 745 return ""; //$NON-NLS-1$ 746 } 747 } 748 } 749 750 751 /** Qualified key for formatter preference. */ 752 private static final String DEFAULT_FORMATTER_PREFERENCE_KEY= "org.eclipse.ui.texteditor.templates.preferences.format_templates"; //$NON-NLS-1$ 753 754 /** The table presenting the templates. */ 755 private CheckboxTableViewer fTableViewer; 756 757 /* buttons */ 758 private Button fAddButton; 759 private Button fEditButton; 760 private Button fImportButton; 761 private Button fExportButton; 762 private Button fRemoveButton; 763 private Button fRestoreButton; 764 private Button fRevertButton; 765 766 /** The viewer displays the pattern of selected template. */ 767 private SourceViewer fPatternViewer; 768 /** Format checkbox. This gets conditionally added. */ 769 private Button fFormatButton; 770 /** The store for our templates. */ 771 private TemplateStore fTemplateStore; 772 /** The context type registry. */ 773 private ContextTypeRegistry fContextTypeRegistry; 774 775 776 /** 777 * Creates a new template preference page. 778 */ TemplatePreferencePage()779 protected TemplatePreferencePage() { 780 super(); 781 782 setDescription(TemplatesMessages.TemplatePreferencePage_message); 783 } 784 785 /** 786 * Returns the template store. 787 * 788 * @return the template store 789 */ getTemplateStore()790 public TemplateStore getTemplateStore() { 791 return fTemplateStore; 792 } 793 794 /** 795 * Returns the context type registry. 796 * 797 * @return the context type registry 798 */ getContextTypeRegistry()799 public ContextTypeRegistry getContextTypeRegistry() { 800 return fContextTypeRegistry; 801 } 802 803 /** 804 * Sets the template store. 805 * 806 * @param store the new template store 807 */ setTemplateStore(TemplateStore store)808 public void setTemplateStore(TemplateStore store) { 809 fTemplateStore= store; 810 } 811 812 /** 813 * Sets the context type registry. 814 * 815 * @param registry the new context type registry 816 */ setContextTypeRegistry(ContextTypeRegistry registry)817 public void setContextTypeRegistry(ContextTypeRegistry registry) { 818 fContextTypeRegistry= registry; 819 } 820 821 @Override init(IWorkbench workbench)822 public void init(IWorkbench workbench) { 823 } 824 825 @Override createContents(Composite ancestor)826 protected Control createContents(Composite ancestor) { 827 Composite parent= new Composite(ancestor, SWT.NONE); 828 GridLayout layout= new GridLayout(); 829 layout.numColumns= 2; 830 layout.marginHeight= 0; 831 layout.marginWidth= 0; 832 parent.setLayout(layout); 833 834 Composite innerParent= new Composite(parent, SWT.NONE); 835 GridLayout innerLayout= new GridLayout(); 836 innerLayout.numColumns= 2; 837 innerLayout.marginHeight= 0; 838 innerLayout.marginWidth= 0; 839 innerParent.setLayout(innerLayout); 840 GridData gd= new GridData(GridData.FILL_BOTH); 841 gd.horizontalSpan= 2; 842 innerParent.setLayoutData(gd); 843 844 Composite tableComposite= new Composite(innerParent, SWT.NONE); 845 GridData data= new GridData(GridData.FILL_BOTH); 846 data.widthHint= 360; 847 data.heightHint= convertHeightInCharsToPixels(10); 848 tableComposite.setLayoutData(data); 849 850 ColumnLayout columnLayout= new ColumnLayout(); 851 tableComposite.setLayout(columnLayout); 852 Table table= new Table(tableComposite, SWT.CHECK | SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL); 853 854 table.setHeaderVisible(true); 855 table.setLinesVisible(true); 856 857 GC gc= new GC(getShell()); 858 gc.setFont(JFaceResources.getDialogFont()); 859 860 TemplateViewerComparator viewerComparator= new TemplateViewerComparator(); 861 862 TableColumn column1= new TableColumn(table, SWT.NONE); 863 column1.setText(TemplatesMessages.TemplatePreferencePage_column_name); 864 int minWidth= computeMinimumColumnWidth(gc, TemplatesMessages.TemplatePreferencePage_column_name); 865 columnLayout.addColumnData(new ColumnWeightData(2, minWidth, true)); 866 column1.addSelectionListener(new TemplateColumnSelectionAdapter(column1, 0, viewerComparator)); 867 868 TableColumn column2= new TableColumn(table, SWT.NONE); 869 column2.setText(TemplatesMessages.TemplatePreferencePage_column_context); 870 minWidth= computeMinimumContextColumnWidth(gc); 871 columnLayout.addColumnData(new ColumnWeightData(1, minWidth, true)); 872 column2.addSelectionListener(new TemplateColumnSelectionAdapter(column2, 1, viewerComparator)); 873 874 TableColumn column3= new TableColumn(table, SWT.NONE); 875 column3.setText(TemplatesMessages.TemplatePreferencePage_column_description); 876 minWidth= computeMinimumColumnWidth(gc, TemplatesMessages.TemplatePreferencePage_column_description); 877 columnLayout.addColumnData(new ColumnWeightData(3, minWidth, true)); 878 column3.addSelectionListener(new TemplateColumnSelectionAdapter(column3, 2, viewerComparator)); 879 880 TableColumn column4= new TableColumn(table, SWT.NONE); 881 column4.setAlignment(SWT.CENTER); 882 column4.setText(TemplatesMessages.TemplatePreferencePage_column_autoinsert); 883 minWidth= computeMinimumColumnWidth(gc, TemplatesMessages.TemplatePreferencePage_column_autoinsert); 884 minWidth= Math.max(minWidth, computeMinimumColumnWidth(gc, TemplatesMessages.TemplatePreferencePage_on)); 885 columnLayout.addColumnData(new ColumnPixelData(minWidth, false, false)); 886 column4.addSelectionListener(new TemplateColumnSelectionAdapter(column4, 3, viewerComparator)); 887 888 gc.dispose(); 889 890 fTableViewer= new CheckboxTableViewer(table); 891 fTableViewer.setLabelProvider(new TemplateLabelProvider()); 892 fTableViewer.setContentProvider(new TemplateContentProvider()); 893 fTableViewer.setComparator(viewerComparator); 894 895 // Specify default sorting 896 table.setSortColumn(column1); 897 table.setSortDirection(viewerComparator.getDirection()); 898 899 fTableViewer.addDoubleClickListener(e -> edit()); 900 901 fTableViewer.addSelectionChangedListener(e -> selectionChanged1()); 902 903 fTableViewer.addCheckStateListener(event -> { 904 TemplatePersistenceData d = (TemplatePersistenceData) event.getElement(); 905 d.setEnabled(event.getChecked()); 906 }); 907 908 BidiUtils.applyTextDirection(fTableViewer.getControl(), BidiUtils.BTD_DEFAULT); 909 910 Composite buttons= new Composite(innerParent, SWT.NONE); 911 buttons.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); 912 layout= new GridLayout(); 913 layout.marginHeight= 0; 914 layout.marginWidth= 0; 915 buttons.setLayout(layout); 916 917 fAddButton= new Button(buttons, SWT.PUSH); 918 fAddButton.setText(TemplatesMessages.TemplatePreferencePage_new); 919 fAddButton.setLayoutData(getButtonGridData(fAddButton)); 920 fAddButton.addListener(SWT.Selection, e -> add()); 921 922 fEditButton= new Button(buttons, SWT.PUSH); 923 fEditButton.setText(TemplatesMessages.TemplatePreferencePage_edit); 924 fEditButton.setLayoutData(getButtonGridData(fEditButton)); 925 fEditButton.addListener(SWT.Selection, e -> edit()); 926 927 fRemoveButton= new Button(buttons, SWT.PUSH); 928 fRemoveButton.setText(TemplatesMessages.TemplatePreferencePage_remove); 929 fRemoveButton.setLayoutData(getButtonGridData(fRemoveButton)); 930 fRemoveButton.addListener(SWT.Selection, e -> remove()); 931 932 createSeparator(buttons); 933 934 fRestoreButton= new Button(buttons, SWT.PUSH); 935 fRestoreButton.setText(TemplatesMessages.TemplatePreferencePage_restore); 936 fRestoreButton.setLayoutData(getButtonGridData(fRestoreButton)); 937 fRestoreButton.addListener(SWT.Selection, e -> restoreDeleted()); 938 939 fRevertButton= new Button(buttons, SWT.PUSH); 940 fRevertButton.setText(TemplatesMessages.TemplatePreferencePage_revert); 941 fRevertButton.setLayoutData(getButtonGridData(fRevertButton)); 942 fRevertButton.addListener(SWT.Selection, e -> revert()); 943 944 createSeparator(buttons); 945 946 fImportButton= new Button(buttons, SWT.PUSH); 947 fImportButton.setText(TemplatesMessages.TemplatePreferencePage_import); 948 fImportButton.setLayoutData(getButtonGridData(fImportButton)); 949 fImportButton.addListener(SWT.Selection, e -> import_()); 950 951 fExportButton= new Button(buttons, SWT.PUSH); 952 fExportButton.setText(TemplatesMessages.TemplatePreferencePage_export); 953 fExportButton.setLayoutData(getButtonGridData(fExportButton)); 954 fExportButton.addListener(SWT.Selection, e -> export()); 955 956 fPatternViewer= doCreateViewer(parent); 957 958 if (isShowFormatterSetting()) { 959 fFormatButton= new Button(parent, SWT.CHECK); 960 fFormatButton.setText(TemplatesMessages.TemplatePreferencePage_use_code_formatter); 961 GridData gd1= new GridData(); 962 gd1.horizontalSpan= 2; 963 fFormatButton.setLayoutData(gd1); 964 fFormatButton.setSelection(getPreferenceStore().getBoolean(getFormatterPreferenceKey())); 965 } 966 967 fTableViewer.setInput(fTemplateStore); 968 fTableViewer.setAllChecked(false); 969 fTableViewer.setCheckedElements(getEnabledTemplates()); 970 971 updateButtons(); 972 Dialog.applyDialogFont(parent); 973 innerParent.layout(); 974 975 return parent; 976 } 977 978 /* 979 * @since 3.2 980 */ computeMinimumColumnWidth(GC gc, String string)981 private int computeMinimumColumnWidth(GC gc, String string) { 982 return gc.stringExtent(string).x + 10; // pad 10 to accommodate table header trimmings 983 } 984 985 /* 986 * @since 3.4 987 */ computeMinimumContextColumnWidth(GC gc)988 private int computeMinimumContextColumnWidth(GC gc) { 989 int width= gc.stringExtent(TemplatesMessages.TemplatePreferencePage_column_context).x; 990 Iterator<TemplateContextType> iter= getContextTypeRegistry().contextTypes(); 991 while (iter.hasNext()) { 992 TemplateContextType contextType= iter.next(); 993 width= Math.max(width, gc.stringExtent(contextType.getName()).x); 994 } 995 return width; 996 } 997 998 /** 999 * Creates a separator between buttons. 1000 * 1001 * @param parent the parent composite 1002 * @return a separator 1003 */ createSeparator(Composite parent)1004 private Label createSeparator(Composite parent) { 1005 Label separator= new Label(parent, SWT.NONE); 1006 separator.setVisible(false); 1007 GridData gd= new GridData(); 1008 gd.horizontalAlignment= GridData.FILL; 1009 gd.verticalAlignment= GridData.BEGINNING; 1010 gd.heightHint= 4; 1011 separator.setLayoutData(gd); 1012 return separator; 1013 } 1014 1015 1016 /** 1017 * Returns whether the formatter preference checkbox should be shown. 1018 * 1019 * @return <code>true</code> if the formatter preference checkbox should 1020 * be shown, <code>false</code> otherwise 1021 */ isShowFormatterSetting()1022 protected boolean isShowFormatterSetting() { 1023 return true; 1024 } 1025 getEnabledTemplates()1026 private TemplatePersistenceData[] getEnabledTemplates() { 1027 List<TemplatePersistenceData> enabled= new ArrayList<>(); 1028 TemplatePersistenceData[] datas= fTemplateStore.getTemplateData(false); 1029 for (TemplatePersistenceData data : datas) { 1030 if (data.isEnabled()) { 1031 enabled.add(data); 1032 } 1033 } 1034 return enabled.toArray(new TemplatePersistenceData[enabled.size()]); 1035 } 1036 doCreateViewer(Composite parent)1037 private SourceViewer doCreateViewer(Composite parent) { 1038 Label label= new Label(parent, SWT.NONE); 1039 label.setText(TemplatesMessages.TemplatePreferencePage_preview); 1040 GridData data= new GridData(); 1041 data.horizontalSpan= 2; 1042 label.setLayoutData(data); 1043 1044 SourceViewer viewer= createViewer(parent); 1045 1046 viewer.setEditable(false); 1047 Cursor arrowCursor= viewer.getTextWidget().getDisplay().getSystemCursor(SWT.CURSOR_ARROW); 1048 viewer.getTextWidget().setCursor(arrowCursor); 1049 1050 // Don't set caret to 'null' as this causes https://bugs.eclipse.org/293263 1051 // viewer.getTextWidget().setCaret(null); 1052 1053 Control control= viewer.getControl(); 1054 data= new GridData(GridData.FILL_BOTH); 1055 data.horizontalSpan= 2; 1056 data.heightHint= convertHeightInCharsToPixels(5); 1057 control.setLayoutData(data); 1058 1059 return viewer; 1060 } 1061 1062 /** 1063 * Creates, configures and returns a source viewer to present the template 1064 * pattern on the preference page. Clients may override to provide a custom 1065 * source viewer featuring e.g. syntax coloring. 1066 * 1067 * @param parent the parent control 1068 * @return a configured source viewer 1069 */ createViewer(Composite parent)1070 protected SourceViewer createViewer(Composite parent) { 1071 SourceViewer viewer= new SourceViewer(parent, null, null, false, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); 1072 SourceViewerConfiguration configuration= new SourceViewerConfiguration(); 1073 viewer.configure(configuration); 1074 IDocument document= new Document(); 1075 viewer.setDocument(document); 1076 return viewer; 1077 } 1078 1079 /** 1080 * Return the grid data for the button. 1081 * 1082 * @param button the button 1083 * @return the grid data 1084 */ getButtonGridData(Button button)1085 private static GridData getButtonGridData(Button button) { 1086 GridData data= new GridData(GridData.FILL_HORIZONTAL); 1087 // TODO replace SWTUtil 1088 // data.widthHint= SWTUtil.getButtonWidthHint(button); 1089 // data.heightHint= SWTUtil.getButtonHeightHint(button); 1090 1091 return data; 1092 } 1093 selectionChanged1()1094 private void selectionChanged1() { 1095 updateViewerInput(); 1096 updateButtons(); 1097 } 1098 1099 /** 1100 * Updates the pattern viewer. 1101 */ updateViewerInput()1102 protected void updateViewerInput() { 1103 IStructuredSelection selection = fTableViewer.getStructuredSelection(); 1104 1105 if (selection.size() == 1) { 1106 TemplatePersistenceData data= (TemplatePersistenceData) selection.getFirstElement(); 1107 Template template= data.getTemplate(); 1108 fPatternViewer.getDocument().set(template.getPattern()); 1109 } else { 1110 fPatternViewer.getDocument().set(""); //$NON-NLS-1$ 1111 } 1112 } 1113 1114 /** 1115 * Updates the buttons. 1116 */ updateButtons()1117 protected void updateButtons() { 1118 IStructuredSelection selection = fTableViewer.getStructuredSelection(); 1119 int selectionCount= selection.size(); 1120 int itemCount= fTableViewer.getTable().getItemCount(); 1121 boolean canRestore= fTemplateStore.getTemplateData(true).length != fTemplateStore.getTemplateData(false).length; 1122 boolean canRevert= false; 1123 for (Iterator<?> it= selection.iterator(); it.hasNext();) { 1124 TemplatePersistenceData data= (TemplatePersistenceData) it.next(); 1125 if (data.isModified()) { 1126 canRevert= true; 1127 break; 1128 } 1129 } 1130 1131 fEditButton.setEnabled(selectionCount == 1); 1132 fExportButton.setEnabled(selectionCount > 0); 1133 fRemoveButton.setEnabled(selectionCount > 0 && selectionCount <= itemCount); 1134 fRestoreButton.setEnabled(canRestore); 1135 fRevertButton.setEnabled(canRevert); 1136 } 1137 add()1138 private void add() { 1139 1140 Iterator<TemplateContextType> it= fContextTypeRegistry.contextTypes(); 1141 if (it.hasNext()) { 1142 Template template= new Template("", "", it.next().getId(), "", true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 1143 1144 Template newTemplate= editTemplate(template, false, true); 1145 if (newTemplate != null) { 1146 TemplatePersistenceData data= new TemplatePersistenceData(newTemplate, true); 1147 fTemplateStore.add(data); 1148 fTableViewer.refresh(); 1149 fTableViewer.setChecked(data, true); 1150 fTableViewer.setSelection(new StructuredSelection(data)); 1151 } 1152 } 1153 } 1154 1155 /** 1156 * Creates the edit dialog. Subclasses may override this method to provide a 1157 * custom dialog. 1158 * 1159 * @param template the template being edited 1160 * @param edit whether the dialog should be editable 1161 * @param isNameModifiable whether the template name may be modified 1162 * @return an <code>EditTemplateDialog</code> which will be opened. 1163 * @deprecated not called any longer as of 3.1 - use {@link #editTemplate(Template, boolean, boolean)} 1164 */ 1165 @Deprecated createTemplateEditDialog(Template template, boolean edit, boolean isNameModifiable)1166 protected Dialog createTemplateEditDialog(Template template, boolean edit, boolean isNameModifiable) { 1167 return new EditTemplateDialog(getShell(), template, edit, isNameModifiable, fContextTypeRegistry); 1168 } 1169 1170 /** 1171 * Creates the edit dialog. Subclasses may override this method to provide a 1172 * custom dialog. 1173 * 1174 * @param template the template being edited 1175 * @param edit whether the dialog should be editable 1176 * @param isNameModifiable whether the template name may be modified 1177 * @return the created or modified template, or <code>null</code> if the edition failed 1178 * @since 3.1 1179 */ editTemplate(Template template, boolean edit, boolean isNameModifiable)1180 protected Template editTemplate(Template template, boolean edit, boolean isNameModifiable) { 1181 EditTemplateDialog dialog= new EditTemplateDialog(getShell(), template, edit, isNameModifiable, fContextTypeRegistry); 1182 if (dialog.open() == Window.OK) { 1183 return dialog.getTemplate(); 1184 } 1185 return null; 1186 } 1187 edit()1188 private void edit() { 1189 IStructuredSelection selection = fTableViewer.getStructuredSelection(); 1190 1191 Object[] objects= selection.toArray(); 1192 if ((objects == null) || (objects.length != 1)) 1193 return; 1194 1195 TemplatePersistenceData data= (TemplatePersistenceData) selection.getFirstElement(); 1196 edit(data); 1197 } 1198 edit(TemplatePersistenceData data)1199 private void edit(TemplatePersistenceData data) { 1200 Template oldTemplate= data.getTemplate(); 1201 Template newTemplate= editTemplate(new Template(oldTemplate), true, true); 1202 if (newTemplate != null) { 1203 1204 if (!newTemplate.getName().equals(oldTemplate.getName()) && 1205 openCreateNewOrRenameDialog()) 1206 { 1207 data= new TemplatePersistenceData(newTemplate, true); 1208 fTemplateStore.add(data); 1209 fTableViewer.refresh(); 1210 } else { 1211 data.setTemplate(newTemplate); 1212 fTableViewer.refresh(data); 1213 } 1214 selectionChanged1(); 1215 fTableViewer.setChecked(data, data.isEnabled()); 1216 fTableViewer.setSelection(new StructuredSelection(data)); 1217 } 1218 } 1219 import_()1220 private void import_() { 1221 FileDialog dialog= new FileDialog(getShell()); 1222 dialog.setText(TemplatesMessages.TemplatePreferencePage_import_title); 1223 dialog.setFilterExtensions(new String[] {TemplatesMessages.TemplatePreferencePage_import_extension}); 1224 String path= dialog.open(); 1225 1226 if (path == null) 1227 return; 1228 1229 try { 1230 ArrayList<TemplatePersistenceData> selection= new ArrayList<>(); 1231 TemplateReaderWriter reader= new TemplateReaderWriter(); 1232 File file= new File(path); 1233 if (file.exists()) { 1234 try (InputStream input = new BufferedInputStream(new FileInputStream(file))) { 1235 TemplatePersistenceData[] datas= reader.read(input, null); 1236 for (TemplatePersistenceData data : datas) { 1237 fTemplateStore.add(data); 1238 String id= data.getId(); 1239 if (id == null) { 1240 selection.add(data); 1241 } else { 1242 data= fTemplateStore.getTemplateData(id); 1243 if (data != null) { 1244 selection.add(data); 1245 } 1246 } 1247 } 1248 } 1249 } 1250 1251 fTableViewer.refresh(); 1252 fTableViewer.setAllChecked(false); 1253 fTableViewer.setCheckedElements(getEnabledTemplates()); 1254 fTableViewer.setSelection(new StructuredSelection(selection), true); 1255 selectionChanged1(); 1256 1257 } catch (FileNotFoundException e) { 1258 openReadErrorDialog(e); 1259 } catch (IOException e) { 1260 openReadErrorDialog(e); 1261 } 1262 } 1263 export()1264 private void export() { 1265 IStructuredSelection selection = fTableViewer.getStructuredSelection(); 1266 Object[] templates= selection.toArray(); 1267 1268 TemplatePersistenceData[] datas= new TemplatePersistenceData[templates.length]; 1269 for (int i= 0; i != templates.length; i++) 1270 datas[i]= (TemplatePersistenceData) templates[i]; 1271 1272 export(datas); 1273 } 1274 export(TemplatePersistenceData[] templates)1275 private void export(TemplatePersistenceData[] templates) { 1276 FileDialog dialog= new FileDialog(getShell(), SWT.SAVE); 1277 dialog.setText(TemplatesMessages.TemplatePreferencePage_export_title); 1278 dialog.setFilterExtensions(new String[] {TemplatesMessages.TemplatePreferencePage_export_extension}); 1279 dialog.setFileName(TemplatesMessages.TemplatePreferencePage_export_filename); 1280 String path= dialog.open(); 1281 1282 if (path == null) 1283 return; 1284 1285 File file= new File(path); 1286 1287 if (file.isHidden()) { 1288 String title= TemplatesMessages.TemplatePreferencePage_export_error_title; 1289 String message= NLSUtility.format(TemplatesMessages.TemplatePreferencePage_export_error_hidden, file.getAbsolutePath()); 1290 MessageDialog.openError(getShell(), title, message); 1291 return; 1292 } 1293 1294 if (file.exists() && !file.canWrite()) { 1295 String title= TemplatesMessages.TemplatePreferencePage_export_error_title; 1296 String message= NLSUtility.format(TemplatesMessages.TemplatePreferencePage_export_error_canNotWrite, file.getAbsolutePath()); 1297 MessageDialog.openError(getShell(), title, message); 1298 return; 1299 } 1300 1301 if (!file.exists() || confirmOverwrite(file)) { 1302 try (OutputStream output = new BufferedOutputStream(new FileOutputStream(file))) { 1303 TemplateReaderWriter writer= new TemplateReaderWriter(); 1304 writer.save(templates, output); 1305 } catch (IOException e) { 1306 openWriteErrorDialog(e); 1307 } 1308 } 1309 } 1310 confirmOverwrite(File file)1311 private boolean confirmOverwrite(File file) { 1312 return MessageDialog.openQuestion(getShell(), 1313 TemplatesMessages.TemplatePreferencePage_export_exists_title, 1314 NLSUtility.format(TemplatesMessages.TemplatePreferencePage_export_exists_message, file.getAbsolutePath())); 1315 } 1316 remove()1317 private void remove() { 1318 IStructuredSelection selection = fTableViewer.getStructuredSelection(); 1319 1320 Iterator<?> elements= selection.iterator(); 1321 while (elements.hasNext()) { 1322 TemplatePersistenceData data= (TemplatePersistenceData) elements.next(); 1323 fTemplateStore.delete(data); 1324 } 1325 1326 fTableViewer.refresh(); 1327 } 1328 restoreDeleted()1329 private void restoreDeleted() { 1330 TemplatePersistenceData[] oldTemplates= fTemplateStore.getTemplateData(false); 1331 fTemplateStore.restoreDeleted(); 1332 TemplatePersistenceData[] newTemplates= fTemplateStore.getTemplateData(false); 1333 fTableViewer.refresh(); 1334 fTableViewer.setCheckedElements(getEnabledTemplates()); 1335 ArrayList<TemplatePersistenceData> selection= new ArrayList<>(); 1336 selection.addAll(Arrays.asList(newTemplates)); 1337 selection.removeAll(Arrays.asList(oldTemplates)); 1338 fTableViewer.setSelection(new StructuredSelection(selection), true); 1339 selectionChanged1(); 1340 } 1341 revert()1342 private void revert() { 1343 IStructuredSelection selection = fTableViewer.getStructuredSelection(); 1344 1345 Iterator<?> elements= selection.iterator(); 1346 while (elements.hasNext()) { 1347 TemplatePersistenceData data= (TemplatePersistenceData) elements.next(); 1348 data.revert(); 1349 fTableViewer.setChecked(data, data.isEnabled()); 1350 } 1351 1352 selectionChanged1(); 1353 fTableViewer.refresh(); 1354 } 1355 1356 @Override setVisible(boolean visible)1357 public void setVisible(boolean visible) { 1358 super.setVisible(visible); 1359 if (visible) 1360 setTitle(TemplatesMessages.TemplatePreferencePage_title); 1361 } 1362 1363 @Override performDefaults()1364 protected void performDefaults() { 1365 if (isShowFormatterSetting()) { 1366 IPreferenceStore prefs= getPreferenceStore(); 1367 fFormatButton.setSelection(prefs.getDefaultBoolean(getFormatterPreferenceKey())); 1368 } 1369 1370 fTemplateStore.restoreDefaults(false); 1371 1372 // refresh 1373 fTableViewer.refresh(); 1374 fTableViewer.setAllChecked(false); 1375 fTableViewer.setCheckedElements(getEnabledTemplates()); 1376 } 1377 1378 @Override performOk()1379 public boolean performOk() { 1380 if (isShowFormatterSetting()) { 1381 IPreferenceStore prefs= getPreferenceStore(); 1382 prefs.setValue(getFormatterPreferenceKey(), fFormatButton.getSelection()); 1383 } 1384 1385 try { 1386 fTemplateStore.save(); 1387 } catch (IOException e) { 1388 openWriteErrorDialog(e); 1389 } 1390 1391 return super.performOk(); 1392 } 1393 1394 /** 1395 * Returns the key to use for the formatter preference. 1396 * 1397 * @return the formatter preference key 1398 */ getFormatterPreferenceKey()1399 protected String getFormatterPreferenceKey() { 1400 return DEFAULT_FORMATTER_PREFERENCE_KEY; 1401 } 1402 1403 @Override performCancel()1404 public boolean performCancel() { 1405 try { 1406 fTemplateStore.load(); 1407 } catch (IOException e) { 1408 openReadErrorDialog(e); 1409 return false; 1410 } 1411 return super.performCancel(); 1412 } 1413 1414 /* 1415 * @since 3.2 1416 */ openReadErrorDialog(IOException ex)1417 private void openReadErrorDialog(IOException ex) { 1418 IStatus status= new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, "Failed to read templates.", ex); //$NON-NLS-1$ 1419 TextEditorPlugin.getDefault().getLog().log(status); 1420 String title= TemplatesMessages.TemplatePreferencePage_error_read_title; 1421 String message= TemplatesMessages.TemplatePreferencePage_error_read_message; 1422 MessageDialog.openError(getShell(), title, message); 1423 } 1424 1425 /* 1426 * @since 3.2 1427 */ openWriteErrorDialog(IOException ex)1428 private void openWriteErrorDialog(IOException ex) { 1429 IStatus status= new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, "Failed to write templates.", ex); //$NON-NLS-1$ 1430 TextEditorPlugin.getDefault().getLog().log(status); 1431 String title= TemplatesMessages.TemplatePreferencePage_error_write_title; 1432 String message= TemplatesMessages.TemplatePreferencePage_error_write_message; 1433 MessageDialog.openError(getShell(), title, message); 1434 } 1435 openCreateNewOrRenameDialog()1436 private boolean openCreateNewOrRenameDialog() { 1437 MessageDialog dialog = new MessageDialog(getShell(), 1438 TemplatesMessages.TemplatePreferencePage_question_create_new_title, null, 1439 TemplatesMessages.TemplatePreferencePage_question_create_new_message, MessageDialog.QUESTION, 0, 1440 TemplatesMessages.TemplatePreferencePage_question_create_new_button_create, 1441 TemplatesMessages.TemplatePreferencePage_question_create_new_button_rename); 1442 return dialog.open() == 0; 1443 } 1444 getViewer()1445 protected SourceViewer getViewer() { 1446 return fPatternViewer; 1447 } 1448 getTableViewer()1449 protected TableViewer getTableViewer() { 1450 return fTableViewer; 1451 } 1452 1453 private final class TemplateViewerComparator extends ViewerComparator { 1454 1455 private int fSortColumn; 1456 1457 private int fSortOrder; // 1 = asc, -1 = desc 1458 TemplateViewerComparator()1459 public TemplateViewerComparator() { 1460 fSortColumn= 0; 1461 fSortOrder= 1; 1462 } 1463 1464 /** 1465 * Returns the {@linkplain SWT} style constant for the sort direction. 1466 * 1467 * @return {@link SWT#DOWN} for asc sorting, {@link SWT#UP} otherwise 1468 */ getDirection()1469 public int getDirection() { 1470 return fSortOrder == 1 ? SWT.DOWN : SWT.UP; 1471 } 1472 1473 /** 1474 * Sets the sort column. If the newly set sort column equals the previous set sort column, 1475 * the sort direction changes. 1476 * 1477 * @param column New sort column 1478 */ setColumn(int column)1479 public void setColumn(int column) { 1480 if (column == fSortColumn) { 1481 fSortOrder*= -1; 1482 } else { 1483 fSortColumn= column; 1484 fSortOrder= 1; 1485 } 1486 } 1487 1488 @Override compare(Viewer viewer, Object e1, Object e2)1489 public int compare(Viewer viewer, Object e1, Object e2) { 1490 1491 if (viewer instanceof TableViewer) { 1492 IBaseLabelProvider baseLabel= ((TableViewer)viewer).getLabelProvider(); 1493 1494 String left= ((TemplateLabelProvider)baseLabel).getColumnText(e1, fSortColumn); 1495 String right= ((TemplateLabelProvider)baseLabel).getColumnText(e2, fSortColumn); 1496 int sortResult= getComparator().compare(left, right); 1497 return sortResult * fSortOrder; 1498 } 1499 1500 return super.compare(viewer, e1, e2); 1501 } 1502 } 1503 1504 private final class TemplateColumnSelectionAdapter extends SelectionAdapter { 1505 1506 private final TableColumn fTableColumn; 1507 1508 private final int fColumnIndex; 1509 1510 private final TemplateViewerComparator fViewerComparator; 1511 TemplateColumnSelectionAdapter(TableColumn column, int index, TemplateViewerComparator vc)1512 public TemplateColumnSelectionAdapter(TableColumn column, int index, TemplateViewerComparator vc) { 1513 fTableColumn= column; 1514 fColumnIndex= index; 1515 fViewerComparator= vc; 1516 } 1517 1518 @Override widgetSelected(SelectionEvent e)1519 public void widgetSelected(SelectionEvent e) { 1520 fViewerComparator.setColumn(fColumnIndex); 1521 int dir= fViewerComparator.getDirection(); 1522 fTableViewer.getTable().setSortDirection(dir); 1523 fTableViewer.getTable().setSortColumn(fTableColumn); 1524 fTableViewer.refresh(); 1525 } 1526 } 1527 } 1528