KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > Display


1 /*
2  * Display.java
3  *
4  * Copyright (C) 1998-2004 Peter Graves
5  * $Id: Display.java,v 1.17 2004/04/01 18:48:42 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j;
23
24 import java.awt.Color JavaDoc;
25 import java.awt.Font JavaDoc;
26 import java.awt.FontMetrics JavaDoc;
27 import java.awt.Graphics2D JavaDoc;
28 import java.awt.Graphics JavaDoc;
29 import java.awt.Image JavaDoc;
30 import java.awt.Point JavaDoc;
31 import java.awt.Rectangle JavaDoc;
32 import java.awt.RenderingHints JavaDoc;
33 import java.awt.Toolkit JavaDoc;
34 import java.awt.event.ActionEvent JavaDoc;
35 import java.awt.event.ActionListener JavaDoc;
36 import java.awt.event.FocusEvent JavaDoc;
37 import java.awt.event.FocusListener JavaDoc;
38 import java.awt.event.MouseEvent JavaDoc;
39 import java.awt.font.GlyphVector JavaDoc;
40 import java.util.HashMap JavaDoc;
41 import javax.swing.JComponent JavaDoc;
42 import javax.swing.SwingUtilities JavaDoc;
43 import javax.swing.Timer JavaDoc;
44
45 public final class Display extends JComponent JavaDoc implements Constants,
46     ActionListener JavaDoc, FocusListener JavaDoc
47 {
48     private static final int MAX_LINE_NUMBER_CHARS = 6;
49
50     private static final Preferences preferences = Editor.preferences();
51
52     private static Font JavaDoc plainFont;
53     private static Font JavaDoc boldFont;
54     private static Font JavaDoc italicFont;
55
56     private static int charHeight;
57     private static int charDescent;
58     private static int charAscent;
59     private static int charLeading;
60     private static int charWidth;
61     private static int spaceWidth; // Width of a space character.
62
private static int minCharWidth;
63
64     private static boolean antialias;
65     private static boolean underlineBold;
66     private static boolean emulateBold;
67
68     private static int changeMarkWidth;
69
70     private static Font JavaDoc gutterFont;
71     private static int gutterCharAscent;
72     private static int gutterCharWidth;
73
74     private static boolean showGutterBorder = true;
75
76     private static int leftMargin;
77
78     private final HashMap JavaDoc changedLines = new HashMap JavaDoc();
79
80     private final Editor editor;
81
82     private Line topLine;
83     private int pixelsAboveTopLine;
84
85     // The offset of the first visible column of the display.
86
int shift = 0;
87
88     // The column containing the caret, relative to the first visible column
89
// of the display. The absolute column number will be different if we're
90
// horizontally scrolled (absolute column number = caretCol + shift).
91
int caretCol;
92
93     private char[] textArray;
94     private int[] formatArray;
95
96     private int updateFlag;
97
98     // These are set in initializePaint().
99
private boolean showChangeMarks;
100     private boolean enableChangeMarks;
101     private boolean showLineNumbers;
102     private int gutterWidth;
103     private Color JavaDoc changeColor;
104     private Color JavaDoc savedChangeColor;
105     private Color JavaDoc lineNumberColor;
106     private int verticalRuleX;
107     private Color JavaDoc verticalRuleColor;
108     private Color JavaDoc gutterBorderColor;
109     private boolean highlightBrackets;
110     private boolean highlightMatchingBracket;
111     private Position posBracket;
112     private Position posMatch;
113
114     private Timer JavaDoc timer;
115     private boolean caretVisible = true;
116
117     public Display(Editor editor)
118     {
119         this.editor = editor;
120         if (plainFont == null)
121             initializeStaticValues();
122         initialize();
123         setFocusTraversalKeysEnabled(false);
124         setToolTipText("");
125     }
126
127     public static void initializeStaticValues()
128     {
129         final String JavaDoc fontName = preferences.getStringProperty(Property.FONT_NAME);
130         final int fontSize = preferences.getIntegerProperty(Property.FONT_SIZE);
131
132         plainFont = new Font JavaDoc(fontName, Font.PLAIN, fontSize);
133         boldFont = new Font JavaDoc(fontName, Font.BOLD, fontSize);
134         if (preferences.getBooleanProperty(Property.ENABLE_ITALICS))
135             italicFont = new Font JavaDoc(fontName, Font.ITALIC, fontSize);
136         else
137             italicFont = plainFont;
138
139         FontMetrics JavaDoc fm = Toolkit.getDefaultToolkit().getFontMetrics(plainFont);
140
141         final int plainAscent = fm.getAscent();
142         final int plainDescent = fm.getDescent();
143         final int plainLeading = fm.getLeading();
144
145         charWidth = fm.charWidth('a');
146         spaceWidth = fm.charWidth(' ');
147         minCharWidth = getMinCharWidth(fm);
148
149         leftMargin = charWidth;
150
151         fm = Toolkit.getDefaultToolkit().getFontMetrics(boldFont);
152
153         final int boldAscent = fm.getAscent();
154         final int boldDescent = fm.getDescent();
155         final int boldLeading = fm.getLeading();
156
157         charAscent = plainAscent;
158         if (boldAscent > charAscent)
159             charAscent = boldAscent;
160
161         charDescent = plainDescent;
162         if (boldDescent > charDescent)
163             charDescent = boldDescent;
164
165         // Use no more than 1 pixel of leading.
166
charLeading = (plainLeading > 0 || boldLeading > 0) ? 1 : 0;
167
168         // Apply user-specified adjustments.
169
final int adjustAscent = preferences.getIntegerProperty(Property.ADJUST_ASCENT);
170         final int adjustDescent = preferences.getIntegerProperty(Property.ADJUST_DESCENT);
171         final int adjustLeading = preferences.getIntegerProperty(Property.ADJUST_LEADING);
172
173         if (charAscent + adjustAscent >= 0)
174             charAscent += adjustAscent;
175         if (charDescent + adjustDescent >= 0)
176             charDescent += adjustDescent;
177         if (charLeading + adjustLeading >= 0)
178             charLeading += adjustLeading;
179
180         charHeight = charAscent + charDescent + charLeading;
181
182         antialias = preferences.getBooleanProperty(Property.ANTIALIAS);
183         underlineBold = preferences.getBooleanProperty(Property.UNDERLINE_BOLD);
184         emulateBold = preferences.getBooleanProperty(Property.EMULATE_BOLD);
185
186         String JavaDoc gutterFontName = preferences.getStringProperty(Property.GUTTER_FONT_NAME);
187         if (gutterFontName == null)
188             gutterFontName = fontName;
189         int gutterFontSize = preferences.getIntegerProperty(Property.GUTTER_FONT_SIZE);
190         if (gutterFontSize == 0)
191             gutterFontSize = fontSize;
192         gutterFont = new Font JavaDoc(gutterFontName, Font.PLAIN, gutterFontSize);
193
194         fm = Toolkit.getDefaultToolkit().getFontMetrics(gutterFont);
195         gutterCharAscent = fm.getAscent();
196         gutterCharWidth = fm.charWidth('0');
197
198         changeMarkWidth =
199             preferences.getIntegerProperty(Property.CHANGE_MARK_WIDTH);
200     }
201
202     public synchronized void initialize()
203     {
204         // Explicitly set this to null here. We might be resetting the display.
205
paintLineImage = null;
206
207         // Allocate text and format arrays big enough to handle full screen
208
// width for narrowest character in font, plus some slack (runs of
209
// italics tend to get compressed). An extra 25% should be plenty.
210
int size = Toolkit.getDefaultToolkit().getScreenSize().width * 5 / (minCharWidth * 4);
211         textArray = new char[size];
212         formatArray = new int[size];
213
214         if (preferences.getBooleanProperty(Property.BLINK_CARET)) {
215             if (timer == null) {
216                 timer = new Timer JavaDoc(500, this);
217                 addFocusListener(this);
218             }
219         } else {
220             if (timer != null) {
221                 timer.stop();
222                 timer = null;
223                 removeFocusListener(this);
224                 setCaretVisible(true);
225             }
226         }
227     }
228
229     protected void finalize() throws Throwable JavaDoc
230     {
231         if (timer != null) {
232             timer.stop();
233             timer = null;
234         }
235     }
236
237     private static final int getMinCharWidth(FontMetrics JavaDoc fm)
238     {
239         int minWidth = Integer.MAX_VALUE;
240         int[] widths = fm.getWidths();
241         int limit = widths.length > 0x7e ? 0x7e : widths.length;
242         for (int i = limit; i >= 32; i--)
243             if (widths[i] != 0 && widths[i] < minWidth)
244                 minWidth = widths[i];
245         return minWidth;
246     }
247
248     public static final int getCharHeight()
249     {
250         return charHeight;
251     }
252
253     public static final int getCharWidth()
254     {
255         return charWidth;
256     }
257
258     public static final int getImageBorderHeight()
259     {
260         return 5;
261     }
262
263     public static final int getImageBorderWidth()
264     {
265         return 5;
266     }
267
268     public final Line getTopLine()
269     {
270         return topLine;
271     }
272
273     public final int getTopLineNumber()
274     {
275         return topLine.lineNumber();
276     }
277
278     public final void setTopLine(Line line)
279     {
280         topLine = line;
281         pixelsAboveTopLine = 0;
282     }
283
284     public final int getPixelsAboveTopLine()
285     {
286         return pixelsAboveTopLine;
287     }
288
289     public final void setPixelsAboveTopLine(int pixels)
290     {
291         pixelsAboveTopLine = pixels;
292     }
293
294     public final int getShift()
295     {
296         return shift;
297     }
298
299     public final void setShift(int n)
300     {
301         this.shift = n;
302     }
303
304     public final int getCaretCol()
305     {
306         return caretCol;
307     }
308
309     public final void setCaretCol(int n)
310     {
311         caretCol = n;
312     }
313
314     public int getAbsoluteCaretCol()
315     {
316         return caretCol + shift;
317     }
318
319     public void setAbsoluteCaretCol(int col)
320     {
321         caretCol = col - shift;
322     }
323
324     public final void repaintNow()
325     {
326         paintImmediately(0, 0, getWidth(), getHeight());
327     }
328
329     public synchronized void repaintChangedLines()
330     {
331         if (!Editor.displayReady())
332             return;
333         if ((updateFlag & REPAINT) == REPAINT) {
334             repaint();
335             return;
336         }
337         if (changedLines.isEmpty())
338             return;
339         final Buffer buffer = editor.getBuffer();
340         initializePaint();
341         try {
342             buffer.lockRead();
343         }
344         catch (InterruptedException JavaDoc e) {
345             Log.error(e);
346             return;
347         }
348         try {
349             Graphics2D JavaDoc g2d = (Graphics2D JavaDoc) getGraphics();
350             if (g2d != null) {
351                 Line line = topLine;
352                 int y = - pixelsAboveTopLine;
353                 final int limit = getHeight();
354                 while (line != null && y < limit) {
355                     if (changedLines.containsKey(line))
356                         paintLine(line, g2d, y);
357                     y += line.getHeight();
358                     line = line.nextVisible();
359                 }
360                 if (y < limit) {
361                     g2d.setColor(editor.getFormatter().getBackgroundColor());
362                     final int height = limit - y;
363                     g2d.fillRect(0, y, getWidth(), height);
364                     if (showLineNumbers)
365                         drawGutterBorder(g2d, y, height);
366                     drawVerticalRule(g2d, y, height);
367                 }
368                 drawCaret(g2d);
369             }
370         }
371         finally {
372             buffer.unlockRead();
373         }
374         changedLines.clear();
375     }
376
377     // Set caret column to be where dot is.
378
public void moveCaretToDotCol()
379     {
380         if (editor.getDot() == null)
381             return;
382         int absCol = editor.getDotCol();
383         if (caretCol != absCol - shift) {
384             caretCol = absCol - shift;
385             lineChanged(editor.getDotLine());
386         }
387         setUpdateFlag(REFRAME);
388     }
389
390     // Assumes line is after topLine.
391
private int getY(Line line)
392     {
393         int y = - pixelsAboveTopLine;
394         int limit = getHeight();
395         for (Line l = topLine; y < limit && l != null && l != line; l = l.nextVisible())
396             y += l.getHeight();
397         return y;
398     }
399
400     // Does NOT assume line is after topLine.
401
private int getAbsoluteY(Line line)
402     {
403         int y = 0;
404         for (Line l = editor.getBuffer().getFirstLine(); l != null && l != line; l = l.nextVisible())
405             y += l.getHeight();
406         return y;
407     }
408
409     private void initializePaint()
410     {
411         final Buffer buffer = editor.getBuffer();
412         final Mode mode = editor.getMode();
413         showChangeMarks = buffer.getBooleanProperty(Property.SHOW_CHANGE_MARKS);
414         enableChangeMarks = showChangeMarks && !buffer.isNewFile();
415         if (showChangeMarks) {
416             changeColor = mode.getColorProperty(Property.COLOR_CHANGE);
417             if (changeColor == null)
418                 changeColor = DefaultTheme.getColor("change");
419             savedChangeColor = mode.getColorProperty(Property.COLOR_SAVED_CHANGE);
420             if (savedChangeColor == null)
421                 savedChangeColor = DefaultTheme.getColor("savedChange");
422         }
423         showLineNumbers = buffer.getBooleanProperty(Property.SHOW_LINE_NUMBERS);
424         gutterWidth = getGutterWidth(showChangeMarks, showLineNumbers);
425         if (showLineNumbers) {
426             lineNumberColor = mode.getColorProperty(Property.COLOR_LINE_NUMBER);
427             if (lineNumberColor == null)
428                 lineNumberColor = DefaultTheme.getColor("lineNumber");
429             gutterBorderColor = mode.getColorProperty(Property.COLOR_GUTTER_BORDER);
430             if (gutterBorderColor == null)
431                 gutterBorderColor = DefaultTheme.getColor("gutterBorder");
432         }
433         int col = buffer.getIntegerProperty(Property.VERTICAL_RULE);
434         if (col != 0) {
435             verticalRuleX = gutterWidth + (col - shift) * charWidth - 1;
436             verticalRuleColor = mode.getColorProperty(Property.COLOR_VERTICAL_RULE);
437             if (verticalRuleColor == null)
438                 verticalRuleColor = DefaultTheme.getColor("verticalRule");
439         } else
440             verticalRuleX = 0;
441         highlightBrackets =
442             buffer.getBooleanProperty(Property.HIGHLIGHT_BRACKETS);
443         highlightMatchingBracket = highlightBrackets ||
444             buffer.getBooleanProperty(Property.HIGHLIGHT_MATCHING_BRACKET);
445
446         if (highlightMatchingBracket) {
447             Position oldPosMatch = posMatch;
448             posBracket = null;
449             posMatch = null;
450             if (editor.getDot() != null) {
451                 Position dot = editor.getDotCopy();
452                 char c = dot.getChar();
453                 if (c == '{' || c == '[' || c == '(') {
454                     posBracket = dot;
455                     posMatch = editor.findMatchInternal(dot, 200);
456                 } else if (dot.getOffset() > 0) {
457                     int end = editor.getBuffer().getCol(dot.getLine(),
458                         dot.getLine().length());
459                     if (shift + caretCol <= end) {
460                         dot.skip(-1);
461                         c = dot.getChar();
462                         if (c == '}' || c == ']' || c == ')') {
463                             posBracket = dot;
464                             posMatch = editor.findMatchInternal(dot, 200);
465                         }
466                     }
467                 }
468             }
469             if (oldPosMatch != null && oldPosMatch != posMatch)
470                 lineChanged(oldPosMatch.getLine());
471             if (posMatch != null && posMatch != oldPosMatch)
472                 lineChanged(posMatch.getLine());
473             if (!highlightBrackets)
474                 posBracket = null;
475         }
476     }
477
478     private void drawVerticalRule(Graphics JavaDoc g, int y, int height)
479     {
480         if (verticalRuleX > gutterWidth) {
481             g.setColor(verticalRuleColor);
482             g.drawLine(verticalRuleX, y, verticalRuleX, y + height);
483         }
484     }
485
486     private Position dragCaretPos;
487
488     public void setDragCaretPos(Position pos)
489     {
490         if (dragCaretPos != null)
491             lineChanged(dragCaretPos.getLine());
492         dragCaretPos = pos;
493         if (pos != null)
494             lineChanged(pos.getLine());
495     }
496
497     private int dragCaretCol;
498
499     public void setDragCaretCol(int col)
500     {
501         dragCaretCol = col;
502     }
503
504     // Called only from synchronized methods.
505
private void drawDragCaret()
506     {
507         if (dragCaretPos == null)
508             return;
509         if (topLine == null)
510             return;
511         final Line line = dragCaretPos.getLine();
512         if (line.lineNumber() < topLine.lineNumber())
513             return;
514         final int absCol;
515         if (editor.getBuffer().getBooleanProperty(Property.RESTRICT_CARET))
516             absCol = editor.getBuffer().getCol(dragCaretPos);
517         else
518             absCol = dragCaretCol; // We can go beyond the end of the line.
519
if (absCol < shift)
520             return;
521         final int col = absCol - shift;
522         formatLine(line, shift, col);
523         Graphics2D JavaDoc g2d = (Graphics2D JavaDoc) getGraphics();
524         final int x =
525             gutterWidth + measureLine(g2d, textArray, col, formatArray);
526         final int y = getY(line);
527         g2d.setColor(editor.getFormatter().getCaretColor());
528         g2d.fillRect(x, y, 1, charAscent + charDescent);
529     }
530
531     // Called only from synchronized methods.
532
private void drawCaret(Graphics2D JavaDoc g2d)
533     {
534         if (dragCaretPos != null) {
535             drawDragCaret();
536             return;
537         }
538
539         if (!caretVisible)
540             return;
541         if (topLine == null)
542             return;
543         if (editor != Editor.currentEditor())
544             return;
545         if (editor.getDot() == null)
546             return;
547         if (editor.getMark() != null && !editor.getMark().equals(editor.getDot()))
548             return;
549         if (caretCol < 0)
550             return;
551         if (!editor.getFrame().isActive())
552             return;
553         if (editor.getFrame().getFocusedComponent() != this)
554             return;
555         final Line dotLine = editor.getDotLine();
556         if (dotLine instanceof ImageLine)
557             return;
558         if (editor.getBuffer().needsRenumbering())
559             editor.getBuffer().renumber();
560         if (dotLine.lineNumber() < topLine.lineNumber())
561             return;
562
563         int x;
564         if (caretCol == 0)
565             x = gutterWidth;
566         else if (dotLine.length() == 0)
567             x = gutterWidth + caretCol * spaceWidth;
568         else {
569             formatLine(dotLine, shift, caretCol);
570             x = gutterWidth + measureLine(g2d, textArray, caretCol, formatArray);
571         }
572
573         if (x > getWidth())
574             return;
575
576         int y = getY(dotLine);
577         if (y >= getHeight())
578             return;
579
580         g2d.setColor(editor.getFormatter().getCaretColor());
581
582         // Caret width is 1 pixel.
583
g2d.fillRect(x, y, 1, charAscent + charDescent);
584     }
585
586     public synchronized void setCaretVisible(boolean b)
587     {
588         caretVisible = b;
589         if (b && timer != null && timer.isRunning())
590             timer.restart();
591     }
592
593     private synchronized void blinkCaret()
594     {
595         Position dot = editor.getDot();
596         if (dot != null) {
597             caretVisible = !caretVisible;
598             final Line line = dot.getLine();
599             Runnable JavaDoc r = new Runnable JavaDoc() {
600                 public void run()
601                 {
602                     repaintLine(line);
603                 }
604             };
605             SwingUtilities.invokeLater(r);
606         }
607     }
608
609     private synchronized void repaintLine(Line l)
610     {
611         if (!Editor.displayReady())
612             return;
613         if ((updateFlag & REPAINT) == REPAINT) {
614             repaint();
615             return;
616         }
617         final Buffer buffer = editor.getBuffer();
618         initializePaint();
619         try {
620             buffer.lockRead();
621         }
622         catch (InterruptedException JavaDoc e) {
623             Log.error(e);
624             return;
625         }
626         try {
627             Graphics2D JavaDoc g2d = (Graphics2D JavaDoc) getGraphics();
628             if (g2d != null) {
629                 Line line = topLine;
630                 int y = - pixelsAboveTopLine;
631                 final int limit = getHeight();
632                 while (line != null && y < limit) {
633                     if (line == l) {
634                         paintLine(line, g2d, y);
635                         break;
636                     }
637                     y += line.getHeight();
638                     line = line.nextVisible();
639                 }
640                 drawCaret(g2d);
641             }
642         }
643         finally {
644             buffer.unlockRead();
645         }
646     }
647
648     // Timer event handler.
649
public void actionPerformed(ActionEvent JavaDoc e)
650     {
651         blinkCaret();
652     }
653
654     public synchronized void focusGained(FocusEvent JavaDoc e)
655     {
656         if (timer != null)
657             timer.start();
658     }
659
660     public synchronized void focusLost(FocusEvent JavaDoc e)
661     {
662         if (timer != null)
663             timer.stop();
664     }
665
666     private int formatLine(final Line line, final int begin, final int maxCols)
667     {
668         // Avoid getfield overhead.
669
final int[] fa = formatArray;
670         final char[] ta = textArray;
671         final int taLength = ta.length;
672         Debug.assertTrue(taLength == fa.length);
673         for (int i = taLength; i-- > 0;) {
674             ta[i] = ' ';
675             fa[i] = 0;
676         }
677         final int limit = Math.min(maxCols, taLength);
678         final LineSegmentList segmentList = editor.getFormatter().formatLine(line);
679         int segmentStart = 0;
680         int totalChars = 0;
681         final int size = segmentList.size();
682         for (int i = 0; i < size; i++) {
683             final LineSegment segment = segmentList.getSegment(i);
684             final int segmentLength = segment.length();
685             if (segmentStart + segmentLength < begin) {
686                 segmentStart += segmentLength;
687                 continue;
688             }
689             final int maxAppend = limit - totalChars;
690             if (segmentStart >= begin) {
691                 if (segmentLength <= maxAppend) {
692                     segment.getChars(0, segmentLength, ta, totalChars);
693                     totalChars += segmentLength;
694                 } else if (maxAppend > 0) {
695                     segment.getChars(0, maxAppend, ta, totalChars);
696                     totalChars += maxAppend;
697                 }
698             } else {
699                 // segmentStart < begin && segmentStart + segmentLength >= begin
700
String JavaDoc text = segment.substring(begin - segmentStart);
701                 if (text.length() <= maxAppend) {
702                     text.getChars(0, text.length(), ta, totalChars);
703                     totalChars += text.length();
704                 } else if (maxAppend > 0) {
705                     text.getChars(0, maxAppend, ta, totalChars);
706                     totalChars += maxAppend;
707                 }
708             }
709             final int format = segment.getFormat();
710             int k = segmentStart - begin;
711             if (k > limit)
712                 break;
713             for (int j = 0; j < segmentLength; j++, k++) {
714                 if (k >= 0 && k < limit)
715                     fa[k] = format;
716             }
717             segmentStart += segmentLength;
718         }
719         return totalChars;
720     }
721
722     private Image JavaDoc paintLineImage;
723     private int paintLineImageWidth;
724     private int paintLineImageHeight;
725     private Graphics2D JavaDoc paintLineGraphics;
726
727     private final void providePaintLineImage(int width, int height)
728     {
729         if (paintLineImage != null &&
730             paintLineImageWidth == width &&
731             paintLineImageHeight == height)
732             return;
733
734         // Otherwise...
735
paintLineImage = createImage(width, height);
736         paintLineImageWidth = width;
737         paintLineImageHeight = height;
738         paintLineGraphics = (Graphics2D JavaDoc) paintLineImage.getGraphics();
739
740         if (antialias) {
741             paintLineGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
742                                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
743         }
744     }
745
746     private final void paintLine(Line line, Graphics2D JavaDoc g2d, int y)
747     {
748         if (line instanceof ImageLine)
749             paintImageLine((ImageLine)line, g2d, y);
750         else
751             paintTextLine(line, g2d, y);
752     }
753
754     private synchronized void paintTextLine(Line line, Graphics JavaDoc g, int y)
755     {
756         int displayWidth = getWidth();
757         int maxCols = getMaxCols();
758
759         providePaintLineImage(displayWidth, charHeight);
760
761         Color JavaDoc backgroundColor;
762         if (line == getCurrentLine())
763             backgroundColor = editor.getFormatter().getCurrentLineBackgroundColor();
764         else
765             backgroundColor = editor.getFormatter().getBackgroundColor();
766         drawBackgroundForLine(paintLineGraphics, backgroundColor, line, 0);
767
768         int totalChars = formatLine(line, shift, maxCols);
769
770         if (editor.getMark() != null) {
771             // Selection.
772
Region r = new Region(editor);
773             handleSelection(r, line, formatArray, paintLineGraphics, 0);
774         } else if (posMatch != null) {
775             if (posMatch.getLine() == line)
776                 highlightBracket(posMatch, line, formatArray,
777                     paintLineGraphics, 0);
778             if (posBracket != null && posBracket.getLine() == line)
779                 highlightBracket(posBracket, line, formatArray,
780                     paintLineGraphics, 0);
781         }
782
783         drawGutterText(paintLineGraphics, line, 0);
784         if (showLineNumbers && editor.getDot() != null)
785             drawGutterBorder(paintLineGraphics, 0, line.getHeight());
786         drawVerticalRule(paintLineGraphics, 0, line.getHeight());
787         drawText(paintLineGraphics, textArray, totalChars, formatArray, 0);
788         changedLines.remove(line);
789
790         g.drawImage(paintLineImage, 0, y, null);
791     }
792
793     private void paintImageLine(ImageLine imageLine, Graphics JavaDoc g, int y)
794     {
795         final int displayWidth = getWidth();
796         final int lineHeight = imageLine.getHeight();
797         final int imageWidth = imageLine.getImageWidth();
798         final int imageHeight = imageLine.getImageHeight();
799         final int x = gutterWidth - shift * charWidth;
800
801         Color JavaDoc backgroundColor;
802         if (imageLine == getCurrentLine())
803             backgroundColor = editor.getFormatter().getCurrentLineBackgroundColor();
804         else
805             backgroundColor = editor.getFormatter().getBackgroundColor();
806
807         g.setColor(backgroundColor);
808         // Left.
809
g.fillRect(0, y, x, lineHeight);
810         // Right.
811
g.fillRect(x + imageWidth, y, displayWidth - (x + imageWidth), lineHeight);
812         // Bottom.
813
if (imageHeight < lineHeight)
814             g.fillRect(0, y + imageHeight, displayWidth,
815                 lineHeight - imageHeight);
816
817         Rectangle JavaDoc rect = imageLine.getRect();
818         g.drawImage(imageLine.getImage(),
819             x, y, x + rect.width, y + rect.height,
820             rect.x, rect.y, rect.x + rect.width, rect.y + rect.height,
821             null);
822     }
823
824     private void drawBackgroundForLine(Graphics2D JavaDoc g2d, Color JavaDoc backgroundColor,
825         Line line, int y)
826     {
827         if (enableChangeMarks && line.isModified()) {
828             g2d.setColor(line.isSaved() ? savedChangeColor : changeColor);
829             g2d.fillRect(0, y, changeMarkWidth, line.getHeight());
830             g2d.setColor(backgroundColor);
831             g2d.fillRect(changeMarkWidth, y, getWidth() - changeMarkWidth, line.getHeight());
832         } else {
833             g2d.setColor(backgroundColor);
834             g2d.fillRect(0, y, getWidth(), line.getHeight());
835         }
836     }
837
838     private void drawGutterText(Graphics JavaDoc g, Line line, int y)
839     {
840         int x = showChangeMarks ? changeMarkWidth : 0;
841         char c = 0;
842         Annotation annotation = line.getAnnotation();
843         if (annotation != null)
844             c = annotation.getGutterChar();
845         else if (line.next() != null && line.next().isHidden())
846             c = '+';
847         if (c != 0) {
848             char[] chars = new char[1];
849             chars[0] = c;
850             g.setColor(editor.getFormatter().getColor(0)); // Default text color.
851
g.setFont(plainFont);
852             g.drawChars(chars, 0, 1, x, y + charAscent);
853         }
854         x += charWidth;
855         if (showLineNumbers) {
856             final String JavaDoc s = String.valueOf(line.lineNumber() + 1);
857             final int pad = MAX_LINE_NUMBER_CHARS - s.length();
858             if (pad >= 0) {
859                 x += pad * gutterCharWidth;
860                 g.setColor(lineNumberColor);
861                 g.setFont(gutterFont);
862                 g.drawString(s, x, y + charAscent);
863             }
864         }
865     }
866
867     private void drawGutterBorder(Graphics JavaDoc g)
868     {
869         int x = getGutterWidth(editor.getBuffer()) - 4;
870         g.setColor(gutterBorderColor);
871         g.drawLine(x, 0, x, getHeight());
872     }
873
874     private void drawGutterBorder(Graphics JavaDoc g, int y, int height)
875     {
876         int x = getGutterWidth(editor.getBuffer()) - 4;
877         g.setColor(gutterBorderColor);
878         g.drawLine(x, y, x, y + height);
879     }
880
881     // Returns width in pixels.
882
public static final int getGutterWidth(Buffer buffer)
883     {
884         return getGutterWidth(buffer.getBooleanProperty(Property.SHOW_CHANGE_MARKS),
885             buffer.getBooleanProperty(Property.SHOW_LINE_NUMBERS));
886     }
887
888     // Returns width in pixels.
889
private static final int getGutterWidth(boolean showChangeMarks,
890         boolean showLineNumbers)
891     {
892         int width = charWidth;
893         if (showChangeMarks)
894             width += changeMarkWidth;
895         if (showLineNumbers)
896             width += MAX_LINE_NUMBER_CHARS * gutterCharWidth + 5;
897         return width;
898     }
899
900     private void drawText(Graphics2D JavaDoc g2d, char[] textArray, int length,
901         int[] formatArray, int y)
902     {
903         int i = 0;
904         double x = gutterWidth;
905         final Formatter formatter = editor.getFormatter();
906         while (i < length) {
907             int format = formatArray[i];
908             int start = i;
909             while (formatArray[i] == format && i < length)
910                 ++i;
911             int style;
912             FormatTableEntry entry = formatter.getFormatTableEntry(format);
913             if (entry != null) {
914                 g2d.setColor(entry.getColor());
915                 style = entry.getStyle();
916             } else {
917                 // Web mode.
918
g2d.setColor(formatter.getColor(format));
919                 style = formatter.getStyle(format);
920             }
921             Font JavaDoc font;
922             switch (style) {
923                 case Font.BOLD:
924                     font = boldFont;
925                     break;
926                 case Font.ITALIC:
927                     font = italicFont;
928                     break;
929                 case Font.PLAIN:
930                 default:
931                     font = plainFont;
932                     break;
933             }
934             char[] chars = new char[i - start];
935             System.arraycopy(textArray, start, chars, 0, i - start);
936             GlyphVector JavaDoc gv = font.createGlyphVector(g2d.getFontRenderContext(), chars);
937             final double width = gv.getLogicalBounds().getWidth();
938             if (style == Font.BOLD) {
939                 if (boldFont == plainFont) {
940                     if (underlineBold)
941                         g2d.drawLine((int)x, y + charAscent + 1, (int)(x + width), y + charAscent + 1);
942                     else
943                         g2d.drawGlyphVector(gv, (int)x + 1, y + charAscent);
944                 } else if (emulateBold)
945                     g2d.drawGlyphVector(gv, (float)x + 1, y + charAscent);
946             }
947             if (formatter.getUnderline(format))
948                 g2d.drawLine((int)x, y + charAscent + 1, (int)(x + width), y + charAscent + 1);
949             g2d.drawGlyphVector(gv, (float)x, y + charAscent);
950             x += width;
951         }
952     }
953
954     private int measureLine(Graphics2D JavaDoc g2d, char[] textArray, int length, int[] formatArray)
955     {
956         if (length == 0)
957             return 0;
958         final int limit = Math.min(length, textArray.length);
959         double totalWidth = 0;
960         int i = 0;
961         Formatter formatter = editor.getFormatter();
962         while (i < limit) {
963             int format = formatArray[i];
964             int startCol = i;
965             while (i < limit && formatArray[i] == format)
966                 ++i;
967             Font JavaDoc font;
968             switch (formatter.getStyle(format)) {
969                 case Font.BOLD:
970                     font = boldFont;
971                     break;
972                 case Font.ITALIC:
973                     font = italicFont;
974                     break;
975                 case Font.PLAIN:
976                 default:
977                     font = plainFont;
978                     break;
979             }
980             char[] chars = new char[i - startCol];
981             System.arraycopy(textArray, startCol, chars, 0, i - startCol);
982             GlyphVector JavaDoc gv = font.createGlyphVector(g2d.getFontRenderContext(), chars);
983             totalWidth += gv.getLogicalBounds().getWidth();
984         }
985         return (int) totalWidth;
986     }
987
988     public void paintComponent(Graphics JavaDoc g)
989     {
990         final Buffer buffer = editor.getBuffer();
991         if (!Editor.displayReady()) {
992             if (buffer != null && buffer.getModeId() == IMAGE_MODE)
993                 g.setColor(ImageBuffer.getDefaultBackgroundColor());
994             else
995                 g.setColor(editor.getFormatter().getBackgroundColor());
996             g.fillRect(0, 0, getWidth(), getHeight());
997             return;
998         }
999         try {
1000            buffer.lockRead();
1001        }
1002        catch (InterruptedException JavaDoc e) {
1003            Log.error(e);
1004            return;
1005        }
1006        try {
1007            if (buffer.getModeId() == IMAGE_MODE)
1008                paintImage(g);
1009            else
1010                paintComponentInternal(g);
1011        }
1012        finally {
1013            buffer.unlockRead();
1014        }
1015    }
1016
1017    private void paintImage(Graphics JavaDoc g)
1018    {
1019        ImageBuffer ib = (ImageBuffer) editor.getBuffer();
1020        g.setColor(ib.getBackgroundColor());
1021        g.fillRect(0, 0, getWidth(), getHeight());
1022        Image JavaDoc image = ib.getImage();
1023        if (image != null) {
1024            int imageWidth = image.getWidth(null);
1025            int imageHeight = image.getHeight(null);
1026            int x = 0;
1027            int y = 0;
1028            if (imageWidth > 0 && imageHeight > 0) {
1029                if (imageWidth < getWidth())
1030                    x = (getWidth() - imageWidth) / 2;
1031                if (imageHeight < getHeight())
1032                    y = (getHeight() - imageHeight) / 2;
1033            }
1034            if (x < getImageBorderWidth())
1035                x = getImageBorderWidth();
1036            if (y < getImageBorderHeight())
1037                y = getImageBorderHeight();
1038            g.drawImage(image, x - shift * charWidth, y - pixelsAboveTopLine, this);
1039        }
1040    }
1041
1042    private synchronized void paintComponentInternal(Graphics JavaDoc g)
1043    {
1044        initializePaint();
1045        Graphics2D JavaDoc g2d = (Graphics2D JavaDoc) g;
1046        if (antialias) {
1047            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
1048                                 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1049        }
1050        final Rectangle JavaDoc clipBounds = g2d.getClipBounds();
1051        final int displayWidth = getWidth();
1052        final int maxCols = getMaxCols();
1053
1054        // Selection.
1055
final Region r = editor.getMark() != null ? new Region(editor) : null;
1056
1057        // Current line.
1058
final Line currentLine = getCurrentLine();
1059
1060        final Color JavaDoc colorBackground = editor.getFormatter().getBackgroundColor();
1061        int y = - pixelsAboveTopLine;
1062        Line line = topLine;
1063        while (line != null && y + line.getHeight() < clipBounds.y) {
1064            y += line.getHeight();
1065            line = line.nextVisible();
1066        }
1067        final int limit = clipBounds.y + clipBounds.height;
1068        while (line != null && y < limit) {
1069            if (line instanceof ImageLine) {
1070                paintImageLine((ImageLine)line, g2d, y);
1071            } else {
1072                Color JavaDoc backgroundColor;
1073                if (line == currentLine) {
1074                    backgroundColor =
1075                        editor.getFormatter().getCurrentLineBackgroundColor();
1076                } else
1077                    backgroundColor = colorBackground;
1078                drawBackgroundForLine(g2d, backgroundColor, line, y);
1079                int totalChars = formatLine(line, shift, maxCols);
1080                if (r != null)
1081                    handleSelection(r, line, formatArray, g2d, y);
1082                else if (posMatch != null) {
1083                    if (posMatch.getLine() == line)
1084                        highlightBracket(posMatch, line, formatArray, g2d, y);
1085                    if (posBracket != null && posBracket.getLine() == line)
1086                        highlightBracket(posBracket, line, formatArray, g2d, y);
1087                }
1088                drawGutterText(g2d, line, y);
1089                if (totalChars > 0) {
1090                    // Draw vertical rule first so it will be behind the text.
1091
drawVerticalRule(g2d, y, line.getHeight());
1092                    drawText(g2d, textArray, totalChars, formatArray, y);
1093                } else
1094                    drawVerticalRule(g2d, y, line.getHeight());
1095                changedLines.remove(line);
1096            }
1097            y += line.getHeight();
1098            line = line.nextVisible();
1099        }
1100        if (y < limit) {
1101            g2d.setColor(colorBackground);
1102            g2d.fillRect(0, y, displayWidth, limit - y);
1103            drawVerticalRule(g2d, y, limit - y);
1104        }
1105        drawCaret(g2d);
1106        if (showLineNumbers && editor.getDot() != null)
1107            drawGutterBorder(g2d);
1108        updateFlag &= ~REPAINT;
1109    }
1110
1111    private Line getCurrentLine()
1112    {
1113        if (editor.getDot() != null && editor.getMark() == null)
1114            return editor.getDotLine();
1115        return null;
1116    }
1117
1118    private void handleSelection(Region r, Line line, int[] formatArray,
1119        Graphics2D JavaDoc g2d, int y)
1120    {
1121        if (r == null)
1122            return;
1123
1124        int maxCols = getMaxCols();
1125        int fillHeight = charHeight;
1126        int fillWidth = 0;
1127        int beginCol = 0;
1128        int endCol = 0;
1129        int x = 0;
1130
1131        if (r.isColumnRegion()) {
1132            if (line.lineNumber() >= r.getBeginLineNumber() &&
1133                line.lineNumber() <= r.getEndLineNumber()) {
1134                beginCol = r.getBeginCol() - shift;
1135                endCol = r.getEndCol() - shift;
1136
1137                if (beginCol < 0)
1138                    beginCol = 0;
1139                if (endCol < 0)
1140                    endCol = 0;
1141                if (endCol > maxCols)
1142                    endCol = maxCols;
1143
1144                int x1 = measureLine(g2d, textArray, beginCol, formatArray);
1145                int x2 = measureLine(g2d, textArray, endCol, formatArray);
1146                fillWidth = x2 - x1;
1147                x = gutterWidth + x1;
1148            }
1149        } else if (line == r.getBeginLine()) {
1150            beginCol = r.getBeginCol() - shift;
1151            if (line == r.getEndLine())
1152                endCol = r.getEndCol() - shift;
1153
1154            if (beginCol < 0)
1155                beginCol = 0;
1156            if (endCol < 0)
1157                endCol = 0;
1158            if (endCol > maxCols)
1159                endCol = maxCols;
1160
1161            if (line == r.getEndLine()) {
1162                int x1 = measureLine(g2d, textArray, beginCol, formatArray);
1163                int x2 = measureLine(g2d, textArray, endCol, formatArray);
1164                fillWidth = x2 - x1;
1165                x = gutterWidth + x1;
1166            } else {
1167                fillWidth = getWidth();
1168                x = gutterWidth + measureLine(g2d, textArray, beginCol, formatArray);
1169            }
1170        } else if (line.lineNumber() > r.getBeginLineNumber() &&
1171            line.lineNumber() < r.getEndLineNumber()) {
1172            // Entire line is selected.
1173
fillWidth = getWidth();
1174            x = gutterWidth;
1175        } else if (line == r.getEndLine()) {
1176            // Last line of selection that spans more than one line.
1177
endCol = r.getEndCol() - shift;
1178
1179            if (endCol < 0)
1180                endCol = 0;
1181            if (endCol > maxCols)
1182                endCol = maxCols;
1183
1184            int x1 = measureLine(g2d, textArray, beginCol, formatArray);
1185            int x2 = measureLine(g2d, textArray, endCol, formatArray);
1186            fillWidth = x2 - x1;
1187            x = gutterWidth + x1;
1188        }
1189
1190        if (fillWidth > 0) {
1191            g2d.setColor(editor.getFormatter().getSelectionBackgroundColor());
1192            g2d.fillRect(x, y, fillWidth, charHeight);
1193        }
1194    }
1195
1196    private void highlightBracket(Position pos, Line line, int[] formatArray,
1197        Graphics2D JavaDoc g2d, int y)
1198    {
1199        if (pos == null) {
1200            Debug.bug();
1201            return;
1202        }
1203        if (pos.getLine() != line) {
1204            Debug.bug();
1205            return;
1206        }
1207        if (editor.getFrame().getFocusedComponent() != this)
1208            return;
1209        int beginCol = editor.getBuffer().getCol(pos) - shift;
1210        if (beginCol < 0)
1211            return;
1212        int endCol = beginCol + 1;
1213        int x1 = measureLine(g2d, textArray, beginCol, formatArray);
1214        int x2 = measureLine(g2d, textArray, endCol, formatArray);
1215        int fillWidth = x2 - x1;
1216        int x = gutterWidth + x1;
1217        if (fillWidth > 0) {
1218            g2d.setColor(editor.getFormatter().getMatchingBracketBackgroundColor());
1219            g2d.fillRect(x, y, fillWidth, charHeight);
1220        }
1221    }
1222
1223    // Scroll up in the buffer, moving the content in the window down.
1224
private void scrollUp()
1225    {
1226        if (editor.getModeId() == IMAGE_MODE) {
1227            if (pixelsAboveTopLine >= charHeight) {
1228                pixelsAboveTopLine -= charHeight;
1229                repaint();
1230            }
1231            return;
1232        }
1233
1234        if (topLine != null) {
1235            if (topLine instanceof ImageLine) {
1236                if (pixelsAboveTopLine - charHeight >= 0) {
1237                    pixelsAboveTopLine -= charHeight;
1238                    scrollPixelsUp(charHeight);
1239                    Rectangle JavaDoc r = new Rectangle JavaDoc(0, 0, getWidth(), charHeight);
1240                    paintImmediately(r);
1241                    return;
1242                }
1243            }
1244            Line line = topLine.previousVisible();
1245            if (line == null)
1246                return;
1247            if (line instanceof ImageLine) {
1248                scrollPixelsUp(charHeight);
1249                topLine = line;
1250                pixelsAboveTopLine = line.getHeight() - charHeight;
1251                editor.update(topLine);
1252                return;
1253            }
1254            scrollPixelsUp(charHeight);
1255            setTopLine(line);
1256            editor.update(topLine);
1257        }
1258    }
1259
1260    // Scroll down in the buffer, moving the content in the window up.
1261
private void scrollDown()
1262    {
1263        if (editor.getModeId() == IMAGE_MODE) {
1264            pixelsAboveTopLine += charHeight;
1265            repaint();
1266            return;
1267        }
1268        if (topLine != null) {
1269            if (topLine instanceof ImageLine) {
1270                if (pixelsAboveTopLine + charHeight < topLine.getHeight()) {
1271                    pixelsAboveTopLine += charHeight;
1272                    scrollPixelsDown(charHeight);
1273                    Rectangle JavaDoc r = new Rectangle JavaDoc();
1274                    r.x = 0;
1275                    r.y = getHeight() - charHeight;
1276                    r.width = getWidth();
1277                    r.height = charHeight;
1278                    paintImmediately(r);
1279                    return;
1280                }
1281            }
1282            Line line = topLine.nextVisible();
1283            if (line != null) {
1284                scrollPixelsDown(charHeight);
1285                setTopLine(line);
1286                Line bottomLine = getBottomLine();
1287                editor.update(bottomLine);
1288                Line nextVisible = bottomLine.nextVisible();
1289                if (nextVisible != null)
1290                    editor.update(nextVisible);
1291            }
1292        }
1293    }
1294
1295    // Move content down.
1296
private void scrollPixelsUp(int dy)
1297    {
1298        Point JavaDoc pt1 = editor.getFrame().getLocationOnScreen();
1299        Point JavaDoc pt2 = getLocationOnScreen();
1300        int x = pt2.x - pt1.x;
1301        int y = pt2.y - pt1.y;
1302        editor.getFrame().getGraphics().copyArea(x, y, getWidth(),
1303            getHeight() - dy, 0, dy);
1304    }
1305
1306    // Move content up.
1307
private void scrollPixelsDown(int dy)
1308    {
1309        Point JavaDoc pt1 = editor.getFrame().getLocationOnScreen();
1310        Point JavaDoc pt2 = getLocationOnScreen();
1311        int x = pt2.x - pt1.x;
1312        int y = pt2.y - pt1.y + dy;
1313        editor.getFrame().getGraphics().copyArea(x, y, getWidth(),
1314            getHeight() - dy, 0, - dy);
1315    }
1316
1317    public Line getBottomLine()
1318    {
1319        Line line = topLine;
1320        int y = line.getHeight() - pixelsAboveTopLine;
1321        final int limit = getHeight();
1322        while (true) {
1323            Line next = line.nextVisible();
1324            if (next == null)
1325                break;
1326            y += next.getHeight();
1327            if (y > limit)
1328                break;
1329            line = next;
1330        }
1331        return line;
1332    }
1333
1334    public void up(boolean select)
1335    {
1336        if (editor.getDot() == null)
1337            return;
1338
1339        if (select) {
1340            if (editor.getMark() == null)
1341                editor.addUndo(SimpleEdit.MOVE);
1342            else if (editor.getLastCommand() != COMMAND_UP)
1343                editor.addUndo(SimpleEdit.MOVE);
1344        } else {
1345            if (editor.getMark() != null) {
1346                boolean isLineBlock =
1347                    (editor.getDotOffset() == 0 && editor.getMarkOffset() == 0);
1348                editor.addUndo(SimpleEdit.MOVE);
1349                editor.beginningOfBlock();
1350                editor.setGoalColumn(editor.getDotCol());
1351                if (isLineBlock)
1352                    return;
1353            } else if (editor.getLastCommand() != COMMAND_UP)
1354                editor.addUndo(SimpleEdit.MOVE);
1355        }
1356
1357        Line dotLine = editor.getDotLine();
1358        Line prevLine = dotLine.previousVisible();
1359        if (prevLine == null)
1360            return;
1361        if (dotLine == topLine) {
1362            // Need to scroll.
1363
windowUp();
1364        }
1365        editor.updateDotLine();
1366        final Buffer buffer = editor.getBuffer();
1367        boolean selectLine = false;
1368        if (select && editor.getMark() == null) {
1369            Position savedDot = null;
1370            if (buffer.getBooleanProperty(Property.AUTO_SELECT_LINE)) {
1371                Line nextLine = editor.getDotLine().next();
1372                if (nextLine != null) {
1373                    selectLine = true;
1374                    savedDot = new Position(editor.getDot());
1375                    editor.getDot().moveTo(nextLine, 0);
1376                    caretCol = 0;
1377                }
1378            }
1379            if (selectLine) {
1380                // Make sure absMarkCol will be correct.
1381
int savedShift = shift;
1382                shift = 0;
1383                editor.setMarkAtDot();
1384                shift = savedShift;
1385            } else
1386                editor.setMarkAtDot();
1387            if (savedDot != null)
1388                editor.getSelection().setSavedDot(savedDot);
1389        }
1390        editor.getDot().setLine(prevLine);
1391        editor.updateDotLine();
1392        if (selectLine)
1393            editor.setGoalColumn(0);
1394        editor.moveDotToGoalCol();
1395    }
1396
1397    public void down(boolean select)
1398    {
1399        if (editor.getDot() == null)
1400            return;
1401        if (select) {
1402            if (editor.getMark() == null)
1403                editor.addUndo(SimpleEdit.MOVE);
1404            else if (editor.getLastCommand() != COMMAND_DOWN)
1405                editor.addUndo(SimpleEdit.MOVE);
1406        } else {
1407            if (editor.getMark() != null) {
1408                boolean isLineBlock =
1409                    (editor.getDotOffset() == 0 && editor.getMarkOffset() == 0);
1410                editor.addUndo(SimpleEdit.MOVE);
1411                editor.endOfBlock();
1412                editor.setGoalColumn(editor.getDotCol());
1413                if (isLineBlock)
1414                    return;
1415            }
1416            else if (editor.getLastCommand() != COMMAND_DOWN)
1417                editor.addUndo(SimpleEdit.MOVE);
1418        }
1419        final Line dotLine = editor.getDotLine();
1420        final Line nextLine = dotLine.nextVisible();
1421        if (nextLine == null)
1422            return;
1423        if (getY(nextLine) + nextLine.getHeight() > getHeight()) {
1424            // Need to scroll.
1425
windowDown();
1426        }
1427        editor.updateDotLine();
1428        final Buffer buffer = editor.getBuffer();
1429        boolean selectLine = false;
1430        if (select && editor.getMark() == null) {
1431            Position savedDot = null;
1432            if (buffer.getBooleanProperty(Property.AUTO_SELECT_LINE)) {
1433                selectLine = true;
1434                savedDot = new Position(editor.getDot());
1435                editor.getDot().setOffset(0);
1436                caretCol = 0;
1437            }
1438            if (selectLine) {
1439                // Make sure absMarkCol will be correct.
1440
int savedShift = shift;
1441                shift = 0;
1442                editor.setMarkAtDot();
1443                shift = savedShift;
1444            } else
1445                editor.setMarkAtDot();
1446            if (savedDot != null)
1447                editor.getSelection().setSavedDot(savedDot);
1448        }
1449        editor.getDot().setLine(nextLine);
1450        editor.updateDotLine();
1451        if (selectLine)
1452            editor.setGoalColumn(0);
1453        editor.moveDotToGoalCol();
1454    }
1455
1456    public void windowUp()
1457    {
1458        if (getHeight() < editor.getBuffer().getDisplayHeight())
1459            scrollUp();
1460    }
1461
1462    public void windowDown()
1463    {
1464        final int totalHeight = editor.getBuffer().getDisplayHeight();
1465        final int windowHeight = getHeight();
1466        if (windowHeight < totalHeight) {
1467            // Add up cumulative height to bottom of window.
1468
int y;
1469            if (topLine != null)
1470                y = editor.getBuffer().getY(topLine) + pixelsAboveTopLine + windowHeight;
1471            else
1472                y = pixelsAboveTopLine + windowHeight;
1473            if (y < totalHeight)
1474                scrollDown();
1475        }
1476    }
1477
1478    public void windowUp(int lines)
1479    {
1480        Line line = topLine;
1481        if (line == null) {
1482            if (editor.getModeId() == IMAGE_MODE) {
1483                pixelsAboveTopLine -= lines * charHeight;
1484                if (pixelsAboveTopLine < 0)
1485                    pixelsAboveTopLine = 0;
1486                repaint();
1487            }
1488            return;
1489        }
1490        if (line instanceof ImageLine) {
1491            imageLineWindowUp(lines);
1492            return;
1493        }
1494        int actual = 0;
1495        for (int i = 0; i < lines; i++) {
1496            Line prev = line.previousVisible();
1497            if (prev == null)
1498                break;
1499            if (prev instanceof ImageLine) {
1500                imageLineWindowUp(lines);
1501                return;
1502            }
1503            line = prev;
1504            lineChanged(line);
1505            ++actual;
1506        }
1507        scrollPixelsUp(actual * charHeight);
1508        setTopLine(line);
1509        editor.maybeScrollCaret();
1510        editor.updateDisplay();
1511    }
1512
1513    private void imageLineWindowUp(int lines)
1514    {
1515        int oldY = getAbsoluteY(topLine) + pixelsAboveTopLine;
1516        int newY = oldY - lines * charHeight;
1517        if (newY < 0)
1518            newY = 0;
1519        Line line = lineFromAbsoluteY(newY);
1520        if (line != null) {
1521            int y = getAbsoluteY(line);
1522            topLine = line;
1523            pixelsAboveTopLine = newY - y;
1524            setUpdateFlag(REPAINT);
1525            editor.updateDisplay();
1526        }
1527    }
1528
1529    public void windowDown(final int lines)
1530    {
1531        Line top = topLine;
1532        if (top == null) {
1533            if (editor.getModeId() == IMAGE_MODE) {
1534                int windowHeight = getHeight();
1535                int bufferHeight = editor.getBuffer().getDisplayHeight();
1536                pixelsAboveTopLine += lines * charHeight;
1537                if (pixelsAboveTopLine + windowHeight > bufferHeight)
1538                    pixelsAboveTopLine = bufferHeight - windowHeight;
1539                repaint();
1540            }
1541            return;
1542        }
1543        if (top instanceof ImageLine) {
1544            imageLineWindowDown(lines);
1545            return;
1546        }
1547        int actual = 0;
1548        Line bottom = getBottomLine();
1549        for (int i = 0; i < lines; i++) {
1550            bottom = bottom.nextVisible();
1551            if (bottom == null)
1552                break;
1553            lineChanged(bottom);
1554            Line next = top.nextVisible();
1555            if (next instanceof ImageLine) {
1556                imageLineWindowDown(lines);
1557                return;
1558            }
1559            if (next == null)
1560                break;
1561            top = next;
1562            ++actual;
1563        }
1564        if (bottom != null) {
1565            bottom = bottom.nextVisible();
1566            if (bottom != null)
1567                lineChanged(bottom);
1568        }
1569        scrollPixelsDown(actual * charHeight);
1570        setTopLine(top);
1571        editor.maybeScrollCaret();
1572        editor.updateDisplay();
1573    }
1574
1575    private void imageLineWindowDown(int lines)
1576    {
1577        Line top = topLine;
1578        int oldY = getAbsoluteY(top) + pixelsAboveTopLine;
1579        int newY = oldY + lines * charHeight;
1580        int windowHeight = getHeight();
1581        int bufferHeight = editor.getBuffer().getDisplayHeight();
1582        if (newY + windowHeight > bufferHeight)
1583            newY = bufferHeight - windowHeight;
1584        if (newY == oldY)
1585            return;
1586        Line line = lineFromAbsoluteY(newY);
1587        if (line != null) {
1588            int y = getAbsoluteY(line);
1589            topLine = line;
1590            pixelsAboveTopLine = newY - y;
1591            setUpdateFlag(REPAINT);
1592            editor.updateDisplay();
1593        }
1594    }
1595
1596    public void setUpdateFlag(int mask)
1597    {
1598        updateFlag |= mask;
1599    }
1600
1601    private int reframeParam = 0;
1602
1603    public void setReframe(int n)
1604    {
1605        reframeParam = n;
1606    }
1607
1608    public void reframe()
1609    {
1610        if (!Editor.displayReady())
1611            return;
1612        if (editor.getDot() == null)
1613            return;
1614        if (getHeight() == 0)
1615            return; // Not visible yet.
1616
if (topLine == null || (updateFlag & REFRAME) != 0) {
1617            final Buffer buffer = editor.getBuffer();
1618            try {
1619                buffer.lockRead();
1620            }
1621            catch (InterruptedException JavaDoc e) {
1622                Log.error(e);
1623                return;
1624            }
1625            try {
1626                reframeHorizontally();
1627                reframeVertically();
1628            }
1629            finally {
1630                buffer.unlockRead();
1631            }
1632            updateFlag &= ~REFRAME;
1633        }
1634    }
1635
1636    private void reframeVertically()
1637    {
1638        if (topLine != null && topLine.isHidden()) {
1639            Line prev = topLine.previousVisible();
1640            setTopLine(prev != null ? prev : topLine.nextVisible());
1641            setUpdateFlag(REPAINT);
1642        }
1643
1644        if (topLine == null || mustReframe()) {
1645            Line top = findNewTopLine(editor.getDotLine());
1646            setTopLine(top);
1647            setUpdateFlag(REPAINT);
1648        }
1649
1650        reframeParam = 0;
1651
1652        // Now we need to check to make sure there's not unnecessary
1653
// whitespace at the bottom of the display.
1654

1655        // If we can't go back any further, we have no choice.
1656
if (topLine.previousVisible() == null)
1657            return;
1658
1659        int y = getAbsoluteY(topLine);
1660        int limit =
1661            editor.getBuffer().getDisplayHeight() - getHeight() + charHeight;
1662        if (y > limit) {
1663            // We need to scroll back in the buffer a bit.
1664
Line top = lineFromAbsoluteY(limit);
1665            setTopLine(top);
1666            setUpdateFlag(REPAINT);
1667            reframeParam = 0;
1668        }
1669    }
1670
1671    // Returns true if necessary to reframe vertically.
1672
private boolean mustReframe()
1673    {
1674        final int height = getHeight();
1675        final Line dotLine = editor.getDotLine();
1676        Line line = topLine;
1677        int y = - pixelsAboveTopLine;
1678        while (y < height) {
1679            if (line == dotLine) {
1680                // Whole line must fit.
1681
return y + line.getHeight() > height;
1682            }
1683            Line next = line.nextVisible();
1684            if (next == null)
1685                return true;
1686            y += line.getHeight();
1687            line = next;
1688        }
1689        return true;
1690    }
1691
1692    // Helper for reframeVertically().
1693
private Line findNewTopLine(final Line dotLine)
1694    {
1695        int y;
1696        if (reframeParam == 0)
1697            y = getHeight() / 2; // Default.
1698
else if (reframeParam > 0)
1699            y = (reframeParam - 1) * charHeight;
1700        else // reframeParam < 0
1701
y = getHeight() + reframeParam * charHeight;
1702
1703        Line line = dotLine;
1704        while (true) {
1705            Line prev = line.previousVisible();
1706            if (prev == null)
1707                break;
1708            y -= prev.getHeight();
1709            if (y < 0)
1710                break;
1711            line = prev;
1712        }
1713
1714        return line;
1715    }
1716
1717    private void reframeHorizontally()
1718    {
1719        if (editor.getDot() == null)
1720            return;
1721        if (editor.getDotLine() instanceof ImageLine)
1722            return;
1723        final int absCaretCol = caretCol + shift;
1724        Debug.assertTrue(absCaretCol >= 0);
1725        ensureColumnVisible(editor.getDotLine(), absCaretCol);
1726        caretCol = absCaretCol - shift;
1727    }
1728
1729    public synchronized void ensureColumnVisible(Line line, int absCol)
1730    {
1731        final int oldShift = shift;
1732        if (absCol < 50)
1733            shift = 0;
1734        int col = absCol - shift;
1735        if (col < 0) {
1736            do {
1737                shift -= 8;
1738                if (shift < 0)
1739                    shift = 0;
1740                col = absCol - shift;
1741            } while (col < 0);
1742        } else {
1743            if (col > getMaxCols()) {
1744                shift = absCol - getMaxCols();
1745                col = absCol - shift;
1746                Debug.assertTrue(col == getMaxCols());
1747            }
1748            Graphics2D JavaDoc g2d = (Graphics2D JavaDoc) getGraphics();
1749            if (g2d == null) {
1750                Log.error("ensureColumnVisible g2d is null");
1751                return;
1752            }
1753            formatLine(line, shift, col);
1754            int x = measureLine(g2d, textArray, col, formatArray);
1755            final int maxWidth = getWidth() - gutterWidth - charWidth;
1756            while (x > maxWidth) {
1757                shift += 8;
1758                col = absCol - shift;
1759                formatLine(line, shift, col);
1760                x = measureLine(g2d, textArray, col, formatArray);
1761            }
1762        }
1763        if (shift != oldShift)
1764            setUpdateFlag(REPAINT);
1765    }
1766
1767    public void toCenter()
1768    {
1769        Line line = editor.getDotLine();
1770        int limit = getRows() / 2;
1771        for (int i = 0; i < limit; i++) {
1772            Line prev = line.previousVisible();
1773            if (prev == null)
1774                break;
1775            line = prev;
1776        }
1777        setTopLine(line);
1778        setUpdateFlag(REPAINT);
1779    }
1780
1781    public void toTop()
1782    {
1783        Line goal = editor.getDotLine().previousVisible();
1784        if (goal == null)
1785            goal = editor.getDotLine();
1786        if (topLine != goal) {
1787            setTopLine(goal);
1788            setUpdateFlag(REPAINT);
1789        }
1790    }
1791
1792    // Does nothing if entire region is already visible.
1793
public void centerRegion(Line begin, Line end)
1794    {
1795        if (begin == null)
1796            return;
1797        if (end != null) {
1798            if (isLineVisible(begin) && isLineVisible(end))
1799                return; // Entire region is already visible.
1800
}
1801        Line newTopLine = null;
1802        int linesInRegion = 0;
1803        if (end != null) {
1804            for (Line line = begin; line != null && line.isBefore(end); line = line.nextVisible())
1805                ++linesInRegion;
1806        } else {
1807            for (Line line = begin; line != null; line = line.nextVisible())
1808                ++linesInRegion;
1809        }
1810        int numRows = getRows();
1811        if (numRows > linesInRegion) {
1812            int linesAbove = (numRows - linesInRegion) / 2;
1813            newTopLine = begin;
1814            do {
1815                Line prev = newTopLine.previousVisible();
1816                if (prev != null)
1817                    newTopLine = prev;
1818                else
1819                    break;
1820                --linesAbove;
1821            }
1822            while (linesAbove > 0);
1823        }
1824        if (newTopLine == null) {
1825            Line prev = begin.previousVisible();
1826            newTopLine = prev != null ? prev : begin;
1827        }
1828        if (topLine != newTopLine) {
1829            setTopLine(newTopLine);
1830            setUpdateFlag(REPAINT);
1831        }
1832    }
1833
1834    private boolean isLineVisible(Line line)
1835    {
1836        return (line.lineNumber() >= getTopLineNumber() &&
1837            line.lineNumber() < getTopLineNumber() + getRows());
1838    }
1839
1840    private final int getMaxCols()
1841    {
1842        // We need some slack here (runs of italics tend to get compressed).
1843
// An extra 25% should be plenty.
1844
return (getWidth() / charWidth) * 5 / 4;
1845    }
1846
1847    public final int getColumns()
1848    {
1849        return (getWidth()-getGutterWidth(editor.getBuffer()))/charWidth - 1;
1850    }
1851
1852    public final int getRows()
1853    {
1854        return getHeight() / charHeight;
1855    }
1856
1857    public void moveCaretToPoint(Point JavaDoc point)
1858    {
1859        final Position dot = editor.getDot();
1860        if (dot == null)
1861            return;
1862        final Line line = lineFromY(point.y);
1863        if (line == null)
1864            return;
1865        if (line != dot.getLine()) {
1866            editor.updateDotLine();
1867            dot.setLine(line);
1868        }
1869        caretCol = Math.max(getColumn(line, point.x), 0);
1870        editor.moveDotToCol(caretCol + shift);
1871    }
1872
1873    public synchronized Position positionFromPoint(Point JavaDoc point, int shift)
1874    {
1875        int savedShift = this.shift;
1876        this.shift = shift;
1877        Position pos = positionFromPoint(point);
1878        this.shift = savedShift;
1879        return pos;
1880    }
1881
1882    public Position positionFromPoint(Point JavaDoc point)
1883    {
1884        return positionFromPoint(point.x, point.y);
1885    }
1886
1887    public Position positionFromPoint(int x, int y)
1888    {
1889        Line line = lineFromY(y);
1890        if (line == null)
1891            return null;
1892        Position pos = new Position(line, 0);
1893        int col = getColumn(line, x);
1894        pos.moveToCol(col + shift, editor.getBuffer().getTabWidth());
1895        return pos;
1896    }
1897
1898    // y is offset from top of window.
1899
public Line lineFromY(int y)
1900    {
1901        if (topLine == null)
1902            return null;
1903        Line line = topLine;
1904        int total = - pixelsAboveTopLine;
1905        final int limit = getHeight();
1906        while (true) {
1907            total += line.getHeight();
1908            if (total > y)
1909                break;
1910            if (total > limit)
1911                break;
1912            Line next = line.nextVisible();
1913            if (next == null)
1914                break;
1915            line = next;
1916        }
1917        return line;
1918    }
1919
1920    // y is absolute offset from start of buffer.
1921
private Line lineFromAbsoluteY(int y)
1922    {
1923        Line line = editor.getBuffer().getFirstLine();
1924        if (line != null) {
1925            int total = 0;
1926            while (true) {
1927                total += line.getHeight();
1928                if (total > y)
1929                    break;
1930                Line next = line.nextVisible();
1931                if (next != null)
1932                    line = line.nextVisible();
1933                else
1934                    break;
1935            }
1936        }
1937        return line;
1938    }
1939
1940    public synchronized int getColumn(Line line, int x)
1941    {
1942        if (line instanceof ImageLine)
1943            return 0;
1944        int maxCols = getMaxCols();
1945        formatLine(line, shift, maxCols);
1946        Graphics2D JavaDoc g2d = (Graphics2D JavaDoc) getGraphics();
1947        int begin = 0;
1948        int end = maxCols;
1949        while (end - begin > 4) {
1950            int pivot = (begin + end) / 2;
1951            int width = measureLine(g2d, textArray, pivot, formatArray);
1952            if (width + gutterWidth > x)
1953                end = pivot + 1;
1954            else
1955                begin = pivot - 1;
1956        }
1957        for (int i = begin; i < end; i++) {
1958            int width = measureLine(g2d, textArray, i, formatArray);
1959            if (width + gutterWidth > x)
1960                return i - 1;
1961        }
1962        return 0; // Shouldn't happen.
1963
}
1964
1965    public boolean isOpaque()
1966    {
1967        return true;
1968    }
1969
1970    public synchronized final void lineChanged(Line line)
1971    {
1972        // Avoid NPE in Hashtable.put().
1973
if (line == null) {
1974            Debug.bug("lineChanged line is null");
1975            return;
1976        }
1977        changedLines.put(line, line);
1978    }
1979
1980    public static void resetDisplay()
1981    {
1982        if (plainFont == null)
1983            return; // Not initialized yet. Nothing to do.
1984
initializeStaticValues();
1985        for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1986            Display display = it.nextEditor().getDisplay();
1987            display.initialize();
1988            display.repaint();
1989        }
1990    }
1991
1992    public static void setRenderingHints(Graphics JavaDoc g)
1993    {
1994        if (antialias) {
1995            Graphics2D JavaDoc g2d = (Graphics2D JavaDoc) g;
1996            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
1997                                 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1998        }
1999    }
2000
2001    public String JavaDoc getToolTipText(MouseEvent JavaDoc e)
2002    {
2003        return editor.getMode().getToolTipText(editor, e);
2004    }
2005}
2006
Popular Tags