Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[cdt-patch] Take 2: Hyperlink Navigation for 2.0


OK  - take 2 on the hyperlink patch. Made changes to reflect some of the concerns raised by Alain and others. The Open Include action now follows the same logic as the parser when trying to find includes:

- #incude "foo.h" > OpenInclude action will search local project, then, if nothing is found, include directories
- #include <foo.h> OpenInclude action will search include directories

Regarding the CPathEntry references - once this come online, Open Include Action can be modified in whichever way necessary to search a referenced project's exported path entries.

- B





Bogdan Gheorghe/Ottawa/IBM@IBMCA
Sent by: cdt-patch-admin@xxxxxxxxxxx

12/05/2003 05:04 PM

Please respond to
cdt-patch@xxxxxxxxxxx

To
cdt-patch@xxxxxxxxxxx
cc
Subject
[cdt-patch] Hyperlink Navigation for 2.0






Here is the first go at Hyperlink navigation for CDT 2.0. The main points:


* Navigation can be turned on/off from the C Editor preference page


* To activate hyperlinks, hold the Ctrl key down and move the mouse over the element you want to navigate to; clicking the button is equivalent to F3/selecting Open Declarations


* Opening includes works as well (as long as the file can be found - ie. make sure to set up the paths properly if you are referencing external files) - this equivalent to selecting 'Open Include' from the outline view. I should also mention that I modified the behaviour of the OpenIncludeAction a bit. The workflow used to be: i) Search the project's include paths for the file in question, ii) If the file was not found on any of the include paths, search the project itself. This has been changed to: i) Search include paths, ii) Search project regardless of what was found.


What's left to do:


* Put in a better way of figuring out what elements should be "linkable" (JDT does this by trying to do a code complete on the element, if any hits come back then it is considered a valid link) (Bug 48186)

* Improve the Open Declarations action (Bug 48187)


Suite run on Windows


Index: ChangeLog
===================================================================
retrieving revision 1.226
diff -u -r1.226 ChangeLog
--- ChangeLog	12 Dec 2003 02:03:55 -0000	1.226
+++ ChangeLog	12 Dec 2003 18:14:52 -0000
@@ -1,3 +1,14 @@
+2003-12-12 Bogdan Gheorghe
+	Added MouseClickListener class to CEditor to enable hyperlink browsing
+	Modified OpenIncludeAction to get it working from the editor
+	Modified OpenIncludeAction to take into account the type of include 
+	being used
+	Added Navigation tab to C/C++ Editor preference page 
+	
+	* src/org/eclipse/cdt/internal/ui/preferences/CEditor.java
+	* src/org/eclipse/cdt/internal/ui/preferences/OpenIncludeAction.java
+	* src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java
+
 2003-12-11 John Camelon
 	Updated CompletionEngine to deal with new signatures/exceptions in parser.  
 
Index: src/org/eclipse/cdt/internal/ui/editor/CEditor.java
===================================================================
retrieving revision 1.38
diff -u -r1.38 CEditor.java
--- src/org/eclipse/cdt/internal/ui/editor/CEditor.java	10 Dec 2003 19:23:20 -0000	1.38
+++ src/org/eclipse/cdt/internal/ui/editor/CEditor.java	12 Dec 2003 18:14:58 -0000
@@ -11,6 +11,7 @@
 import java.util.StringTokenizer;
 
 import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.ICLogConstants;
 import org.eclipse.cdt.core.model.CModelException;
 import org.eclipse.cdt.core.model.CoreModel;
 import org.eclipse.cdt.core.model.ICElement;
@@ -36,14 +37,21 @@
 import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.DocumentCommand;
+import org.eclipse.jface.text.DocumentEvent;
 import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
 import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextInputListener;
 import org.eclipse.jface.text.ITextOperationTarget;
 import org.eclipse.jface.text.ITextSelection;
 import org.eclipse.jface.text.ITextViewerExtension;
+import org.eclipse.jface.text.ITextViewerExtension2;
+import org.eclipse.jface.text.ITextViewerExtension3;
 import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
 import org.eclipse.jface.text.contentassist.ContentAssistant;
 import org.eclipse.jface.text.contentassist.IContentAssistant;
 import org.eclipse.jface.text.source.Annotation;
@@ -56,6 +64,7 @@
 import org.eclipse.jface.text.source.OverviewRuler;
 import org.eclipse.jface.text.source.SourceViewer;
 import org.eclipse.jface.text.source.SourceViewerConfiguration;
+import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.PropertyChangeEvent;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
@@ -63,8 +72,26 @@
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.GC;
 import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.IEditorActionBarContributor;
 import org.eclipse.ui.IEditorInput;
@@ -113,6 +140,8 @@
     
     /** The property change listener */
     private PropertyChangeListener fPropertyChangeListener = new PropertyChangeListener();
+    /** The mouse listener */
+    private MouseClickListener fMouseListener;
 
 	protected final static char[] BRACKETS = { '{', '}', '(', ')', '[', ']' };
 
@@ -138,6 +167,9 @@
     /** Preference key for compiler task tags */
     private final static String TRANSLATION_TASK_TAGS= CCorePlugin.TRANSLATION_TASK_TAGS;
 
+    /** Preference key for hyperlink enablement */
+    public final static String HYPERLINK_ENABLED = "hyperlinkEnable"; //$NON-NLS-1$
+       
     private class PropertyChangeListener implements org.eclipse.core.runtime.Preferences.IPropertyChangeListener, org.eclipse.jface.util.IPropertyChangeListener {      
         /*
          * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
@@ -270,6 +302,14 @@
 					return;
 				}
 				
+				if (HYPERLINK_ENABLED.equals(property)) {
+					if (hyperLinkEnabled())
+						enableBrowserLikeLinks();
+					else
+						disableBrowserLikeLinks();
+					return;
+				}
+				
 				IContentAssistant c= asv.getContentAssistant();
 				if (c instanceof ContentAssistant)
 					ContentAssistPreference.changeConfiguration((ContentAssistant) c, getPreferenceStore(), event);
@@ -281,6 +321,16 @@
 	}
 
 	/**
+	 * 
+	 */
+	private void disableBrowserLikeLinks() {
+		if (fMouseListener != null) {
+			fMouseListener.uninstall();
+			fMouseListener= null;
+		}
+	}
+
+	/**
 	 * @see ISelectionChangedListener#selectionChanged
 	 */
 	public void selectionChanged(SelectionChangedEvent event) {
@@ -460,11 +510,30 @@
 		action.setActionDefinitionId(ICEditorActionDefinitionIds.OPEN_DECL);
 		setAction("OpenDeclarations", action);
 
+		//
+		action = new OpenIncludeAction(this, getOutlinePage());
+		setAction("OpenInclude",action);
+		//
+		
 		fFileSearchAction = new FileSearchAction(getSelectionProvider());
 		
 		fFileSearchActionInWorkingSet = new FileSearchActionInWorkingSet(getSelectionProvider());
 		
 		fSearchDialogAction = new SearchDialogAction(getSelectionProvider(), this);
+		
+		if (hyperLinkEnabled()){
+			enableBrowserLikeLinks();
+		}
+		
+	}
+
+	/**
+	 * @return
+	 */
+	private boolean hyperLinkEnabled() {
+		IPreferenceStore store= getPreferenceStore();
+		boolean Value = store.getBoolean(HYPERLINK_ENABLED);
+		return Value;
 	}
 
 	public void editorContextMenuAboutToShow(IMenuManager menu) {
@@ -920,5 +989,637 @@
             if (sourceViewer != null && affectsTextPresentation(event))
                 sourceViewer.invalidateTextPresentation();
         }
+    }
+    
+    //Links
+    /**
+     * Enables browser like links.
+     */
+    private void enableBrowserLikeLinks() {
+    	if (fMouseListener == null) {
+    		fMouseListener= new MouseClickListener();
+    		fMouseListener.install();
+    	}
+    }
+    
+    class MouseClickListener
+    implements
+    MouseListener,
+    KeyListener,
+    MouseMoveListener,
+    FocusListener,
+    PaintListener,
+    IPropertyChangeListener{
+    	
+    	/** The session is active. */
+    	private boolean fActive;
+
+    	/** The currently active style range. */
+    	private IRegion fActiveRegion;
+    	/** The currently active style range as position. */
+    	private Position fRememberedPosition;
+    	/** The hand cursor. */
+    	private Cursor fCursor;
+    	/** The link color. */
+    	private Color fColor;
+    	/** The key modifier mask. */
+    	private int fKeyModifierMask;
+    	/** The key modifier mask. */
+    	private boolean fIncludeMode;
+    	
+    	//Temp. Keywords: Need this to filter out some of the hyperlink nav options - this
+    	//will be taken out for the 2.0 release and replaced with a simple code complete check
+    	//(if an element can be completed then we'll let it be hyperlinked)
+    	
+    	private  String[] fgKeywords= { 
+    				"and", "and_eq", "asm", "auto", 
+					"bitand", "bitor", "break",
+					"case", "catch", "class", "compl", "const", "const_cast", "continue",
+					"default", "delete", "do", "dynamic_cast",
+					"else", "enum", "explicit", "export", "extern",
+					"false", "final", "finally", "for",	"friend",
+					"goto", 
+					"if", "inline",
+					"mutable",
+					"namespace", "new", "not", "not_eq",
+					"operator", "or", "or_eq", 
+					"private", "protected", "public", 
+					"redeclared", "register", "reinterpret_cast", "return", "restrict",
+					"sizeof", "static", "static_cast", "struct", "switch", 
+					"template", "this", "throw", "true", "try", "typedef", "typeid", "typename",
+					"union", "using",
+					"virtual", "volatile", 
+					"while",
+					"xor", "xor_eq", "bool", "char", "double", "float", "int", "long", "short", "signed", "unsigned", "void", "wchar_t", "_Bool", "_Complex", "_Imaginary",
+    				"false", "NULL", "true", "__DATE__", "__LINE__", "__TIME__", "__FILE__", "__STDC__",
+    				"#define", "#undef", "#error", "#warning", "#pragma", "#ifdef", "#ifndef", "#line", "#undef", "#if", "#else", "#elif", "#endif"
+    	};
+    	
+    	public void deactivate() {
+    		deactivate(false);
+    	}
+
+    	public void deactivate(boolean redrawAll) {
+    		if (!fActive)
+    			return;
+
+    		repairRepresentation(redrawAll);			
+    		fActive= false;
+    		fIncludeMode = false;
+    	}
+    	
+    	private void repairRepresentation(boolean redrawAll) {			
+
+    		if (fActiveRegion == null)
+    			return;
+    		
+    		ISourceViewer viewer= getSourceViewer();
+    		if (viewer != null) {
+    			resetCursor(viewer);
+
+    			int offset= fActiveRegion.getOffset();
+    			int length= fActiveRegion.getLength();
+
+    			// remove style
+    			if (!redrawAll && viewer instanceof ITextViewerExtension2)
+    				((ITextViewerExtension2) viewer).invalidateTextPresentation(offset, length);
+    			else
+    				viewer.invalidateTextPresentation();
+
+    			// remove underline				
+    			if (viewer instanceof ITextViewerExtension3) {
+    				ITextViewerExtension3 extension= (ITextViewerExtension3) viewer;
+    				offset= extension.modelOffset2WidgetOffset(offset);
+    			} else {
+    				offset -= viewer.getVisibleRegion().getOffset();
+    			}
+    			
+    			StyledText text= viewer.getTextWidget();
+    			try {
+    				text.redrawRange(offset, length, true);
+    			} catch (IllegalArgumentException x) {
+    				org.eclipse.cdt.internal.core.model.Util.log(x, "Error in CEditor.MouseClickListener.repairRepresentation", ICLogConstants.CDT);
+    			}
+    		}
+    		
+    		fActiveRegion= null;
+    	}
+    	
+    	private void activateCursor(ISourceViewer viewer) {
+    		StyledText text= viewer.getTextWidget();
+    		if (text == null || text.isDisposed())
+    			return;
+    		Display display= text.getDisplay();
+    		if (fCursor == null)
+    			fCursor= new Cursor(display, SWT.CURSOR_HAND);
+    		text.setCursor(fCursor);
+    	}
+    	
+    	private void resetCursor(ISourceViewer viewer) {
+    		StyledText text= viewer.getTextWidget();
+    		if (text != null && !text.isDisposed())
+    			text.setCursor(null);
+    		
+    		if (fCursor != null) {
+    			fCursor.dispose();
+    			fCursor= null;
+    		}
+    	}
+    	
+    	public void install() {
+
+    		ISourceViewer sourceViewer= getSourceViewer();
+    		if (sourceViewer == null)
+    			return;
+    		
+    		StyledText text= sourceViewer.getTextWidget();			
+    		if (text == null || text.isDisposed())
+    			return;
+    		
+    		updateColor(sourceViewer);
+		
+    		text.addKeyListener(this);
+    		text.addMouseListener(this);
+    		text.addMouseMoveListener(this);
+    		text.addFocusListener(this);
+    		text.addPaintListener(this);
+    		
+    		updateKeyModifierMask();
+    		
+    		IPreferenceStore preferenceStore= getPreferenceStore();
+    		preferenceStore.addPropertyChangeListener(this);			
+    	}
+    	
+    	public void uninstall() {
+
+    		if (fColor != null) {
+    			fColor.dispose();
+    			fColor= null;
+    		}
+    		
+    		if (fCursor != null) {
+    			fCursor.dispose();
+    			fCursor= null;
+    		}
+    		
+    		ISourceViewer sourceViewer= getSourceViewer();
+    		if (sourceViewer == null)
+    			return;
+    		
+    		IPreferenceStore preferenceStore= getPreferenceStore();
+    		if (preferenceStore != null)
+    			preferenceStore.removePropertyChangeListener(this);
+    		
+    		StyledText text= sourceViewer.getTextWidget();
+    		if (text == null || text.isDisposed())
+    			return;
+    		
+    		text.removeKeyListener(this);
+    		text.removeMouseListener(this);
+    		text.removeMouseMoveListener(this);
+    		text.removeFocusListener(this);
+    		text.removePaintListener(this);
+    	}
+    	private void updateKeyModifierMask() {
+    		//Add code here to allow for specification of hyperlink trigger key
+    		fKeyModifierMask=262144;
+    	}
+    	
+    	private void updateColor(ISourceViewer viewer) {
+    		if (fColor != null)
+    			fColor.dispose();
+    		
+    		StyledText text= viewer.getTextWidget();
+    		if (text == null || text.isDisposed())
+    			return;
+
+    		Display display= text.getDisplay();
+    		fColor= createColor(getPreferenceStore(), LINKED_POSITION_COLOR, display);
+    	}
+    	
+    	/**
+    	 * Creates a color from the information stored in the given preference store.
+    	 * Returns <code>null</code> if there is no such information available.
+    	 */
+    	private Color createColor(IPreferenceStore store, String key, Display display) {
+    		
+    		RGB rgb= null;		
+    		
+    		if (store.contains(key)) {
+    			
+    			if (store.isDefault(key))
+    				rgb= PreferenceConverter.getDefaultColor(store, key);
+    			else
+    				rgb= PreferenceConverter.getColor(store, key);
+    			
+    			if (rgb != null)
+    				return new Color(display, rgb);
+    		}
+    		
+    		return null;
+    	}	
+    	
+    	public void mouseDoubleClick(MouseEvent e) {}
+
+    	public void mouseDown(MouseEvent event) {
+    		if (!fActive)
+    			return;
+    		
+    		if (event.stateMask != fKeyModifierMask) {
+    			deactivate();
+    			return;	
+    		}
+    		
+    		if (event.button != 1) {
+    			deactivate();
+    			return;	
+    		}			
+    	}
+
+    	public void mouseUp(MouseEvent e) {
+    		if (!fActive)
+    			return;
+    		
+    		if (e.button != 1) {
+    			deactivate();
+    			return;
+    		}
+    		
+    		boolean wasActive= fCursor != null;
+    		boolean wasInclude = fIncludeMode;
+    		
+    		deactivate();
+
+    		if (wasActive) {
+    			if (wasInclude){
+    				IAction action= getAction("OpenInclude");  //$NON-NLS-1$
+    				if (action != null){
+    					action.run();
+    				}
+    			}
+    			else {
+    			IAction action= getAction("OpenDeclarations");  //$NON-NLS-1$
+    			if (action != null)
+    				action.run();
+    			}
+    		}
+    	}
+
+    	public void keyPressed(KeyEvent event) {
+    		if (fActive) {
+    			deactivate();
+    			return;	
+    		}
+
+    		if (event.keyCode != fKeyModifierMask) {
+    			deactivate();
+    			return;
+    		}
+    		
+    		fActive= true;
+    	}
+
+    	public void keyReleased(KeyEvent event) {
+    		if (!fActive)
+    			return;
+
+    		deactivate();
+    	}
+
+    	public void mouseMove(MouseEvent event) {
+    		if (event.widget instanceof Control && !((Control) event.widget).isFocusControl()) {
+    			deactivate();
+    			return;
+    		}
+    		
+    		if (!fActive) {
+    			if (event.stateMask != fKeyModifierMask)
+    				return;
+    			// modifier was already pressed
+    			fActive= true;
+    		}
+    		
+    		ISourceViewer viewer= getSourceViewer();
+    		if (viewer == null) {
+    			deactivate();
+    			return;
+    		}
+    		
+    		StyledText text= viewer.getTextWidget();
+    		if (text == null || text.isDisposed()) {
+    			deactivate();
+    			return;
+    		}
+    		
+    		if ((event.stateMask & SWT.BUTTON1) != 0 && text.getSelectionCount() != 0) {
+    			deactivate();
+    			return;
+    		}
+    		
+    		IRegion region= getCurrentTextRegion(viewer);
+    		if (region == null || region.getLength() == 0) {
+    			repairRepresentation();
+    			return;
+    		}
+    		
+    		highlightRegion(viewer, region);	
+    		activateCursor(viewer);				
+    	}
+    	
+    	IRegion getCurrentTextRegion(ISourceViewer viewer) {
+    		int offset= getCurrentTextOffset(viewer);				
+    		if (offset == -1)
+    			return null;
+
+    		//Need some code in here to determine if the selected input should
+    		//be selected - the JDT does this by doing a code complete on the input -
+    		//if there are any elements presented it selects the word
+    		
+    		return selectWord(viewer.getDocument(), offset);	
+    	}
+	
+    	private IRegion selectWord(IDocument document, int anchor) {
+    		
+    		try {		
+    			int offset= anchor;
+    			char c;
+    			
+    			while (offset >= 0) {
+    				c= document.getChar(offset);
+    				if (!Character.isJavaIdentifierPart(c))
+    					break;
+    				--offset;
+    			}
+    			
+    			int start= offset;
+    			
+    			offset= anchor;
+    			int length= document.getLength();
+    			
+    			while (offset < length) {
+    				c= document.getChar(offset);
+    				if (!Character.isJavaIdentifierPart(c))
+    					break;
+    				++offset;
+    			}
+    			
+    			int end= offset;
+    			//Allow for new lines
+    			if (start == end)
+    				return new Region(start, 0);
+    			else{
+    				String selWord = null;
+    				String slas = document.get(start,1);
+    				if (slas.equals("\n") ||
+    					slas.equals("\t") ||
+    				   slas.equals(" "))	
+    				 {
+    					
+    					selWord =document.get(start+1, end - start - 1);
+    				}
+    				else{
+    					selWord =document.get(start, end - start);  	
+    				}
+    				//Check for keyword
+    				if (isKeyWord(selWord))
+    					return null;
+    				//Avoid selecting literals, includes etc.
+    				char charX = selWord.charAt(0);
+    				if (charX == '"' ||
+    					charX == '.' ||
+    					charX == '<' ||
+    					charX == '>')
+    					return null;
+    				
+    				if (selWord.equals("#include"))
+    				{
+    					//get start of next identifier
+    					
+    				 
+    				  int end2 = end;
+    	
+    				  while (!Character.isJavaIdentifierPart(document.getChar(end2))){
+    				    	++end2;
+    				    	
+    				  }
+    				  
+    				  while (end2 < length){
+    				  	c = document.getChar(end2);
+    				  	
+    				  	if (!Character.isJavaIdentifierPart(c) &&
+    				  		 c != '.')
+    				  		break;
+    				  	++end2;
+    				  }
+    				  
+    				  int finalEnd = end2;
+    				  selWord =document.get(start, finalEnd - start);
+    				  end = finalEnd + 1;
+    				  start--;
+    				  fIncludeMode = true;
+    				}
+    				
+    				  
+    				
+    				return new Region(start + 1, end - start - 1);
+    			}
+    			
+    		} catch (BadLocationException x) {
+    			return null;
+    		}
+    	}
+    	
+		private boolean isKeyWord(String selWord) {
+			for (int i=0; i<fgKeywords.length; i++){
+				if (selWord.equals(fgKeywords[i]))
+					return  true;
+			}
+			return false;
+		}
+
+		private int getCurrentTextOffset(ISourceViewer viewer) {
+
+    		try {					
+    			StyledText text= viewer.getTextWidget();			
+    			if (text == null || text.isDisposed())
+    				return -1;
+
+    			Display display= text.getDisplay();				
+    			Point absolutePosition= display.getCursorLocation();
+    			Point relativePosition= text.toControl(absolutePosition);
+    			
+    			int widgetOffset= text.getOffsetAtLocation(relativePosition);
+    			if (viewer instanceof ITextViewerExtension3) {
+    				ITextViewerExtension3 extension= (ITextViewerExtension3) viewer;
+    				return extension.widgetOffset2ModelOffset(widgetOffset);
+    			} else {
+    				return widgetOffset + viewer.getVisibleRegion().getOffset();
+    			}
+
+    		} catch (IllegalArgumentException e) {
+    			return -1;
+    		}			
+    	}
+    	
+    	private void highlightRegion(ISourceViewer viewer, IRegion region) {
+
+    		if (region.equals(fActiveRegion))
+    			return;
+
+    		repairRepresentation();
+
+    		StyledText text= viewer.getTextWidget();
+    		if (text == null || text.isDisposed())
+    			return;
+
+    		// highlight region
+    		int offset= 0;
+    		int length= 0;
+    		
+    		if (viewer instanceof ITextViewerExtension3) {
+    			ITextViewerExtension3 extension= (ITextViewerExtension3) viewer;
+    			IRegion widgetRange= extension.modelRange2WidgetRange(region);
+    			if (widgetRange == null)
+    				return;
+    			
+    			offset= widgetRange.getOffset();
+    			length= widgetRange.getLength();
+    			
+    		} else {
+    			offset= region.getOffset() - viewer.getVisibleRegion().getOffset();
+    			length= region.getLength();
+    		}
+    		
+    		StyleRange oldStyleRange= text.getStyleRangeAtOffset(offset);
+    		Color foregroundColor= fColor;
+    		Color backgroundColor= oldStyleRange == null ? text.getBackground() : oldStyleRange.background;
+    		StyleRange styleRange= new StyleRange(offset, length, foregroundColor, backgroundColor);
+    		text.setStyleRange(styleRange);
+
+    		// underline
+    		text.redrawRange(offset, length, true);
+
+    		fActiveRegion= region;
+    	}
+    	
+    	
+    	private void repairRepresentation() {			
+    		repairRepresentation(false);
+    	}
+
+    	/* (non-Javadoc)
+    	 * @see java.awt.event.FocusListener#focusGained(java.awt.event.FocusEvent)
+    	 */
+    	public void focusGained(FocusEvent arg0) {
+    		// TODO Auto-generated method stub
+
+    	}
+
+    	/* (non-Javadoc)
+    	 * @see java.awt.event.FocusListener#focusLost(java.awt.event.FocusEvent)
+    	 */
+    	public void focusLost(FocusEvent arg0) {
+    		deactivate();
+    	}
+
+    	/* (non-Javadoc)
+    	 * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
+    	 */
+    	public void paintControl(PaintEvent event) {
+    		if (fActiveRegion == null)
+    			return;
+    		
+    		ISourceViewer viewer= getSourceViewer();
+    		if (viewer == null)
+    			return;
+    		
+    		StyledText text= viewer.getTextWidget();
+    		if (text == null || text.isDisposed())
+    			return;
+    		
+    		
+    		int offset= 0;
+    		int length= 0;
+
+    		if (viewer instanceof ITextViewerExtension3) {
+    			
+    			ITextViewerExtension3 extension= (ITextViewerExtension3) viewer;
+    			IRegion widgetRange= extension.modelRange2WidgetRange(new Region(offset, length));
+    			if (widgetRange == null)
+    				return;
+    			
+    			offset= widgetRange.getOffset();
+    			length= widgetRange.getLength();
+    			
+    		} else {
+    			
+    			IRegion region= viewer.getVisibleRegion();			
+    			if (!includes(region, fActiveRegion))
+    				return;		    
+
+    			offset= fActiveRegion.getOffset() - region.getOffset();
+    			length= fActiveRegion.getLength();
+    		}
+    		
+    		// support for bidi
+    		Point minLocation= getMinimumLocation(text, offset, length);
+    		Point maxLocation= getMaximumLocation(text, offset, length);
+    		
+    		int x1= minLocation.x;
+    		int x2= minLocation.x + maxLocation.x - minLocation.x - 1;
+    		int y= minLocation.y + text.getLineHeight() - 1;
+    		
+    		GC gc= event.gc;
+    		if (fColor != null && !fColor.isDisposed())
+    			gc.setForeground(fColor);
+    		gc.drawLine(x1, y, x2, y);
+    	
+    	}
+
+    	private boolean includes(IRegion region, IRegion position) {
+    		return
+    		position.getOffset() >= region.getOffset() &&
+    		position.getOffset() + position.getLength() <= region.getOffset() + region.getLength();
+    	}
+
+    	private Point getMinimumLocation(StyledText text, int offset, int length) {
+    		Point minLocation= new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
+    		
+    		for (int i= 0; i <= length; i++) {
+    			Point location= text.getLocationAtOffset(offset + i);
+    			
+    			if (location.x < minLocation.x)
+    				minLocation.x= location.x;			
+    			if (location.y < minLocation.y)
+    				minLocation.y= location.y;			
+    		}	
+    		
+    		return minLocation;
+    	}
+    	
+    	private Point getMaximumLocation(StyledText text, int offset, int length) {
+    		Point maxLocation= new Point(Integer.MIN_VALUE, Integer.MIN_VALUE);
+    		
+    		for (int i= 0; i <= length; i++) {
+    			Point location= text.getLocationAtOffset(offset + i);
+    			
+    			if (location.x > maxLocation.x)
+    				maxLocation.x= location.x;			
+    			if (location.y > maxLocation.y)
+    				maxLocation.y= location.y;			
+    		}	
+    		
+    		return maxLocation;
+    	}
+    	
+    	public void propertyChange(PropertyChangeEvent event) {
+    		if (event.getProperty().equals(CEditor.LINKED_POSITION_COLOR)) {
+    			ISourceViewer viewer= getSourceViewer();
+    			if (viewer != null)	
+    				updateColor(viewer);
+    		}
+    	}
+
+    	
     }
 }
Index: src/org/eclipse/cdt/internal/ui/editor/OpenIncludeAction.java
===================================================================
retrieving revision 1.8
diff -u -r1.8 OpenIncludeAction.java
--- src/org/eclipse/cdt/internal/ui/editor/OpenIncludeAction.java	8 Oct 2003 20:10:23 -0000	1.8
+++ src/org/eclipse/cdt/internal/ui/editor/OpenIncludeAction.java	12 Dec 2003 18:14:58 -0000
@@ -12,6 +12,7 @@
 import org.eclipse.cdt.core.CCorePlugin;
 import org.eclipse.cdt.core.model.CModelException;
 import org.eclipse.cdt.core.model.ICElement;
+import org.eclipse.cdt.core.model.IInclude;
 import org.eclipse.cdt.core.parser.IScannerInfo;
 import org.eclipse.cdt.core.parser.IScannerInfoProvider;
 import org.eclipse.cdt.core.resources.FileStorage;
@@ -30,6 +31,9 @@
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.jface.action.Action;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextSelection;
 import org.eclipse.jface.viewers.ILabelProvider;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionProvider;
@@ -39,6 +43,7 @@
 import org.eclipse.ui.IEditorDescriptor;
 import org.eclipse.ui.IEditorRegistry;
 import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.IDocumentProvider;
 
 
 public class OpenIncludeAction extends Action {
@@ -51,6 +56,10 @@
 	
 	private ISelectionProvider fSelectionProvider;
 
+	//Used with hyperlinks
+	private CEditor fEditor;
+	private CContentOutlinePage fOutline;
+	private boolean fStandardInclude = false;
 
 	public OpenIncludeAction(ISelectionProvider provider) {
 		super(CUIPlugin.getResourceString(PREFIX + "label"));
@@ -61,19 +70,58 @@
 		
 		fSelectionProvider= provider;
 	}
-			
+	
+
+	public OpenIncludeAction(CEditor editor, CContentOutlinePage page) {
+		fEditor = editor;
+		fOutline = page;	
+	}
+	
 	public void run() {
-		ICElement include= getIncludeStatement(fSelectionProvider.getSelection());
-		if (include == null) {
-			return;
+		
+		ICElement include = null;
+		IResource res = null;
+		IProject proj = null;
+		String includeName = null;
+		fStandardInclude = false;
+		
+		//See if invoked from the editor or from the Outline
+		if (fEditor != null){
+			ITextSelection selection= (ITextSelection) fEditor.getSelectionProvider().getSelection();
+		 	String selectionText = selection.getText();
+			if (selectionText.equals("")){
+				int selStart =  selection.getOffset();
+				IDocumentProvider prov = fEditor.getDocumentProvider();
+				IDocument doc = prov.getDocument(fEditor.getEditorInput());
+				selectionText = getSelection(doc, selStart);
+				ICElement rootElement = fOutline.getRoot();
+				proj = rootElement.getCProject().getProject();
+				res = rootElement.getCProject().getUnderlyingResource();
+				includeName = selectionText;
+			}
+		}
+		else{
+			include= getIncludeStatement(fSelectionProvider.getSelection());
+			if (include == null) {
+				return;
+			}
+
+			if (((IInclude) include).isStandard()){
+				fStandardInclude = true;
+			}
+			
+			res = include.getUnderlyingResource();
+			if (res!= null){
+				proj = res.getProject();
+				includeName = include.getElementName();
+			}
 		}
 		
 		try {
-			IResource res = include.getUnderlyingResource();
+			
 			ArrayList filesFound= new ArrayList(4);
 			if (res != null) {
-				IProject proj = res.getProject();
-				String includeName = include.getElementName();
+			
 				// Search in the scannerInfo information
 				IScannerInfoProvider provider =  CCorePlugin.getDefault().getScannerInfoProvider(proj);
 				if (provider != null) {
@@ -82,14 +130,25 @@
 					if (info == null) {
 						info = provider.getScannerInformation(proj);
 					}
-					if (info != null) {
-						String[] includePaths = info.getIncludePaths();
-						findFile(includePaths, includeName, filesFound);
-					}
-					if (filesFound.size() == 0) {
-						// Fall back and search the project
+					if (fStandardInclude){
+						if (info != null) {
+							String[] includePaths = info.getIncludePaths();
+							findFile(includePaths, includeName, filesFound);
+						}
+					} 
+					else {
+						//Local include, search project first
 						findFile(proj, new Path(includeName), filesFound);
-					}
+	
+						//Not found locally, search includes
+						if (filesFound.size()==0) {
+							if (info != null) {
+								String[] includePaths = info.getIncludePaths();
+								findFile(includePaths, includeName, filesFound);
+							}
+						}	
+					}			
+				
 				}
 			}
 			IPath fileToOpen;
@@ -117,7 +176,70 @@
 			CUIPlugin.getDefault().log(e.getStatus());
 		}
 	}
-
+	
+	public String getSelection(IDocument doc, int fPos){
+		int pos= fPos;
+		char c;
+		int fStartPos =0, fEndPos=0;
+		String selectedWord=null;
+		
+		try{
+			while (pos >= 0) {
+				c= doc.getChar(pos);
+				if (!Character.isJavaIdentifierPart(c))
+					break;
+				--pos;
+			}
+			fStartPos= pos + 1;
+			
+			pos= fPos;
+			int length= doc.getLength();
+			while (pos < length) {
+				c= doc.getChar(pos);
+				if (!Character.isJavaIdentifierPart(c))
+					break;
+				++pos;
+			}
+			fEndPos= pos;
+			selectedWord = doc.get(fStartPos, (fEndPos - fStartPos));
+			
+			//find the include word - if not include word leave
+			if (!selectedWord.equals("include")){
+				return null;
+			}
+			
+			//find the next word
+			int end2 = fEndPos;
+			
+			while (!Character.isJavaIdentifierPart(doc.getChar(end2))){
+				if (doc.getChar(end2) == '<'){
+					fStandardInclude = true;
+				}
+				++end2;
+			}
+			
+			int start2 = end2;
+			while (end2 < length){
+				c = doc.getChar(end2);
+				
+				if (!Character.isJavaIdentifierPart(c) &&
+						c != '.')
+					break;
+				++end2;
+			}
+			
+			int finalEnd = end2;
+			String selWord =doc.get(start2, finalEnd - start2);
+			selectedWord = selWord;
+			
+		}
+		catch(BadLocationException e){
+		}
+		
+		return selectedWord;
+		
+	}
+	
 	private void findFile(String[] includePaths,  String name, ArrayList list)  throws CoreException {
 		for (int i = 0; i < includePaths.length; i++) {
 			IPath path = new Path(includePaths[i] + "/" + name);
@@ -209,4 +331,5 @@
 		}
 		return null;
 	}
+	
 }
Index: src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java
===================================================================
retrieving revision 1.18
diff -u -r1.18 CEditorPreferencePage.java
--- src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java	9 Dec 2003 19:58:16 -0000	1.18
+++ src/org/eclipse/cdt/internal/ui/preferences/CEditorPreferencePage.java	12 Dec 2003 18:14:59 -0000
@@ -208,7 +208,8 @@
         overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, PreferenceConstants.EDITOR_TASK_INDICATION_COLOR));
         overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_TASK_INDICATION));
         overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_TASK_INDICATION_IN_OVERVIEW_RULER));
-		
+        overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, CEditor.HYPERLINK_ENABLED));
+       
         OverlayPreferenceStore.OverlayKey[] keys = new OverlayPreferenceStore.OverlayKey[overlayKeys.size()];
 		overlayKeys.toArray(keys);
 		return new OverlayPreferenceStore(getPreferenceStore(), keys);
@@ -297,6 +298,9 @@
 		store.setDefault(ContentAssistPreference.PROJECT_SEARCH_SCOPE, false);
 		store.setDefault(ContentAssistPreference.PROJECT_AND_DEPENDENCY_SEARCH_SCOPE, false);
 		
+		store.setDefault(CEditor.HYPERLINK_ENABLED,true);
+		
+
 	}
 
 	/*
@@ -931,9 +935,52 @@
 		item.setImage(CPluginImages.get(CPluginImages.IMG_OBJS_TUNIT));
 		item.setControl(createContentAssistPage(folder));
 
+		item = new TabItem(folder, SWT.NONE);
+		item.setText("Navigation");
+		item.setImage(CPluginImages.get(CPluginImages.IMG_OBJS_TUNIT));
+		item.setControl(createNavPage(folder));
+		
 		initialize();
 
 		return folder;
+	}
+
+	/**
+	 * @param folder
+	 * @return
+	 */
+	private Control createNavPage(Composite parent) {
+		Composite navComposite = new Composite(parent, SWT.NULL);
+		GridLayout layout = new GridLayout();
+		layout.numColumns = 2;
+		navComposite.setLayout(layout);
+
+		Button navCheck = new Button(navComposite,SWT.CHECK);
+		navCheck.setText("Enable Hyperlink Navigation");
+		GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+		gd.horizontalSpan = 2;
+		navCheck.setLayoutData(gd);
+		navCheck.addSelectionListener(fCheckBoxListener);
+		fCheckBoxes.put(navCheck, CEditor.HYPERLINK_ENABLED);
+
+		//Hyperlink key spec section (disabled for now)
+		
+//		Label label = new Label(navComposite , SWT.NONE);
+//		label.setText("Hyperlink trigger key");
+//		gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+//		label.setLayoutData(gd);
+//		
+//		Text hyperText = new Text(navComposite, SWT.SINGLE | SWT.BORDER);
+//		gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+//		gd.widthHint = convertWidthInCharsToPixels(13);
+//		hyperText.setTextLimit(12);
+//		hyperText.setLayoutData(gd);
+//		hyperText.setEnabled(false);
+//		fTextFields.put(hyperText, CEditor.HYPERLINK_KEY);
+//		hyperText.addModifyListener(fTextFieldListener);
+		
+		
+		return navComposite;
 	}
 
 	private void initialize() {

Back to the top