/**
* This file Copyright (c) 2005-2008 Aptana, Inc. This program is
* dual-licensed under both the Aptana Public License and the GNU General
* Public license. You may elect to use one or the other of these licenses.
*
* This program is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. Redistribution, except as permitted by whichever of
* the GPL or APL you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or modify this
* program under the terms of the GNU General Public License,
* Version 3, as published by the Free Software Foundation. You should
* have received a copy of the GNU General Public License, Version 3 along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Aptana provides a special exception to allow redistribution of this file
* with certain Eclipse Public Licensed code and certain additional terms
* pursuant to Section 7 of the GPL. You may view the exception and these
* terms on the web at http://www.aptana.com/legal/gpl/.
*
* 2. For the Aptana Public License (APL), this program and the
* accompanying materials are made available under the terms of the APL
* v1.0 which accompanies this distribution, and is available at
* http://www.aptana.com/legal/apl/.
*
* You may view the GPL, Aptana's exception and additional terms, and the
* APL in the file titled license.html at the root of the corresponding
* plugin containing this source file.
*
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.logging.view;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel;
import org.eclipse.ui.texteditor.IUpdate;
import org.eclipse.ui.texteditor.ResourceAction;
import com.aptana.ide.core.resources.IUniformResource;
import com.aptana.ide.core.resources.MarkerUtils;
import com.aptana.ide.logging.ILogResource;
/**
* Marker ruler action.
* @author Denis Denisenko
*/
public class LoggingMarkerRulerAction extends ResourceAction implements IUpdate
{
/** The maximum length of an proposed label. */
private static final int MAX_LABEL_LENGTH = 80;
/**
* Resource bundle.
*/
private final ResourceBundle bundle;
/**
* Add label.
*/
private String label;
/**
* Action prefix.
*/
private final String prefix;
/**
* Shell.
*/
private final Shell shell;
/**
* Ruler.
*/
private final IVerticalRuler ruler;
/**
* Whether to ask for label.
*/
private final boolean askForLabel;
/**
* Marker type.
*/
private final String markerType;
/**
* Document to watch.
*/
private final IDocument document;
/**
* Annotation model.
*/
private final IAnnotationModel model;
/**
* Resource.
*/
private final IUniformResource resource;
/**
* Markers for ruler position.
*
*/
private List markers;
/**
* LoggingMarkerRulerAction constructor.
* @param bundle - resource bundle.
* @param prefix - prefix.
* @param resource - resource.
* @param document - document.
* @param ruler - vertical ruler.
* @param annotationModel - annotation model.
* @param askForLabel - whether to ask for level.
* @param markerType - marker type.
* @param shell - shell.
*/
public LoggingMarkerRulerAction(ResourceBundle bundle, String prefix,
ILogResource resource,
IDocument document, IAnnotationModel annotationModel,
IVerticalRuler ruler, boolean askForLabel, String markerType,
Shell shell)
{
super(bundle, prefix);
this.bundle = bundle;
this.prefix = prefix;
this.ruler = ruler;
this.askForLabel = askForLabel;
this.markerType = markerType;
this.document = document;
this.model = annotationModel;
this.shell = shell;
this.resource = resource;
label= getString(bundle, prefix + ".label", prefix + ".label"); //$NON-NLS-2$ //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
public void run() {
update();
if (markers.isEmpty())
addMarker();
else
removeMarkers(markers);
}
/**
* {@inheritDoc}
*/
public void update() {
int line= ruler.getLineOfLastMouseButtonActivity() + 1;
if (line > document.getNumberOfLines()) {
setEnabled(false);
setText(label);
} else {
markers= getMarkers();
setEnabled(resource != null);
setText(label);
}
}
/**
* Creates a new marker according to the specification of this action and
* adds it to the marker resource.
*/
protected void addMarker() {
Map attributes= createAttributes();
if (askForLabel) {
if (!askForLabel(attributes))
return;
}
try {
MarkerUtils.createMarkerForExternalResource(resource, attributes, markerType);
} catch (CoreException x) {
handleCoreException(x, "Unxepected exception"); //$NON-NLS-1$
}
ruler.update();
}
/**
* Asks the user for a marker label. Returns <code>true</code> if a label
* is entered, <code>false</code> if the user cancels the input dialog.
* Sets the value of the attribute <code>message</code> in the given
* map of attributes.
*
* @param attributes the map of attributes
* @return <code>true</code> if the map of attributes has successfully been initialized
*/
protected boolean askForLabel(Map attributes) {
Object o= attributes.get("message"); //$NON-NLS-1$
String proposal= (o instanceof String) ? (String) o : ""; //$NON-NLS-1$
if (proposal == null)
proposal= ""; //$NON-NLS-1$
String title= getString(bundle, prefix + "add.dialog.title", prefix + "add.dialog.title"); //$NON-NLS-2$ //$NON-NLS-1$
String message= getString(bundle, prefix + "add.dialog.message", prefix + "add.dialog.message"); //$NON-NLS-2$ //$NON-NLS-1$
IInputValidator inputValidator = new IInputValidator() {
public String isValid(String newText) {
return (newText == null || newText.trim().length() == 0) ? " " : null; //$NON-NLS-1$
}
};
InputDialog dialog= new InputDialog(shell, title, message, proposal, inputValidator);
String label= null;
if (dialog.open() != Window.CANCEL)
label= dialog.getValue();
if (label == null)
return false;
label= label.trim();
if (label.length() == 0)
return false;
MarkerUtils.setMessage(attributes, label);
return true;
}
/**
* Removes the markers.
*
* @param markers to remove
*/
protected void removeMarkers(final List markers) {
try {
for (int i= 0; i < markers.size(); ++i) {
IMarker marker= (IMarker) markers.get(i);
marker.delete();
}
} catch (CoreException x) {
handleCoreException(x, "Unexpected exception"); //$NON-NLS-1$
}
}
/**
* Creates initial marker attributes.
*/
private Map createAttributes()
{
Map attributes= new HashMap();
int line= ruler.getLineOfLastMouseButtonActivity();
int start= -1;
int end= -1;
int length= 0;
try {
IRegion lineInformation= document.getLineInformation(line);
start= lineInformation.getOffset();
length= lineInformation.getLength();
end= start + length;
} catch (BadLocationException x) {
}
// marker line numbers are 1-based
MarkerUtils.setMessage(attributes, getLabelProposal(document, start, length));
MarkerUtils.setLineNumber(attributes, line + 1);
MarkerUtils.setCharStart(attributes, start);
MarkerUtils.setCharEnd(attributes, end);
return attributes;
}
/**
* Returns the initial label for the marker.
*
* @param document the document from which to extract a label proposal
* @param offset the document offset of the range from which to extract the label proposal
* @param length the length of the range from which to extract the label proposal
* @return the label proposal
* @since 3.0
*/
protected String getLabelProposal(IDocument document, int offset, int length) {
try {
String label= document.get(offset, length).trim();
if (label.length() <= MAX_LABEL_LENGTH)
return label;
return label.substring(0, MAX_LABEL_LENGTH);
} catch (BadLocationException x) {
// don't propose label then
return null;
}
}
/**
* Handles core exceptions. This implementation logs the exceptions
* with the workbench plug-in and shows an error dialog.
*
* @param exception the exception to be handled
* @param message the message to be logged with the given exception
*/
protected void handleCoreException(CoreException exception, String message) {
// if (message != null)
// log.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, message, exception));
// else
// log.log(exception.getStatus());
String title= getString(bundle, prefix + "error.dialog.title", prefix + "error.dialog.title"); //$NON-NLS-2$ //$NON-NLS-1$
String msg= getString(bundle, prefix + "error.dialog.message", prefix + "error.dialog.message"); //$NON-NLS-2$ //$NON-NLS-1$
ErrorDialog.openError(shell, title, msg, exception.getStatus());
}
/**
* Returns all markers which include the ruler's line of activity.
*
* @return all a list of markers which include the ruler's line of activity
*/
protected List getMarkers() {
List markers= new ArrayList();
if (resource != null && model != null)
{
IMarker[] allMarkers= MarkerUtils.findMarkers(resource, markerType, true);
if (allMarkers != null)
{
for (int i= 0; i < allMarkers.length; i++)
{
if (includesRulerLine(
((AbstractMarkerAnnotationModel)model).getMarkerPosition(
allMarkers[i]), document))
{
markers.add(allMarkers[i]);
}
}
}
}
return markers;
}
/**
* Checks whether a position includes the ruler's line of activity.
*
* @param position the position to be checked
* @param document the document the position refers to
* @return <code>true</code> if the line is included by the given position
*/
protected boolean includesRulerLine(Position position, IDocument document) {
if (position != null) {
try {
int markerLine= document.getLineOfOffset(position.getOffset());
int line= ruler.getLineOfLastMouseButtonActivity();
if (line == markerLine)
return true;
// commented because of "1GEUOZ9: ITPJUI:ALL - Confusing UI for multi-line Bookmarks and Tasks"
// return (markerLine <= line && line <= document.getLineOfOffset(position.getOffset() + position.getLength()));
} catch (BadLocationException x) {
}
}
return false;
}
}