/*******************************************************************************
* Copyright (c) 2000, 2016 QNX Software Systems 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:
* QNX Software Systems - Initial API and implementation
* ARM Limited - https://bugs.eclipse.org/bugs/show_bug.cgi?id=186981
*******************************************************************************/
package org.eclipse.cdt.debug.internal.ui;
import java.net.URI;
import java.util.Iterator;
import org.eclipse.cdt.debug.core.model.ICBreakpoint;
import org.eclipse.cdt.debug.core.model.ICDebugElementStatus;
import org.eclipse.cdt.debug.core.model.ICStackFrame;
import org.eclipse.cdt.debug.core.model.ICType;
import org.eclipse.cdt.debug.core.model.ICValue;
import org.eclipse.cdt.debug.core.model.IEnableDisableTarget;
import org.eclipse.cdt.debug.ui.CDebugUIPlugin;
import org.eclipse.cdt.debug.ui.breakpoints.CBreakpointPropertyDialogAction;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.contexts.IDebugContextListener;
import org.eclipse.debug.ui.contexts.IDebugContextProvider;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.dialogs.MessageDialog;
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.Region;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.SimpleMarkerAnnotation;
import com.ibm.icu.text.MessageFormat;
/**
* Utility methods for C/C++ Debug UI.
*/
public class CDebugUIUtils {
static public IRegion findWord( IDocument document, int offset ) {
int start = -1;
int end = -1;
try {
int pos = offset;
char c;
while( pos >= 0 ) {
c = document.getChar( pos );
if ( !Character.isJavaIdentifierPart( c ) )
break;
--pos;
}
start = pos;
pos = offset;
int length = document.getLength();
while( pos < length ) {
c = document.getChar( pos );
if ( !Character.isJavaIdentifierPart( c ) )
break;
++pos;
}
end = pos;
}
catch( BadLocationException x ) {
}
if ( start > -1 && end > -1 ) {
if ( start == offset && end == offset )
return new Region( offset, 0 );
else if ( start == offset )
return new Region( start, end - start );
else
return new Region( start + 1, end - start - 1 );
}
return null;
}
/**
* Returns the currently selected stack frame or the topmost frame
* in the currently selected thread in the Debug view
* of the current workbench page. Returns <code>null</code>
* if no stack frame or thread is selected, or if not called from the UI thread.
*
* @return the currently selected stack frame or the topmost frame
* in the currently selected thread
*/
static public ICStackFrame getCurrentStackFrame() {
IAdaptable context = DebugUITools.getDebugContext();
return ( context != null ) ? (ICStackFrame)context.getAdapter( ICStackFrame.class ) : null;
}
/**
* Moved from CDebugModelPresentation because it is also used by CVariableLabelProvider.
*/
static public String getValueText( IValue value ) {
StringBuilder label = new StringBuilder();
if ( value instanceof ICDebugElementStatus && !((ICDebugElementStatus)value).isOK() ) {
label.append( MessageFormat.format( CDebugUIMessages.getString( "CDTDebugModelPresentation.4" ), (Object[]) new String[] { ((ICDebugElementStatus)value).getMessage() } ) ); //$NON-NLS-1$
}
else if ( value instanceof ICValue ) {
ICType type = null;
try {
type = ((ICValue)value).getType();
}
catch( DebugException e ) {
}
try {
String valueString = value.getValueString();
if ( valueString != null ) {
valueString = valueString.trim();
if ( type != null && type.isCharacter() ) {
if ( valueString.length() == 0 )
valueString = "."; //$NON-NLS-1$
label.append( valueString );
}
else if ( valueString.length() > 0 ) {
label.append( valueString );
}
}
}
catch( DebugException e1 ) {
}
}
return label.toString();
}
/**
* Moved from CDebugModelPresentation because it is also used by CVariableLabelProvider.
*/
public static String getVariableTypeName( ICType type ) {
StringBuilder result = new StringBuilder();
if ( type != null ) {
String typeName = type.getName();
if ( typeName != null )
typeName = typeName.trim();
if ( type.isArray() && typeName != null ) {
int index = typeName.indexOf( '[' );
if ( index != -1 )
typeName = typeName.substring( 0, index ).trim();
}
if ( typeName != null && typeName.length() > 0 ) {
result.append( typeName );
if ( type.isArray() ) {
int[] dims = type.getArrayDimensions();
for( int i = 0; i < dims.length; ++i ) {
result.append( '[' );
result.append( dims[i] );
result.append( ']' );
}
}
}
}
return result.toString();
}
public static String getVariableName( IVariable variable ) throws DebugException {
return decorateText( variable, variable.getName() );
}
public static String getEditorFilePath( IEditorInput input ) throws CoreException {
if ( input instanceof IFileEditorInput ) {
IPath location = ((IFileEditorInput)input).getFile().getLocation();
if (location != null) {
return location.toOSString();
}
URI locationURI = ((IFileEditorInput)input).getFile().getLocationURI();
if (locationURI != null) {
IPath uriPath = URIUtil.toPath(locationURI);
if (uriPath != null) {
return uriPath.toOSString();
}
}
return ""; //$NON-NLS-1$
}
if ( input instanceof IStorageEditorInput ) {
return ((IStorageEditorInput)input).getStorage().getFullPath().toOSString();
}
if ( input instanceof IPathEditorInput ) {
return ((IPathEditorInput)input).getPath().toOSString();
}
if ( input instanceof IURIEditorInput)
{
IPath uriPath = URIUtil.toPath(((IURIEditorInput)input).getURI());
if (uriPath != null)
return uriPath.toOSString();
}
return ""; //$NON-NLS-1$
}
public static String decorateText( Object element, String text ) {
if ( text == null )
return null;
StringBuilder baseText = new StringBuilder( text );
if ( element instanceof ICDebugElementStatus && !((ICDebugElementStatus)element).isOK() ) {
baseText.append( MessageFormat.format( " <{0}>", new Object[] { ((ICDebugElementStatus)element).getMessage() } ) ); //$NON-NLS-1$
}
if ( element instanceof IAdaptable ) {
IEnableDisableTarget target = ((IAdaptable)element).getAdapter( IEnableDisableTarget.class );
if ( target != null ) {
if ( !target.isEnabled() ) {
baseText.append( ' ' );
baseText.append( CDebugUIMessages.getString( "CDTDebugModelPresentation.25" ) ); //$NON-NLS-1$
}
}
}
return baseText.toString();
}
/**
* Helper function to open an error dialog.
* @param title
* @param message
* @param e
*/
static public void openError (final String title, final String message, final Exception e)
{
UIJob uiJob = new UIJob("open error"){ //$NON-NLS-1$
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
// open error for the exception
String detail = ""; //$NON-NLS-1$
if (e != null)
detail = e.getMessage();
Shell shell = CDebugUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell();
MessageDialog.openError(
shell,
title,
message + "\n" + detail); //$NON-NLS-1$
return Status.OK_STATUS;
}};
uiJob.setSystem(true);
uiJob.schedule();
}
/**
* Resolves the {@link IBreakpoint} from the given editor and ruler information. Returns <code>null</code>
* if no breakpoint exists or the operation fails.
*
* @param editor the editor
* @param info the current ruler information
* @return the {@link IBreakpoint} from the current editor position or <code>null</code>
*/
public static IBreakpoint getBreakpointFromEditor(ITextEditor editor, IVerticalRulerInfo info) {
IAnnotationModel annotationModel = editor.getDocumentProvider().getAnnotationModel(editor.getEditorInput());
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
if (annotationModel != null) {
Iterator<Annotation> iterator = annotationModel.getAnnotationIterator();
while (iterator.hasNext()) {
Object object = iterator.next();
if (object instanceof SimpleMarkerAnnotation) {
SimpleMarkerAnnotation markerAnnotation = (SimpleMarkerAnnotation) object;
IMarker marker = markerAnnotation.getMarker();
try {
if (marker.isSubtypeOf(IBreakpoint.BREAKPOINT_MARKER)) {
Position position = annotationModel.getPosition(markerAnnotation);
int line = document.getLineOfOffset(position.getOffset());
if (line == info.getLineOfLastMouseButtonActivity()) {
IBreakpoint breakpoint = DebugPlugin.getDefault().getBreakpointManager().getBreakpoint(marker);
if (breakpoint != null) {
return breakpoint;
}
}
}
} catch (CoreException e) {
} catch (BadLocationException e) {
}
}
}
}
return null;
}
public static void editBreakpointProperties(IWorkbenchPart part, final ICBreakpoint bp) {
final ISelection debugContext = DebugUITools.getDebugContextForPart(part);
CBreakpointPropertyDialogAction propertiesAction = new CBreakpointPropertyDialogAction(
part.getSite(),
new ISelectionProvider() {
@Override
public ISelection getSelection() {
return new StructuredSelection( bp );
}
@Override public void addSelectionChangedListener( ISelectionChangedListener listener ) {}
@Override public void removeSelectionChangedListener( ISelectionChangedListener listener ) {}
@Override public void setSelection( ISelection selection ) {}
},
new IDebugContextProvider() {
@Override
public ISelection getActiveContext() {
return debugContext;
}
@Override public void addDebugContextListener(IDebugContextListener listener) {}
@Override public void removeDebugContextListener(IDebugContextListener listener) {}
@Override public IWorkbenchPart getPart() { return null; }
}
);
propertiesAction.run();
propertiesAction.dispose();
}
/**
* Formats the given key stroke or click name and the modifier keys
* to a key binding string that can be used in action texts.
*
* @param modifierKeys the modifier keys
* @param keyOrClick a key stroke or click, e.g. "Double Click"
* @return the formatted keyboard shortcut string, e.g. "Shift+Double Click"
*
* @since 8.1
*/
public static final String formatKeyBindingString(int modifierKeys, String keyOrClick) {
// this should actually all be delegated to KeyStroke class
return KeyStroke.getInstance(modifierKeys, KeyStroke.NO_KEY).format() + keyOrClick;
}
}