@@ -1,13 +1,22 @@
package org .python .pydev .overview_ruler ;
import java .util .Arrays ;
import org .eclipse .core .runtime .IProgressMonitor ;
import org .eclipse .core .runtime .IStatus ;
import org .eclipse .core .runtime .Status ;
import org .eclipse .core .runtime .jobs .Job ;
import org .eclipse .jface .preference .IPreferenceStore ;
import org .eclipse .jface .resource .StringConverter ;
import org .eclipse .jface .text .IDocumentExtension4 ;
import org .eclipse .jface .text .ITextViewer ;
import org .eclipse .jface .text .source .IAnnotationAccess ;
import org .eclipse .jface .text .source .ISharedTextColors ;
import org .eclipse .swt .SWT ;
import org .eclipse .swt .custom .StyledText ;
import org .eclipse .swt .custom .StyledTextContent ;
import org .eclipse .swt .events .DisposeEvent ;
import org .eclipse .swt .events .DisposeListener ;
import org .eclipse .swt .events .MouseAdapter ;
import org .eclipse .swt .events .MouseEvent ;
import org .eclipse .swt .events .MouseMoveListener ;
@@ -18,36 +27,18 @@
import org .eclipse .swt .graphics .RGB ;
import org .eclipse .swt .graphics .Rectangle ;
import org .eclipse .swt .graphics .Transform ;
import org .eclipse .swt .widgets .Canvas ;
import org .eclipse .swt .widgets .Composite ;
import org .eclipse .swt .widgets .Control ;
import org .eclipse .swt .widgets .Display ;
import org .eclipse .ui .editors .text .EditorsUI ;
import org .eclipse .ui .texteditor .AbstractDecoratedTextEditorPreferenceConstants ;
import org .python .pydev .core .log .Log ;
import org .python .pydev .core .structure .FastStack ;
import org .python .pydev .core .uiutils .RunInUiThread ;
public class MinimapOverviewRuler extends CopiedOverviewRuler {
private boolean mousePressed = false ;
/**
* @param annotationAccess
* @param width
* @param sharedColors
* @param discolorTemporaryAnnotation
*/
public MinimapOverviewRuler (IAnnotationAccess annotationAccess , ISharedTextColors sharedColors ) {
super (annotationAccess , 120 , sharedColors );
}
@ Override
protected void doubleBufferPaint (GC dest ) {
if (fTextViewer != null ) {
fCanvas .setBackground (fTextViewer .getTextWidget ().getBackground ());
fCanvas .setForeground (fTextViewer .getTextWidget ().getForeground ());
}
super .doubleBufferPaint (dest );
}
/**
* Removes whitespaces and tabs at the end of the string.
*/
@@ -62,6 +53,9 @@ public static String rightTrim(final String input) {
return input .substring (0 , len );
}
/**
* Helper to get the first char position in a string.
*/
public static int getFirstCharPosition (String src ) {
int i = 0 ;
boolean breaked = false ;
@@ -78,14 +72,185 @@ public static int getFirstCharPosition(String src) {
}
return (i - 1 );
}
/**
* Lock to access the stacked parameters.
*/
private final static Object lockStackedParameters = new Object ();
/**
* Redraws a temporary image in the background and after that's finished, replaces the new base image and asks
* for a new redraw.
*/
private final class RedrawJob extends Job {
private RedrawJob (String name ) {
super (name );
this .setPriority (Job .SHORT );
this .setSystem (true );
}
private FastStack <Object []> stackedParameters = new FastStack <Object []>();
/**
* Note: the GC and marginColor need to be disposed after they're used.
*/
private void setParameters (GC gc , Color styledTextForeground , Point size , StyledTextContent content , int lineCount , int marginCols ,
Color marginColor , int spacing , int imageHeight , Transform transform , Image tmpImage ) {
synchronized (lockStackedParameters ) {
stackedParameters .push (new Object [] { gc , styledTextForeground , size , content , lineCount , marginCols , marginColor , spacing ,
imageHeight , transform , tmpImage });
}
}
/**
* Redraws the base image (i.e.: lines)
*/
private void redrawBaseImage (GC gc , Color styledTextForeground , Point size , StyledTextContent content , int lineCount ,
int marginCols , Color marginColor , int spacing , int imageHeight , Transform transform , IProgressMonitor monitor ) {
gc .setForeground (styledTextForeground );
gc .setAlpha (200 );
gc .setTransform (transform );
int x1 = 0 , y1 = 0 , x2 = 0 , y2 = 0 ;
for (int i = 0 ; i < lineCount ; i ++) {
if (monitor .isCanceled ()) {
return ;
}
String line = rightTrim (content .getLine (i ));
//if(lineCount > 5000){
// if(!PySelection.matchesClassLine(line) && !PySelection.matchesFunctionLine(line)){
// y1 = y2 = y1 + spacing;
// continue; //Only print lines related to classes/functions
// }
//}
x1 = getFirstCharPosition (line );
x2 = line .length ();
if (x2 > 0 ) {
gc .drawLine (x1 , y1 , x2 , y2 );
}
y1 = y2 = y1 + spacing ;
}
if (monitor .isCanceled ()) {
return ;
}
gc .setForeground (marginColor );
gc .setBackground (marginColor );
gc .drawLine (marginCols , 0 , marginCols , imageHeight );
}
/**
* Calls the method to draw image and later replaces the base image to be used and calls a new redraw.
*/
@ Override
protected IStatus run (IProgressMonitor monitor ) {
Object [] parameters ;
synchronized (lockStackedParameters ) {
parameters = stackedParameters .pop ();
disposeStackedParameters ();
}
GC gc = (GC ) parameters [0 ];
Color styledTextForeground = (Color ) parameters [1 ];
Point size = (Point ) parameters [2 ];
StyledTextContent content = (StyledTextContent ) parameters [3 ];
int lineCount = (Integer ) parameters [4 ];
int marginCols = (Integer ) parameters [5 ];
Color marginColor = (Color ) parameters [6 ];
int spacing = (Integer ) parameters [7 ];
int imageHeight = (Integer ) parameters [8 ];
Transform transform = (Transform ) parameters [9 ];
final Image image = (Image ) parameters [10 ];
try {
redrawBaseImage (gc , styledTextForeground , size , content , lineCount , marginCols , marginColor , spacing , imageHeight ,
transform , monitor );
} catch (Throwable e ) {
Log .log (e );
} finally {
gc .dispose ();
marginColor .dispose ();
}
boolean disposeOfImage = true ;
try {
if (!monitor .isCanceled ()) {
final Canvas c = fCanvas ;
if (c != null && !c .isDisposed ()) {
disposeOfImage = false ;
RunInUiThread .async (new Runnable () {
public void run () {
//The baseImage should only be disposed in the UI thread (so, no locks are needed to
//replace/dispose the image)
if (baseImage != null && !baseImage .isDisposed ()) {
baseImage .dispose ();
}
if (c != null && !c .isDisposed ()) {
baseImage = image ;
MinimapOverviewRuler .this .redraw ();
} else {
image .dispose ();
}
}
});
}
}
} finally {
if (disposeOfImage ) {
image .dispose ();
}
}
return Status .OK_STATUS ;
}
/**
* Disposes of any parameters in the stack that need an explicit dispose().
*/
public void disposeStackedParameters () {
synchronized (lockStackedParameters ) {
while (stackedParameters .size () > 0 ) {
Object [] disposeOfParameters = stackedParameters .pop ();
GC gc = (GC ) disposeOfParameters [0 ];
Color marginColor = (Color ) disposeOfParameters [6 ];
gc .dispose ();
marginColor .dispose ();
}
}
}
}
/**
* Helper to deal with the drag.
*/
private boolean mousePressed = false ;
public MinimapOverviewRuler (IAnnotationAccess annotationAccess , ISharedTextColors sharedColors ) {
super (annotationAccess , 120 , sharedColors );
}
@ Override
protected void doubleBufferPaint (GC dest ) {
if (fTextViewer != null ) {
fCanvas .setBackground (fTextViewer .getTextWidget ().getBackground ());
fCanvas .setForeground (fTextViewer .getTextWidget ().getForeground ());
}
super .doubleBufferPaint (dest );
}
@ Override
public Control createControl (Composite parent , ITextViewer textViewer ) {
Control ret = super .createControl (parent , textViewer );
fCanvas .addMouseListener (new MouseAdapter () {
public void mouseDown (MouseEvent event ) {
onMouseDown (event );
}
@ Override
public void mouseUp (MouseEvent event ) {
onMouseUp (event );
@@ -97,117 +262,142 @@ public void mouseMove(MouseEvent event) {
onMouseMove (event );
}
});
fCanvas .addDisposeListener (new DisposeListener () {
public void widgetDisposed (DisposeEvent event ) {
onDispose ();
}
});
return ret ;
}
private void onMouseDown (MouseEvent event ) {
mousePressed = true ;
}
private void onMouseUp (MouseEvent event ) {
mousePressed = false ;
}
private void onMouseMove (MouseEvent event ) {
if (mousePressed ){
if (mousePressed ) {
event .button = 1 ;
super .handleMouseDown (event );
}
}
private void onDispose () {
if (baseImage != null && !baseImage .isDisposed ()) {
baseImage .dispose ();
baseImage = null ;
}
if (lastImage != null && !lastImage .isDisposed ()) {
lastImage .dispose ();
lastImage = null ;
}
redrawJob .cancel ();
redrawJob .disposeStackedParameters ();
}
private volatile Image baseImage ;
private volatile Image lastImage ;
private Object [] cacheKey ;
private final RedrawJob redrawJob = new RedrawJob ("Redraw overview ruler" );
@ Override
protected void doPaint1 (GC paintGc ) {
//Draw the minimap
if (fTextViewer != null ) {
StyledText styledText = fTextViewer .getTextWidget ();
Point size = fCanvas .getSize ();
if (size .x != 0 && size .y != 0 ) {
StyledTextContent content = styledText .getContent ();
int lineCount = content .getLineCount ();
IPreferenceStore preferenceStore = EditorsUI .getPreferenceStore ();
int marginCols = preferenceStore .getInt (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_PRINT_MARGIN_COLUMN );
String strColor = preferenceStore .getString (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_PRINT_MARGIN_COLOR );
RGB asRGB = StringConverter .asRGB (strColor );
Color color = new Color (Display .getCurrent (), asRGB );
Color black = new Color (Display .getCurrent (), new RGB (0 , 0 , 0 ));
int maxChars = (int ) (marginCols + (marginCols * 0.1 ));
int spacing = 1 ;
int imageHeight = lineCount * spacing ;
int imageWidth = maxChars ;
Image image = new Image (Display .getCurrent (), size .x , size .y );
try {
GC gc = new GC (image );
gc .setAdvanced (true );
gc .setAntialias (SWT .ON );
gc .setBackground (styledText .getBackground ());
gc .setForeground (styledText .getBackground ());
gc .fillRectangle (0 , 0 , size .x , size .y );
gc .setForeground (styledText .getForeground ());
//int newImageHeight = imageHeight;
//if (newImageHeight > size.y) {
// newImageHeight = size.y;
//}
IDocumentExtension4 document = (IDocumentExtension4 ) fTextViewer .getDocument ();
if (document != null ) {
final StyledText styledText = fTextViewer .getTextWidget ();
final Point size = fCanvas .getSize ();
if (size .x != 0 && size .y != 0 ) {
final StyledTextContent content = styledText .getContent ();
final int lineCount = content .getLineCount ();
IPreferenceStore preferenceStore = EditorsUI .getPreferenceStore ();
final int marginCols = preferenceStore
.getInt (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_PRINT_MARGIN_COLUMN );
String strColor = preferenceStore .getString (AbstractDecoratedTextEditorPreferenceConstants .EDITOR_PRINT_MARGIN_COLOR );
RGB marginRgb = StringConverter .asRGB (strColor );
Color marginColor = new Color (Display .getCurrent (), marginRgb );
Color black = new Color (Display .getCurrent (), new RGB (0 , 0 , 0 ));
int maxChars = (int ) (marginCols + (marginCols * 0.1 ));
final int spacing = 1 ;
final int imageHeight = lineCount * spacing ;
int imageWidth = maxChars ;
Object [] currCacheKey = new Object [] { document .getModificationStamp (), size .x , size .y , styledText .getForeground (),
styledText .getBackground (), marginCols , marginRgb };
double scaleX = size .x / (double ) imageWidth ;
double scaleY = size .y / (double ) imageHeight ;
Transform transform = new Transform (Display .getCurrent ());
final Transform transform = new Transform (Display .getCurrent ());
transform .scale ((float ) scaleX , (float ) scaleY );
gc .setTransform (transform );
try {
int x1 = 0 , y1 = 0 , x2 = 0 , y2 = 0 ;
if ( baseImage == null || ! Arrays . equals ( this . cacheKey , currCacheKey )) {
this . cacheKey = currCacheKey ;
for (int i = 0 ; i < lineCount ; i ++) {
String line = rightTrim (content .getLine (i ));
x1 = getFirstCharPosition (line );
x2 = line .length ();
Image tmpImage = new Image (Display .getCurrent (), size .x , size .y );
final GC gc = new GC (tmpImage );
gc .setAdvanced (true );
gc .setAntialias (SWT .ON );
gc .setBackground (styledText .getBackground ());
gc .setForeground (styledText .getBackground ());
gc .fillRectangle (0 , 0 , size .x , size .y );
if ( x2 > 0 ) {
gc . drawLine ( x1 , y1 , x2 , y2 );
}
y1 = y2 = y1 + spacing ;
}
final Color styledTextForeground = styledText . getForeground ();
final Color marginColor2 = new Color ( Display . getCurrent (), marginRgb );
redrawJob . cancel ();
redrawJob . setParameters ( gc , styledTextForeground , size , content , lineCount , marginCols , marginColor2 , spacing ,
imageHeight , transform , tmpImage );
gc .setForeground (color );
gc .setBackground (color );
gc .drawLine (marginCols , 0 , marginCols , imageHeight );
redrawJob .schedule ();
}
try {
if (baseImage != null && !baseImage .isDisposed ()) {
if (lastImage != null && !lastImage .isDisposed ()) {
lastImage .dispose ();
}
Rectangle clientArea = styledText .getClientArea ();
int top = styledText .getLineIndex (0 );
int bottom = styledText .getLineIndex (clientArea .height ) + 1 ;
Image image = new Image (Display .getCurrent (), size .x , size .y );
GC gc2 = new GC (image );
try {
gc2 .drawImage (baseImage , 0 , 0 );
float rect [] = new float [] { 0 , top * spacing , imageWidth , (bottom * spacing ) - (top * spacing ) };
transform .transform (rect );
Rectangle clientArea = styledText .getClientArea ();
int top = styledText .getLineIndex (0 );
int bottom = styledText .getLineIndex (clientArea .height ) + 1 ;
gc .setTransform (null );
float rect [] = new float [] { 0 , top * spacing , imageWidth , (bottom * spacing ) - (top * spacing ) };
transform .transform (rect );
gc .setLineWidth (3 );
gc .setAlpha (150 );
gc .fillRectangle (Math .round (rect [0 ]), Math .round (rect [1 ]), Math .round (rect [2 ]), Math .round (rect [3 ]));
gc .setAlpha (255 );
gc .drawRectangle (Math .round (rect [0 ]), Math .round (rect [1 ]), Math .round (rect [2 ]), Math .round (rect [3 ]));
gc2 .setLineWidth (3 );
gc2 .setAlpha (150 );
gc2 .fillRectangle (Math .round (rect [0 ]), Math .round (rect [1 ]), Math .round (rect [2 ]), Math .round (rect [3 ]));
gc2 .setAlpha (255 );
gc2 .drawRectangle (Math .round (rect [0 ]), Math .round (rect [1 ]), Math .round (rect [2 ]), Math .round (rect [3 ]));
gc .setForeground (black );
gc .drawRectangle (0 , 0 , size .x , size .y );
gc2 .setForeground (black );
gc2 .drawRectangle (0 , 0 , size .x , size .y );
} finally {
gc2 .dispose ();
}
lastImage = image ;
}
if (lastImage != null && !lastImage .isDisposed ()) {
paintGc .drawImage (lastImage , 0 , 0 );
}
} finally {
gc .dispose ();
marginColor .dispose ();
black .dispose ();
}
paintGc .drawImage (image , 0 , 0 );
} finally {
image .dispose ();
color .dispose ();
black .dispose ();
}
}
}
}
super .doPaint1 (paintGc );