diff --git a/core/org.eclipse.cdt.ui/icons/etool16/show_invisible_chars.gif b/core/org.eclipse.cdt.ui/icons/etool16/show_invisible_chars.gif
new file mode 100644
index 00000000000..7eb6f044d54
Binary files /dev/null and b/core/org.eclipse.cdt.ui/icons/etool16/show_invisible_chars.gif differ
diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties
index 235d1bed7f0..53c38ed0957 100644
--- a/core/org.eclipse.cdt.ui/plugin.properties
+++ b/core/org.eclipse.cdt.ui/plugin.properties
@@ -133,8 +133,10 @@ todoTaskPrefName=Task Tags
Editors.DefaultTextEditor = Default Text Editor
AsmEditor.name = Assembly Editor
-CFolderActionSet.label=C Folder Actions
-CFolderActionSet.description=C Folder Action Set
+# Show Invisble Characters
+ShowInvisibleCharactersAction.label=Show Invisible Characters
+ShowInvisibleCharactersAction.tooltip=Show Invisible Characters
+ShowInvisibleCharactersAction.description=Show invisible (whitespace) characters CR, LF, TAB and SPACE
# Task Action
DeleteTaskAction.label=Delete C/C++ Markers
@@ -159,18 +161,13 @@ PathNameSorter.tooltip= Sort the view by Resource Path
# Action sets
CSearchActionSet.label= C/C++ Search
CSearchActionSet.description= Action set containing search related C/C++ actions
-RefactoringActionSet.label= Refactoring
-RefactoringActionSet.description= Action set containing refactoring related actions
+CEditorPresentationActionSet.label=C/C++ Editor Presentation
+CEditorPresentationActionSet.description=Actions to customize the C/C++ editor presentation
# Menus
searchMenu.label= Se&arch
refactoringMenu.label= Re&factor
-# Refactoring
-Refactoring.renameAction.label= Re&name
-Refactoring.undoAction.label= &Undo
-Refactoring.redoAction.label= &Redo
-
# Open Type
OpenTypeAction.label= Open &Type...
OpenTypeAction.tooltip= Open Type
diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml
index 7fff7589b18..8d89b7328f5 100644
--- a/core/org.eclipse.cdt.ui/plugin.xml
+++ b/core/org.eclipse.cdt.ui/plugin.xml
@@ -218,6 +218,7 @@
+
@@ -801,6 +802,24 @@
tooltip="%NewProjectDropDownAction.tooltip">
+
+
+
+
+
+
+
@@ -1052,6 +1071,12 @@
categoryId="org.eclipse.cdt.ui.category.source"
id="org.eclipse.cdt.ui.edit.text.c.show.tooltip">
+
+
= 0 && redrawLength > 0) {
+ fTextWidget.redrawRange(widgetOffset, redrawLength, true);
+ }
+ } catch (BadLocationException e) {
+ // ignore
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IPainter#deactivate(boolean)
+ */
+ public void deactivate(boolean redraw) {
+ if (fIsActive) {
+ fIsActive= false;
+ fTextWidget.removePaintListener(this);
+ if (redraw) {
+ redrawAll(true);
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IPainter#setPositionManager(org.eclipse.jface.text.IPaintPositionManager)
+ */
+ public void setPositionManager(IPaintPositionManager manager) {
+ // no need for a position manager
+ }
+
+ /*
+ * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
+ */
+ public void paintControl(PaintEvent event) {
+ if (fTextWidget != null) {
+ handleDrawRequest(event.gc, event.x, event.y, event.width, event.height);
+ }
+ }
+
+ /**
+ * Draw characters in view range.
+ * @param gc
+ * @param x
+ * @param y
+ * @param w
+ * @param h
+ */
+ private void handleDrawRequest(GC gc, int x, int y, int w, int h) {
+ int lineCount= fTextWidget.getLineCount();
+ int startLine= (y + fTextWidget.getTopPixel()) / fTextWidget.getLineHeight();
+ int endLine= (y + h - 1 + fTextWidget.getTopPixel()) / fTextWidget.getLineHeight();
+ if (startLine <= endLine && startLine < lineCount) {
+ int startOffset= fTextWidget.getOffsetAtLine(startLine);
+ int endOffset =
+ endLine < lineCount - 1 ? fTextWidget.getOffsetAtLine(endLine + 1) : fTextWidget.getCharCount();
+ handleDrawRequest(gc, startOffset, endOffset);
+ }
+ }
+
+ /**
+ * Draw characters of content range.
+ * @param gc
+ * @param startOffset inclusive start index
+ * @param endOffset exclusive end index
+ */
+ private void handleDrawRequest(GC gc, int startOffset, int endOffset) {
+ StyledTextContent content= fTextWidget.getContent();
+ int length= endOffset - startOffset;
+ String text= content.getTextRange(startOffset, length);
+ assert text.length() == length;
+ StyleRange styleRange= null;
+ Color fg= null;
+ Point selection= fTextWidget.getSelection();
+ StringBuffer visibleChar= new StringBuffer(20);
+ for (int textOffset= 0; textOffset <= length; ++textOffset) {
+ int delta= 0;
+ if (textOffset < length) {
+ delta= 1;
+ char c= text.charAt(textOffset);
+ switch (c) {
+ case ' ' :
+ visibleChar.append(MIDDLE_DOT);
+ continue;
+ case '\t' :
+ visibleChar.append(RIGHT_POINTING_DOUBLE_ANGLE);
+ break;
+ case '\r' :
+ visibleChar.append(LATIN_SMALL_LETTER_THORN);
+ if (textOffset >= length - 1 || text.charAt(textOffset + 1) != '\n') {
+ break;
+ }
+ continue;
+ case '\n' :
+ visibleChar.append(PILCROW_SIGN);
+ break;
+ default :
+ delta= 0;
+ break;
+ }
+ }
+ if (visibleChar.length() > 0) {
+ int widgetOffset= startOffset + textOffset - visibleChar.length() + delta;
+ if (widgetOffset >= selection.x && widgetOffset < selection.y) {
+ fg= fTextWidget.getSelectionForeground();
+ } else if (styleRange == null || styleRange.start + styleRange.length <= widgetOffset) {
+ styleRange= fTextWidget.getStyleRangeAtOffset(widgetOffset);
+ if (styleRange == null || styleRange.foreground == null) {
+ fg= fTextWidget.getForeground();
+ } else {
+ fg= styleRange.foreground;
+ }
+ }
+ draw(gc, widgetOffset, visibleChar.toString(), fg);
+ visibleChar.delete(0, visibleChar.length());
+ }
+ }
+ }
+
+ /**
+ * Redraw all of the text widgets visible content.
+ * @param redrawBackground If true, clean background before painting text.
+ */
+ private void redrawAll(boolean redrawBackground) {
+ int startLine= fTextWidget.getTopPixel() / fTextWidget.getLineHeight();
+ int startOffset= fTextWidget.getOffsetAtLine(startLine);
+ int endLine= 1 + (fTextWidget.getTopPixel() + fTextWidget.getClientArea().height) / fTextWidget.getLineHeight();
+ int endOffset;
+ if (endLine >= fTextWidget.getLineCount()) {
+ endOffset= fTextWidget.getCharCount();
+ } else {
+ endOffset= fTextWidget.getOffsetAtLine(endLine);
+ }
+ if (startOffset < endOffset) {
+ // add 2 for line separator characters
+ endOffset= Math.min(endOffset + 2, fTextWidget.getCharCount());
+ int redrawOffset= startOffset;
+ int redrawLength= endOffset - redrawOffset;
+ fTextWidget.redrawRange(startOffset, redrawLength, redrawBackground);
+ }
+ }
+
+ /**
+ * Draw character c at widget offset.
+ * @param gc
+ * @param offset the widget offset
+ * @param c the character to be drawn
+ * @param fg the foreground color
+ */
+ private void draw(GC gc, int offset, String c, Color fg) {
+ Point pos= fTextWidget.getLocationAtOffset(offset);
+ gc.setForeground(fg);
+ gc.drawString(c, pos.x, pos.y, true);
+ }
+
+ /**
+ * Convert a document offset to the corresponding widget offset.
+ * @param documentOffset
+ * @return widget offset
+ */
+ private int getWidgetOffset(int documentOffset) {
+ if (fTextViewer instanceof ITextViewerExtension5) {
+ ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer;
+ return extension.modelOffset2WidgetOffset(documentOffset);
+ } else {
+ IRegion visible= fTextViewer.getVisibleRegion();
+ int widgetOffset= documentOffset - visible.getOffset();
+ if (widgetOffset > visible.getLength()) {
+ return -1;
+ }
+ return widgetOffset;
+ }
+ }
+
+ /**
+ * Convert a widget offset to the corresponding document offset.
+ * @param widgetOffset
+ * @return document offset
+ */
+ private int getDocumentOffset(int widgetOffset) {
+ if (fTextViewer instanceof ITextViewerExtension5) {
+ ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer;
+ return extension.widgetOffset2ModelOffset(widgetOffset);
+ } else {
+ IRegion visible= fTextViewer.getVisibleRegion();
+ if (widgetOffset > visible.getLength()) {
+ return -1;
+ }
+ return widgetOffset + visible.getOffset();
+ }
+ }
+
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/actions/ShowInvisibleCharactersAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/actions/ShowInvisibleCharactersAction.java
new file mode 100644
index 00000000000..b304301a413
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/actions/ShowInvisibleCharactersAction.java
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * Copyright (c) 2006 Wind River Systems, Inc. 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:
+ * Anton Leherbauer (Wind River Systems) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.text.IPainter;
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.ITextViewerExtension2;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IEditorActionDelegate;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.IUpdate;
+
+import org.eclipse.cdt.internal.ui.InvisibleCharacterPainter;
+
+/**
+ * This action toggles the visibility of whitespace characters by
+ * attaching/detaching an {@link InvisibleCharacterPainter} to the
+ * active (text-)editor.
+ *
+ * @author anton.leherbauer@windriver.com
+ */
+public class ShowInvisibleCharactersAction implements IEditorActionDelegate, IWorkbenchWindowActionDelegate, IUpdate {
+
+ /**
+ * The PartListener to act on changes of the active editor.
+ */
+ private class PartListener implements IPartListener2 {
+
+ /*
+ * @see org.eclipse.ui.IPartListener2#partActivated(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ public void partActivated(IWorkbenchPartReference partRef) {
+ if (partRef.getPart(false) instanceof IEditorPart) {
+ updateActiveEditor();
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.IPartListener2#partBroughtToTop(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ public void partBroughtToTop(IWorkbenchPartReference partRef) {
+ }
+
+ /*
+ * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ public void partClosed(IWorkbenchPartReference partRef) {
+ if (partRef.getPart(false) instanceof IEditorPart) {
+ updateActiveEditor();
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.IPartListener2#partDeactivated(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ public void partDeactivated(IWorkbenchPartReference partRef) {
+ }
+
+ /*
+ * @see org.eclipse.ui.IPartListener2#partOpened(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ public void partOpened(IWorkbenchPartReference partRef) {
+ if (partRef.getPart(false) instanceof IEditorPart) {
+ updateActiveEditor();
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.IPartListener2#partHidden(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ public void partHidden(IWorkbenchPartReference partRef) {
+ }
+
+ /*
+ * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ public void partVisible(IWorkbenchPartReference partRef) {
+ if (partRef.getPart(false) instanceof IEditorPart) {
+ updateActiveEditor();
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.IPartListener2#partInputChanged(org.eclipse.ui.IWorkbenchPartReference)
+ */
+ public void partInputChanged(IWorkbenchPartReference partRef) {
+ }
+
+ }
+
+ private IPainter fInvisibleCharPainter;
+ private IWorkbenchWindow fWindow;
+ private IPartListener2 fPartListener;
+ private ITextEditor fTextEditor;
+ private boolean fIsChecked;
+ private IAction fAction;
+
+ public ShowInvisibleCharactersAction() {
+ }
+
+ /**
+ * Add the painter to the current editor.
+ */
+ private void addPainter() {
+ ITextEditor editor= getTextEditor();
+ ITextViewer viewer= getTextViewer(editor);
+ if (viewer instanceof ITextViewerExtension2) {
+ ITextViewerExtension2 viewerExt2= (ITextViewerExtension2)viewer;
+ if (fInvisibleCharPainter == null) {
+ fInvisibleCharPainter= new InvisibleCharacterPainter(viewer);
+ }
+ viewerExt2.addPainter(fInvisibleCharPainter);
+ }
+ }
+
+ /**
+ * Get the ITextViewer from an ITextEditor by adapting
+ * it to a ITextOperationTarget.
+ * @param editor the ITextEditor
+ * @return the text viewer or null
+ */
+ private ITextViewer getTextViewer(ITextEditor editor) {
+ Object target= editor.getAdapter(ITextOperationTarget.class);
+ if (target instanceof ITextViewer) {
+ return (ITextViewer)target;
+ }
+ return null;
+ }
+
+ /**
+ * Remove the painter from the current editor.
+ */
+ private void removePainter() {
+ ITextEditor editor= getTextEditor();
+ ITextViewer viewer= getTextViewer(editor);
+ if (viewer instanceof ITextViewerExtension2) {
+ ITextViewerExtension2 viewerExt2= (ITextViewerExtension2)viewer;
+ viewerExt2.removePainter(fInvisibleCharPainter);
+ }
+ }
+
+ private void setEditor(ITextEditor editor) {
+ if (editor != null && editor == getTextEditor()) {
+ return;
+ }
+ if (fInvisibleCharPainter != null) {
+ removePainter();
+ fInvisibleCharPainter.deactivate(false);
+ fInvisibleCharPainter.dispose();
+ fInvisibleCharPainter= null;
+ }
+ fTextEditor= editor;
+ update();
+ if (fTextEditor != null && fIsChecked) {
+ addPainter();
+ }
+ }
+
+ private ITextEditor getTextEditor() {
+ return fTextEditor;
+ }
+
+ /*
+ * @see org.eclipse.ui.IEditorActionDelegate#setActiveEditor(org.eclipse.jface.action.IAction, org.eclipse.ui.IEditorPart)
+ */
+ public void setActiveEditor(IAction action, IEditorPart targetEditor) {
+ fAction= action;
+ if (targetEditor instanceof ITextEditor) {
+ setEditor((ITextEditor)targetEditor);
+ } else {
+ setEditor(null);
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#init(org.eclipse.ui.IWorkbenchWindow)
+ */
+ public void init(IWorkbenchWindow window) {
+ fWindow= window;
+ fPartListener= new PartListener();
+ fWindow.getActivePage().addPartListener(fPartListener);
+ updateActiveEditor();
+ }
+
+ /*
+ * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#dispose()
+ */
+ public void dispose() {
+ if (fWindow != null) {
+ IWorkbenchPage activePage= fWindow.getActivePage();
+ if (activePage != null) {
+ activePage.removePartListener(fPartListener);
+ }
+ fPartListener= null;
+ fWindow= null;
+ }
+ fAction= null;
+ }
+
+ /*
+ * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
+ */
+ public void run(IAction action) {
+ fAction= action;
+ fIsChecked= action.isChecked();
+ if (fIsChecked) {
+ addPainter();
+ } else if (fInvisibleCharPainter != null) {
+ removePainter();
+ fInvisibleCharPainter.deactivate(true);
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection)
+ */
+ public void selectionChanged(IAction action, ISelection selection) {
+ fAction= action;
+ fIsChecked= action.isChecked();
+ update();
+ }
+
+ /**
+ * Update the active editor.
+ */
+ protected void updateActiveEditor() {
+ IWorkbenchPage page= fWindow.getActivePage();
+ if (page != null) {
+ IEditorPart editorPart= page.getActiveEditor();
+ if (editorPart instanceof ITextEditor) {
+ setEditor((ITextEditor)editorPart);
+ } else {
+ setEditor(null);
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.IUpdate#update()
+ */
+ public void update() {
+ if (fAction != null) {
+ fAction.setEnabled(getTextEditor() != null);
+ }
+ }
+
+}