001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.texteditor;
011:
012: import java.util.ArrayList;
013: import java.util.Collections;
014: import java.util.Iterator;
015: import java.util.List;
016: import java.util.ResourceBundle;
017:
018: import org.osgi.framework.Bundle;
019:
020: import org.eclipse.core.resources.IFile;
021: import org.eclipse.core.resources.IMarker;
022: import org.eclipse.core.resources.IResource;
023: import org.eclipse.core.runtime.CoreException;
024: import org.eclipse.core.runtime.ILog;
025: import org.eclipse.core.runtime.IStatus;
026: import org.eclipse.core.runtime.Platform;
027: import org.eclipse.core.runtime.Status;
028:
029: import org.eclipse.swt.widgets.Shell;
030:
031: import org.eclipse.jface.text.BadLocationException;
032: import org.eclipse.jface.text.IDocument;
033: import org.eclipse.jface.text.Position;
034: import org.eclipse.jface.text.source.Annotation;
035: import org.eclipse.jface.text.source.IAnnotationAccess;
036: import org.eclipse.jface.text.source.IAnnotationAccessExtension;
037: import org.eclipse.jface.text.source.IAnnotationModel;
038: import org.eclipse.jface.text.source.IVerticalRuler;
039: import org.eclipse.jface.text.source.IVerticalRulerInfo;
040:
041: import org.eclipse.jface.dialogs.ErrorDialog;
042: import org.eclipse.ui.IEditorInput;
043: import org.eclipse.ui.IWorkbenchPage;
044: import org.eclipse.ui.PlatformUI;
045: import org.eclipse.ui.ide.IGotoMarker;
046: import org.eclipse.ui.views.markers.MarkerViewUtil;
047:
048: /**
049: * A ruler action which can select the textual range of a marker
050: * that has a visual representation in a vertical ruler.
051: * <p>
052: * This class may be instantiated but is not intended for sub-classing.
053: * </p>
054: * @since 2.0
055: */
056: public class SelectMarkerRulerAction extends ResourceAction implements
057: IUpdate {
058:
059: /** The vertical ruler info of the action's editor. */
060: private IVerticalRulerInfo fRuler;
061: /** The associated editor. */
062: private ITextEditor fTextEditor;
063: /** The action's resource bundle. */
064: private ResourceBundle fBundle;
065: /** The prefix for resource bundle lookups. */
066: private String fPrefix;
067:
068: /**
069: * Creates a new action for the given ruler and editor. The action configures
070: * its visual representation from the given resource bundle.
071: *
072: * @param bundle the resource bundle
073: * @param prefix a prefix to be prepended to the various resource keys
074: * (described in <code>ResourceAction</code> constructor), or <code>null</code> if none
075: * @param editor the editor
076: * @param ruler the ruler
077: *
078: * @see ResourceAction#ResourceAction(ResourceBundle, String)
079: */
080: public SelectMarkerRulerAction(ResourceBundle bundle,
081: String prefix, ITextEditor editor, IVerticalRulerInfo ruler) {
082: super (bundle, prefix);
083: fRuler = ruler;
084: fTextEditor = editor;
085:
086: fBundle = bundle;
087: fPrefix = prefix;
088: }
089:
090: /**
091: * Creates a new action for the given ruler and editor. The action configures
092: * its visual representation from the given resource bundle.
093: *
094: * @param bundle the resource bundle
095: * @param prefix a prefix to be prepended to the various resource keys
096: * @param ruler the ruler
097: * @param editor the editor
098: * @deprecated As of 3.0, replaced by {@link #SelectMarkerRulerAction(ResourceBundle, String, ITextEditor, IVerticalRulerInfo)}
099: */
100: public SelectMarkerRulerAction(ResourceBundle bundle,
101: String prefix, IVerticalRuler ruler, ITextEditor editor) {
102: this (bundle, prefix, editor, ruler);
103: }
104:
105: /*
106: * @see IUpdate#update()
107: */
108: public void update() {
109: setEnabled(hasMarkers());
110: }
111:
112: /*
113: * @see Action#run()
114: */
115: public void run() {
116:
117: IMarker marker = chooseMarker(getMarkers());
118: if (marker == null)
119: return;
120:
121: IWorkbenchPage page = fTextEditor.getSite().getPage();
122: MarkerViewUtil.showMarker(page, marker, false);
123:
124: gotoMarker(marker);
125: }
126:
127: private void gotoMarker(IMarker marker) {
128:
129: // Use the provided adapter if any
130: IGotoMarker gotoMarkerAdapter = (IGotoMarker) fTextEditor
131: .getAdapter(IGotoMarker.class);
132: if (gotoMarkerAdapter != null) {
133: gotoMarkerAdapter.gotoMarker(marker);
134: return;
135: }
136:
137: int start = MarkerUtilities.getCharStart(marker);
138: int end = MarkerUtilities.getCharEnd(marker);
139:
140: boolean selectLine = start < 0 || end < 0;
141:
142: IDocumentProvider documentProvider = fTextEditor
143: .getDocumentProvider();
144: IEditorInput editorInput = fTextEditor.getEditorInput();
145:
146: // look up the current range of the marker when the document has been edited
147: IAnnotationModel model = documentProvider
148: .getAnnotationModel(editorInput);
149: if (model instanceof AbstractMarkerAnnotationModel) {
150:
151: AbstractMarkerAnnotationModel markerModel = (AbstractMarkerAnnotationModel) model;
152: Position pos = markerModel.getMarkerPosition(marker);
153: if (pos != null && !pos.isDeleted()) {
154: // use position instead of marker values
155: start = pos.getOffset();
156: end = pos.getOffset() + pos.getLength();
157: }
158:
159: if (pos != null && pos.isDeleted()) {
160: // do nothing if position has been deleted
161: return;
162: }
163: }
164:
165: IDocument document = documentProvider.getDocument(editorInput);
166:
167: if (selectLine) {
168: int line;
169: try {
170: if (start >= 0)
171: line = document.getLineOfOffset(start);
172: else {
173: line = MarkerUtilities.getLineNumber(marker);
174: // Marker line numbers are 1-based
175: --line;
176: }
177: end = start + document.getLineLength(line) - 1;
178: } catch (BadLocationException e) {
179: return;
180: }
181: }
182:
183: int length = document.getLength();
184: if (end - 1 < length && start < length)
185: fTextEditor.selectAndReveal(start, end - start);
186: }
187:
188: /**
189: * Chooses the marker with the highest layer. If there are multiple
190: * markers at the found layer, the first marker is taken.
191: *
192: * @param markers the list of markers to choose from
193: * @return the chosen marker or <code>null</code> if none of the given markers has a marker annotation in the model
194: */
195: protected IMarker chooseMarker(List markers) {
196:
197: AbstractMarkerAnnotationModel model = getAnnotationModel();
198: IAnnotationAccessExtension access = getAnnotationAccessExtension();
199:
200: IMarker marker = null;
201: int maxLayer = 0;
202:
203: Iterator iter = markers.iterator();
204: while (iter.hasNext()) {
205: IMarker m = (IMarker) iter.next();
206: Annotation a = model.getMarkerAnnotation(m);
207: if (a != null) {
208: if (access == null) {
209: marker = m;
210: break;
211: }
212: int l = access.getLayer(a);
213: if (l == maxLayer) {
214: if (marker == null)
215: marker = m;
216: } else if (l > maxLayer) {
217: maxLayer = l;
218: marker = m;
219: }
220: }
221: }
222:
223: return marker;
224: }
225:
226: /**
227: * Returns the annotation access extension.
228: *
229: * @return the annotation access extension or <code>null</code> if
230: * this action's editor has no such extension
231: * @since 3.0
232: */
233: protected IAnnotationAccessExtension getAnnotationAccessExtension() {
234: Object adapter = fTextEditor
235: .getAdapter(IAnnotationAccess.class);
236: if (adapter instanceof IAnnotationAccessExtension)
237: return (IAnnotationAccessExtension) adapter;
238:
239: return null;
240: }
241:
242: /**
243: * Returns the resource for which to create the marker,
244: * or <code>null</code> if there is no applicable resource.
245: *
246: * @return the resource for which to create the marker or <code>null</code>
247: */
248: protected IResource getResource() {
249: IEditorInput input = fTextEditor.getEditorInput();
250:
251: IResource resource = (IResource) input.getAdapter(IFile.class);
252:
253: if (resource == null)
254: resource = (IResource) input.getAdapter(IResource.class);
255:
256: return resource;
257: }
258:
259: /**
260: * Returns the <code>AbstractMarkerAnnotationModel</code> of the editor's input.
261: *
262: * @return the marker annotation model or <code>null</code> if there's none
263: */
264: protected AbstractMarkerAnnotationModel getAnnotationModel() {
265: IDocumentProvider provider = fTextEditor.getDocumentProvider();
266: IAnnotationModel model = provider
267: .getAnnotationModel(fTextEditor.getEditorInput());
268: if (model instanceof AbstractMarkerAnnotationModel)
269: return (AbstractMarkerAnnotationModel) model;
270: return null;
271: }
272:
273: /**
274: * Returns the <code>IDocument</code> of the editor's input.
275: *
276: * @return the document of the editor's input
277: */
278: protected IDocument getDocument() {
279: IDocumentProvider provider = fTextEditor.getDocumentProvider();
280: return provider.getDocument(fTextEditor.getEditorInput());
281: }
282:
283: /**
284: * Checks whether a position includes the ruler's line of activity.
285: *
286: * @param position the position to be checked
287: * @param document the document the position refers to
288: * @return <code>true</code> if the line is included by the given position
289: */
290: protected boolean includesRulerLine(Position position,
291: IDocument document) {
292:
293: if (position != null) {
294: try {
295: int markerLine = document.getLineOfOffset(position
296: .getOffset());
297: int line = fRuler.getLineOfLastMouseButtonActivity();
298: if (line == markerLine)
299: return true;
300: // commented because of "1GEUOZ9: ITPJUI:ALL - Confusing UI for multi-line Bookmarks and Tasks"
301: // return (markerLine <= line && line <= document.getLineOfOffset(position.getOffset() + position.getLength()));
302: } catch (BadLocationException x) {
303: }
304: }
305:
306: return false;
307: }
308:
309: /**
310: * Checks whether a position includes the ruler's line of activity.
311: *
312: * @param position the position to be checked
313: * @param document the document the position refers to
314: * @param line the line of the last ruler activity
315: * @return <code>true</code> if the line is included by the given position
316: * @since 3.3
317: */
318: private boolean includesLine(Position position, IDocument document,
319: int line) {
320:
321: if (position != null) {
322: try {
323: int markerLine = document.getLineOfOffset(position
324: .getOffset());
325: if (line == markerLine)
326: return true;
327: // commented because of "1GEUOZ9: ITPJUI:ALL - Confusing UI for multi-line Bookmarks and Tasks"
328: // return (markerLine <= line && line <= document.getLineOfOffset(position.getOffset() + position.getLength()));
329: } catch (BadLocationException x) {
330: }
331: }
332:
333: return false;
334: }
335:
336: /**
337: * Handles core exceptions. This implementation logs the exceptions
338: * with the workbench plug-in and shows an error dialog.
339: *
340: * @param exception the exception to be handled
341: * @param message the message to be logged with the given exception
342: */
343: protected void handleCoreException(CoreException exception,
344: String message) {
345: Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
346: ILog log = Platform.getLog(bundle);
347:
348: if (message != null)
349: log.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
350: IStatus.OK, message, exception));
351: else
352: log.log(exception.getStatus());
353:
354: Shell shell = fTextEditor.getSite().getShell();
355: String title = getString(fBundle, fPrefix
356: + "error.dialog.title", fPrefix + "error.dialog.title"); //$NON-NLS-2$ //$NON-NLS-1$
357: String msg = getString(
358: fBundle,
359: fPrefix + "error.dialog.message", fPrefix + "error.dialog.message"); //$NON-NLS-2$ //$NON-NLS-1$
360:
361: ErrorDialog.openError(shell, title, msg, exception.getStatus());
362: }
363:
364: /**
365: * Returns all markers which include the ruler's line of activity.
366: *
367: * @return an unmodifiable list with all markers which include the ruler's line of activity
368: * (element type: {@link IMarker})
369: */
370: protected List getMarkers() {
371: final IResource resource = getResource();
372: if (resource == null || !resource.exists())
373: return Collections.EMPTY_LIST;
374:
375: final IDocument document = getDocument();
376: if (document == null)
377: return Collections.EMPTY_LIST;
378:
379: final AbstractMarkerAnnotationModel model = getAnnotationModel();
380: if (model == null)
381: return Collections.EMPTY_LIST;
382:
383: final IMarker[] allMarkers;
384: try {
385: allMarkers = resource.findMarkers(null, true,
386: IResource.DEPTH_ZERO);
387: } catch (CoreException x) {
388: handleCoreException(
389: x,
390: TextEditorMessages.SelectMarkerRulerAction_getMarker);
391: return Collections.EMPTY_LIST;
392: }
393:
394: if (allMarkers.length == 0)
395: return Collections.EMPTY_LIST;
396:
397: final int activeLine = fRuler
398: .getLineOfLastMouseButtonActivity();
399: List markers = null;
400: for (Iterator it = model.getAnnotationIterator(); it.hasNext();) {
401: Annotation annotation = (Annotation) it.next();
402: if (annotation instanceof MarkerAnnotation) {
403: Position position = model.getPosition(annotation);
404: if (includesLine(position, document, activeLine)) {
405: if (markers == null)
406: markers = new ArrayList(10);
407:
408: markers.add(((MarkerAnnotation) annotation)
409: .getMarker());
410: }
411: }
412: }
413:
414: if (markers == null)
415: return Collections.EMPTY_LIST;
416:
417: return Collections.unmodifiableList(markers);
418: }
419:
420: /**
421: * Returns all markers which include the ruler's line of activity.
422: *
423: * @return an unmodifiable list with all markers which include the ruler's line of activity
424: * (element type: {@link IMarker})
425: * @since 3.3
426: */
427: protected boolean hasMarkers() {
428: final IResource resource = getResource();
429: if (resource == null || !resource.exists())
430: return false;
431:
432: final IDocument document = getDocument();
433: if (document == null)
434: return false;
435:
436: final AbstractMarkerAnnotationModel model = getAnnotationModel();
437: if (model == null)
438: return false;
439:
440: final IMarker[] allMarkers;
441: try {
442: allMarkers = resource.findMarkers(null, true,
443: IResource.DEPTH_ZERO);
444: } catch (CoreException x) {
445: handleCoreException(
446: x,
447: TextEditorMessages.SelectMarkerRulerAction_getMarker);
448: return false;
449: }
450:
451: if (allMarkers.length == 0)
452: return false;
453:
454: final int activeLine = fRuler
455: .getLineOfLastMouseButtonActivity();
456: for (Iterator it = model.getAnnotationIterator(); it.hasNext();) {
457: Annotation annotation = (Annotation) it.next();
458: if (annotation instanceof MarkerAnnotation) {
459: Position position = model.getPosition(annotation);
460: if (includesLine(position, document, activeLine)) {
461: return true;
462: }
463: }
464: }
465:
466: return false;
467: }
468: }
|