blob: 3121839b1c124c8126e5cac0693cef2efbc893b0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Sebastian Davids - bug 57208
* Maik Schreiber - bug 102461
* Eugene Kuleshov (eu@md.pp.ru) - Bug 112742 [Wizards] Add spell check to commit dialog
*******************************************************************************/
package org.eclipse.team.internal.ccvs.ui;
import java.util.*;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.jface.dialogs.*;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.*;
import org.eclipse.jface.text.*;
import org.eclipse.jface.text.reconciler.*;
import org.eclipse.jface.text.source.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.*;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.util.Util;
import org.eclipse.team.internal.ui.SWTUtils;
import org.eclipse.team.internal.ui.dialogs.DialogArea;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
import org.eclipse.ui.texteditor.spelling.*;
/**
* This area provides the widgets for providing the CVS commit comment
*/
public class CommitCommentArea extends DialogArea {
public static final String SPELLING_ERROR = "spelling.error"; //$NON-NLS-1$
private class TextBox implements ModifyListener, TraverseListener, FocusListener, Observer {
private final StyledText fTextField; // updated only by modify events
private final String fMessage;
private String fText;
private LocalResourceManager fResources;
public TextBox(Composite composite, String message, String initialText) {
fMessage= message;
fText= initialText;
// Create a resource manager for the composite so it gets automatically disposed
fResources= new LocalResourceManager(JFaceResources.getResources(), composite);
AnnotationModel annotationModel = new AnnotationModel();
IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess();
AnnotationRulerColumn annotationRuler = new AnnotationRulerColumn(annotationModel, 16, annotationAccess);
CompositeRuler compositeRuler = new CompositeRuler();
compositeRuler.setModel(annotationModel);
compositeRuler.addDecorator(0, annotationRuler);
Composite cc = new Composite(composite, SWT.BORDER);
cc.setLayout(new FillLayout());
cc.setLayoutData(new GridData(GridData.FILL_BOTH));
SourceViewer sourceViewer = new SourceViewer(cc, compositeRuler, null, true,
SWT.MULTI | SWT.V_SCROLL | SWT.WRAP);
sourceViewer.showAnnotations(false);
sourceViewer.showAnnotationsOverview(false);
if (isSpellingAnnotationEnabled()) {
// to paint the annotations
AnnotationPainter ap = new AnnotationPainter(sourceViewer, annotationAccess);
ap.addAnnotationType(SPELLING_ERROR);
ap.setAnnotationTypeColor(SPELLING_ERROR, getSpellingErrorColor(composite));
// this will draw the squiggles under the text
sourceViewer.addPainter(ap);
}
Document document = new Document(initialText);
// NOTE: Configuration must be applied before the document is set in order for
// Hyperlink coloring to work. (Presenter needs document object up front)
sourceViewer.configure(new SourceViewerConfig(annotationModel, document));
sourceViewer.setDocument(document, annotationModel);
fTextField = sourceViewer.getTextWidget();
fTextField.addTraverseListener(this);
fTextField.addModifyListener(this);
fTextField.addFocusListener(this);
}
private boolean isSpellingAnnotationEnabled() {
// Need to determine how to ask the proper question to the AnnotationPreferences
return true;
}
private Color getSpellingErrorColor(Composite composite) {
AnnotationPreference pref = EditorsUI
.getAnnotationPreferenceLookup().getAnnotationPreference(
"org.eclipse.ui.workbench.texteditor.spelling"); // $NON-NLS-1$
String preferenceKey = pref.getColorPreferenceKey();
try {
return fResources.createColor(PreferenceConverter.getColor(EditorsUI.getPreferenceStore(), preferenceKey));
} catch (DeviceResourceException e) {
CVSUIPlugin.log(IStatus.ERROR, CVSUIMessages.internal, e);
return JFaceColors.getErrorText(composite.getDisplay());
}
}
public void modifyText(ModifyEvent e) {
final String old = fText;
fText = fTextField.getText();
firePropertyChangeChange(COMMENT_MODIFIED, old, fText);
}
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_RETURN && (e.stateMask & SWT.CTRL) != 0) {
e.doit = false;
firePropertyChangeChange(OK_REQUESTED, null, null);
}
}
public void focusGained(FocusEvent e) {
if (fText.length() > 0)
return;
fTextField.removeModifyListener(this);
try {
fTextField.setText(fText);
} finally {
fTextField.addModifyListener(this);
}
}
public void focusLost(FocusEvent e) {
if (fText.length() > 0)
return;
fTextField.removeModifyListener(this);
try {
fTextField.setText(fMessage);
fTextField.selectAll();
} finally {
fTextField.addModifyListener(this);
}
}
public void setEnabled(boolean enabled) {
fTextField.setEnabled(enabled);
}
public void update(Observable o, Object arg) {
if (arg instanceof String) {
setText((String)arg); // triggers a modify event
}
}
public String getText() {
return fText;
}
private void setText(String text) {
if (text.length() == 0) {
fTextField.setText(fMessage);
fTextField.selectAll();
} else
fTextField.setText(text);
}
public void setFocus() {
fTextField.setFocus();
}
}
public class SourceViewerConfig extends SourceViewerConfiguration {
private CommentSpellingReconcileStrategy strategy;
public SourceViewerConfig(AnnotationModel annotationModel, Document document) {
strategy = new CommentSpellingReconcileStrategy(annotationModel);
strategy.setDocument(document);
}
public IReconciler getReconciler(ISourceViewer sourceViewer) {
MonoReconciler reconciler = new MonoReconciler(strategy, false);
reconciler.setIsIncrementalReconciler(false);
reconciler.setProgressMonitor(new NullProgressMonitor());
reconciler.setDelay(200);
return reconciler;
}
}
public class CommentSpellingReconcileStrategy implements IReconcilingStrategy {
/** The document to operate on. */
private IDocument fDocument;
private SpellingContext fSpellingContext;
private IAnnotationModel fAnnotationModel;
public CommentSpellingReconcileStrategy(AnnotationModel annotationModel) {
this.fAnnotationModel = annotationModel;
fSpellingContext = new SpellingContext();
fSpellingContext.setContentType(Platform.getContentTypeManager().getContentType(IContentTypeManager.CT_TEXT));
}
public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
reconcile(subRegion);
}
public void reconcile(IRegion region) {
SpellingProblemCollector collector = new SpellingProblemCollector(fAnnotationModel);
EditorsUI.getSpellingService().check(fDocument, fSpellingContext, collector, null);
}
public void setDocument(IDocument document) {
fDocument = document;
}
/**
* Spelling problem collector that forwards {@link SpellingProblem}s as
* {@link IProblem}s to the {@link org.eclipse.jdt.core.IProblemRequestor}.
*/
private class SpellingProblemCollector implements ISpellingProblemCollector {
/** Annotation model */
private IAnnotationModel fAnnotationModel;
/** Annotations to add <ErrorAnnotation, Position> */
private Map fAddAnnotations;
/**
* Initializes this collector with the given annotation model.
*
* @param annotationModel
* the annotation model
*/
public SpellingProblemCollector(IAnnotationModel annotationModel) {
fAnnotationModel = annotationModel;
}
/*
* @see org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector#accept(org.eclipse.ui.texteditor.spelling.SpellingProblem)
*/
public void accept(SpellingProblem problem) {
fAddAnnotations.put(new Annotation(SPELLING_ERROR, false, SPELLING_ERROR),
new Position(problem.getOffset(), problem.getLength()));
}
/*
* @see org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector#beginCollecting()
*/
public void beginCollecting() {
fAddAnnotations = new HashMap();
}
/*
* @see org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector#endCollecting()
*/
public void endCollecting() {
List removeAnnotations = new ArrayList();
for(Iterator iter = fAnnotationModel.getAnnotationIterator(); iter.hasNext();) {
Annotation annotation = (Annotation) iter.next();
if(SPELLING_ERROR.equals(annotation.getType()))
removeAnnotations.add(annotation);
}
for(Iterator iter = removeAnnotations.iterator(); iter.hasNext();)
fAnnotationModel.removeAnnotation((Annotation) iter.next());
for(Iterator iter = fAddAnnotations.keySet().iterator(); iter.hasNext();) {
Annotation annotation = (Annotation) iter.next();
fAnnotationModel.addAnnotation(annotation, (Position) fAddAnnotations.get(annotation));
}
fAddAnnotations = null;
}
}
}
private static class ComboBox extends Observable implements SelectionListener, FocusListener {
private final String fMessage;
private final String [] fComments;
private String[] fCommentTemplates;
private final Combo fCombo;
public ComboBox(Composite composite, String message, String [] options,
String[] commentTemplates) {
fMessage= message;
fComments= options;
fCommentTemplates = commentTemplates;
fCombo = new Combo(composite, SWT.READ_ONLY);
fCombo.setLayoutData(SWTUtils.createHFillGridData());
fCombo.setVisibleItemCount(20);
// populate the previous comment list
populateList();
// We don't want to have an initial selection
// (see bug 32078: http://bugs.eclipse.org/bugs/show_bug.cgi?id=32078)
fCombo.addFocusListener(this);
fCombo.addSelectionListener(this);
}
private void populateList() {
fCombo.removeAll();
fCombo.add(fMessage);
for (int i = 0; i < fCommentTemplates.length; i++) {
fCombo.add(CVSUIMessages.CommitCommentArea_6 + ": " + //$NON-NLS-1$
Util.flattenText(fCommentTemplates[i]));
}
for (int i = 0; i < fComments.length; i++) {
fCombo.add(Util.flattenText(fComments[i]));
}
fCombo.setText(fMessage);
}
public void widgetSelected(SelectionEvent e) {
int index = fCombo.getSelectionIndex();
if (index > 0) {
index--;
setChanged();
// map from combo box index to array index
String message;
if (index < fCommentTemplates.length) {
message = fCommentTemplates[index];
} else {
message = fComments[index - fCommentTemplates.length];
}
notifyObservers(message);
}
}
public void widgetDefaultSelected(SelectionEvent e) {
}
public void focusGained(FocusEvent e) {
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
*/
public void focusLost(FocusEvent e) {
fCombo.removeSelectionListener(this);
try {
fCombo.setText(fMessage);
} finally {
fCombo.addSelectionListener(this);
}
}
public void setEnabled(boolean enabled) {
fCombo.setEnabled(enabled);
}
void setCommentTemplates(String[] templates) {
fCommentTemplates = templates;
populateList();
}
}
private static final String EMPTY_MESSAGE= CVSUIMessages.CommitCommentArea_0;
private static final String COMBO_MESSAGE= CVSUIMessages.CommitCommentArea_1;
private static final String CONFIGURE_TEMPLATES_MESSAGE= CVSUIMessages.CommitCommentArea_5;
public static final String OK_REQUESTED = "OkRequested";//$NON-NLS-1$
public static final String COMMENT_MODIFIED = "CommentModified";//$NON-NLS-1$
private TextBox fTextBox;
private ComboBox fComboBox;
private IProject fMainProject;
private String fProposedComment;
private Composite fComposite;
/**
* @see org.eclipse.team.internal.ccvs.ui.DialogArea#createArea(org.eclipse.swt.widgets.Composite)
*/
public void createArea(Composite parent) {
Dialog.applyDialogFont(parent);
initializeDialogUnits(parent);
fComposite = createGrabbingComposite(parent, 1);
initializeDialogUnits(fComposite);
fTextBox= new TextBox(fComposite, EMPTY_MESSAGE, getInitialComment());
final String [] comments = CVSUIPlugin.getPlugin().getRepositoryManager().getPreviousComments();
final String[] commentTemplates = CVSUIPlugin.getPlugin().getRepositoryManager().getCommentTemplates();
fComboBox= new ComboBox(fComposite, COMBO_MESSAGE, comments, commentTemplates);
Link templatesPrefsLink = new Link(fComposite, 0);
templatesPrefsLink.setText("<a href=\"configureTemplates\">" + //$NON-NLS-1$
CONFIGURE_TEMPLATES_MESSAGE + "</a>"); //$NON-NLS-1$
templatesPrefsLink.addSelectionListener(new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent e) {
openCommentTemplatesPreferencePage();
}
public void widgetSelected(SelectionEvent e) {
openCommentTemplatesPreferencePage();
}
});
fComboBox.addObserver(fTextBox);
}
void openCommentTemplatesPreferencePage() {
PreferencesUtil.createPreferenceDialogOn(
null,
"org.eclipse.team.cvs.ui.CommentTemplatesPreferences", //$NON-NLS-1$
new String[] { "org.eclipse.team.cvs.ui.CommentTemplatesPreferences" }, //$NON-NLS-1$
null).open();
fComboBox.setCommentTemplates(
CVSUIPlugin.getPlugin().getRepositoryManager().getCommentTemplates());
}
public String getComment(boolean save) {
final String comment= fTextBox.getText();
if (comment == null)
return ""; //$NON-NLS-1$
final String stripped= strip(comment);
if (save && comment.length() > 0)
CVSUIPlugin.getPlugin().getRepositoryManager().addComment(comment);
return stripped;
}
public String getCommentWithPrompt(Shell shell) {
final String comment= getComment(false);
if (comment.length() == 0) {
final IPreferenceStore store= CVSUIPlugin.getPlugin().getPreferenceStore();
final String value= store.getString(ICVSUIConstants.PREF_ALLOW_EMPTY_COMMIT_COMMENTS);
if (MessageDialogWithToggle.NEVER.equals(value))
return null;
if (MessageDialogWithToggle.PROMPT.equals(value)) {
final String title= CVSUIMessages.CommitCommentArea_2;
final String message= CVSUIMessages.CommitCommentArea_3;
final String toggleMessage= CVSUIMessages.CommitCommentArea_4;
final MessageDialogWithToggle dialog= MessageDialogWithToggle.openYesNoQuestion(shell, title, message, toggleMessage, false, store, ICVSUIConstants.PREF_ALLOW_EMPTY_COMMIT_COMMENTS);
if (dialog.getReturnCode() != IDialogConstants.YES_ID) {
fTextBox.setFocus();
return null;
}
}
}
return getComment(true);
}
public void setProject(IProject iProject) {
this.fMainProject = iProject;
}
public void setFocus() {
if (fTextBox != null) {
fTextBox.setFocus();
}
}
public void setProposedComment(String proposedComment) {
if (proposedComment == null || proposedComment.length() == 0) {
this.fProposedComment = null;
} else {
this.fProposedComment = proposedComment;
}
}
public boolean hasCommitTemplate() {
try {
String commitTemplate = getCommitTemplate();
return commitTemplate != null && commitTemplate.length() > 0;
} catch (CVSException e) {
CVSUIPlugin.log(e);
return false;
}
}
public void setEnabled(boolean enabled) {
fTextBox.setEnabled(enabled);
fComboBox.setEnabled(enabled);
}
public Composite getComposite() {
return fComposite;
}
protected void firePropertyChangeChange(String property, Object oldValue, Object newValue) {
super.firePropertyChangeChange(property, oldValue, newValue);
}
private String getInitialComment() {
if (fProposedComment != null)
return fProposedComment;
try {
return getCommitTemplate();
} catch (CVSException e) {
CVSUIPlugin.log(e);
return ""; //$NON-NLS-1$
}
}
private String strip(String comment) {
// strip template from the comment entered
try {
final String commitTemplate = getCommitTemplate();
if (comment.startsWith(commitTemplate)) {
return comment.substring(commitTemplate.length());
} else if (comment.endsWith(commitTemplate)) {
return comment.substring(0, comment.length() - commitTemplate.length());
}
} catch (CVSException e) {
// we couldn't get the commit template. Log the error and continue
CVSUIPlugin.log(e);
}
return comment;
}
private CVSTeamProvider getProvider() {
if (fMainProject == null) return null;
return (CVSTeamProvider) RepositoryProvider.getProvider(fMainProject, CVSProviderPlugin.getTypeId());
}
private String getCommitTemplate() throws CVSException {
CVSTeamProvider provider = getProvider();
if (provider == null)
return ""; //$NON-NLS-1$
final String template = provider.getCommitTemplate();
return template != null ? template : ""; //$NON-NLS-1$
}
}