2222
2323import java .awt .Color ;
2424import java .awt .Font ;
25+ import java .awt .Graphics ;
26+ import java .awt .Graphics2D ;
2527import java .awt .event .MouseAdapter ;
2628import java .awt .event .MouseEvent ;
29+ import java .awt .geom .GeneralPath ;
30+ import java .util .List ;
31+
32+ import javax .swing .text .BadLocationException ;
33+ import javax .swing .text .Segment ;
34+ import javax .swing .text .Utilities ;
2735
2836import processing .app .Mode ;
37+ import processing .app .Problem ;
2938import processing .app .ui .Editor ;
3039
3140
41+ /**
42+ * Adds support to TextAreaPainter for background colors,
43+ * and the left hand gutter area with background color and text.
44+ */
3245public class PdeTextAreaPainter extends TextAreaPainter {
3346 public Color errorUnderlineColor ;
3447 public Color warningUnderlineColor ;
@@ -87,14 +100,247 @@ public void setMode(Mode mode) {
87100 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
88101
89102
103+ /**
104+ * Paint a line. Paints the gutter (with background color and text) then the
105+ * line (background color and text).
106+ *
107+ * @param gfx the graphics context
108+ * @param tokenMarker
109+ * @param line 0-based line number
110+ * @param x horizontal position
111+ */
112+ @ Override
113+ protected void paintLine (Graphics gfx , int line , int x , TokenMarker marker ) {
114+ try {
115+ // TODO This line is causing NPEs randomly ever since I added the
116+ // toggle for Java Mode/Debugger toolbar. [Manindra]
117+ super .paintLine (gfx , line , x + Editor .LEFT_GUTTER , marker );
118+
119+ } catch (Exception e ) {
120+ e .printStackTrace ();
121+ }
122+
123+ paintLeftGutter (gfx , line , x );
124+ paintErrorLine (gfx , line , x );
125+ }
126+
127+
128+ /**
129+ * Paints the underline for an error/warning line
130+ */
131+ protected void paintErrorLine (Graphics gfx , int line , int x ) {
132+ List <Problem > problems = getEditor ().findProblems (line );
133+ for (Problem problem : problems ) {
134+ int startOffset = problem .getStartOffset ();
135+ int stopOffset = problem .getStopOffset ();
136+
137+ int lineOffset = textArea .getLineStartOffset (line );
138+
139+ int wiggleStart = Math .max (startOffset , lineOffset );
140+ int wiggleStop = Math .min (stopOffset , textArea .getLineStopOffset (line ));
141+
142+ int y = textArea .lineToY (line ) + fm .getLeading () + fm .getMaxDescent ();
143+
144+ try {
145+ String badCode = null ;
146+ String goodCode = null ;
147+ try {
148+ SyntaxDocument doc = textArea .getDocument ();
149+ badCode = doc .getText (wiggleStart , wiggleStop - wiggleStart );
150+ goodCode = doc .getText (lineOffset , wiggleStart - lineOffset );
151+ //log("paintErrorLine() LineText GC: " + goodCode);
152+ //log("paintErrorLine() LineText BC: " + badCode);
153+ } catch (BadLocationException bl ) {
154+ // Error in the import statements or end of code.
155+ // System.out.print("BL caught. " + ta.getLineCount() + " ,"
156+ // + line + " ,");
157+ // log((ta.getLineStopOffset(line) - start - 1));
158+ return ;
159+ }
160+
161+ int trimmedLength = badCode .trim ().length ();
162+ int rightTrimmedLength = trimRight (badCode ).length ();
163+ int leftTrimLength = rightTrimmedLength - trimmedLength ;
164+
165+ // Fix offsets when bad code is just whitespace
166+ if (trimmedLength == 0 ) {
167+ leftTrimLength = 0 ;
168+ rightTrimmedLength = badCode .length ();
169+ }
170+
171+ int x1 = textArea .offsetToX (line , goodCode .length () + leftTrimLength );
172+ int x2 = textArea .offsetToX (line , goodCode .length () + rightTrimmedLength );
173+ if (x1 == x2 ) x2 += fm .stringWidth (" " );
174+ int y1 = y + fm .getHeight () - 2 ;
175+
176+ if (line != problem .getLineNumber ()) {
177+ x1 = Editor .LEFT_GUTTER ; // on the following lines, wiggle extends to the left border
178+ }
179+
180+ gfx .setColor (errorUnderlineColor );
181+ if (problem .isWarning ()) {
182+ gfx .setColor (warningUnderlineColor );
183+ }
184+ paintSquiggle (gfx , y1 , x1 , x2 );
185+
186+ } catch (Exception e ) {
187+ e .printStackTrace ();
188+ }
189+ }
190+ }
191+
192+
193+ /**
194+ * Paint the gutter: draw the background, draw line numbers, break points.
195+ * @param gfx the graphics context
196+ * @param line 0-based line number
197+ * @param x horizontal position
198+ */
199+ protected void paintLeftGutter (Graphics gfx , int line , int x ) {
200+ int y = textArea .lineToY (line ) + fm .getLeading () + fm .getMaxDescent ();
201+ if (line == textArea .getSelectionStopLine ()) {
202+ gfx .setColor (gutterLineHighlightColor );
203+ gfx .fillRect (0 , y , Editor .LEFT_GUTTER , fm .getHeight ());
204+ } else {
205+ //gfx.setColor(getJavaTextArea().gutterBgColor);
206+ gfx .setClip (0 , y , Editor .LEFT_GUTTER , fm .getHeight ());
207+ gfx .drawImage (((PdeTextArea ) textArea ).getGutterGradient (), 0 , 0 , getWidth (), getHeight (), this );
208+ gfx .setClip (null ); // reset
209+ }
210+
211+ String text = null ;
212+ if (getEditor ().isDebuggerEnabled ()) {
213+ text = getPdeTextArea ().getGutterText (line );
214+ }
215+
216+ gfx .setColor (line < textArea .getLineCount () ? gutterTextColor : gutterPastColor );
217+ // if (line >= textArea.getLineCount()) {
218+ // //gfx.setColor(new Color(gutterTextColor.getRGB(), );
219+ // }
220+ int textRight = Editor .LEFT_GUTTER - Editor .GUTTER_MARGIN ;
221+ int textBaseline = textArea .lineToY (line ) + fm .getHeight ();
222+
223+ if (text != null ) {
224+ if (text .equals (PdeTextArea .BREAK_MARKER )) {
225+ drawDiamond (gfx , textRight - 8 , textBaseline - 8 , 8 , 8 );
226+
227+ } else if (text .equals (PdeTextArea .STEP_MARKER )) {
228+ //drawRightArrow(gfx, textRight - 7, textBaseline - 7, 7, 6);
229+ drawRightArrow (gfx , textRight - 7 , textBaseline - 7.5f , 7 , 7 );
230+ }
231+ } else {
232+ // if no special text for a breakpoint, just show the line number
233+ text = String .valueOf (line + 1 );
234+ //text = makeOSF(String.valueOf(line + 1));
235+
236+ gfx .setFont (gutterTextFont );
237+ // ((Graphics2D) gfx).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
238+ // RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
239+ // Right-align the text
240+ char [] txt = text .toCharArray ();
241+ int tx = textRight - gfx .getFontMetrics ().charsWidth (txt , 0 , txt .length );
242+ // Using 'fm' here because it's relative to the editor text size,
243+ // not the numbers in the gutter
244+ Utilities .drawTabbedText (new Segment (txt , 0 , text .length ()),
245+ tx , textBaseline , gfx , this , 0 );
246+ }
247+ }
248+
249+
250+ static private void drawDiamond (Graphics g ,
251+ float x , float y , float w , float h ) {
252+ Graphics2D g2 = (Graphics2D ) g ;
253+ GeneralPath path = new GeneralPath ();
254+ path .moveTo (x + w /2 , y );
255+ path .lineTo (x + w , y + h /2 );
256+ path .lineTo (x + w /2 , y + h );
257+ path .lineTo (x , y + h /2 );
258+ path .closePath ();
259+ g2 .fill (path );
260+ }
261+
262+
263+ static private void drawRightArrow (Graphics g ,
264+ float x , float y , float w , float h ) {
265+ Graphics2D g2 = (Graphics2D ) g ;
266+ GeneralPath path = new GeneralPath ();
267+ path .moveTo (x , y );
268+ path .lineTo (x + w , y + h /2 );
269+ path .lineTo (x , y + h );
270+ path .closePath ();
271+ g2 .fill (path );
272+ }
273+
274+
275+ /**
276+ * Remove all trailing whitespace from a line
277+ */
278+ static private String trimRight (String str ) {
279+ int i = str .length () - 1 ;
280+ while (i >= 0 && Character .isWhitespace (str .charAt (i ))) {
281+ i --;
282+ }
283+ return str .substring (0 , i +1 );
284+ }
285+
286+
287+ static private void paintSquiggle (Graphics g , int y , int x1 , int x2 ) {
288+ int xx = x1 ;
289+
290+ while (xx < x2 ) {
291+ g .drawLine (xx , y , xx + 2 , y + 1 );
292+ xx += 2 ;
293+ g .drawLine (xx , y + 1 , xx + 2 , y );
294+ xx += 2 ;
295+ }
296+ }
297+
298+
299+ @ Override
300+ public String getToolTipText (MouseEvent event ) {
301+ int line = event .getY () / getFontMetrics ().getHeight () + textArea .getFirstLine ();
302+ if (line >= 0 || line < textArea .getLineCount ()) {
303+ List <Problem > problems = getEditor ().findProblems (line );
304+ for (Problem problem : problems ) {
305+ int lineStart = textArea .getLineStartOffset (line );
306+ int lineEnd = textArea .getLineStopOffset (line );
307+
308+ int errorStart = problem .getStartOffset ();
309+ int errorEnd = problem .getStopOffset () + 1 ;
310+
311+ int startOffset = Math .max (errorStart , lineStart ) - lineStart ;
312+ int stopOffset = Math .min (errorEnd , lineEnd ) - lineStart ;
313+
314+ int x = event .getX ();
315+
316+ if (x >= textArea .offsetToX (line , startOffset ) &&
317+ x <= textArea .offsetToX (line , stopOffset )) {
318+ getEditor ().statusToolTip (this , problem .getMessage (), problem .isError ());
319+ return super .getToolTipText (event );
320+ }
321+ }
322+ }
323+ setToolTipText (null );
324+ return super .getToolTipText (event );
325+ }
326+
327+
328+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
329+
330+
90331 @ Override
91332 public int getScrollWidth () {
92- // https://github.com/processing/processing/issues/3591
333+ // TODO https://github.com/processing/processing/issues/3591
93334 return super .getWidth () - Editor .LEFT_GUTTER ;
94335 }
95336
96337
97338 public Editor getEditor () {
98- return ((PdeTextArea ) textArea ).editor ;
339+ return getPdeTextArea ().editor ;
340+ }
341+
342+
343+ public PdeTextArea getPdeTextArea () {
344+ return (PdeTextArea ) textArea ;
99345 }
100346}
0 commit comments