KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > gjt > sp > jedit > textarea > TextArea


1 /*
2  * TextArea.java - Handles services.xml files in plugins
3  * :tabSize=8:indentSize=8:noTabs=false:
4  * :folding=explicit:collapseFolds=1:
5  *
6  * Copyright (C) 1999, 2005 Slava Pestov
7  * Portions copyright (C) 2000 Ollie Rutherfurd
8  * Portions copyright (C) 2006 Matthieu Casanova
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23  */

24 package org.gjt.sp.jedit.textarea;
25
26 import org.gjt.sp.jedit.input.InputHandlerProvider;
27 import org.gjt.sp.jedit.input.AbstractInputHandler;
28 import org.gjt.sp.jedit.input.DefaultInputHandlerProvider;
29 import org.gjt.sp.jedit.input.TextAreaInputHandler;
30 import org.gjt.sp.jedit.syntax.Chunk;
31 import org.gjt.sp.jedit.syntax.TokenMarker;
32 import org.gjt.sp.jedit.syntax.ParserRuleSet;
33 import org.gjt.sp.jedit.syntax.SyntaxStyle;
34 import org.gjt.sp.jedit.buffer.JEditBuffer;
35 import org.gjt.sp.jedit.Debug;
36 import org.gjt.sp.jedit.TextUtilities;
37 import org.gjt.sp.util.Log;
38 import org.gjt.sp.util.StandardUtilities;
39
40 import javax.swing.event.EventListenerList JavaDoc;
41 import javax.swing.event.MouseInputAdapter JavaDoc;
42 import javax.swing.event.CaretListener JavaDoc;
43 import javax.swing.event.CaretEvent JavaDoc;
44 import javax.swing.text.Segment JavaDoc;
45 import javax.swing.*;
46 import javax.swing.Timer JavaDoc;
47 import javax.swing.plaf.metal.MetalLookAndFeel JavaDoc;
48 import java.awt.*;
49 import java.awt.im.InputMethodRequests JavaDoc;
50 import java.awt.event.*;
51 import java.util.*;
52
53 /**
54  * jEdit's text component.<p>
55  *
56  * Unlike most other text editors, the selection API permits selection and
57  * concurrent manipulation of multiple, non-contiguous regions of text.
58  * Methods in this class that deal with selecting text rely upon classes derived
59  * the {@link Selection} class.
60  *
61  * @author Slava Pestov
62  * @author John Gellene (API documentation)
63  * @version $Id: JEditTextArea.java 7148 2006-09-29 23:09:06 +0200 (ven., 29 sept. 2006) kpouer $
64  */

65 public class TextArea extends JComponent
66 {
67     //{{{ TextArea constructor
68
public TextArea()
69     {
70         this(null);
71         inputHandlerProvider = new DefaultInputHandlerProvider(new TextAreaInputHandler(this));
72         setMouseHandler(new TextAreaMouseHandler(this));
73         Font font1 = new Font("Monospaced", Font.PLAIN, 12);
74         painter.setFont(font1);
75         SyntaxStyle[] styles = new SyntaxStyle[1];
76         styles[0] = new SyntaxStyle(Color.black, Color.white, font1);
77         painter.setStyles(styles);
78         painter.setBlockCaretEnabled(false);
79
80
81
82         painter.setStructureHighlightEnabled(true);
83         painter.setStructureHighlightColor(Color.black);
84         painter.setEOLMarkersPainted(false);
85         painter.setEOLMarkerColor(new Color(255,102,51));
86         painter.setWrapGuidePainted(true);
87         painter.setWrapGuideColor(new Color(125,125,255));
88         painter.setCaretColor(Color.red);
89         painter.setSelectionColor(new Color(204,204,255));
90         painter.setMultipleSelectionColor(new Color(204,255,204));
91         painter.setBackground(Color.white);
92         painter.setForeground(Color.black);
93         painter.setBlockCaretEnabled(false);
94         painter.setLineHighlightEnabled(true);
95         painter.setLineHighlightColor(new Color(255,204,255));
96         painter.setAntiAlias(new AntiAlias(0));
97         painter.setFractionalFontMetricsEnabled(false);
98
99
100         gutter.setExpanded(false);
101         gutter.setHighlightInterval(5);
102         gutter.setCurrentLineHighlightEnabled(true);
103         gutter.setStructureHighlightEnabled(true);
104         gutter.setStructureHighlightColor(new Color(102,102,153));
105         gutter.setBackground(new Color(219,219,219));
106         gutter.setForeground(Color.black);
107         gutter.setHighlightedForeground(new Color(153,0,102));
108         gutter.setFoldColor(new Color(131,131,131));
109         gutter.setCurrentLineForeground(new Color(255,0,51));
110         gutter.setLineNumberAlignment(Gutter.RIGHT);
111         gutter.setFont(new Font("Monospaced", Font.PLAIN, 10));
112         gutter.setBorder(3, new Color(153,0,153), Color.white, painter.getBackground());
113
114         setCaretBlinkEnabled(true);
115         setElectricScroll(3);
116
117         JEditBuffer buffer = new JEditBuffer();
118         TokenMarker tokenMarker = new TokenMarker();
119         tokenMarker.addRuleSet(new ParserRuleSet("text","MAIN"));
120         buffer.setTokenMarker(tokenMarker);
121         buffer.insert(0,"ahaha coucou\ncaca");
122         setBuffer(buffer);
123     } //}}}
124

125     //{{{ TextArea constructor
126
/**
127      * Creates a new JEditTextArea.
128      */

129     public TextArea(InputHandlerProvider inputHandlerProvider)
130     {
131         this.inputHandlerProvider = inputHandlerProvider;
132         enableEvents(AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
133
134         //{{{ Initialize some misc. stuff
135
selectionManager = new SelectionManager(this);
136         chunkCache = new ChunkCache(this);
137         painter = new TextAreaPainter(this);
138         repaintMgr = new FastRepaintManager(this,painter);
139         gutter = new Gutter(this);
140         gutter.setMouseActionsProvider(new MouseActions("gutter"));
141         listenerList = new EventListenerList JavaDoc();
142         caretEvent = new MutableCaretEvent();
143         blink = true;
144         offsetXY = new Point();
145         structureMatchers = new LinkedList<StructureMatcher>();
146         structureMatchers.add(new StructureMatcher.BracketMatcher());
147         //}}}
148

149         //{{{ Initialize the GUI
150
setLayout(new ScrollLayout());
151         add(ScrollLayout.CENTER,painter);
152         add(ScrollLayout.LEFT,gutter);
153
154         // some plugins add stuff in a "right-hand" gutter
155
verticalBox = new Box(BoxLayout.X_AXIS);
156         verticalBox.add(vertical = new JScrollBar(Adjustable.VERTICAL));
157         vertical.setRequestFocusEnabled(false);
158         add(ScrollLayout.RIGHT,verticalBox);
159         add(ScrollLayout.BOTTOM,
160             horizontal = new JScrollBar(Adjustable.HORIZONTAL));
161         horizontal.setRequestFocusEnabled(false);
162
163         horizontal.setValues(0,0,0,0);
164         //}}}
165

166         //{{{ this ensures that the text area's look is slightly
167
// more consistent with the rest of the metal l&f.
168
// while it depends on not-so-well-documented portions
169
// of Swing, it only affects appearance, so future
170
// breakage shouldn't matter
171
if(UIManager.getLookAndFeel() instanceof MetalLookAndFeel JavaDoc)
172         {
173             setBorder(new TextAreaBorder());
174             vertical.putClientProperty("JScrollBar.isFreeStanding",
175                 Boolean.FALSE);
176             horizontal.putClientProperty("JScrollBar.isFreeStanding",
177                 Boolean.FALSE);
178             //horizontal.setBorder(null);
179
}
180         //}}}
181

182         //{{{ Add some event listeners
183
vertical.addAdjustmentListener(new AdjustHandler());
184         horizontal.addAdjustmentListener(new AdjustHandler());
185
186
187         addFocusListener(new FocusHandler());
188         addMouseWheelListener(new MouseWheelHandler());
189
190         //}}}
191

192         // This doesn't seem very correct, but it fixes a problem
193
// when setting the initial caret position for a buffer
194
// (eg, from the recent file list)
195
focusedComponent = this;
196
197     } //}}}
198

199     //{{{ setMouseHandler() method
200
public void setMouseHandler(MouseInputAdapter JavaDoc mouseInputAdapter)
201     {
202         mouseHandler = mouseInputAdapter;
203         painter.addMouseListener(mouseHandler);
204         painter.addMouseMotionListener(mouseHandler);
205     } //}}}
206

207     //{{{ setTransferHandler() method
208
public void setTransferHandler(TransferHandler newHandler)
209     {
210         super.setTransferHandler(newHandler);
211         try
212         {
213             getDropTarget().addDropTargetListener(
214                 new TextAreaDropHandler(this));
215         }
216         catch(TooManyListenersException e)
217         {
218             Log.log(Log.ERROR,this,e);
219         }
220     } //}}}
221

222     //{{{ toString() method
223
public String JavaDoc toString() {
224         StringBuilder JavaDoc builder = new StringBuilder JavaDoc();
225         builder.append("caret: ").append(caret).append('\n');
226         builder.append("caretLine: ").append(caretLine).append('\n');
227         builder.append("caretScreenLine: ").append(caretScreenLine).append('\n');
228         builder.append("electricScroll: ").append(electricScroll).append('\n');
229         builder.append("horizontalOffset: ").append(horizontalOffset).append('\n');
230         builder.append("magicCaret: ").append(magicCaret).append('\n');
231         builder.append("offsetXY").append(offsetXY.toString()).append('\n');
232         builder.append("oldCaretLine: ").append(oldCaretLine).append('\n');
233         builder.append("screenLastLine: ").append(screenLastLine).append('\n');
234         builder.append("visibleLines: ").append(visibleLines).append('\n');
235         builder.append("firstPhysicalLine: ").append(getFirstPhysicalLine()).append('\n');
236         builder.append("physLastLine: ").append(physLastLine).append('\n');
237         return builder.toString();
238     } //}}}
239

240     //{{{ dispose() method
241
/**
242      * Plugins and macros should not call this method.
243      * @since jEdit 4.2pre1
244      */

245     public void dispose()
246     {
247         DisplayManager.textAreaDisposed(this);
248     } //}}}
249

250     //{{{ getInputHandler() method
251
/**
252      * @since jEdit 4.3pre1
253      */

254     public AbstractInputHandler getInputHandler()
255     {
256         return inputHandlerProvider.getInputHandler();
257     } //}}}
258

259     //{{{ getPainter() method
260
/**
261      * Returns the object responsible for painting this text area.
262      */

263     public final TextAreaPainter getPainter()
264     {
265         return painter;
266     } //}}}
267

268     //{{{ getGutter() method
269
/**
270      * Returns the gutter to the left of the text area or null if the gutter
271      * is disabled
272      */

273     public final Gutter getGutter()
274     {
275         return gutter;
276     } //}}}
277

278     //{{{ getDisplayManager() method
279
/**
280      * @return the display manager used by this text area.
281      * @since jEdit 4.2pre1
282      */

283     public DisplayManager getDisplayManager()
284     {
285         return displayManager;
286     } //}}}
287

288     //{{{ isCaretBlinkEnabled() method
289
/**
290      * @return true if the caret is blinking, false otherwise.
291      */

292     public final boolean isCaretBlinkEnabled()
293     {
294         return caretBlinks;
295     } //}}}
296

297     //{{{ setCaretBlinkEnabled() method
298
/**
299      * Toggles caret blinking.
300      * @param caretBlinks True if the caret should blink, false otherwise
301      */

302     public void setCaretBlinkEnabled(boolean caretBlinks)
303     {
304         this.caretBlinks = caretBlinks;
305         if(!caretBlinks)
306             blink = false;
307
308         if(buffer != null)
309             invalidateLine(caretLine);
310     } //}}}
311

312     //{{{ getElectricScroll() method
313

314     /**
315      * @return the minimum distance (in number of lines)
316      * from the caret to the nearest edge of the screen
317      * (top or bottom edge).
318      */

319     public final int getElectricScroll()
320     {
321         return electricScroll;
322     } //}}}
323

324     //{{{ setElectricScroll() method
325
/**
326      * Sets the number of lines from the top and bottom of the text
327      * area that are always visible
328      * @param electricScroll The number of lines always visible from
329      * the top or bottom
330      */

331     public final void setElectricScroll(int electricScroll)
332     {
333         this.electricScroll = electricScroll;
334     } //}}}
335

336     //{{{ isQuickCopyEnabled() method
337
/**
338      * Returns if clicking the middle mouse button pastes the most
339      * recent selection (% register), and if Control-dragging inserts
340      * the selection at the caret.
341      */

342     public final boolean isQuickCopyEnabled()
343     {
344         return quickCopy;
345     } //}}}
346

347     //{{{ setQuickCopyEnabled() method
348
/**
349      * Sets if clicking the middle mouse button pastes the most
350      * recent selection (% register), and if Control-dragging inserts
351      * the selection at the caret.
352      * @param quickCopy A boolean flag
353      */

354     public final void setQuickCopyEnabled(boolean quickCopy)
355     {
356         this.quickCopy = quickCopy;
357     } //}}}
358

359     //{{{ getBuffer() method
360
/**
361      * Returns the buffer this text area is editing.
362      * @since jedit 4.3pre3
363      *
364      * Prior to 4.3pre3, this function returned a "Buffer" type.
365      * If this causes your code to break, try calling view.getBuffer() instead of
366      * view.getTextArea().getBuffer().
367      *
368      */

369     public final JEditBuffer getBuffer()
370     {
371         return buffer;
372     } //}}}
373

374     //{{{ setBuffer() method
375
/**
376      * Sets the buffer this text area is editing. Do not call this method -
377      * use {@link org.gjt.sp.jedit.EditPane#setBuffer(Buffer)} instead.
378      * @param buffer The buffer
379      */

380     public void setBuffer(JEditBuffer buffer)
381     {
382         if(this.buffer == buffer)
383             return;
384
385         try
386         {
387             bufferChanging = true;
388
389             if(this.buffer != null)
390             {
391                 // dubious?
392
//setFirstLine(0);
393

394                 if(!buffer.isLoading())
395                     selectNone();
396                 caretLine = caret = caretScreenLine = 0;
397                 match = null;
398             }
399             boolean inCompoundEdit = false;
400             if (this.buffer != null)
401                 inCompoundEdit = this.buffer.insideCompoundEdit();
402             if (inCompoundEdit)
403                 this.buffer.endCompoundEdit();
404             this.buffer = buffer;
405             if (inCompoundEdit)
406                 this.buffer.beginCompoundEdit();
407
408             chunkCache.setBuffer(buffer);
409             repaintMgr.setFastScroll(false);
410             propertiesChanged();
411
412             if(displayManager != null)
413             {
414                 DisplayManager.releaseDisplayManager(
415                     displayManager);
416             }
417
418             displayManager = DisplayManager.getDisplayManager(
419                 buffer,this);
420
421             displayManager.init();
422
423             if(buffer.isLoading())
424                 updateScrollBar();
425
426             repaint();
427
428             fireScrollEvent(true);
429         }
430         finally
431         {
432             bufferChanging = false;
433         }
434     } //}}}
435

436     //{{{ isEditable() method
437
/**
438      * Returns true if this text area is editable, false otherwise.
439      */

440     public final boolean isEditable()
441     {
442         return buffer.isEditable();
443     } //}}}
444

445     //{{{ isDragInProgress() method
446
/**
447      * Drag and drop of text in jEdit is implementing using jEdit 1.4 APIs,
448      * however since jEdit must run with Java 1.3, this class only has the
449      * necessary support to call a hook method via reflection. This method
450      * is called by the {@link org.gjt.sp.jedit.Java14} class to signal that
451      * a drag is in progress.
452      * @since jEdit 4.2pre5
453      */

454     public boolean isDragInProgress()
455     {
456         return dndInProgress;
457     } //}}}
458

459     //{{{ setDragInProgress() method
460
/**
461      * Drag and drop of text in jEdit is implementing using jEdit 1.4 APIs,
462      * however since jEdit must run with Java 1.3, this class only has the
463      * necessary support to call a hook method via reflection. This method
464      * is called by the {@link org.gjt.sp.jedit.Java14} class to signal that
465      * a drag is in progress.
466      * @since jEdit 4.2pre5
467      */

468     public void setDragInProgress(boolean dndInProgress)
469     {
470         this.dndInProgress = dndInProgress;
471     } //}}}
472

473     //{{{ isDragEnabled() method
474
/**
475      * Returns if drag and drop of text is enabled.
476      * @since jEdit 4.2pre5
477      */

478     public boolean isDragEnabled()
479     {
480         return dndEnabled;
481     } //}}}
482

483     //{{{ setDragEnabled() method
484
/**
485      * Sets if drag and drop of text is enabled.
486      * @since jEdit 4.2pre5
487      */

488     public void setDragEnabled(boolean dndEnabled)
489     {
490         this.dndEnabled = dndEnabled;
491     } //}}}
492

493     //{{{ getJoinNonWordChars() method
494
/**
495      * If set, double clicking will join non-word characters to form one "word".
496      * @since jEdit 4.3pre2
497      */

498     public boolean getJoinNonWordChars()
499     {
500         return joinNonWordChars;
501     } //}}}
502

503     //{{{ setJoinNonWordChars() method
504
/**
505      * If set, double clicking will join non-word characters to form one "word".
506      * @since jEdit 4.3pre2
507      */

508     public void setJoinNonWordChars(boolean joinNonWordChars)
509     {
510         this.joinNonWordChars = joinNonWordChars;
511     } //}}}
512

513     //{{{ Scrolling
514

515     //{{{ getFirstLine() method
516
/**
517      * Returns the vertical scroll bar position.
518      * @since jEdit 4.2pre1
519      */

520     public final int getFirstLine()
521     {
522         return displayManager.firstLine.scrollLine
523             + displayManager.firstLine.skew;
524     } //}}}
525

526     //{{{ setFirstLine() method
527
/**
528      * Sets the vertical scroll bar position
529      *
530      * @param firstLine The scroll bar position
531      */

532     public void setFirstLine(int firstLine)
533     {
534         //{{{ ensure we don't have empty space at the bottom or top, etc
535
int max = displayManager.getScrollLineCount() - visibleLines
536             + (lastLinePartial ? 1 : 0);
537         if(firstLine > max)
538             firstLine = max;
539         if(firstLine < 0)
540             firstLine = 0;
541         //}}}
542

543         if(Debug.SCROLL_DEBUG)
544         {
545             Log.log(Log.DEBUG,this,"setFirstLine() from "
546                 + getFirstLine() + " to " + firstLine);
547         }
548
549         int oldFirstLine = getFirstLine();
550         if(firstLine == oldFirstLine)
551             return;
552
553         displayManager.setFirstLine(oldFirstLine,firstLine);
554
555         repaint();
556
557         fireScrollEvent(true);
558     } //}}}
559

560     //{{{ getFirstPhysicalLine() method
561
/**
562      * Returns the first visible physical line index.
563      * @since jEdit 4.0pre4
564      */

565     public final int getFirstPhysicalLine()
566     {
567         return displayManager.firstLine.physicalLine;
568     } //}}}
569

570     //{{{ setFirstPhysicalLine() method
571
/**
572      * Sets the vertical scroll bar position.
573      * @param physFirstLine The first physical line to display
574      * @since jEdit 4.2pre1
575      */

576     public void setFirstPhysicalLine(int physFirstLine)
577     {
578         setFirstPhysicalLine(physFirstLine,0);
579     } //}}}
580

581     //{{{ setFirstPhysicalLine() method
582
/**
583      * Sets the vertical scroll bar position.
584      * @param physFirstLine The first physical line to display
585      * @param skew A local screen line delta
586      * @since jEdit 4.2pre1
587      */

588     public void setFirstPhysicalLine(int physFirstLine, int skew)
589     {
590         if(Debug.SCROLL_DEBUG)
591         {
592             Log.log(Log.DEBUG,this,"setFirstPhysicalLine("
593                 + physFirstLine + ',' + skew + ')');
594         }
595
596         int amount = physFirstLine - displayManager.firstLine.physicalLine;
597
598         displayManager.setFirstPhysicalLine(amount,skew);
599
600         repaint();
601
602         fireScrollEvent(true);
603     } //}}}
604

605     //{{{ getLastPhysicalLine() method
606
/**
607      * Returns the last visible physical line index.
608      * @since jEdit 4.0pre4
609      */

610     public final int getLastPhysicalLine()
611     {
612         return physLastLine;
613     } //}}}
614

615     //{{{ getLastScreenLine() method
616
/**
617      * @since jEdit 4.3pre1
618      */

619     public int getLastScreenLine()
620     {
621         return screenLastLine;
622     } //}}}
623

624     //{{{ getVisibleLines() method
625
/**
626      * Returns the number of lines visible in this text area.
627      */

628     public final int getVisibleLines()
629     {
630         return visibleLines;
631     } //}}}
632

633     //{{{ getHorizontalOffset() method
634
/**
635      * Returns the horizontal offset of drawn lines.
636      */

637     public final int getHorizontalOffset()
638     {
639         return horizontalOffset;
640     } //}}}
641

642     //{{{ setHorizontalOffset() method
643
/**
644      * Sets the horizontal offset of drawn lines. This can be used to
645      * implement horizontal scrolling.
646      * @param horizontalOffset offset The new horizontal offset
647      */

648     public void setHorizontalOffset(int horizontalOffset)
649     {
650         if(horizontalOffset > 0)
651             horizontalOffset = 0;
652
653         if(horizontalOffset == this.horizontalOffset)
654             return;
655
656         this.horizontalOffset = horizontalOffset;
657         painter.repaint();
658
659         fireScrollEvent(false);
660     } //}}}
661

662     //{{{ scrollUpLine() method
663
/**
664      * Scrolls up by one line.
665      * @since jEdit 2.7pre2
666      */

667     public void scrollUpLine()
668     {
669         setFirstLine(getFirstLine() - 1);
670     } //}}}
671

672     //{{{ scrollUpPage() method
673
/**
674      * Scrolls up by one page.
675      * @since jEdit 2.7pre2
676      */

677     public void scrollUpPage()
678     {
679         setFirstLine(getFirstLine() - getVisibleLines()
680             + (lastLinePartial ? 1 : 0));
681     } //}}}
682

683     //{{{ scrollDownLine() method
684
/**
685      * Scrolls down by one line.
686      * @since jEdit 2.7pre2
687      */

688     public void scrollDownLine()
689     {
690         setFirstLine(getFirstLine() + 1);
691     } //}}}
692

693     //{{{ scrollDownPage() method
694
/**
695      * Scrolls down by one page.
696      * @since jEdit 2.7pre2
697      */

698     public void scrollDownPage()
699     {
700         setFirstLine(getFirstLine() + getVisibleLines()
701             - (lastLinePartial ? 1 : 0));
702     } //}}}
703

704     //{{{ scrollToCaret() method
705
/**
706      * Ensures that the caret is visible by scrolling the text area if
707      * necessary.
708      * @param doElectricScroll If true, electric scrolling will be performed
709      */

710     public void scrollToCaret(boolean doElectricScroll)
711     {
712         scrollTo(caretLine,caret - buffer.getLineStartOffset(caretLine),
713             doElectricScroll);
714     } //}}}
715

716     //{{{ scrollTo() method
717
/**
718      * Ensures that the specified location in the buffer is visible.
719      * @param offset The offset from the start of the buffer
720      * @param doElectricScroll If true, electric scrolling will be performed
721      * @since jEdit 4.2pre3
722      */

723     public void scrollTo(int offset, boolean doElectricScroll)
724     {
725         int line = buffer.getLineOfOffset(offset);
726         scrollTo(line,offset - buffer.getLineStartOffset(line),
727             doElectricScroll);
728     } //}}}
729

730     //{{{ scrollTo() method
731
/**
732      * Ensures that the specified location in the buffer is visible.
733      * @param line The line number
734      * @param offset The offset from the start of the line
735      * @param doElectricScroll If true, electric scrolling will be performed
736      * @since jEdit 4.0pre6
737      */

738     public void scrollTo(int line, int offset, boolean doElectricScroll)
739     {
740         if(Debug.SCROLL_TO_DEBUG)
741             Log.log(Log.DEBUG,this,"scrollTo(), lineCount="
742                 + getLineCount());
743
744         //{{{ Get ready
745
int extraEndVirt;
746         int lineLength = buffer.getLineLength(line);
747         if(offset > lineLength)
748         {
749             extraEndVirt = charWidth * (offset - lineLength);
750             offset = lineLength;
751         }
752         else
753             extraEndVirt = 0;
754
755         int _electricScroll = doElectricScroll
756             && visibleLines - 1 > (electricScroll << 1)
757                       ? electricScroll : 0;
758         //}}}
759

760         if(visibleLines <= 1)
761         {
762             if(Debug.SCROLL_TO_DEBUG)
763                 Log.log(Log.DEBUG,this,"visibleLines <= 0");
764             setFirstPhysicalLine(line,_electricScroll);
765             return;
766         }
767
768         //{{{ Scroll vertically
769
int screenLine = chunkCache.getScreenLineOfOffset(line,offset);
770         int visibleLines = getVisibleLines();
771         if(screenLine == -1)
772         {
773             if(Debug.SCROLL_TO_DEBUG)
774                 Log.log(Log.DEBUG,this,"screenLine == -1");
775             ChunkCache.LineInfo[] infos = chunkCache
776                 .getLineInfosForPhysicalLine(line);
777             int subregion = ChunkCache.getSubregionOfOffset(
778                 offset,infos);
779             int prevLine = displayManager.getPrevVisibleLine(getFirstPhysicalLine());
780             int nextLine = displayManager.getNextVisibleLine(getLastPhysicalLine());
781             if(line == getFirstPhysicalLine())
782             {
783                 if(Debug.SCROLL_TO_DEBUG)
784                     Log.log(Log.DEBUG,this,line + " == " + getFirstPhysicalLine());
785                 setFirstPhysicalLine(line,subregion
786                     - _electricScroll);
787             }
788             else if(line == prevLine)
789             {
790                 if(Debug.SCROLL_TO_DEBUG)
791                     Log.log(Log.DEBUG,this,line + " == " + prevLine);
792                 setFirstPhysicalLine(prevLine,subregion
793                     - _electricScroll);
794             }
795             else if(line == getLastPhysicalLine())
796             {
797                 if(Debug.SCROLL_TO_DEBUG)
798                     Log.log(Log.DEBUG,this,line + " == " + getLastPhysicalLine());
799                 setFirstPhysicalLine(line,
800                     subregion + _electricScroll
801                     - visibleLines
802                     + (lastLinePartial ? 2 : 1));
803             }
804             else if(line == nextLine)
805             {
806                 if(Debug.SCROLL_TO_DEBUG)
807                     Log.log(Log.DEBUG,this,line + " == " + nextLine);
808                 setFirstPhysicalLine(nextLine,
809                     subregion + electricScroll
810                     - visibleLines
811                     + (lastLinePartial ? 2 : 1));
812             }
813             else
814             {
815                 if(Debug.SCROLL_TO_DEBUG)
816                 {
817                     Log.log(Log.DEBUG,this,"neither");
818                     Log.log(Log.DEBUG,this,"Last physical line is " + getLastPhysicalLine());
819                 }
820                 setFirstPhysicalLine(line,subregion
821                     - (visibleLines >> 1));
822                 if(Debug.SCROLL_TO_DEBUG)
823                 {
824                     Log.log(Log.DEBUG,this,"Last physical line is " + getLastPhysicalLine());
825                 }
826             }
827         }
828         else if(screenLine < _electricScroll)
829         {
830             if(Debug.SCROLL_TO_DEBUG)
831                 Log.log(Log.DEBUG,this,"electric up");
832             setFirstLine(getFirstLine() - _electricScroll + screenLine);
833         }
834         else if(screenLine > visibleLines - _electricScroll
835             - (lastLinePartial ? 2 : 1))
836         {
837             if(Debug.SCROLL_TO_DEBUG)
838                 Log.log(Log.DEBUG,this,"electric down");
839             setFirstLine(getFirstLine() + _electricScroll - visibleLines + screenLine + (lastLinePartial ? 2 : 1));
840         } //}}}
841

842         //{{{ Scroll horizontally
843
if(!displayManager.isLineVisible(line))
844             return;
845
846         Point point = offsetToXY(line,offset,offsetXY);
847         if(point == null)
848         // FIXME - we need to reset the state of this window so that it has the right
849
// dimensions again.
850
{
851
852             Log.log(Log.ERROR,this,"BUG: screenLine=" + screenLine
853                 + ",visibleLines=" + visibleLines
854                 + ",physicalLine=" + line
855                 + ",offset=" + offset
856                 + ",firstPhysicalLine=" + getFirstPhysicalLine()
857                 + ",lastPhysicalLine=" + getLastPhysicalLine());
858
859         }
860         point.x += extraEndVirt;
861
862         if(point.x < 0)
863         {
864             setHorizontalOffset(horizontalOffset
865                 - point.x + charWidth + 5);
866         }
867         else if(point.x >= painter.getWidth() - charWidth - 5)
868         {
869             setHorizontalOffset(horizontalOffset +
870                 (painter.getWidth() - point.x)
871                 - charWidth - 5);
872         } //}}}
873
} //}}}
874

875     //{{{ addScrollListener() method
876
/**
877      * Adds a scroll listener to this text area.
878      * @param listener The listener
879      * @since jEdit 3.2pre2
880      */

881     public final void addScrollListener(ScrollListener listener)
882     {
883         listenerList.add(ScrollListener.class,listener);
884     } //}}}
885

886     //{{{ removeScrollListener() method
887
/**
888      * Removes a scroll listener from this text area.
889      * @param listener The listener
890      * @since jEdit 3.2pre2
891      */

892     public final void removeScrollListener(ScrollListener listener)
893     {
894         listenerList.remove(ScrollListener.class,listener);
895     } //}}}
896

897     //}}}
898

899     //{{{ Screen line stuff
900

901     //{{{ getPhysicalLineOfScreenLine() method
902
/**
903      * Returns the physical line number that contains the specified screen
904      * line.
905      * @param screenLine The screen line
906      * @since jEdit 4.0pre6
907      */

908     public int getPhysicalLineOfScreenLine(int screenLine)
909     {
910         return chunkCache.getLineInfo(screenLine).physicalLine;
911     } //}}}
912

913     //{{{ getScreenLineOfOffset() method
914
/**
915      * Returns the screen (wrapped) line containing the specified offset.
916      * Returns -1 if the line is not currently visible on the screen.
917      * @param offset The offset
918      * @since jEdit 4.0pre4
919      */

920     public int getScreenLineOfOffset(int offset)
921     {
922         int line = buffer.getLineOfOffset(offset);
923         offset -= buffer.getLineStartOffset(line);
924         return chunkCache.getScreenLineOfOffset(line,offset);
925     } //}}}
926

927     //{{{ getScreenLineStartOffset() method
928
/**
929      * Returns the start offset of the specified screen (wrapped) line.
930      * @param line The line
931      * @since jEdit 4.0pre4
932      */

933     public int getScreenLineStartOffset(int line)
934     {
935         ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(line);
936         if(lineInfo.physicalLine == -1)
937             return -1;
938
939         return buffer.getLineStartOffset(lineInfo.physicalLine)
940             + lineInfo.offset;
941     } //}}}
942

943     //{{{ getScreenLineEndOffset() method
944
/**
945      * Returns the end offset of the specified screen (wrapped) line.
946      * @param line The line
947      * @since jEdit 4.0pre4
948      */

949     public int getScreenLineEndOffset(int line)
950     {
951         ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(line);
952         if(lineInfo.physicalLine == -1)
953             return -1;
954
955         return buffer.getLineStartOffset(lineInfo.physicalLine)
956             + lineInfo.offset + lineInfo.length;
957     } //}}}
958

959     //}}}
960

961     //{{{ Offset conversion
962

963     //{{{ xyToOffset() method
964
/**
965      * Converts a point to an offset.
966      * Note that unlike in previous jEdit versions, this method now returns
967      * -1 if the y co-ordinate is out of bounds.
968      *
969      * @param x The x co-ordinate of the point
970      * @param y The y co-ordinate of the point
971      */

972     public int xyToOffset(int x, int y)
973     {
974         return xyToOffset(x,y,true);
975     } //}}}
976

977     //{{{ xyToOffset() method
978
/**
979      * Converts a point to an offset.
980      * Note that unlike in previous jEdit versions, this method now returns
981      * -1 if the y co-ordinate is out of bounds.
982      *
983      * @param x The x co-ordinate of the point
984      * @param y The y co-ordinate of the point
985      * @param round Round up to next letter if past the middle of a letter?
986      * @since jEdit 3.2pre6
987      */

988     public int xyToOffset(int x, int y, boolean round)
989     {
990         FontMetrics fm = painter.getFontMetrics();
991         int height = fm.getHeight();
992         int line = y / height;
993
994         if(line < 0 || line >= visibleLines)
995             return -1;
996
997         return xToScreenLineOffset(line,x,round);
998     } //}}}
999

1000    //{{{ xToScreenLineOffset() method
1001
/**
1002     * Converts a point in a given screen line to an offset.
1003     * Note that unlike in previous jEdit versions, this method now returns
1004     * -1 if the y co-ordinate is out of bounds.
1005     *
1006     * @param x The x co-ordinate of the point
1007     * @param screenLine The screen line
1008     * @param round Round up to next letter if past the middle of a letter?
1009     * @since jEdit 3.2pre6
1010     */

1011    public int xToScreenLineOffset(int screenLine, int x, boolean round)
1012    {
1013        ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(screenLine);
1014        if(lineInfo.physicalLine == -1)
1015        {
1016            return getLineEndOffset(displayManager
1017                .getLastVisibleLine()) - 1;
1018        }
1019        else
1020        {
1021            int offset = Chunk.xToOffset(lineInfo.chunks,
1022                x - horizontalOffset,round);
1023            if(offset == -1 || offset == lineInfo.offset + lineInfo.length)
1024                offset = lineInfo.offset + lineInfo.length - 1;
1025
1026            return getLineStartOffset(lineInfo.physicalLine) + offset;
1027        }
1028    } //}}}
1029

1030    //{{{ offsetToXY() method
1031
/**
1032     * Converts an offset into a point in the text area painter's
1033     * co-ordinate space.
1034     * @param offset The offset
1035     * @return The location of the offset on screen, or <code>null</code>
1036     * if the specified offset is not visible
1037     */

1038    public Point offsetToXY(int offset)
1039    {
1040        int line = buffer.getLineOfOffset(offset);
1041        offset -= buffer.getLineStartOffset(line);
1042        Point retVal = new Point();
1043        return offsetToXY(line,offset,retVal);
1044    } //}}}
1045

1046    //{{{ offsetToXY() method
1047
/**
1048     * Converts an offset into a point in the text area painter's
1049     * co-ordinate space.
1050     * @param line The line
1051     * @param offset The offset
1052     * @return The location of the offset on screen, or <code>null</code>
1053     * if the specified offset is not visible
1054     */

1055    public Point offsetToXY(int line, int offset)
1056    {
1057        return offsetToXY(line,offset,new Point());
1058    } //}}}
1059

1060    //{{{ offsetToXY() method
1061
/**
1062     * Converts a line,offset pair into an x,y (pixel) point relative to the
1063     * upper left corner (0,0) of the text area.
1064     *
1065     * @param line The physical line number (from top of document)
1066     * @param offset The offset in characters, from the start of the line
1067     * @param retVal The point to store the return value in
1068     * @return <code>retVal</code> for convenience, or <code>null</code>
1069     * if the specified offset is not visible
1070     * @since jEdit 4.0pre4
1071     */

1072    public Point offsetToXY(int line, int offset, Point retVal)
1073    {
1074        if(!displayManager.isLineVisible(line))
1075            return null;
1076        int screenLine = chunkCache.getScreenLineOfOffset(line,offset);
1077        if(screenLine == -1)
1078            return null;
1079
1080        FontMetrics fm = painter.getFontMetrics();
1081
1082        retVal.y = screenLine * fm.getHeight();
1083
1084        ChunkCache.LineInfo info = chunkCache.getLineInfo(screenLine);
1085
1086        retVal.x = (int)(horizontalOffset + Chunk.offsetToX(
1087            info.chunks,offset));
1088
1089        return retVal;
1090    } //}}}
1091

1092    //}}}
1093

1094    //{{{ Painting
1095

1096    //{{{ invalidateScreenLineRange() method
1097
/**
1098     * Marks a range of screen lines as needing a repaint.
1099     * @param start The first line
1100     * @param end The last line
1101     * @since jEdit 4.0pre4
1102     */

1103    public void invalidateScreenLineRange(int start, int end)
1104    {
1105        if(buffer.isLoading())
1106            return;
1107
1108        if(start > end)
1109        {
1110            int tmp = end;
1111            end = start;
1112            start = tmp;
1113        }
1114
1115        if(chunkCache.needFullRepaint())
1116            end = visibleLines;
1117
1118        FontMetrics fm = painter.getFontMetrics();
1119        int y = start * fm.getHeight();
1120        int height = (end - start + 1) * fm.getHeight();
1121        painter.repaint(0,y,painter.getWidth(),height);
1122        gutter.repaint(0,y,gutter.getWidth(),height);
1123    } //}}}
1124

1125    //{{{ invalidateLine() method
1126
/**
1127     * Marks a line as needing a repaint.
1128     * @param line The physical line to invalidate
1129     */

1130    public void invalidateLine(int line)
1131    {
1132        if(!isShowing()
1133            || buffer.isLoading()
1134            || line < getFirstPhysicalLine()
1135            || line > physLastLine
1136            || !displayManager.isLineVisible(line))
1137            return;
1138
1139        int startLine = -1;
1140        int endLine = -1;
1141
1142        for(int i = 0; i < visibleLines; i++)
1143        {
1144            ChunkCache.LineInfo info = chunkCache.getLineInfo(i);
1145
1146            if((info.physicalLine >= line || info.physicalLine == -1)
1147                && startLine == -1)
1148            {
1149                startLine = i;
1150            }
1151
1152            if((info.physicalLine >= line && info.lastSubregion)
1153                || info.physicalLine == -1)
1154            {
1155                endLine = i;
1156                break;
1157            }
1158        }
1159
1160        if(chunkCache.needFullRepaint() || endLine == -1)
1161            endLine = visibleLines;
1162
1163        invalidateScreenLineRange(startLine,endLine);
1164    } //}}}
1165

1166    //{{{ invalidateLineRange() method
1167
/**
1168     * Marks a range of physical lines as needing a repaint.
1169     * @param start The first line to invalidate
1170     * @param end The last line to invalidate
1171     */

1172    public void invalidateLineRange(int start, int end)
1173    {
1174        if(!isShowing() || buffer.isLoading())
1175            return;
1176
1177        if(end < start)
1178        {
1179            int tmp = end;
1180            end = start;
1181            start = tmp;
1182        }
1183
1184        if(end < getFirstPhysicalLine() || start > getLastPhysicalLine())
1185            return;
1186
1187        int startScreenLine = -1;
1188        int endScreenLine = -1;
1189
1190        for(int i = 0; i < visibleLines; i++)
1191        {
1192            ChunkCache.LineInfo info = chunkCache.getLineInfo(i);
1193
1194            if((info.physicalLine >= start || info.physicalLine == -1)
1195                && startScreenLine == -1)
1196            {
1197                startScreenLine = i;
1198            }
1199
1200            if((info.physicalLine >= end && info.lastSubregion)
1201                || info.physicalLine == -1)
1202            {
1203                endScreenLine = i;
1204                break;
1205            }
1206        }
1207
1208        if(startScreenLine == -1)
1209            startScreenLine = 0;
1210
1211        if(chunkCache.needFullRepaint() || endScreenLine == -1)
1212            endScreenLine = visibleLines;
1213
1214        invalidateScreenLineRange(startScreenLine,endScreenLine);
1215    } //}}}
1216

1217    //}}}
1218

1219    //{{{ Convenience methods
1220

1221    //{{{ getBufferLength() method
1222
/**
1223     * Returns the length of the buffer.
1224     */

1225    public final int getBufferLength()
1226    {
1227        return buffer.getLength();
1228    } //}}}
1229

1230    //{{{ getLineCount() method
1231
/**
1232     * Returns the number of physical lines in the buffer.
1233     */

1234    public final int getLineCount()
1235    {
1236        return buffer.getLineCount();
1237    } //}}}
1238

1239    //{{{ getLineOfOffset() method
1240
/**
1241     * Returns the line containing the specified offset.
1242     * @param offset The offset
1243     */

1244    public final int getLineOfOffset(int offset)
1245    {
1246        return buffer.getLineOfOffset(offset);
1247    } //}}}
1248

1249    //{{{ getLineStartOffset() method
1250
/**
1251     * Returns the start offset of the specified line.
1252     * @param line The line
1253     * @return The start offset of the specified line, or -1 if the line is
1254     * invalid
1255     */

1256    public int getLineStartOffset(int line)
1257    {
1258        return buffer.getLineStartOffset(line);
1259    } //}}}
1260

1261    //{{{ getLineEndOffset() method
1262
/**
1263     * Returns the end offset of the specified line.
1264     * @param line The line
1265     * @return The end offset of the specified line, or -1 if the line is
1266     * invalid.
1267     */

1268    public int getLineEndOffset(int line)
1269    {
1270        return buffer.getLineEndOffset(line);
1271    } //}}}
1272

1273    //{{{ getLineLength() method
1274
/**
1275     * Returns the length of the specified line.
1276     * @param line The line
1277     */

1278    public int getLineLength(int line)
1279    {
1280        return buffer.getLineLength(line);
1281    } //}}}
1282

1283    //{{{ getText() method
1284
/**
1285     * Returns the specified substring of the buffer.
1286     * @param start The start offset
1287     * @param len The length of the substring
1288     * @return The substring
1289     */

1290    public final String JavaDoc getText(int start, int len)
1291    {
1292        return buffer.getText(start,len);
1293    } //}}}
1294

1295    //{{{ getText() method
1296
/**
1297     * Copies the specified substring of the buffer into a segment.
1298     * @param start The start offset
1299     * @param len The length of the substring
1300     * @param segment The segment
1301     */

1302    public final void getText(int start, int len, Segment JavaDoc segment)
1303    {
1304        buffer.getText(start,len,segment);
1305    } //}}}
1306

1307    //{{{ getLineText() method
1308
/**
1309     * Returns the text on the specified line.
1310     * @param lineIndex The line
1311     * @return The text, or null if the line is invalid
1312     */

1313    public final String JavaDoc getLineText(int lineIndex)
1314    {
1315        return buffer.getLineText(lineIndex);
1316    } //}}}
1317

1318    //{{{ getLineText() method
1319
/**
1320     * Copies the text on the specified line into a segment. If the line
1321     * is invalid, the segment will contain a null string.
1322     * @param lineIndex The line
1323     */

1324    public final void getLineText(int lineIndex, Segment JavaDoc segment)
1325    {
1326        buffer.getLineText(lineIndex,segment);
1327    } //}}}
1328

1329    //{{{ getText() method
1330
/**
1331     * Returns the entire text of this text area.
1332     */

1333    public String JavaDoc getText()
1334    {
1335        return buffer.getText(0,buffer.getLength());
1336    } //}}}
1337

1338    //{{{ setText() method
1339
/**
1340     * Sets the entire text of this text area.
1341     */

1342    public void setText(String JavaDoc text)
1343    {
1344        try
1345        {
1346            buffer.beginCompoundEdit();
1347            buffer.remove(0,buffer.getLength());
1348            buffer.insert(0,text);
1349        }
1350        finally
1351        {
1352            buffer.endCompoundEdit();
1353        }
1354    } //}}}
1355

1356    //}}}
1357

1358    //{{{ Selection
1359

1360    //{{{ selectAll() method
1361
/**
1362     * Selects all text in the buffer. Preserves the scroll position.
1363     */

1364    public final void selectAll()
1365    {
1366        int firstLine = getFirstLine();
1367        int horizOffset = getHorizontalOffset();
1368
1369        setSelection(new Selection.Range(0,buffer.getLength()));
1370        moveCaretPosition(buffer.getLength(),true);
1371
1372        setFirstLine(firstLine);
1373        setHorizontalOffset(horizOffset);
1374    } //}}}
1375

1376    //{{{ selectLine() method
1377
/**
1378     * Selects the current line.
1379     * @since jEdit 2.7pre2
1380     */

1381    public void selectLine()
1382    {
1383        int caretLine = getCaretLine();
1384        int start = getLineStartOffset(caretLine);
1385        int end = getLineEndOffset(caretLine) - 1;
1386        Selection s = new Selection.Range(start,end);
1387        if(multi)
1388            addToSelection(s);
1389        else
1390            setSelection(s);
1391        moveCaretPosition(end);
1392    } //}}}
1393

1394    //{{{ selectParagraph() method
1395
/**
1396     * Selects the paragraph at the caret position.
1397     * @since jEdit 2.7pre2
1398     */

1399    public void selectParagraph()
1400    {
1401        int caretLine = getCaretLine();
1402
1403        if(getLineLength(caretLine) == 0)
1404        {
1405            getToolkit().beep();
1406            return;
1407        }
1408
1409        int start = caretLine;
1410        int end = caretLine;
1411
1412        while(start >= 0)
1413        {
1414            if(getLineLength(start) == 0)
1415                break;
1416            else
1417                start--;
1418        }
1419
1420        while(end < getLineCount())
1421        {
1422            if(getLineLength(end) == 0)
1423                break;
1424            else
1425                end++;
1426        }
1427
1428        int selectionStart = getLineStartOffset(start + 1);
1429        int selectionEnd = getLineEndOffset(end - 1) - 1;
1430        Selection s = new Selection.Range(selectionStart,selectionEnd);
1431        if(multi)
1432            addToSelection(s);
1433        else
1434            setSelection(s);
1435        moveCaretPosition(selectionEnd);
1436    } //}}}
1437

1438    //{{{ selectWord() method
1439
/**
1440     * Selects the word at the caret position.
1441     * @since jEdit 2.7pre2
1442     */

1443    public void selectWord()
1444    {
1445        int line = getCaretLine();
1446        int lineStart = getLineStartOffset(line);
1447        int offset = getCaretPosition() - lineStart;
1448
1449        if(getLineLength(line) == 0)
1450            return;
1451
1452        String JavaDoc lineText = getLineText(line);
1453        String JavaDoc noWordSep = buffer.getStringProperty("noWordSep");
1454
1455        if(offset == getLineLength(line))
1456            offset--;
1457
1458        int wordStart = TextUtilities.findWordStart(lineText,offset,noWordSep);
1459        int wordEnd = TextUtilities.findWordEnd(lineText,offset+1,noWordSep);
1460
1461        Selection s = new Selection.Range(lineStart + wordStart,
1462            lineStart + wordEnd);
1463        if(multi)
1464            addToSelection(s);
1465        else
1466            setSelection(s);
1467        moveCaretPosition(lineStart + wordEnd);
1468    } //}}}
1469

1470    //{{{ selectToMatchingBracket() method
1471
/**
1472     * Selects from the bracket at the specified position to the
1473     * corresponding bracket.
1474     * @since jEdit 4.2pre1
1475     */

1476    public Selection selectToMatchingBracket(int position,
1477        boolean quickCopy)
1478    {
1479        int positionLine = buffer.getLineOfOffset(position);
1480        int lineOffset = position - buffer.getLineStartOffset(positionLine);
1481        if(getLineLength(positionLine) != 0)
1482        {
1483            int bracket = TextUtilities.findMatchingBracket(buffer,
1484                positionLine,Math.max(0,lineOffset - 1));
1485
1486            if(bracket != -1)
1487            {
1488                Selection s;
1489
1490                if(bracket < position)
1491                {
1492                    if(!quickCopy)
1493                        moveCaretPosition(position,false);
1494                    s = new Selection.Range(bracket,position);
1495                }
1496                else
1497                {
1498                    if(!quickCopy)
1499                        moveCaretPosition(bracket + 1,false);
1500                    s = new Selection.Range(position - 1,bracket + 1);
1501                }
1502
1503                if(!multi && !quickCopy)
1504                    selectNone();
1505
1506                addToSelection(s);
1507                return s;
1508            }
1509        }
1510
1511        return null;
1512    } //}}}
1513

1514    //{{{ selectToMatchingBracket() method
1515
/**
1516     * Selects from the bracket at the caret position to the corresponding
1517     * bracket.
1518     * @since jEdit 4.0pre2
1519     */

1520    public void selectToMatchingBracket()
1521    {
1522        selectToMatchingBracket(caret,false);
1523    } //}}}
1524

1525    //{{{ selectBlock() method
1526
/**
1527     * Selects the code block surrounding the caret.
1528     * @since jEdit 2.7pre2
1529     */

1530    public void selectBlock()
1531    {
1532
1533        Selection s = getSelectionAtOffset(caret);
1534        int start, end;
1535        if(s == null)
1536            start = end = caret;
1537        else
1538        {
1539            start = s.start;
1540            end = s.end;
1541        }
1542
1543        String JavaDoc text = getText(0,buffer.getLength());
1544
1545        // We can't do the backward scan if start == 0
1546
if(start == 0)
1547        {
1548            getToolkit().beep();
1549            return;
1550        }
1551
1552        // Scan backwards, trying to find a bracket
1553
String JavaDoc openBrackets = "([{";
1554        String JavaDoc closeBrackets = ")]}";
1555        int count = 1;
1556        char openBracket = '\0';
1557        char closeBracket = '\0';
1558
1559backward_scan: while(--start > 0)
1560        {
1561            char c = text.charAt(start);
1562            int index = openBrackets.indexOf(c);
1563            if(index != -1)
1564            {
1565                if(--count == 0)
1566                {
1567                    openBracket = c;
1568                    closeBracket = closeBrackets.charAt(index);
1569                    break backward_scan;
1570                }
1571            }
1572            else if(closeBrackets.indexOf(c) != -1)
1573                count++;
1574        }
1575
1576        // Reset count
1577
count = 1;
1578
1579        // Scan forward, matching that bracket
1580
if(openBracket == '\0')
1581        {
1582            getToolkit().beep();
1583            return;
1584        }
1585forward_scan: do
1586        {
1587            char c = text.charAt(end);
1588            if(c == closeBracket)
1589            {
1590                if(--count == 0)
1591                {
1592                    end++;
1593                    break forward_scan;
1594                }
1595            }
1596            else if(c == openBracket)
1597                count++;
1598        }
1599        while(++end < buffer.getLength());
1600
1601        s = new Selection.Range(start,end);
1602        if(multi)
1603            addToSelection(s);
1604        else
1605            setSelection(s);
1606        moveCaretPosition(end);
1607    } //}}}
1608

1609    //{{{ lineInStructureScope() method
1610
/**
1611     * Returns if the specified line is contained in the currently
1612     * matched structure's scope.
1613     * @since jEdit 4.2pre3
1614     */

1615    public boolean lineInStructureScope(int line)
1616    {
1617        if(match == null)
1618            return false;
1619
1620        if(match.startLine < caretLine)
1621            return line >= match.startLine && line <= caretLine;
1622        else
1623            return line <= match.endLine && line >= caretLine;
1624    } //}}}
1625

1626    //{{{ invertSelection() method
1627
/**
1628     * Inverts the selection.
1629     * @since jEdit 4.0pre1
1630     */

1631    public final void invertSelection()
1632    {
1633        selectionManager.invertSelection();
1634    } //}}}
1635

1636    //{{{ getSelectionCount() method
1637
/**
1638     * Returns the number of selections. This can be used to test
1639     * for the existence of selections.
1640     * @since jEdit 3.2pre2
1641     */

1642    public int getSelectionCount()
1643    {
1644        return selectionManager.getSelectionCount();
1645    } //}}}
1646

1647    //{{{ getSelection() method
1648
/**
1649     * Returns the current selection.
1650     * @since jEdit 3.2pre1
1651     */

1652    public Selection[] getSelection()
1653    {
1654        return selectionManager.getSelection();
1655    } //}}}
1656

1657    //{{{ getSelectionIterator() method
1658
/**
1659     * Returns the current selection.
1660     * @since jEdit 4.3pre1
1661     */

1662    public Iterator<Selection> getSelectionIterator()
1663    {
1664        return selectionManager.selection.iterator();
1665    } //}}}
1666

1667    //{{{ getSelection() method
1668
/**
1669     * Returns the selection with the specified index. This must be
1670     * between 0 and the return value of <code>getSelectionCount()</code>.
1671     * @since jEdit 4.3pre1
1672     * @param index the index of the selection you want
1673     */

1674    public Selection getSelection(int index)
1675    {
1676        return selectionManager.selection.get(index);
1677    } //}}}
1678

1679    //{{{ selectNone() method
1680
/**
1681     * Deselects everything.
1682     */

1683    public void selectNone()
1684    {
1685        invalidateSelectedLines();
1686        setSelection((Selection)null);
1687    } //}}}
1688

1689    //{{{ setSelection() method
1690
/**
1691     * Sets the selection. Nested and overlapping selections are merged
1692     * where possible. Null elements of the array are ignored.
1693     * @param selection The new selection
1694     * since jEdit 3.2pre1
1695     */

1696    public void setSelection(Selection[] selection)
1697    {
1698        // invalidate the old selection
1699
invalidateSelectedLines();
1700        selectionManager.setSelection(selection);
1701        finishCaretUpdate(caretLine,NO_SCROLL,true);
1702    } //}}}
1703

1704    //{{{ setSelection() method
1705
/**
1706     * Sets the selection. Nested and overlapping selections are merged
1707     * where possible.
1708     * @param selection The new selection
1709     * since jEdit 3.2pre1
1710     */

1711    public void setSelection(Selection selection)
1712    {
1713        invalidateSelectedLines();
1714        selectionManager.setSelection(selection);
1715        finishCaretUpdate(caretLine,NO_SCROLL,true);
1716    } //}}}
1717

1718    //{{{ addToSelection() method
1719
/**
1720     * Adds to the selection. Nested and overlapping selections are merged
1721     * where possible.
1722     * @param selection The new selection
1723     * since jEdit 3.2pre1
1724     */

1725    public void addToSelection(Selection[] selection)
1726    {
1727        invalidateSelectedLines();
1728        selectionManager.addToSelection(selection);
1729        finishCaretUpdate(caretLine,NO_SCROLL,true);
1730    } //}}}
1731

1732    //{{{ addToSelection() method
1733
/**
1734     * Adds to the selection. Nested and overlapping selections are merged
1735     * where possible.
1736     * @param selection The new selection
1737     * since jEdit 3.2pre1
1738     */

1739    public void addToSelection(Selection selection)
1740    {
1741        invalidateSelectedLines();
1742        selectionManager.addToSelection(selection);
1743        finishCaretUpdate(caretLine,NO_SCROLL,true);
1744    } //}}}
1745

1746    //{{{ getSelectionAtOffset() method
1747
/**
1748     * Returns the selection containing the specific offset, or <code>null</code>
1749     * if there is no selection at that offset.
1750     * @param offset The offset
1751     * @since jEdit 3.2pre1
1752     */

1753    public Selection getSelectionAtOffset(int offset)
1754    {
1755        return selectionManager.getSelectionAtOffset(offset);
1756    } //}}}
1757

1758    //{{{ removeFromSelection() method
1759
/**
1760     * Deactivates the specified selection.
1761     * @param sel The selection
1762     * @since jEdit 3.2pre1
1763     */

1764    public void removeFromSelection(Selection sel)
1765    {
1766        invalidateSelectedLines();
1767        selectionManager.removeFromSelection(sel);
1768        finishCaretUpdate(caretLine,NO_SCROLL,true);
1769    } //}}}
1770

1771    //{{{ removeFromSelection() method
1772
/**
1773     * Deactivates the selection at the specified offset. If there is
1774     * no selection at that offset, does nothing.
1775     * @param offset The offset
1776     * @since jEdit 3.2pre1
1777     */

1778    public void removeFromSelection(int offset)
1779    {
1780        Selection sel = getSelectionAtOffset(offset);
1781        if(sel == null)
1782            return;
1783
1784        invalidateSelectedLines();
1785        selectionManager.removeFromSelection(sel);
1786        finishCaretUpdate(caretLine,NO_SCROLL,true);
1787    } //}}}
1788

1789    //{{{ resizeSelection() method
1790
/**
1791     * Resizes the selection at the specified offset, or creates a new
1792     * one if there is no selection at the specified offset. This is a
1793     * utility method that is mainly useful in the mouse event handler
1794     * because it handles the case of end being before offset gracefully
1795     * (unlike the rest of the selection API).
1796     * @param offset The offset
1797     * @param end The new selection end
1798     * @param extraEndVirt Only for rectangular selections - specifies how
1799     * far it extends into virtual space.
1800     * @param rect Make the selection rectangular?
1801     * @since jEdit 3.2pre1
1802     */

1803    public void resizeSelection(int offset, int end, int extraEndVirt,
1804        boolean rect)
1805    {
1806        Selection s = selectionManager.getSelectionAtOffset(offset);
1807        if(s != null)
1808        {
1809            invalidateLineRange(s.startLine,s.endLine);
1810            selectionManager.removeFromSelection(s);
1811        }
1812
1813        selectionManager.resizeSelection(offset,end,extraEndVirt,rect);
1814        fireCaretEvent();
1815    } //}}}
1816

1817    //{{{ extendSelection() method
1818
/**
1819     * Extends the selection at the specified offset, or creates a new
1820     * one if there is no selection at the specified offset. This is
1821     * different from resizing in that the new chunk is added to the
1822     * selection in question, instead of replacing it.
1823     * @param offset The offset
1824     * @param end The new selection end
1825     * @since jEdit 3.2pre1
1826     */

1827    public void extendSelection(int offset, int end)
1828    {
1829        extendSelection(offset,end,0,0);
1830    } //}}}
1831

1832    //{{{ extendSelection() method
1833
/**
1834     * Extends the selection at the specified offset, or creates a new
1835     * one if there is no selection at the specified offset. This is
1836     * different from resizing in that the new chunk is added to the
1837     * selection in question, instead of replacing it.
1838     * @param offset The offset
1839     * @param end The new selection end
1840     * @param extraStartVirt Extra virtual space at the start
1841     * @param extraEndVirt Extra virtual space at the end
1842     * @since jEdit 4.2pre1
1843     */

1844    public void extendSelection(int offset, int end,
1845        int extraStartVirt, int extraEndVirt)
1846    {
1847        Selection s = getSelectionAtOffset(offset);
1848        if(s != null)
1849        {
1850            invalidateLineRange(s.startLine,s.endLine);
1851            selectionManager.removeFromSelection(s);
1852
1853            if(offset == s.start)
1854            {
1855                offset = end;
1856                end = s.end;
1857            }
1858            else if(offset == s.end)
1859            {
1860                offset = s.start;
1861            }
1862        }
1863
1864        if(end < offset)
1865        {
1866            int tmp = end;
1867            end = offset;
1868            offset = tmp;
1869        }
1870
1871        if(rectangularSelectionMode)
1872        {
1873            s = new Selection.Rect(offset,end);
1874            ((Selection.Rect)s).extraStartVirt = extraStartVirt;
1875            ((Selection.Rect)s).extraEndVirt = extraEndVirt;
1876        }
1877        else
1878            s = new Selection.Range(offset,end);
1879
1880        selectionManager.addToSelection(s);
1881        fireCaretEvent();
1882
1883        if(rectangularSelectionMode && extraEndVirt != 0)
1884        {
1885            int line = getLineOfOffset(end);
1886            scrollTo(line,getLineLength(line) + extraEndVirt,false);
1887        }
1888    } //}}}
1889

1890    //{{{ getSelectedText() method
1891
/**
1892     * Returns the text in the specified selection.
1893     * @param s The selection
1894     * @since jEdit 3.2pre1
1895     */

1896    public String JavaDoc getSelectedText(Selection s)
1897    {
1898        StringBuffer JavaDoc buf = new StringBuffer JavaDoc(s.end - s.start);
1899        s.getText(buffer,buf);
1900        return buf.toString();
1901    } //}}}
1902

1903    //{{{ getSelectedText() method
1904
/**
1905     * Returns the text in all active selections.
1906     * @param separator The string to insert between each text chunk
1907     * (for example, a newline)
1908     * @since jEdit 3.2pre1
1909     */

1910    public String JavaDoc getSelectedText(String JavaDoc separator)
1911    {
1912        Selection[] sel = selectionManager.getSelection();
1913        if(sel.length == 0)
1914            return null;
1915
1916        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1917        for(int i = 0; i < sel.length; i++)
1918        {
1919            if(i != 0)
1920                buf.append(separator);
1921
1922            sel[i].getText(buffer,buf);
1923        }
1924
1925        return buf.toString();
1926    } //}}}
1927

1928    //{{{ getSelectedText() method
1929
/**
1930     * Returns the text in all active selections, with a newline
1931     * between each text chunk.
1932     */

1933    public String JavaDoc getSelectedText()
1934    {
1935        return getSelectedText("\n");
1936    } //}}}
1937

1938    //{{{ setSelectedText() method
1939
/**
1940     * Replaces the selection with the specified text.
1941     * @param s The selection
1942     * @param selectedText The new text
1943     * @since jEdit 3.2pre1
1944     */

1945    public void setSelectedText(Selection s, String JavaDoc selectedText)
1946    {
1947        if(!isEditable())
1948        {
1949            throw new InternalError JavaDoc("Text component"
1950                + " read only");
1951        }
1952
1953        try
1954        {
1955            buffer.beginCompoundEdit();
1956
1957            moveCaretPosition(s.setText(buffer,selectedText));
1958        }
1959        // No matter what happends... stops us from leaving buffer
1960
// in a bad state
1961
finally
1962        {
1963            buffer.endCompoundEdit();
1964        }
1965
1966        // no no no!!!!
1967
//selectNone();
1968
} //}}}
1969

1970    //{{{ setSelectedText() method
1971
/**
1972     * Replaces the selection at the caret with the specified text.
1973     * If there is no selection at the caret, the text is inserted at
1974     * the caret position.
1975     */

1976    public void setSelectedText(String JavaDoc selectedText)
1977    {
1978        int newCaret = replaceSelection(selectedText);
1979        if(newCaret != -1)
1980            moveCaretPosition(newCaret);
1981        selectNone();
1982    } //}}}
1983

1984    //{{{ setSelectedText() method
1985
/**
1986     * Replaces the selection at the caret with the specified text.
1987     * If there is no selection at the caret, the text is inserted at
1988     * the caret position.
1989     * @param selectedText The new selection
1990     * @param moveCaret Move caret to insertion location if necessary
1991     * @since jEdit 4.2pre5
1992     */

1993    public void setSelectedText(String JavaDoc selectedText, boolean moveCaret)
1994    {
1995        int newCaret = replaceSelection(selectedText);
1996        if(moveCaret && newCaret != -1)
1997            moveCaretPosition(newCaret);
1998        selectNone();
1999    } //}}}
2000

2001    //{{{ replaceSelection() method
2002
/**
2003     * Set the selection, but does not deactivate it, and does not move the
2004     * caret.
2005     *
2006     * Please use {@link #setSelectedText(String)} instead.
2007     *
2008     * @param selectedText The new selection
2009     * @return The new caret position
2010     * @since 4.3pre1
2011     */

2012    public int replaceSelection(String JavaDoc selectedText)
2013    {
2014        if(!isEditable())
2015            throw new RuntimeException JavaDoc("Text component read only");
2016
2017        int newCaret = -1;
2018
2019        if(getSelectionCount() == 0)
2020        {
2021            // for compatibility with older jEdit versions
2022
buffer.insert(caret,selectedText);
2023        }
2024        else
2025        {
2026            try
2027            {
2028
2029                buffer.beginCompoundEdit();
2030
2031                Selection[] selection = getSelection();
2032                for(int i = 0; i < selection.length; i++)
2033                    newCaret = selection[i].setText(buffer,selectedText);
2034            }
2035            finally
2036            {
2037                buffer.endCompoundEdit();
2038            }
2039        }
2040
2041        return newCaret;
2042    } //}}}
2043

2044    //{{{ getSelectedLines() method
2045
/**
2046     * Returns a sorted array of line numbers on which a selection or
2047     * selections are present.<p>
2048     *
2049     * This method is the most convenient way to iterate through selected
2050     * lines in a buffer. The line numbers in the array returned by this
2051     * method can be passed as a parameter to such methods as
2052     * {@link JEditBuffer#getLineText(int)}.
2053     *
2054     * @since jEdit 3.2pre1
2055     */

2056    public int[] getSelectedLines()
2057    {
2058        if(selectionManager.getSelectionCount() == 0)
2059            return new int[] { caretLine };
2060
2061        return selectionManager.getSelectedLines();
2062    } //}}}
2063

2064    //}}}
2065

2066    //{{{ Caret
2067

2068    //{{{ caretAutoScroll() method
2069
/**
2070     * Return if change in buffer should scroll this text area.
2071     * @since jEdit 4.3pre2
2072     */

2073    public boolean caretAutoScroll()
2074    {
2075        return focusedComponent == this;
2076    } //}}}
2077

2078    //{{{ addStructureMatcher() method
2079
/**
2080     * Adds a structure matcher.
2081     * @since jEdit 4.2pre3
2082     */

2083    public void addStructureMatcher(StructureMatcher matcher)
2084    {
2085        structureMatchers.add(matcher);
2086    } //}}}
2087

2088    //{{{ removeStructureMatcher() method
2089
/**
2090     * Removes a structure matcher.
2091     * @since jEdit 4.2pre3
2092     */

2093    public void removeStructureMatcher(StructureMatcher matcher)
2094    {
2095        structureMatchers.remove(matcher);
2096    } //}}}
2097

2098    //{{{ getStructureMatchStart() method
2099
/**
2100     * Returns the structure element (bracket, or XML tag, etc) matching the
2101     * one before the caret.
2102     * @since jEdit 4.2pre3
2103     */

2104    public StructureMatcher.Match getStructureMatch()
2105    {
2106        return match;
2107    } //}}}
2108

2109    //{{{ blinkCaret() method
2110
/**
2111     * Blinks the caret.
2112     */

2113    public final void blinkCaret()
2114    {
2115        if(caretBlinks)
2116        {
2117            blink = !blink;
2118            invalidateLine(caretLine);
2119        }
2120        else
2121            blink = true;
2122    } //}}}
2123

2124    //{{{ centerCaret() method
2125
/**
2126     * Centers the caret on the screen.
2127     * @since jEdit 2.7pre2
2128     */

2129    public void centerCaret()
2130    {
2131        int offset = getScreenLineStartOffset(visibleLines >> 1);
2132        if(offset == -1)
2133            getToolkit().beep();
2134        else
2135            setCaretPosition(offset);
2136    } //}}}
2137

2138    //{{{ setCaretPosition() method
2139
/**
2140     * Sets the caret position and deactivates the selection.
2141     * @param newCaret The caret position
2142     */

2143    public void setCaretPosition(int newCaret)
2144    {
2145        selectNone();
2146        moveCaretPosition(newCaret,true);
2147    } //}}}
2148

2149    //{{{ setCaretPosition() method
2150
/**
2151     * Sets the caret position and deactivates the selection.
2152     * @param newCaret The caret position
2153     * @param doElectricScroll Do electric scrolling?
2154     */

2155    public void setCaretPosition(int newCaret, boolean doElectricScroll)
2156    {
2157        selectNone();
2158        moveCaretPosition(newCaret,doElectricScroll);
2159    } //}}}
2160

2161    //{{{ moveCaretPosition() method
2162
/**
2163     * Sets the caret position without deactivating the selection.
2164     * @param newCaret The caret position
2165     */

2166    public void moveCaretPosition(int newCaret)
2167    {
2168        moveCaretPosition(newCaret,true);
2169    } //}}}
2170

2171    //{{{ moveCaretPosition() method
2172
/**
2173     * Sets the caret position without deactivating the selection.
2174     * @param newCaret The caret position
2175     * @param doElectricScroll Do electric scrolling?
2176     */

2177    public void moveCaretPosition(int newCaret, boolean doElectricScroll)
2178    {
2179        moveCaretPosition(newCaret,doElectricScroll ? ELECTRIC_SCROLL
2180            : NORMAL_SCROLL);
2181    } //}}}
2182

2183    //{{{ moveCaretPosition() method
2184
public static final int NO_SCROLL = 0;
2185    public static final int NORMAL_SCROLL = 1;
2186    public static final int ELECTRIC_SCROLL = 2;
2187    /**
2188     * Sets the caret position without deactivating the selection.
2189     * @param newCaret The caret position
2190     * @param scrollMode The scroll mode (NO_SCROLL, NORMAL_SCROLL, or
2191     * ELECTRIC_SCROLL).
2192     * @since jEdit 4.2pre1
2193     */

2194    public void moveCaretPosition(int newCaret, int scrollMode)
2195    {
2196        if(newCaret < 0 || newCaret > buffer.getLength())
2197        {
2198            throw new IllegalArgumentException JavaDoc("caret out of bounds: "
2199                + newCaret);
2200        }
2201
2202        int oldCaretLine = caretLine;
2203
2204        if(caret == newCaret)
2205            finishCaretUpdate(oldCaretLine,scrollMode,false);
2206        else
2207        {
2208            caret = newCaret;
2209            caretLine = getLineOfOffset(newCaret);
2210
2211            magicCaret = -1;
2212
2213            finishCaretUpdate(oldCaretLine,scrollMode,true);
2214        }
2215    } //}}}
2216

2217    //{{{ getCaretPosition() method
2218
/**
2219     * Returns a zero-based index of the caret position.
2220     */

2221    public int getCaretPosition()
2222    {
2223        return caret;
2224    } //}}}
2225

2226    //{{{ getCaretLine() method
2227
/**
2228     * Returns the line number containing the caret.
2229     */

2230    public int getCaretLine()
2231    {
2232        return caretLine;
2233    } //}}}
2234

2235    //{{{ getMagicCaretPosition() method
2236
/**
2237     * Returns an internal position used to keep the caret in one
2238     * column while moving around lines of varying lengths.
2239     * @since jEdit 4.2pre1
2240     */

2241    public int getMagicCaretPosition()
2242    {
2243        if(magicCaret == -1)
2244        {
2245            magicCaret = chunkCache.subregionOffsetToX(
2246                caretLine,caret - getLineStartOffset(caretLine));
2247        }
2248
2249        return magicCaret;
2250    } //}}}
2251

2252    //{{{ setMagicCaretPosition() method
2253
/**
2254     * Sets the `magic' caret position. This can be used to preserve
2255     * the column position when moving up and down lines.
2256     * @param magicCaret The magic caret position
2257     * @since jEdit 4.2pre1
2258     */

2259    public void setMagicCaretPosition(int magicCaret)
2260    {
2261        this.magicCaret = magicCaret;
2262    } //}}}
2263

2264    //{{{ addCaretListener() method
2265
/**
2266     * Adds a caret change listener to this text area.
2267     * @param listener The listener
2268     */

2269    public final void addCaretListener(CaretListener JavaDoc listener)
2270    {
2271        listenerList.add(CaretListener JavaDoc.class,listener);
2272    } //}}}
2273

2274    //{{{ removeCaretListener() method
2275
/**
2276     * Removes a caret change listener from this text area.
2277     * @param listener The listener
2278     */

2279    public final void removeCaretListener(CaretListener JavaDoc listener)
2280    {
2281        listenerList.remove(CaretListener JavaDoc.class,listener);
2282    } //}}}
2283

2284    //{{{ goToNextBracket() method
2285
/**
2286     * Moves the caret to the next closing bracket.
2287     * @param select true if you want to extend selection
2288     * @since jEdit 2.7pre2.
2289     */

2290    public void goToNextBracket(boolean select)
2291    {
2292        int newCaret = -1;
2293
2294        if(caret != buffer.getLength())
2295        {
2296            String JavaDoc text = getText(caret,buffer.getLength()
2297                - caret - 1);
2298
2299loop: for(int i = 0; i < text.length(); i++)
2300            {
2301                switch(text.charAt(i))
2302                {
2303                case ')': case ']': case '}':
2304                    newCaret = caret + i + 1;
2305                    break loop;
2306                }
2307            }
2308        }
2309
2310        if(newCaret == -1)
2311            getToolkit().beep();
2312        else
2313        {
2314            if(select)
2315                extendSelection(caret,newCaret);
2316            else if(!multi)
2317                selectNone();
2318            moveCaretPosition(newCaret);
2319        }
2320    } //}}}
2321

2322    //{{{ goToNextCharacter() method
2323
/**
2324     * Moves the caret to the next character.
2325     * @param select true if you want to extend selection
2326     * @since jEdit 2.7pre2.
2327     */

2328    public void goToNextCharacter(boolean select)
2329    {
2330        Selection s = getSelectionAtOffset(caret);
2331
2332        if(!select && s instanceof Selection.Range)
2333        {
2334            if(multi)
2335            {
2336                if(caret != s.end)
2337                {
2338                    moveCaretPosition(s.end);
2339                    return;
2340                }
2341            }
2342            else
2343            {
2344                setCaretPosition(s.end);
2345                return;
2346            }
2347        }
2348
2349        int extraStartVirt, extraEndVirt;
2350        if(s instanceof Selection.Rect)
2351        {
2352            extraStartVirt = ((Selection.Rect)s).extraStartVirt;
2353            extraEndVirt = ((Selection.Rect)s).extraEndVirt;
2354        }
2355        else
2356        {
2357            extraStartVirt = 0;
2358            extraEndVirt = 0;
2359        }
2360
2361        int newCaret = caret;
2362
2363        if(caret == buffer.getLength())
2364        {
2365            if(select && (rectangularSelectionMode || s instanceof Selection.Rect))
2366            {
2367                if(s != null && caret == s.start)
2368                    extraStartVirt++;
2369                else
2370                    extraEndVirt++;
2371            }
2372            else
2373            {
2374                getToolkit().beep();
2375                return;
2376            }
2377        }
2378        else if(caret == getLineEndOffset(caretLine) - 1)
2379        {
2380            if(select && (rectangularSelectionMode || s instanceof Selection.Rect))
2381            {
2382                if(s != null && caret == s.start)
2383                    extraStartVirt++;
2384                else
2385                    extraEndVirt++;
2386            }
2387            else
2388            {
2389                int line = displayManager.getNextVisibleLine(caretLine);
2390                if(line == -1)
2391                {
2392                    getToolkit().beep();
2393                    return;
2394                }
2395                else
2396                    newCaret = getLineStartOffset(line);
2397            }
2398        }
2399        else
2400            newCaret = caret + 1;
2401
2402        if(select)
2403            extendSelection(caret,newCaret,extraStartVirt,extraEndVirt);
2404        else if(!multi)
2405            selectNone();
2406
2407        moveCaretPosition(newCaret);
2408    } //}}}
2409

2410    //{{{ goToNextLine() method
2411
/**
2412     * Move the caret to the next line.
2413     * @param select true if you want to extend selection
2414     * @since jEdit 2.7pre2
2415     */

2416    public void goToNextLine(boolean select)
2417    {
2418        Selection s = getSelectionAtOffset(caret);
2419        boolean rectSelect = s == null ? rectangularSelectionMode
2420            : s instanceof Selection.Rect;
2421        int magic = getMagicCaretPosition();
2422        int newCaret = chunkCache.getBelowPosition(caretLine,
2423            caret - buffer.getLineStartOffset(caretLine),magic + 1,
2424            rectSelect && select);
2425        if(newCaret == -1)
2426        {
2427            int end = getLineEndOffset(caretLine) - 1;
2428            if(caret == end)
2429            {
2430                getToolkit().beep();
2431                return;
2432            }
2433            else
2434                newCaret = end;
2435        }
2436
2437        _changeLine(select, newCaret);
2438
2439        setMagicCaretPosition(magic);
2440    }//}}}
2441

2442    //{{{ goToNextPage() method
2443
/**
2444     * Moves the caret to the next screenful.
2445     * @param select true if you want to extend selection
2446     * @since jEdit 2.7pre2.
2447     */

2448    public void goToNextPage(boolean select)
2449    {
2450        scrollToCaret(false);
2451        int magic = getMagicCaretPosition();
2452        if(caretLine < displayManager.getFirstVisibleLine())
2453        {
2454            caretLine = displayManager.getNextVisibleLine(
2455                caretLine);
2456        }
2457
2458        int newCaret;
2459
2460        if(getFirstLine() + getVisibleLines() >= displayManager
2461            .getScrollLineCount())
2462        {
2463            int lastVisibleLine = displayManager
2464                .getLastVisibleLine();
2465            newCaret = getLineEndOffset(lastVisibleLine) - 1;
2466        }
2467        else
2468        {
2469            int caretScreenLine = getScreenLineOfOffset(caret);
2470
2471            scrollDownPage();
2472
2473            newCaret = xToScreenLineOffset(caretScreenLine,
2474                magic,true);
2475        }
2476
2477        if(select)
2478            extendSelection(caret,newCaret);
2479        else if(!multi)
2480            selectNone();
2481
2482        moveCaretPosition(newCaret,false);
2483
2484        setMagicCaretPosition(magic);
2485    } //}}}
2486

2487    //{{{ goToNextParagraph() method
2488
/**
2489     * Moves the caret to the start of the next paragraph.
2490     * @param select true if you want to extend selection
2491     * @since jEdit 2.7pre2
2492     */

2493    public void goToNextParagraph(boolean select)
2494    {
2495        int lineNo = getCaretLine();
2496
2497        int newCaret = getBufferLength();
2498
2499        boolean foundBlank = false;
2500
2501loop: for(int i = lineNo + 1; i < getLineCount(); i++)
2502        {
2503            if(!displayManager.isLineVisible(i))
2504                continue;
2505
2506            getLineText(i,lineSegment);
2507
2508            for(int j = 0; j < lineSegment.count; j++)
2509            {
2510                switch(lineSegment.array[lineSegment.offset + j])
2511                {
2512                case ' ':
2513                case '\t':
2514                    break;
2515                default:
2516                    if(foundBlank)
2517                    {
2518                        newCaret = getLineStartOffset(i);
2519                        break loop;
2520                    }
2521                    else
2522                        continue loop;
2523                }
2524            }
2525
2526            foundBlank = true;
2527        }
2528
2529        if(select)
2530            extendSelection(caret,newCaret);
2531        else if(!multi)
2532            selectNone();
2533        moveCaretPosition(newCaret);
2534    } //}}}
2535

2536    //{{{ goToNextWord() method
2537
/**
2538     * Moves the caret to the start of the next word.
2539     * Note that if the "view.eatWhitespace" boolean propery is false,
2540     * this method moves the caret to the end of the current word instead.
2541     * @param select true if you want to extend selection
2542     * @since jEdit 2.7pre2
2543     */

2544    public void goToNextWord(boolean select)
2545    {
2546        goToNextWord(select,false);
2547    } //}}}
2548

2549    //{{{ goToNextWord() method
2550
/**
2551     * Moves the caret to the start of the next word.
2552     * @since jEdit 4.1pre5
2553     */

2554    public void goToNextWord(boolean select, boolean eatWhitespace)
2555    {
2556        int lineStart = getLineStartOffset(caretLine);
2557        int newCaret = caret - lineStart;
2558        String JavaDoc lineText = getLineText(caretLine);
2559
2560        if(newCaret == lineText.length())
2561        {
2562            int nextLine = displayManager.getNextVisibleLine(caretLine);
2563            if(nextLine == -1)
2564            {
2565                getToolkit().beep();
2566                return;
2567            }
2568
2569            newCaret = getLineStartOffset(nextLine);
2570        }
2571        else
2572        {
2573            String JavaDoc noWordSep = buffer.getStringProperty("noWordSep");
2574            newCaret = TextUtilities.findWordEnd(lineText,
2575                newCaret + 1,noWordSep,true,eatWhitespace);
2576
2577            newCaret += lineStart;
2578        }
2579
2580        if(select)
2581            extendSelection(caret,newCaret);
2582        else if(!multi)
2583            selectNone();
2584        moveCaretPosition(newCaret);
2585    } //}}}
2586

2587    //{{{ goToPrevBracket() method
2588
/**
2589     * Moves the caret to the previous bracket.
2590     * @param select true if you want to extend selection
2591     * @since jEdit 2.7pre2
2592     */

2593    public void goToPrevBracket(boolean select)
2594    {
2595        String JavaDoc text = getText(0,caret);
2596
2597        int newCaret = -1;
2598
2599loop: for(int i = getCaretPosition() - 1; i >= 0; i--)
2600        {
2601            switch(text.charAt(i))
2602            {
2603            case '(': case '[': case '{':
2604                newCaret = i;
2605                break loop;
2606            }
2607        }
2608
2609        if(newCaret == -1)
2610            getToolkit().beep();
2611        else
2612        {
2613            if(select)
2614                extendSelection(caret,newCaret);
2615            else if(!multi)
2616                selectNone();
2617            moveCaretPosition(newCaret);
2618        }
2619    } //}}}
2620

2621    //{{{ goToPrevCharacter() method
2622
/**
2623     * Moves the caret to the previous character.
2624     * @param select true if you want to extend selection
2625     * @since jEdit 2.7pre2.
2626     */

2627    public void goToPrevCharacter(boolean select)
2628    {
2629        Selection s = getSelectionAtOffset(caret);
2630
2631        if(caret == 0)
2632        {
2633            getToolkit().beep();
2634            return;
2635        }
2636
2637        if(!select && s instanceof Selection.Range)
2638        {
2639            if(multi)
2640            {
2641                if(caret != s.start)
2642                {
2643                    moveCaretPosition(s.start);
2644                    return;
2645                }
2646            }
2647            else
2648            {
2649                setCaretPosition(s.start);
2650                return;
2651            }
2652        }
2653
2654        int extraStartVirt = 0;
2655        int extraEndVirt = 0;
2656        int newCaret = caret;
2657
2658        if(select && caret == getLineEndOffset(caretLine) - 1)
2659        {
2660            if(s instanceof Selection.Rect)
2661            {
2662                extraStartVirt = ((Selection.Rect)s).extraStartVirt;
2663                extraEndVirt = ((Selection.Rect)s).extraEndVirt;
2664                if(caret == s.start)
2665                {
2666                    if(extraStartVirt == 0)
2667                        newCaret = caret - 1;
2668                    else
2669                        extraStartVirt--;
2670                }
2671                else
2672                {
2673                    if(extraEndVirt == 0)
2674                        newCaret = caret - 1;
2675                    else
2676                        extraEndVirt--;
2677                }
2678            }
2679            else
2680                newCaret = caret - 1;
2681        }
2682        else if(caret == getLineStartOffset(caretLine))
2683        {
2684            int line = displayManager.getPrevVisibleLine(caretLine);
2685            if(line == -1)
2686            {
2687                getToolkit().beep();
2688                return;
2689            }
2690            newCaret = getLineEndOffset(line) - 1;
2691        }
2692        else
2693            newCaret = caret - 1;
2694
2695        if(select)
2696            extendSelection(caret,newCaret,extraStartVirt,extraEndVirt);
2697        else if(!multi)
2698            selectNone();
2699        moveCaretPosition(newCaret);
2700    } //}}}
2701

2702    //{{{ goToPrevLine() method
2703
/**
2704     * Moves the caret to the previous line.
2705     * @param select true if you want to extend selection
2706     * @since jEdit 2.7pre2
2707     */

2708    public void goToPrevLine(boolean select)
2709    {
2710        Selection s = getSelectionAtOffset(caret);
2711        boolean rectSelect = s == null ? rectangularSelectionMode
2712            : s instanceof Selection.Rect;
2713        int magic = getMagicCaretPosition();
2714
2715        int newCaret = chunkCache.getAbovePosition(caretLine,
2716            caret - buffer.getLineStartOffset(caretLine),magic + 1,
2717            rectSelect && select);
2718        if(newCaret == -1)
2719        {
2720            int start = getLineStartOffset(caretLine);
2721            if(caret == start)
2722            {
2723                getToolkit().beep();
2724                return;
2725            }
2726            else
2727                newCaret = start;
2728        }
2729
2730        _changeLine(select, newCaret);
2731
2732        setMagicCaretPosition(magic);
2733    } //}}}
2734

2735    //{{{ goToPrevPage() method
2736
/**
2737     * Moves the caret to the previous screenful.
2738     * @param select true if you want to extend selection
2739     * @since jEdit 2.7pre2
2740     */

2741    public void goToPrevPage(boolean select)
2742    {
2743        scrollToCaret(false);
2744        int magic = getMagicCaretPosition();
2745
2746        if(caretLine < displayManager.getFirstVisibleLine())
2747        {
2748            caretLine = displayManager.getNextVisibleLine(
2749                caretLine);
2750        }
2751
2752        int newCaret;
2753
2754        if(getFirstLine() == 0)
2755        {
2756            int firstVisibleLine = displayManager
2757                .getFirstVisibleLine();
2758            newCaret = getLineStartOffset(firstVisibleLine);
2759        }
2760        else
2761        {
2762            int caretScreenLine = getScreenLineOfOffset(caret);
2763
2764            scrollUpPage();
2765
2766            newCaret = xToScreenLineOffset(caretScreenLine,
2767                magic,true);
2768        }
2769
2770        if(select)
2771            extendSelection(caret,newCaret);
2772        else if(!multi)
2773            selectNone();
2774        moveCaretPosition(newCaret,false);
2775
2776        setMagicCaretPosition(magic);
2777    } //}}}
2778

2779    //{{{ goToPrevParagraph() method
2780
/**
2781     * Moves the caret to the start of the previous paragraph.
2782     * @param select true if you want to extend selection
2783     * @since jEdit 2.7pre2
2784     */

2785    public void goToPrevParagraph(boolean select)
2786    {
2787        int lineNo = caretLine;
2788        int newCaret = 0;
2789
2790        boolean foundBlank = false;
2791
2792loop: for(int i = lineNo - 1; i >= 0; i--)
2793        {
2794            if(!displayManager.isLineVisible(i))
2795                continue;
2796
2797            getLineText(i,lineSegment);
2798
2799            for(int j = 0; j < lineSegment.count; j++)
2800            {
2801                switch(lineSegment.array[lineSegment.offset + j])
2802                {
2803                case ' ':
2804                case '\t':
2805                    break;
2806                default:
2807                    if(foundBlank)
2808                    {
2809                        newCaret = getLineEndOffset(i) - 1;
2810                        break loop;
2811                    }
2812                    else
2813                        continue loop;
2814                }
2815            }
2816
2817            foundBlank = true;
2818        }
2819
2820        if(select)
2821            extendSelection(caret,newCaret);
2822        else if(!multi)
2823            selectNone();
2824        moveCaretPosition(newCaret);
2825    } //}}}
2826

2827    //{{{ goToPrevWord() method
2828
/**
2829     * Moves the caret to the start of the previous word.
2830     * @param select true if you want to extend selection
2831     * @since jEdit 2.7pre2
2832     */

2833    public void goToPrevWord(boolean select)
2834    {
2835        goToPrevWord(select,false);
2836    } //}}}
2837

2838    //{{{ goToPrevWord() method
2839
/**
2840     * Moves the caret to the start of the previous word.
2841     * @since jEdit 4.1pre5
2842     */

2843    public void goToPrevWord(boolean select, boolean eatWhitespace)
2844    {
2845        int lineStart = getLineStartOffset(caretLine);
2846        int newCaret = caret - lineStart;
2847        String JavaDoc lineText = getLineText(caretLine);
2848
2849        if(newCaret == 0)
2850        {
2851            if(lineStart == 0)
2852            {
2853                getToolkit().beep();
2854                return;
2855            }
2856            else
2857            {
2858                int prevLine = displayManager.getPrevVisibleLine(caretLine);
2859                if(prevLine == -1)
2860                {
2861                    getToolkit().beep();
2862                    return;
2863                }
2864
2865                newCaret = getLineEndOffset(prevLine) - 1;
2866            }
2867        }
2868        else
2869        {
2870            String JavaDoc noWordSep = buffer.getStringProperty("noWordSep");
2871            newCaret = TextUtilities.findWordStart(lineText,
2872                newCaret - 1,noWordSep,true,eatWhitespace);
2873
2874            newCaret += lineStart;
2875        }
2876
2877        if(select)
2878            extendSelection(caret,newCaret);
2879        else if(!multi)
2880            selectNone();
2881        moveCaretPosition(newCaret);
2882    } //}}}
2883

2884    //{{{ smartHome() method
2885
/**
2886     * On subsequent invocations, first moves the caret to the first
2887     * non-whitespace character of the line, then the beginning of the
2888     * line, then to the first visible line.
2889     * @param select true if you want to extend selection
2890     * @since jEdit 4.3pre7
2891     */

2892    public void smartHome(boolean select)
2893    {
2894        switch(getInputHandler().getLastActionCount())
2895        {
2896        case 1:
2897            goToStartOfWhiteSpace(select);
2898            break;
2899        case 2:
2900            goToStartOfLine(select);
2901            break;
2902        default: //case 3:
2903
goToFirstVisibleLine(select);
2904            break;
2905        }
2906    } //}}}
2907

2908    //{{{ smartEnd() method
2909
/**
2910     * On subsequent invocations, first moves the caret to the last
2911     * non-whitespace character of the line, then the end of the
2912     * line, then to the last visible line.
2913     * @param select true if you want to extend selection
2914     * @since jEdit 4.3pre7
2915     */

2916    public void smartEnd(boolean select)
2917    {
2918        switch(getInputHandler().getLastActionCount())
2919        {
2920        case 1:
2921            goToEndOfWhiteSpace(select);
2922            break;
2923        case 2:
2924            goToEndOfLine(select);
2925            break;
2926        default: //case 3:
2927
goToLastVisibleLine(select);
2928            break;
2929        }
2930    } //}}}
2931

2932    //{{{ goToStartOfLine() method
2933
/**
2934     * Moves the caret to the beginning of the current line.
2935     * @param select true if you want to extend selection
2936     * @since jEdit 2.7pre2
2937     */

2938    public void goToStartOfLine(boolean select)
2939    {
2940        Selection s = getSelectionAtOffset(caret);
2941        int line = select || s == null ? caretLine : s.startLine;
2942        int newCaret = getLineStartOffset(line);
2943        if(select)
2944            extendSelection(caret,newCaret);
2945        else if(!multi)
2946            selectNone();
2947        moveCaretPosition(newCaret);
2948    } //}}}
2949

2950    //{{{ goToEndOfLine() method
2951
/**
2952     * Moves the caret to the end of the current line.
2953     * @param select true if you want to extend selection
2954     * @since jEdit 2.7pre2
2955     */

2956    public void goToEndOfLine(boolean select)
2957    {
2958        Selection s = getSelectionAtOffset(caret);
2959        int line = select || s == null ? caretLine : s.endLine;
2960        int newCaret = getLineEndOffset(line) - 1;
2961        if(select)
2962            extendSelection(caret,newCaret);
2963        else if(!multi)
2964            selectNone();
2965        moveCaretPosition(newCaret);
2966
2967        // so that end followed by up arrow will always put caret at
2968
// the end of the previous line, for example
2969
//setMagicCaretPosition(Integer.MAX_VALUE);
2970
} //}}}
2971

2972    //{{{ goToStartOfWhiteSpace() method
2973
/**
2974     * Moves the caret to the first non-whitespace character of the current
2975     * line.
2976     * @param select true if you want to extend selection
2977     * @since jEdit 2.7pre2
2978     */

2979    public void goToStartOfWhiteSpace(boolean select)
2980    {
2981        Selection s = getSelectionAtOffset(caret);
2982        int line, offset;
2983        if(select || s == null)
2984        {
2985            line = caretLine;
2986            offset = caret - buffer.getLineStartOffset(line);
2987        }
2988        else
2989        {
2990            line = s.startLine;
2991            offset = s.start - buffer.getLineStartOffset(line);
2992        }
2993
2994        int firstIndent = chunkCache.getSubregionStartOffset(line,offset);
2995        if(firstIndent == getLineStartOffset(line))
2996        {
2997            firstIndent = StandardUtilities.getLeadingWhiteSpace(getLineText(line));
2998            if(firstIndent == getLineLength(line))
2999                firstIndent = 0;
3000            firstIndent += getLineStartOffset(line);
3001        }
3002
3003        if(select)
3004            extendSelection(caret,firstIndent);
3005        else if(!multi)
3006            selectNone();
3007        moveCaretPosition(firstIndent);
3008    } //}}}
3009

3010    //{{{ goToEndOfWhiteSpace() method
3011
/**
3012     * Moves the caret to the last non-whitespace character of the current
3013     * line.
3014     * @param select true if you want to extend selection
3015     * @since jEdit 2.7pre2
3016     */

3017    public void goToEndOfWhiteSpace(boolean select)
3018    {
3019        Selection s = getSelectionAtOffset(caret);
3020        int line, offset;
3021        if(select || s == null)
3022        {
3023            line = caretLine;
3024            offset = caret - getLineStartOffset(line);
3025        }
3026        else
3027        {
3028            line = s.endLine;
3029            offset = s.end - getLineStartOffset(line);
3030        }
3031
3032        int lastIndent = chunkCache.getSubregionEndOffset(line,offset);
3033
3034        if(lastIndent == getLineEndOffset(line))
3035        {
3036            lastIndent = getLineLength(line) - StandardUtilities.getTrailingWhiteSpace(getLineText(line));
3037            if(lastIndent == 0)
3038                lastIndent = getLineLength(line);
3039            lastIndent += getLineStartOffset(line);
3040        }
3041        else
3042        {
3043            lastIndent--;
3044        }
3045
3046        if(select)
3047            extendSelection(caret,lastIndent);
3048        else if(!multi)
3049            selectNone();
3050        moveCaretPosition(lastIndent);
3051    } //}}}
3052

3053    //{{{ goToFirstVisibleLine() method
3054
/**
3055     * Moves the caret to the first visible line.
3056     * @param select true if you want to extend selection
3057     * @since jEdit 2.7pre2
3058     */

3059    public void goToFirstVisibleLine(boolean select)
3060    {
3061        int firstVisibleLine = getFirstLine() == 0 ? 0 : electricScroll;
3062        int firstVisible = getScreenLineStartOffset(firstVisibleLine);
3063        if(firstVisible == -1)
3064        {
3065            firstVisible = getLineStartOffset(displayManager
3066                .getFirstVisibleLine());
3067        }
3068
3069        if(select)
3070            extendSelection(caret,firstVisible);
3071        else if(!multi)
3072            selectNone();
3073        moveCaretPosition(firstVisible);
3074    } //}}}
3075

3076    //{{{ goToLastVisibleLine() method
3077
/**
3078     * Moves the caret to the last visible line.
3079     * @param select true if you want to extend selection
3080     * @since jEdit 2.7pre2
3081     */

3082    public void goToLastVisibleLine(boolean select)
3083    {
3084        int lastVisible;
3085
3086        if(getFirstLine() + visibleLines >=
3087            displayManager.getScrollLineCount())
3088        {
3089            lastVisible = getLineEndOffset(displayManager
3090                .getLastVisibleLine()) - 1;
3091        }
3092        else
3093        {
3094            lastVisible = visibleLines - electricScroll - 1;
3095            if(lastLinePartial)
3096                lastVisible--;
3097            if(lastVisible < 0)
3098                lastVisible = 0;
3099            lastVisible = getScreenLineEndOffset(lastVisible) - 1;
3100            if(lastVisible == -1)
3101            {
3102                lastVisible = getLineEndOffset(displayManager
3103                    .getLastVisibleLine()) - 1;
3104            }
3105        }
3106
3107        if(select)
3108            extendSelection(caret,lastVisible);
3109        else if(!multi)
3110            selectNone();
3111        moveCaretPosition(lastVisible);
3112    } //}}}
3113

3114    //{{{ goToBufferStart() method
3115
/**
3116     * Moves the caret to the beginning of the buffer.
3117     * @param select true if you want to extend selection
3118     * @since jEdit 4.0pre3
3119     */

3120    public void goToBufferStart(boolean select)
3121    {
3122        int start = buffer.getLineStartOffset(
3123            displayManager.getFirstVisibleLine());
3124        if(select)
3125            extendSelection(caret,start);
3126        else if(!multi)
3127            selectNone();
3128        moveCaretPosition(start);
3129    } //}}}
3130

3131    //{{{ goToBufferEnd() method
3132
/**
3133     * Moves the caret to the end of the buffer.
3134     * @param select true if you want to extend selection
3135     * @since jEdit 4.0pre3
3136     */

3137    public void goToBufferEnd(boolean select)
3138    {
3139        int end = buffer.getLineEndOffset(
3140            displayManager.getLastVisibleLine()) - 1;
3141        if(select)
3142            extendSelection(caret,end);
3143        else if(!multi)
3144            selectNone();
3145        moveCaretPosition(end);
3146    } //}}}
3147

3148    //{{{ goToMatchingBracket() method
3149
/**
3150     * Moves the caret to the bracket matching the one before the caret.
3151     * @since jEdit 2.7pre3
3152     */

3153    public void goToMatchingBracket()
3154    {
3155        if(getLineLength(caretLine) != 0)
3156        {
3157            int dot = caret - getLineStartOffset(caretLine);
3158
3159            int bracket = TextUtilities.findMatchingBracket(
3160                buffer,caretLine,Math.max(0,dot - 1));
3161            if(bracket != -1)
3162            {
3163                selectNone();
3164                moveCaretPosition(bracket + 1,false);
3165                return;
3166            }
3167        }
3168
3169        getToolkit().beep();
3170    } //}}}
3171

3172    //}}}
3173

3174    //{{{ User input
3175

3176    //{{{ userInput() method
3177
/**
3178     * Handles the insertion of the specified character. It performs the
3179     * following operations above and beyond simply inserting the text:
3180     * <ul>
3181     * <li>Inserting a TAB with a selection will shift to the right
3182     * <li>Inserting a space with automatic abbrev expansion enabled will
3183     * try to expand the abbrev
3184     * <li>Inserting an indent open/close bracket will re-indent the current
3185     * line as necessary
3186     * </ul>
3187     *
3188     * @param ch The character
3189     * @see #setSelectedText(String)
3190     * @see #isOverwriteEnabled()
3191     * @since jEdit 4.3pre7
3192     */

3193    public void userInput(char ch)
3194    {
3195        if(!isEditable())
3196        {
3197            getToolkit().beep();
3198            return;
3199        }
3200
3201        /* Null before addNotify() */
3202        if(hiddenCursor != null)
3203            getPainter().setCursor(hiddenCursor);
3204
3205        if(ch == '\t')
3206            userInputTab();
3207        else
3208        {
3209            boolean indent = buffer.isElectricKey(ch, caretLine);
3210            String JavaDoc str = String.valueOf(ch);
3211            if(getSelectionCount() == 0)
3212            {
3213                if(!doWordWrap(ch == ' '))
3214                    insert(str,indent);
3215            }
3216            else
3217                replaceSelection(str);
3218        }
3219    } //}}}
3220

3221    //{{{ isOverwriteEnabled() method
3222
/**
3223     * Returns true if overwrite mode is enabled, false otherwise.
3224     */

3225    public final boolean isOverwriteEnabled()
3226    {
3227        return overwrite;
3228    } //}}}
3229

3230    //{{{ setOverwriteEnabled() method
3231
/**
3232     * Sets overwrite mode.
3233     */

3234    public final void setOverwriteEnabled(boolean overwrite)
3235    {
3236        blink = true;
3237        caretTimer.restart();
3238
3239        this.overwrite = overwrite;
3240        invalidateLine(caretLine);
3241        fireStatusChanged(StatusListener.OVERWRITE_CHANGED,overwrite);
3242    } //}}}
3243

3244    //{{{ toggleOverwriteEnabled() method
3245
/**
3246     * Toggles overwrite mode.
3247     * @since jEdit 2.7pre2
3248     */

3249    public final void toggleOverwriteEnabled()
3250    {
3251        setOverwriteEnabled(!overwrite);
3252    } //}}}
3253

3254    //{{{ backspace() method
3255
/**
3256     * Deletes the character before the caret, or the selection, if one is
3257     * active.
3258     * @since jEdit 2.7pre2
3259     */

3260    public void backspace()
3261    {
3262        delete(false);
3263    } //}}}
3264

3265    //{{{ backspaceWord() method
3266
/**
3267     * Deletes the word before the caret.
3268     * @since jEdit 2.7pre2
3269     */

3270    public void backspaceWord()
3271    {
3272        backspaceWord(false);
3273    } //}}}
3274

3275    //{{{ backspaceWord() method
3276
/**
3277     * Deletes the word before the caret.
3278     * @param eatWhitespace If true, will eat whitespace
3279     * @since jEdit 4.2pre5
3280     */

3281    public void backspaceWord(boolean eatWhitespace)
3282    {
3283        if(!buffer.isEditable())
3284        {
3285            getToolkit().beep();
3286            return;
3287        }
3288
3289        if(getSelectionCount() != 0)
3290        {
3291            setSelectedText("");
3292            return;
3293        }
3294
3295        int lineStart = getLineStartOffset(caretLine);
3296        int _caret = caret - lineStart;
3297
3298        String JavaDoc lineText = getLineText(caretLine);
3299
3300        if(_caret == 0)
3301        {
3302            if(lineStart == 0)
3303            {
3304                getToolkit().beep();
3305                return;
3306            }
3307            _caret--;
3308        }
3309        else
3310        {
3311            String JavaDoc noWordSep = buffer.getStringProperty("noWordSep");
3312            _caret = TextUtilities.findWordStart(lineText,_caret-1,
3313                noWordSep,true,eatWhitespace);
3314        }
3315
3316        buffer.remove(_caret + lineStart,
3317            caret - (_caret + lineStart));
3318    } //}}}
3319

3320    //{{{ delete() method
3321
/**
3322     * Deletes the character after the caret.
3323     * @since jEdit 2.7pre2
3324     */

3325    public void delete()
3326    {
3327        delete(true);
3328    } //}}}
3329

3330    //{{{ deleteToEndOfLine() method
3331
/**
3332     * Deletes from the caret to the end of the current line.
3333     * @since jEdit 2.7pre2
3334     */

3335    public void deleteToEndOfLine()
3336    {
3337        if(!buffer.isEditable())
3338        {
3339            getToolkit().beep();
3340            return;
3341        }
3342
3343        buffer.remove(caret,getLineEndOffset(caretLine)
3344            - caret - 1);
3345    } //}}}
3346

3347    //{{{ deleteLine() method
3348
/**
3349     * Deletes the line containing the caret.
3350     * @since jEdit 2.7pre2
3351     */

3352    public void deleteLine()
3353    {
3354        if(!buffer.isEditable())
3355        {
3356            getToolkit().beep();
3357            return;
3358        }
3359
3360        int x = chunkCache.subregionOffsetToX(caretLine,caret - getLineStartOffset(caretLine));
3361        int[] lines = getSelectedLines();
3362
3363        try
3364        {
3365            buffer.beginCompoundEdit();
3366
3367            for (int i = lines.length - 1; i >= 0; i--)
3368            {
3369                int start = getLineStartOffset(lines[i]);
3370                int end = getLineEndOffset(lines[i]);
3371                if (end > buffer.getLength())
3372                {
3373                    if (start != 0)
3374                        start--;
3375                    end--;
3376                }
3377                buffer.remove(start,end - start);
3378            }
3379        }
3380        finally
3381        {
3382            buffer.endCompoundEdit();
3383        }
3384
3385        int lastLine = displayManager.getLastVisibleLine();
3386
3387        if(caretLine == lastLine)
3388        {
3389            int offset = chunkCache.xToSubregionOffset(lastLine,0,x,true);
3390            setCaretPosition(buffer.getLineStartOffset(lastLine)
3391            + offset);
3392        }
3393        else
3394        {
3395            int offset = chunkCache.xToSubregionOffset(caretLine,0,x,true);
3396            setCaretPosition(getLineStartOffset(caretLine) + offset);
3397        }
3398    } //}}}
3399

3400    //{{{ deleteParagraph() method
3401
/**
3402     * Deletes the paragraph containing the caret.
3403     * @since jEdit 2.7pre2
3404     */

3405    public void deleteParagraph()
3406    {
3407        if(!buffer.isEditable())
3408        {
3409            getToolkit().beep();
3410            return;
3411        }
3412
3413        // find the beginning of the paragraph.
3414
int start = 0;
3415        for(int i = caretLine - 1; i >= 0; i--)
3416        {
3417            if (lineContainsSpaceAndTabs(i))
3418            {
3419                start = getLineStartOffset(i);
3420                break;
3421            }
3422        }
3423
3424        // Find the end of the paragraph
3425
int end = buffer.getLength();
3426        for(int i = caretLine + 1; i < getLineCount(); i++)
3427        {
3428            //if(!displayManager.isLineVisible(i))
3429
// continue loop;
3430

3431            if (lineContainsSpaceAndTabs(i))
3432            {
3433                end = getLineEndOffset(i) - 1;
3434                break;
3435            }
3436        }
3437
3438        buffer.remove(start,end - start);
3439    } //}}}
3440

3441    //{{{ deleteToStartOfLine() method
3442
/**
3443     * Deletes from the caret to the beginning of the current line.
3444     * @since jEdit 2.7pre2
3445     */

3446    public void deleteToStartOfLine()
3447    {
3448        if(!buffer.isEditable())
3449        {
3450            getToolkit().beep();
3451            return;
3452        }
3453
3454        buffer.remove(getLineStartOffset(caretLine),
3455            caret - getLineStartOffset(caretLine));
3456    } //}}}
3457

3458    //{{{ deleteWord() method
3459
/**
3460     * Deletes the word in front of the caret.
3461     * @since jEdit 2.7pre2
3462     */

3463    public void deleteWord()
3464    {
3465        deleteWord(false);
3466    } //}}}
3467

3468    //{{{ deleteWord() method
3469
/**
3470     * Deletes the word in front of the caret.
3471     *
3472. * @param eatWhitespace If true, will eat whitespace
3473     * @since jEdit 4.2pre5
3474     */

3475    public void deleteWord(boolean eatWhitespace)
3476    {
3477        if(!buffer.isEditable())
3478        {
3479            getToolkit().beep();
3480            return;
3481        }
3482
3483        if(getSelectionCount() != 0)
3484        {
3485            setSelectedText("");
3486            return;
3487        }
3488
3489        int lineStart = getLineStartOffset(caretLine);
3490        int _caret = caret - lineStart;
3491
3492        String JavaDoc lineText = getLineText(caretLine);
3493
3494        if(_caret == lineText.length())
3495        {
3496            if(lineStart + _caret == buffer.getLength())
3497            {
3498                getToolkit().beep();
3499                return;
3500            }
3501            _caret++;
3502        }
3503        else
3504        {
3505            String JavaDoc noWordSep = buffer.getStringProperty("noWordSep");
3506            _caret = TextUtilities.findWordEnd(lineText,
3507                _caret+1,noWordSep,true,eatWhitespace);
3508        }
3509
3510        buffer.remove(caret,(_caret + lineStart) - caret);
3511    } //}}}
3512

3513    //{{{ isMultipleSelectionEnabled() method
3514
/**
3515     * Returns if multiple selection is enabled.
3516     * @since jEdit 3.2pre1
3517     */

3518    public final boolean isMultipleSelectionEnabled()
3519    {
3520        return multi;
3521    } //}}}
3522

3523    //{{{ toggleMultipleSelectionEnabled() method
3524
/**
3525     * Toggles multiple selection.
3526     * @since jEdit 3.2pre1
3527     */

3528    public final void toggleMultipleSelectionEnabled()
3529    {
3530        setMultipleSelectionEnabled(!multi);
3531    } //}}}
3532

3533    //{{{ setMultipleSelectionEnabled() method
3534
/**
3535     * Set multiple selection on or off according to the value of
3536     * <code>multi</code>. This only affects the ability to
3537     * make multiple selections in the user interface; macros and plugins
3538     * can manipulate them regardless of the setting of this flag. In fact,
3539     * in most cases, calling this method should not be necessary.
3540     *
3541     * @param multi Should multiple selection be enabled?
3542     * @since jEdit 3.2pre1
3543     */

3544    public final void setMultipleSelectionEnabled(boolean multi)
3545    {
3546        this.multi = multi;
3547        fireStatusChanged(StatusListener.MULTI_SELECT_CHANGED,multi);
3548        painter.repaint();
3549    } //}}}
3550

3551    //{{{ isRectangularSelectionEnabled() method
3552
/**
3553     * Returns if rectangular selection is enabled.
3554     * @since jEdit 4.2pre1
3555     */

3556    public final boolean isRectangularSelectionEnabled()
3557    {
3558        return rectangularSelectionMode;
3559    } //}}}
3560

3561    //{{{ toggleRectangularSelectionEnabled() method
3562
/**
3563     * Toggles rectangular selection.
3564     * @since jEdit 4.2pre1
3565     */

3566    public final void toggleRectangularSelectionEnabled()
3567    {
3568        setRectangularSelectionEnabled(!rectangularSelectionMode);
3569
3570        if(getSelectionCount() == 1)
3571        {
3572            Selection s = getSelection(0);
3573            removeFromSelection(s);
3574            if(rectangularSelectionMode)
3575            {
3576                addToSelection(new Selection.Rect(
3577                    s.getStart(),s.getEnd()));
3578            }
3579            else
3580            {
3581                addToSelection(new Selection.Range(
3582                    s.getStart(),s.getEnd()));
3583            }
3584        }
3585    } //}}}
3586

3587    //{{{ setRectangularSelectionEnabled() method
3588
/**
3589     * Set rectangular selection on or off according to the value of
3590     * <code>rectangularSelectionMode</code>. This only affects the ability
3591     * to make multiple selections from the keyboard. A rectangular
3592     * selection can always be created by dragging with the mouse by holding
3593     * down <b>Control</b>, regardless of the state of this flag.
3594     *
3595     * @param rectangularSelectionMode Should rectangular selection be
3596     * enabled?
3597     * @since jEdit 4.2pre1
3598     */

3599    public final void setRectangularSelectionEnabled(
3600        boolean rectangularSelectionMode)
3601    {
3602        this.rectangularSelectionMode = rectangularSelectionMode;
3603        fireStatusChanged(StatusListener.RECT_SELECT_CHANGED,
3604            rectangularSelectionMode);
3605        painter.repaint();
3606    } //}}}
3607

3608    //}}}
3609

3610    //{{{ Folding
3611

3612    //{{{ goToParentFold() method
3613
/**
3614     * Moves the caret to the fold containing the one at the caret
3615     * position.
3616     * @since jEdit 4.0pre3
3617     */

3618    public void goToParentFold()
3619    {
3620        int line = -1;
3621        int level = buffer.getFoldLevel(caretLine);
3622        for(int i = caretLine - 1; i >= 0; i--)
3623        {
3624            if(buffer.getFoldLevel(i) < level)
3625            {
3626                line = i;
3627                break;
3628            }
3629        }
3630
3631        if(line == -1)
3632        {
3633            getToolkit().beep();
3634            return;
3635        }
3636
3637        int magic = getMagicCaretPosition();
3638
3639        int newCaret = buffer.getLineStartOffset(line)
3640            + chunkCache.xToSubregionOffset(line,0,magic + 1,true);
3641        if(!multi)
3642            selectNone();
3643
3644        moveCaretPosition(newCaret);
3645        setMagicCaretPosition(magic);
3646    } //}}}
3647

3648    //{{{ goToNextFold() method
3649
/**
3650     * Moves the caret to the next fold.
3651     * @param select true if you want to extend selection
3652     * @since jEdit 4.0pre3
3653     */

3654    public void goToNextFold(boolean select)
3655    {
3656        int nextFold = -1;
3657        for(int i = caretLine + 1; i < buffer.getLineCount(); i++)
3658        {
3659            if(buffer.isFoldStart(i)
3660                && displayManager.isLineVisible(i))
3661            {
3662                nextFold = i;
3663                break;
3664            }
3665        }
3666
3667        if(nextFold == -1)
3668        {
3669            getToolkit().beep();
3670            return;
3671        }
3672
3673        int magic = getMagicCaretPosition();
3674
3675        int newCaret = buffer.getLineStartOffset(nextFold)
3676            + chunkCache.xToSubregionOffset(nextFold,0,magic + 1,true);
3677        if(select)
3678            extendSelection(caret,newCaret);
3679        else if(!multi)
3680            selectNone();
3681
3682        moveCaretPosition(newCaret);
3683        setMagicCaretPosition(magic);
3684    } //}}}
3685

3686    //{{{ goToPrevFold() method
3687
/**
3688     * Moves the caret to the previous fold.
3689     * @param select true if you want to extend selection
3690     * @since jEdit 4.0pre3
3691     */

3692    public void goToPrevFold(boolean select)
3693    {
3694        int prevFold = -1;
3695        for(int i = caretLine - 1; i >= 0; i--)
3696        {
3697            if(buffer.isFoldStart(i)
3698                && displayManager.isLineVisible(i))
3699            {
3700                prevFold = i;
3701                break;
3702            }
3703        }
3704
3705        if(prevFold == -1)
3706        {
3707            getToolkit().beep();
3708            return;
3709        }
3710
3711        int magic = getMagicCaretPosition();
3712
3713        int newCaret = buffer.getLineStartOffset(prevFold)
3714            + chunkCache.xToSubregionOffset(prevFold,0,magic + 1,true);
3715        if(select)
3716            extendSelection(caret,newCaret);
3717        else if(!multi)
3718            selectNone();
3719
3720        moveCaretPosition(newCaret);
3721        setMagicCaretPosition(magic);
3722    } //}}}
3723

3724    //{{{ collapseFold() method
3725
/**
3726     * Like {@link DisplayManager#collapseFold(int)}, but
3727     * also moves the caret to the first line of the fold.
3728     * @since jEdit 4.0pre3
3729     */

3730    public void collapseFold()
3731    {
3732        collapseFold(caretLine);
3733    } //}}}
3734

3735    //{{{ collapseFold() method
3736
/**
3737     * Like {@link DisplayManager#collapseFold(int)}, but
3738     * also moves the caret to the first line of the fold.
3739     * @since jEdit 4.3pre7
3740     */

3741    public void collapseFold(int line)
3742    {
3743        int x = chunkCache.subregionOffsetToX(caretLine,
3744            caret - getLineStartOffset(caretLine));
3745
3746        displayManager.collapseFold(line);
3747
3748        if(displayManager.isLineVisible(caretLine))
3749            return;
3750
3751        line = displayManager.getPrevVisibleLine(caretLine);
3752
3753        if(!multi)
3754        {
3755            // cannot use selectNone() beacause the finishCaretUpdate method will reopen the fold
3756
invalidateSelectedLines();
3757            selectionManager.setSelection((Selection) null);
3758        }
3759        moveCaretPosition(buffer.getLineStartOffset(line)
3760            + chunkCache.xToSubregionOffset(line,0,x,true));
3761    } //}}}
3762

3763    //{{{ expandFold() method
3764
/**
3765     * Like {@link DisplayManager#expandFold(int,boolean)}, but
3766     * also moves the caret to the first sub-fold.
3767     * @since jEdit 4.0pre3
3768     */

3769    public void expandFold(boolean fully)
3770    {
3771        int x = chunkCache.subregionOffsetToX(caretLine,
3772            caret - getLineStartOffset(caretLine));
3773
3774        int line = displayManager.expandFold(caretLine,fully);
3775
3776        if(!fully && line != -1)
3777        {
3778            if(!multi)
3779                selectNone();
3780            moveCaretPosition(getLineStartOffset(line)
3781                + chunkCache.xToSubregionOffset(line,0,x,true));
3782        }
3783    } //}}}
3784

3785    //{{{ selectFold() method
3786
/**
3787     * Selects the fold that contains the caret line number.
3788     * @since jEdit 3.1pre3
3789     */

3790    public void selectFold()
3791    {
3792        selectFold(caretLine);
3793    } //}}}
3794

3795    //{{{ selectFold() method
3796
/**
3797     * Selects the fold that contains the specified line number.
3798     * @param line The line number
3799     * @since jEdit 4.0pre1
3800     */

3801    public void selectFold(int line)
3802    {
3803        int[] lines = buffer.getFoldAtLine(line);
3804
3805        int newCaret = getLineEndOffset(lines[1]) - 1;
3806        Selection s = new Selection.Range(getLineStartOffset(lines[0]),newCaret);
3807        if(multi)
3808            addToSelection(s);
3809        else
3810            setSelection(s);
3811        moveCaretPosition(newCaret);
3812    } //}}}
3813

3814    //{{{ narrowToFold() method
3815
/**
3816     * Hides all lines except those in the fold containing the caret.
3817     * @since jEdit 4.0pre1
3818     */

3819    public void narrowToFold()
3820    {
3821        int[] lines = buffer.getFoldAtLine(caretLine);
3822        if(lines[0] == 0 && lines[1] == buffer.getLineCount() - 1)
3823            getToolkit().beep();
3824        else
3825            displayManager.narrow(lines[0],lines[1]);
3826    } //}}}
3827

3828    //{{{ narrowToSelection() method
3829
/**
3830     * Hides all lines except those in the selection.
3831     * @since jEdit 4.0pre1
3832     */

3833    public void narrowToSelection()
3834    {
3835        if(getSelectionCount() != 1)
3836        {
3837            getToolkit().beep();
3838            return;
3839        }
3840
3841        Selection sel = getSelection(0);
3842        displayManager.narrow(sel.getStartLine(),sel.getEndLine());
3843
3844        selectNone();
3845    } //}}}
3846

3847    //{{{ addExplicitFold() method
3848
/**
3849     * Surrounds the selection with explicit fold markers.
3850     * @since jEdit 4.0pre3
3851     */

3852    public void addExplicitFold() throws TextAreaException
3853    {
3854        if(!buffer.isEditable())
3855        {
3856            getToolkit().beep();
3857            return;
3858        }
3859        if(!buffer.getStringProperty("folding").equals("explicit"))
3860        {
3861            throw new TextAreaException("folding-not-explicit");
3862        }
3863
3864        try
3865        {
3866            buffer.beginCompoundEdit();
3867
3868            if (getSelectionCount() == 0)
3869            {
3870                addExplicitFold(caret, caret, caretLine, caretLine);
3871            }
3872            else
3873            {
3874                Selection[] selections = getSelection();
3875                Selection selection = null;
3876                int caretBack = 0;
3877                for (int i = 0; i < selections.length; i++)
3878                {
3879                    selection = selections[i];
3880                    caretBack = addExplicitFold(selection.start, selection.end, selection.startLine,selection.endLine);
3881                }
3882                // Selection cannot be null because there is at least 1 selection
3883
assert selection != null;
3884                setCaretPosition(selection.start - caretBack, false);
3885            }
3886        }
3887        finally
3888        {
3889            buffer.endCompoundEdit();
3890        }
3891    } //}}}
3892
//}}}
3893

3894    //{{{ Text editing
3895

3896    //{{{ lineComment() method
3897
/**
3898     * Prepends each line of the selection with the line comment string.
3899     * @since jEdit 3.2pre1
3900     */

3901    public void lineComment()
3902    {
3903        String JavaDoc comment = buffer.getContextSensitiveProperty(caret,"lineComment");
3904        if(!buffer.isEditable() || comment == null || comment.length() == 0)
3905        {
3906            getToolkit().beep();
3907            return;
3908        }
3909
3910        comment += ' ';
3911
3912        buffer.beginCompoundEdit();
3913
3914        int[] lines = getSelectedLines();
3915
3916        try
3917        {
3918            for(int i = 0; i < lines.length; i++)
3919            {
3920                String JavaDoc text = getLineText(lines[i]);
3921                buffer.insert(getLineStartOffset(lines[i])
3922                    + StandardUtilities.getLeadingWhiteSpace(text),
3923                    comment);
3924            }
3925        }
3926        finally
3927        {
3928            buffer.endCompoundEdit();
3929        }
3930
3931        selectNone();
3932    } //}}}
3933

3934    //{{{ rangeComment() method
3935
/**
3936     * Adds comment start and end strings to the beginning and end of the
3937     * selection.
3938     * @since jEdit 3.2pre1
3939     */

3940    public void rangeComment()
3941    {
3942        String JavaDoc commentStart = buffer.getContextSensitiveProperty(caret,"commentStart");
3943        String JavaDoc commentEnd = buffer.getContextSensitiveProperty(caret,"commentEnd");
3944        if(!buffer.isEditable() || commentStart == null || commentEnd == null
3945            || commentStart.length() == 0 || commentEnd.length() == 0)
3946        {
3947            getToolkit().beep();
3948            return;
3949        }
3950
3951        commentStart += ' ';
3952        commentEnd = ' ' + commentEnd;
3953
3954        try
3955        {
3956            buffer.beginCompoundEdit();
3957
3958            Selection[] selection = getSelection();
3959
3960            if(selection.length == 0)
3961            {
3962                int oldCaret = caret;
3963                buffer.insert(caret,commentStart);
3964                buffer.insert(caret,commentEnd);
3965                setCaretPosition(oldCaret + commentStart.length());
3966            }
3967
3968            for(int i = 0; i < selection.length; i++)
3969            {
3970                Selection s = selection[i];
3971                if(s instanceof Selection.Range)
3972                {
3973                    buffer.insert(s.start,commentStart);
3974                    buffer.insert(s.end,commentEnd);
3975                }
3976                else if(s instanceof Selection.Rect)
3977                {
3978                    Selection.Rect rect = (Selection.Rect)s;
3979                    int start = rect.getStartColumn(buffer);
3980                    int end = rect.getEndColumn(buffer);
3981
3982                    for(int j = s.startLine; j <= s.endLine; j++)
3983                    {
3984                        buffer.insertAtColumn(j,end,
3985                            commentEnd);
3986                        buffer.insertAtColumn(j,start,
3987                            commentStart);
3988                    }
3989                }
3990            }
3991
3992            selectNone();
3993        }
3994        finally
3995        {
3996            buffer.endCompoundEdit();
3997        }
3998    } //}}}
3999

4000    //{{{ formatParagraph() method
4001
/**
4002     * Formats the paragraph containing the caret.
4003     * @since jEdit 2.7pre2
4004     */

4005    public void formatParagraph() throws TextAreaException
4006    {
4007        if(!buffer.isEditable())
4008        {
4009            getToolkit().beep();
4010            return;
4011        }
4012
4013        if(maxLineLen <= 0)
4014        {
4015            throw new TextAreaException("format-maxlinelen");
4016        }
4017
4018        Selection[] selection = getSelection();
4019        if(selection.length != 0)
4020        {
4021            buffer.beginCompoundEdit();
4022
4023            for(int i = 0; i < selection.length; i++)
4024            {
4025                Selection s = selection[i];
4026                setSelectedText(s,TextUtilities.format(
4027                    getSelectedText(s),maxLineLen,
4028                    buffer.getTabSize()));
4029            }
4030
4031            buffer.endCompoundEdit();
4032        }
4033        else
4034        {
4035            int lineNo = getCaretLine();
4036
4037            int start = 0, end = buffer.getLength();
4038
4039            for(int i = lineNo - 1; i >= 0; i--)
4040            {
4041                if (lineContainsSpaceAndTabs(i))
4042                {
4043                    start = getLineEndOffset(i);
4044                    break;
4045                }
4046            }
4047
4048            for(int i = lineNo + 1; i < getLineCount(); i++)
4049            {
4050                if (lineContainsSpaceAndTabs(i))
4051                {
4052                    end = getLineStartOffset(i) - 1;
4053                    break;
4054                }
4055            }
4056
4057            try
4058            {
4059                buffer.beginCompoundEdit();
4060
4061                String JavaDoc text = buffer.getText(start,end - start);
4062                int offset = getCaretPosition() - start;
4063                int noSpaceOffset = TextUtilities.indexIgnoringWhitespace(
4064                    text,offset);
4065                buffer.remove(start,end - start);
4066                text = TextUtilities.format(
4067                    text,maxLineLen,buffer.getTabSize());
4068                buffer.insert(start,text);
4069                int caretPos = start;
4070                if (text.length() != 0)
4071                {
4072                    caretPos += Math.min(text.length(),
4073                    TextUtilities.ignoringWhitespaceIndex(
4074                    text,noSpaceOffset));
4075                }
4076                moveCaretPosition(caretPos);
4077            }
4078            finally
4079            {
4080                buffer.endCompoundEdit();
4081            }
4082        }
4083    } //}}}
4084

4085    //{{{ spacesToTabs() method
4086
/**
4087     * Converts spaces to tabs in the selection.
4088     * @since jEdit 2.7pre2
4089     */

4090    public void spacesToTabs()
4091    {
4092        Selection[] selection = getSelection();
4093
4094        if(!buffer.isEditable())
4095        {
4096            getToolkit().beep();
4097            return;
4098        }
4099
4100        buffer.beginCompoundEdit();
4101
4102        if(selection.length == 0)
4103        {
4104            setText(TextUtilities.spacesToTabs(
4105                getText(), buffer.getTabSize()));
4106        }
4107        else
4108        {
4109            for(int i = 0; i < selection.length; i++)
4110            {
4111                Selection s = selection[i];
4112                setSelectedText(s,TextUtilities.spacesToTabs(
4113                    getSelectedText(s),buffer.getTabSize()));
4114            }
4115        }
4116
4117        buffer.endCompoundEdit();
4118    } //}}}
4119

4120    //{{{ tabsToSpaces() method
4121
/**
4122     * Converts tabs to spaces in the selection.
4123     * @since jEdit 2.7pre2
4124     */

4125    public void tabsToSpaces()
4126    {
4127        Selection[] selection = getSelection();
4128
4129        if(!buffer.isEditable())
4130        {
4131            getToolkit().beep();
4132            return;
4133        }
4134
4135        buffer.beginCompoundEdit();
4136
4137        if(selection.length == 0)
4138        {
4139            setText(TextUtilities.tabsToSpaces(
4140                getText(), buffer.getTabSize()));
4141        }
4142        else
4143        {
4144            for(int i = 0; i < selection.length; i++)
4145            {
4146                Selection s = selection[i];
4147                setSelectedText(s,TextUtilities.tabsToSpaces(
4148                    getSelectedText(s),buffer.getTabSize()));
4149            }
4150        }
4151
4152        buffer.endCompoundEdit();
4153    } //}}}
4154

4155    //{{{ toUpperCase() method
4156
/**
4157     * Converts the selected text to upper case.
4158     * @since jEdit 2.7pre2
4159     */

4160    public void toUpperCase()
4161    {
4162        if(!buffer.isEditable())
4163        {
4164            getToolkit().beep();
4165            return;
4166        }
4167
4168        Selection[] selection = getSelection();
4169        int caret = -1;
4170        if (selection.length == 0)
4171        {
4172            caret = getCaretPosition();
4173            selectWord();
4174            selection = getSelection();
4175        }
4176        if (selection.length == 0)
4177        {
4178            if (caret != -1)
4179                setCaretPosition(caret);
4180            getToolkit().beep();
4181            return;
4182        }
4183
4184        buffer.beginCompoundEdit();
4185
4186        for(int i = 0; i < selection.length; i++)
4187        {
4188            Selection s = selection[i];
4189            setSelectedText(s,getSelectedText(s).toUpperCase());
4190        }
4191
4192        buffer.endCompoundEdit();
4193        if (caret != -1)
4194            setCaretPosition(caret);
4195    } //}}}
4196

4197    //{{{ toLowerCase() method
4198
/**
4199     * Converts the selected text to lower case.
4200     * @since jEdit 2.7pre2
4201     */

4202    public void toLowerCase()
4203    {
4204        if(!buffer.isEditable())
4205        {
4206            getToolkit().beep();
4207            return;
4208        }
4209
4210        Selection[] selection = getSelection();
4211        int caret = -1;
4212        if (selection.length == 0)
4213        {
4214            caret = getCaretPosition();
4215            selectWord();
4216            selection = getSelection();
4217        }
4218        if (selection.length == 0)
4219        {
4220            if (caret != -1)
4221                setCaretPosition(caret);
4222            getToolkit().beep();
4223            return;
4224        }
4225
4226        buffer.beginCompoundEdit();
4227
4228        for (int i = 0; i < selection.length; i++)
4229        {
4230            Selection s = selection[i];
4231            setSelectedText(s,getSelectedText(s).toLowerCase());
4232        }
4233
4234        buffer.endCompoundEdit();
4235        if (caret != -1)
4236            setCaretPosition(caret);
4237    } //}}}
4238

4239    //{{{ removeTrailingWhiteSpace() method
4240
/**
4241     * Removes trailing whitespace from all lines in the selection.
4242     * @since jEdit 2.7pre2
4243     */

4244    public void removeTrailingWhiteSpace()
4245    {
4246        if(!buffer.isEditable())
4247            getToolkit().beep();
4248        else
4249        {
4250            buffer.removeTrailingWhiteSpace(getSelectedLines());
4251        }
4252    } //}}}
4253

4254    //{{{ insertEnterAndIndent() method
4255
public void insertEnterAndIndent()
4256    {
4257        if(!isEditable())
4258            getToolkit().beep();
4259        else
4260        {
4261            try
4262            {
4263                buffer.beginCompoundEdit();
4264                setSelectedText("\n");
4265                buffer.indentLine(caretLine,true);
4266            }
4267            finally
4268            {
4269                buffer.endCompoundEdit();
4270            }
4271        }
4272    } //}}}
4273

4274    //{{{ insertTabAndIndent() method
4275
public void insertTabAndIndent()
4276    {
4277        if(!isEditable())
4278        {
4279            getToolkit().beep();
4280            return;
4281        }
4282
4283        if(getSelectionCount() == 0)
4284        {
4285            // if caret is inside leading whitespace, indent.
4286
String JavaDoc text = buffer.getLineText(caretLine);
4287            int start = buffer.getLineStartOffset(caretLine);
4288            int whiteSpace = StandardUtilities.getLeadingWhiteSpace(text);
4289
4290            if(caret - start <= whiteSpace
4291                && buffer.indentLine(caretLine,false))
4292                return;
4293        }
4294
4295        userInput('\t');
4296    } //}}}
4297

4298    //{{{ indentSelectedLines() method
4299
/**
4300     * Indents all selected lines.
4301     * @since jEdit 3.1pre3
4302     */

4303    public void indentSelectedLines()
4304    {
4305        if(!buffer.isEditable())
4306            getToolkit().beep();
4307        else
4308        {
4309            buffer.indentLines(getSelectedLines());
4310            selectNone();
4311        }
4312    } //}}}
4313

4314    //{{{ shiftIndentLeft() method
4315
/**
4316     * Shifts the indent to the left.
4317     * @since jEdit 2.7pre2
4318     */

4319    public void shiftIndentLeft()
4320    {
4321        if(!buffer.isEditable())
4322            getToolkit().beep();
4323        else
4324        {
4325            buffer.shiftIndentLeft(getSelectedLines());
4326        }
4327    } //}}}
4328

4329    //{{{ shiftIndentRight() method
4330
/**
4331     * Shifts the indent to the right.
4332     * @since jEdit 2.7pre2
4333     */

4334    public void shiftIndentRight()
4335    {
4336        if(!buffer.isEditable())
4337            getToolkit().beep();
4338        else
4339            buffer.shiftIndentRight(getSelectedLines());
4340    } //}}}
4341

4342    //{{{ joinLines() method
4343
/**
4344     * Joins the current and the next line.
4345     * @since jEdit 2.7pre2
4346     */

4347    public void joinLines()
4348    {
4349        if (getSelectionCount() == 0)
4350        {
4351            int end = getLineEndOffset(caretLine);
4352            if(!buffer.isEditable() || end > buffer.getLength())
4353            {
4354                getToolkit().beep();
4355                return;
4356            }
4357
4358            try
4359            {
4360                buffer.beginCompoundEdit();
4361                String JavaDoc nextLineText = buffer.getLineText(caretLine + 1);
4362                buffer.remove(end - 1,StandardUtilities.getLeadingWhiteSpace(
4363                    nextLineText) + 1);
4364                if (nextLineText.length() != 0)
4365                    buffer.insert(end - 1, " ");
4366            }
4367            finally
4368            {
4369                buffer.endCompoundEdit();
4370            }
4371            setCaretPosition(end - 1);
4372        }
4373        else
4374        {
4375            try
4376            {
4377                buffer.beginCompoundEdit();
4378                Selection[] selections = selectionManager.getSelection();
4379                for (int i = 0; i < selections.length; i++)
4380                {
4381                    Selection selection = selections[i];
4382                    joinLines(selection);
4383                }
4384            }
4385            finally
4386            {
4387                buffer.endCompoundEdit();
4388            }
4389        }
4390    } //}}}
4391

4392    //{{{ joinLines() method
4393
/**
4394     * Join the lines in the selection.
4395     * If you use this method you have to lock the buffer in compound edit mode
4396     *
4397     * @param selection the selection
4398     * @since jEdit 4.3pre8
4399     */

4400    private void joinLines(Selection selection)
4401    {
4402        do
4403        {
4404            if (selection.startLine == buffer.getLineCount() - 1)
4405                return;
4406            int end = getLineEndOffset(selection.startLine);
4407            String JavaDoc nextLineText = buffer.getLineText(selection.startLine + 1);
4408            buffer.remove(end - 1,StandardUtilities.getLeadingWhiteSpace(
4409                nextLineText) + 1);
4410            if (nextLineText.length() != 0)
4411                buffer.insert(end - 1, " ");
4412        }
4413        while (selection.startLine < selection.endLine);
4414    } //}}}
4415
//}}}
4416

4417    //{{{ AWT stuff
4418

4419    //{{{ addLeftOfScrollBar() method
4420
/**
4421     * Adds a component to the box left of the vertical scroll bar. The
4422     * ErrorList plugin uses this to show a global error overview, for
4423     * example.
4424     *
4425     * @param comp The component
4426     * @since jEdit 4.2pre1
4427     */

4428    public void addLeftOfScrollBar(Component comp)
4429    {
4430        verticalBox.add(comp,verticalBox.getComponentCount() - 1);
4431    } //}}}
4432

4433    //{{{ removeLeftOfScrollBar() method
4434
/**
4435     * Removes a component from the box left of the vertical scroll bar.
4436     *
4437     * @param comp The component
4438     * @since jEdit 4.2pre1
4439     */

4440    public void removeLeftOfScrollBar(Component comp)
4441    {
4442        verticalBox.remove(comp);
4443    } //}}}
4444

4445    //{{{ addNotify() method
4446
/**
4447     * Called by the AWT when this component is added to a parent.
4448     * Adds document listener.
4449     */

4450    public void addNotify()
4451    {
4452        super.addNotify();
4453
4454        ToolTipManager.sharedInstance().registerComponent(painter);
4455        ToolTipManager.sharedInstance().registerComponent(gutter);
4456
4457        recalculateVisibleLines();
4458        if(!buffer.isLoading())
4459            recalculateLastPhysicalLine();
4460        propertiesChanged();
4461
4462        hiddenCursor = getToolkit().createCustomCursor(
4463            getGraphicsConfiguration()
4464            .createCompatibleImage(16,16,
4465            Transparency.BITMASK),
4466            new Point(0,0),"Hidden");
4467    } //}}}
4468

4469    //{{{ removeNotify() method
4470
/**
4471     * Called by the AWT when this component is removed from it's parent.
4472     * This clears the pointer to the currently focused component.
4473     * Also removes document listener.
4474     */

4475    public void removeNotify()
4476    {
4477        super.removeNotify();
4478
4479        ToolTipManager.sharedInstance().unregisterComponent(painter);
4480        ToolTipManager.sharedInstance().unregisterComponent(gutter);
4481
4482        if(focusedComponent == this)
4483            focusedComponent = null;
4484    } //}}}
4485

4486    //{{{ getFocusTraversalKeysEnabled() method
4487
/**
4488     * Java 1.4 compatibility fix to make Tab key work.
4489     * @since jEdit 3.2pre4
4490     */

4491    public boolean getFocusTraversalKeysEnabled()
4492    {
4493        return false;
4494    } //}}}
4495

4496    //{{{ getFocusCycleRoot() method
4497
/**
4498     * Java 1.4 compatibility fix to make Tab traversal work in a sane
4499     * manner.
4500     * @since jEdit 4.2pre3
4501     */

4502    public boolean getFocusCycleRoot()
4503    {
4504        return true;
4505    } //}}}
4506

4507    //{{{ processKeyEvent() method
4508
public void processKeyEvent(KeyEvent evt)
4509    {
4510        getInputHandler().processKeyEvent(evt,1, false);
4511        if(!evt.isConsumed())
4512            super.processKeyEvent(evt);
4513
4514    } //}}}
4515

4516    //{{{ addTopComponent() method
4517
/**
4518     * Adds a component above the gutter, text area, and vertical scroll bar.
4519     *
4520     * @since jEdit 4.2pre3
4521     */

4522    public void addTopComponent(Component comp)
4523    {
4524        add(ScrollLayout.TOP,comp);
4525    } //}}}
4526

4527    //{{{ removeTopComponent() method
4528
/**
4529     * Removes a component from above the gutter, text area, and vertical scroll bar.
4530     *
4531     * @since jEdit 4.2pre3
4532     */

4533    public void removeTopComponent(Component comp)
4534    {
4535        remove(comp);
4536    } //}}}
4537

4538    //{{{ Input method support
4539
private InputMethodSupport inputMethodSupport;
4540    public InputMethodRequests JavaDoc getInputMethodRequests()
4541    {
4542        if(inputMethodSupport == null)
4543        {
4544            inputMethodSupport = new InputMethodSupport(this);
4545            Log.log(Log.DEBUG, this, "InputMethodSupport is activated");
4546        }
4547        return inputMethodSupport;
4548    } //}}}
4549
//}}}
4550

4551    //{{{ addStatusListener() method
4552
/**
4553     * Adds a scroll listener to this text area.
4554     * @param listener The listener
4555     * @since jEdit 4.3pre2
4556     */

4557    public final void addStatusListener(StatusListener listener)
4558    {
4559        listenerList.add(StatusListener.class,listener);
4560    } //}}}
4561

4562    //{{{ removeStatusListener() method
4563
/**
4564     * Removes a scroll listener from this text area.
4565     * @param listener The listener
4566     * @since jEdit 4.3pre2
4567     */

4568    public final void removeStatusListener(StatusListener listener)
4569    {
4570        listenerList.remove(StatusListener.class,listener);
4571    } //}}}
4572

4573    //{{{ propertiesChanged() method
4574
/**
4575     * Called by jEdit when necessary. Plugins should not call this method.
4576     */

4577    public void propertiesChanged()
4578    {
4579        if(buffer == null)
4580            return;
4581
4582        int _tabSize = buffer.getTabSize();
4583        char[] foo = new char[_tabSize];
4584        for(int i = 0; i < foo.length; i++)
4585            foo[i] = ' ';
4586
4587        tabSize = painter.getStringWidth(new String JavaDoc(foo));
4588
4589        charWidth = (int)Math.round(
4590            painter.getFont().getStringBounds(foo,0,1,
4591            painter.getFontRenderContext()).getWidth());
4592
4593        String JavaDoc oldWrap = wrap;
4594        wrap = buffer.getStringProperty("wrap");
4595        hardWrap = wrap.equals("hard");
4596        softWrap = wrap.equals("soft");
4597        boolean oldWrapToWidth = wrapToWidth;
4598        int oldWrapMargin = wrapMargin;
4599        setMaxLineLength(buffer.getIntegerProperty("maxLineLen",0));
4600
4601        boolean wrapSettingsChanged = !(wrap.equals(oldWrap)
4602            && oldWrapToWidth == wrapToWidth
4603            && oldWrapMargin == wrapMargin);
4604
4605        if(displayManager != null && !bufferChanging
4606            && !buffer.isLoading() && wrapSettingsChanged)
4607        {
4608            displayManager.invalidateScreenLineCounts();
4609            displayManager.notifyScreenLineChanges();
4610        }
4611
4612        repaintMgr.setFastScroll(false);
4613        chunkCache.invalidateAll();
4614        gutter.repaint();
4615        painter.repaint();
4616    } //}}}
4617

4618    //{{{ Deprecated methods
4619

4620    //{{{ getSelectionStart() method
4621
/**
4622     * @deprecated Instead, obtain a Selection instance using
4623     * any means, and call its <code>getStart()</code> method
4624     */

4625    public final int getSelectionStart()
4626    {
4627        if(getSelectionCount() != 1)
4628            return caret;
4629
4630        return getSelection(0).getStart();
4631    } //}}}
4632

4633    //{{{ getSelectionStart() method
4634
/**
4635     * @deprecated Instead, obtain a Selection instance using
4636     * any means, and call its <code>getStart(int)</code> method
4637     */

4638    public int getSelectionStart(int line)
4639    {
4640        if(getSelectionCount() != 1)
4641            return caret;
4642
4643        return getSelection(0).getStart(buffer,line);
4644    } //}}}
4645

4646    //{{{ getSelectionStartLine() method
4647
/**
4648     * @deprecated Instead, obtain a Selection instance using
4649     * any means, and call its <code>getStartLine()</code> method
4650     */

4651    public final int getSelectionStartLine()
4652    {
4653        if(getSelectionCount() != 1)
4654            return caret;
4655
4656        return getSelection(0).getStartLine();
4657    } //}}}
4658

4659    //{{{ setSelectionStart() method
4660
/**
4661     * @deprecated Do not use.
4662     */

4663    public final void setSelectionStart(int selectionStart)
4664    {
4665        int selectionEnd = getSelectionCount() == 1 ? getSelection(0).getEnd() : caret;
4666        select(selectionStart,selectionEnd,true);
4667    } //}}}
4668

4669    //{{{ getSelectionEnd() method
4670
/**
4671     * @deprecated Instead, obtain a Selection instance using
4672     * any means, and call its <code>getEnd()</code> method
4673     */

4674    public final int getSelectionEnd()
4675    {
4676        return getSelectionCount() == 1 ? getSelection(0).getEnd() : caret;
4677
4678    } //}}}
4679

4680    //{{{ getSelectionEnd() method
4681
/**
4682     * @deprecated Instead, obtain a Selection instance using
4683     * any means, and call its <code>getEnd(int)</code> method
4684     */

4685    public int getSelectionEnd(int line)
4686    {
4687        if(getSelectionCount() != 1)
4688            return caret;
4689
4690        return getSelection(0).getEnd(buffer,line);
4691    } //}}}
4692

4693    //{{{ getSelectionEndLine() method
4694
/**
4695     * @deprecated Instead, obtain a Selection instance using
4696     * any means, and call its <code>getEndLine()</code> method
4697     */

4698    public final int getSelectionEndLine()
4699    {
4700        if(getSelectionCount() != 1)
4701            return caret;
4702
4703        return getSelection(0).getEndLine();
4704    } //}}}
4705

4706    //{{{ setSelectionEnd() method
4707
/**
4708     * @deprecated Do not use.
4709     */

4710    public final void setSelectionEnd(int selectionEnd)
4711    {
4712        select(getSelectionStart(),selectionEnd,true);
4713    } //}}}
4714

4715    //{{{ getMarkPosition() method
4716
/**
4717     * @deprecated Do not use.
4718     */

4719    public final int getMarkPosition()
4720    {
4721        Selection s = getSelectionAtOffset(caret);
4722        if(s == null)
4723            return caret;
4724
4725        if(s.start == caret)
4726            return s.end;
4727        else if(s.end == caret)
4728            return s.start;
4729        else
4730            return caret;
4731    } //}}}
4732

4733    //{{{ getMarkLine() method
4734
/**
4735     * @deprecated Do not use.
4736     */

4737    public final int getMarkLine()
4738    {
4739        if(getSelectionCount() != 1)
4740            return caretLine;
4741
4742        Selection s = getSelection(0);
4743        if(s.start == caret)
4744            return s.endLine;
4745        else if(s.end == caret)
4746            return s.startLine;
4747        else
4748            return caretLine;
4749    } //}}}
4750

4751    //{{{ select() method
4752
/**
4753     * @deprecated Instead, call either <code>addToSelection()</code>,
4754     * or <code>setSelection()</code> with a new Selection instance.
4755     */

4756    public void select(int start, int end)
4757    {
4758        select(start,end,true);
4759    } //}}}
4760

4761    //{{{ select() method
4762
/**
4763     * @deprecated Instead, call either <code>addToSelection()</code>,
4764     * or <code>setSelection()</code> with a new Selection instance.
4765     */

4766    public void select(int start, int end, boolean doElectricScroll)
4767    {
4768        selectNone();
4769
4770        int newStart, newEnd;
4771        if(start < end)
4772        {
4773            newStart = start;
4774            newEnd = end;
4775        }
4776        else
4777        {
4778            newStart = end;
4779            newEnd = start;
4780        }
4781
4782        setSelection(new Selection.Range(newStart,newEnd));
4783        moveCaretPosition(end,doElectricScroll);
4784    } //}}}
4785

4786    //{{{ isSelectionRectangular() method
4787
/**
4788     * @deprecated Instead, check if the appropriate Selection
4789     * is an instance of the Selection.Rect class.
4790     */

4791    public boolean isSelectionRectangular()
4792    {
4793        Selection s = getSelectionAtOffset(caret);
4794        return s != null && s instanceof Selection.Rect;
4795    } //}}}
4796

4797    //}}}
4798

4799    //{{{ Package-private members
4800

4801    static TextArea focusedComponent;
4802
4803    //{{{ Instance variables
4804
final Segment JavaDoc lineSegment = new Segment JavaDoc();
4805    MouseInputAdapter JavaDoc mouseHandler;
4806    final ChunkCache chunkCache;
4807    final FastRepaintManager repaintMgr;
4808    DisplayManager displayManager;
4809    final SelectionManager selectionManager;
4810    boolean bufferChanging;
4811
4812    int maxHorizontalScrollWidth;
4813
4814    String JavaDoc wrap;
4815    boolean hardWrap;
4816    boolean softWrap;
4817    boolean wrapToWidth;
4818    int maxLineLen;
4819    int wrapMargin;
4820    float tabSize;
4821    int charWidth;
4822
4823    boolean scrollBarsInitialized;
4824
4825    /**
4826     * Cursor location, measured as an offset (in pixels) from upper left corner
4827     * of the TextArea.
4828     */

4829    final Point offsetXY;
4830
4831    boolean lastLinePartial;
4832
4833    boolean blink;
4834    //}}}
4835

4836    //{{{ isCaretVisible() method
4837
/**
4838     * Returns true if the caret is visible, false otherwise.
4839     */

4840    final boolean isCaretVisible()
4841    {
4842        return blink && hasFocus();
4843    } //}}}
4844

4845    //{{{ isStructureHighlightVisible() method
4846
/**
4847     * Returns true if the structure highlight is visible, false otherwise.
4848     * @since jEdit 4.2pre3
4849     */

4850    final boolean isStructureHighlightVisible()
4851    {
4852        return match != null
4853            && hasFocus()
4854            && displayManager.isLineVisible(match.startLine)
4855            && displayManager.isLineVisible(match.endLine);
4856    } //}}}
4857

4858    //{{{ updateMaxHorizontalScrollWidth() method
4859
void updateMaxHorizontalScrollWidth()
4860    {
4861        int max = chunkCache.getMaxHorizontalScrollWidth();
4862
4863        if(max != maxHorizontalScrollWidth)
4864        {
4865            maxHorizontalScrollWidth = max;
4866            horizontal.setValues(Math.max(0,
4867                Math.min(maxHorizontalScrollWidth + charWidth
4868                - painter.getWidth(),
4869                -horizontalOffset)),
4870                painter.getWidth(),
4871                0,maxHorizontalScrollWidth
4872                + charWidth);
4873            horizontal.setUnitIncrement(10);
4874            horizontal.setBlockIncrement(painter.getWidth());
4875        }
4876        else if (horizontal.getValue() != -horizontalOffset)
4877        {
4878            horizontal.setValue(-horizontalOffset);
4879        }
4880    } //}}}
4881

4882    //{{{ recalculateVisibleLines() method
4883
void recalculateVisibleLines()
4884    {
4885        if(painter == null)
4886            return;
4887        int height = painter.getHeight();
4888        int lineHeight = painter.getFontMetrics().getHeight();
4889        if(lineHeight == 0)
4890            visibleLines = 0;
4891        else if(height <= 0)
4892        {
4893            visibleLines = 0;
4894            lastLinePartial = false;
4895        }
4896        else
4897        {
4898            visibleLines = height / lineHeight;
4899            lastLinePartial = (height % lineHeight != 0);
4900            if(lastLinePartial)
4901                visibleLines++;
4902        }
4903
4904        chunkCache.recalculateVisibleLines();
4905
4906        // this does the "trick" to eliminate blank space at the end
4907
if(displayManager != null && buffer != null && !buffer.isLoading())
4908            setFirstLine(getFirstLine());
4909
4910        updateScrollBar();
4911    } //}}}
4912

4913    //{{{ foldStructureChanged() method
4914
void foldStructureChanged()
4915    {
4916        repaintMgr.setFastScroll(false);
4917        chunkCache.invalidateAll();
4918        recalculateLastPhysicalLine();
4919        repaint();
4920    } //}}}
4921

4922    //{{{ updateScrollBar() method
4923
/**
4924     * Updates the state of the scroll bars. This should be called
4925     * if the number of lines in the buffer changes, or when the
4926     * size of the text are changes.
4927     */

4928    void updateScrollBar()
4929    {
4930        if(buffer == null)
4931            return;
4932
4933        if(Debug.SCROLL_DEBUG)
4934            Log.log(Log.DEBUG,this,"updateScrollBar(), slc="
4935                + displayManager.getScrollLineCount());
4936
4937        if(vertical != null && visibleLines != 0)
4938        {
4939            if(Debug.SCROLL_DEBUG)
4940                Log.log(Log.DEBUG,this,"Vertical ok");
4941            int lineCount = displayManager.getScrollLineCount();
4942            int firstLine = getFirstLine();
4943            int visible = visibleLines - (lastLinePartial ? 1 : 0);
4944
4945            vertical.setValues(firstLine,visible,0,lineCount);
4946            vertical.setUnitIncrement(2);
4947            vertical.setBlockIncrement(visible);
4948        }
4949    } //}}}
4950

4951    //{{{ _finishCaretUpdate() method
4952
/* called by DisplayManager.BufferChangeHandler.transactionComplete() */
4953    void _finishCaretUpdate()
4954    {
4955        if(!queuedCaretUpdate)
4956            return;
4957
4958        try
4959        {
4960            if(match != null)
4961            {
4962                if(oldCaretLine < match.startLine)
4963                    invalidateLineRange(oldCaretLine,match.endLine);
4964                else
4965                    invalidateLineRange(match.startLine,oldCaretLine);
4966                match = null;
4967            }
4968
4969            int newCaretScreenLine = chunkCache.getScreenLineOfOffset(caretLine,
4970                caret - buffer.getLineStartOffset(caretLine));
4971            if(caretScreenLine == -1)
4972                invalidateScreenLineRange(newCaretScreenLine,newCaretScreenLine);
4973            else
4974                invalidateScreenLineRange(caretScreenLine,newCaretScreenLine);
4975            caretScreenLine = newCaretScreenLine;
4976
4977            invalidateSelectedLines();
4978
4979            // When the user is typing, etc, we don't want the caret
4980
// to blink
4981
blink = true;
4982            caretTimer.restart();
4983
4984            if(!displayManager.isLineVisible(caretLine))
4985            {
4986                if(caretLine < displayManager.getFirstVisibleLine()
4987                    || caretLine > displayManager.getLastVisibleLine())
4988                {
4989                    int collapseFolds = buffer.getIntegerProperty(
4990                        "collapseFolds",0);
4991                    if(collapseFolds != 0)
4992                    {
4993                        displayManager.expandFolds(collapseFolds);
4994                        displayManager.expandFold(caretLine,false);
4995                    }
4996                    else
4997                        displayManager.expandAllFolds();
4998                }
4999                else
5000                    displayManager.expandFold(caretLine,false);
5001            }
5002
5003            if(queuedScrollMode == ELECTRIC_SCROLL)
5004                scrollToCaret(true);
5005            else if(queuedScrollMode == NORMAL_SCROLL)
5006                scrollToCaret(false);
5007
5008            updateBracketHighlightWithDelay();
5009            if(queuedFireCaretEvent)
5010                fireCaretEvent();
5011        }
5012        // in case one of the above fails, we still want to
5013
// clear these flags.
5014
finally
5015        {
5016            queuedCaretUpdate = queuedFireCaretEvent = false;
5017            queuedScrollMode = NO_SCROLL;
5018        }
5019    } //}}}
5020

5021    //{{{ invalidateStructureMatch() method
5022
void invalidateStructureMatch()
5023    {
5024        if(match != null)
5025            invalidateLineRange(match.startLine,match.endLine);
5026    } //}}}
5027

5028    //{{{ startDragAndDrop() method
5029
void startDragAndDrop(InputEvent evt, boolean copy)
5030    {
5031        TransferHandler transferHandler = getTransferHandler();
5032        if (transferHandler != null)
5033        {
5034            Log.log(Log.DEBUG,this,"Drag and drop callback");
5035            transferHandler.exportAsDrag(this,evt,
5036                copy ? TransferHandler.COPY
5037                : TransferHandler.MOVE);
5038        }
5039    } //}}}
5040

5041    //{{{ fireNarrowActive() method
5042
void fireNarrowActive()
5043    {
5044        Object JavaDoc[] listeners = listenerList.getListenerList();
5045        for(int i = listeners.length - 2; i >= 0; i--)
5046        {
5047            if(listeners[i] == StatusListener.class)
5048            {
5049                try
5050                {
5051                    ((StatusListener)listeners[i+1])
5052                        .narrowActive(this);
5053                }
5054                catch(Throwable JavaDoc t)
5055                {
5056                    Log.log(Log.ERROR,this,t);
5057                }
5058            }
5059        }
5060    } //}}}
5061

5062    //}}}
5063

5064    //{{{ Private members
5065

5066    //{{{ Static variables
5067
private static final Timer JavaDoc caretTimer;
5068    private static final Timer JavaDoc structureTimer;
5069    //}}}
5070

5071    //{{{ Instance variables
5072
protected Cursor hiddenCursor;
5073
5074    private final Gutter gutter;
5075    protected final TextAreaPainter painter;
5076
5077    private final EventListenerList JavaDoc listenerList;
5078    private final MutableCaretEvent caretEvent;
5079
5080    private boolean caretBlinks;
5081    private InputHandlerProvider inputHandlerProvider;
5082
5083    private int physLastLine;
5084    private int screenLastLine;
5085
5086    private int visibleLines;
5087    private int electricScroll;
5088
5089    private int horizontalOffset;
5090
5091    private boolean quickCopy;
5092
5093    // JDiff, error list add stuff here
5094
private final Box verticalBox;
5095    private final JScrollBar vertical;
5096    private final JScrollBar horizontal;
5097
5098    protected JEditBuffer buffer;
5099
5100    protected int caret;
5101    protected int caretLine;
5102    private int caretScreenLine;
5103
5104    private final java.util.List JavaDoc<StructureMatcher> structureMatchers;
5105    private StructureMatcher.Match match;
5106
5107    private int magicCaret;
5108    /** Flag that tells if multiple selection is on. */
5109    protected boolean multi;
5110    private boolean overwrite;
5111    private boolean rectangularSelectionMode;
5112
5113    private boolean dndEnabled;
5114    private boolean dndInProgress;
5115
5116    // see finishCaretUpdate() & _finishCaretUpdate()
5117
private boolean queuedCaretUpdate;
5118    private int queuedScrollMode;
5119    private boolean queuedFireCaretEvent;
5120    private int oldCaretLine;
5121
5122    private boolean joinNonWordChars;
5123    //}}}
5124

5125    //{{{ invalidateSelectedLines() method
5126
/**
5127     * Repaints the lines containing the selection.
5128     */

5129    private void invalidateSelectedLines()
5130    {
5131        // to hide line highlight if selections are being added later on
5132
invalidateLine(caretLine);
5133
5134        for (Selection s : selectionManager.selection)
5135            invalidateLineRange(s.startLine,s.endLine);
5136    } //}}}
5137

5138    //{{{ finishCaretUpdate() method
5139
/**
5140     * the collapsing of scrolling/event firing inside compound edits
5141     * greatly speeds up replace-all.
5142     */

5143    private void finishCaretUpdate(int oldCaretLine,
5144        int scrollMode, boolean fireCaretEvent)
5145    {
5146        queuedFireCaretEvent |= fireCaretEvent;
5147        queuedScrollMode = Math.max(scrollMode,queuedScrollMode);
5148
5149        if(queuedCaretUpdate)
5150            return;
5151
5152        this.oldCaretLine = oldCaretLine;
5153        queuedCaretUpdate = true;
5154
5155        if(!buffer.isTransactionInProgress())
5156            _finishCaretUpdate();
5157        /* otherwise DisplayManager.BufferChangeHandler calls */
5158
5159        repaintMgr.setFastScroll(false);
5160    } //}}}
5161

5162    //{{{ fireCaretEvent() method
5163
private void fireCaretEvent()
5164    {
5165        Object JavaDoc[] listeners = listenerList.getListenerList();
5166        for(int i = listeners.length - 2; i >= 0; i--)
5167        {
5168            if(listeners[i] == CaretListener JavaDoc.class)
5169            {
5170                try
5171                {
5172                    ((CaretListener JavaDoc)listeners[i+1]).caretUpdate(caretEvent);
5173                }
5174                catch(Throwable JavaDoc t)
5175                {
5176                    Log.log(Log.ERROR,this,t);
5177                }
5178            }
5179        }
5180    } //}}}
5181

5182    //{{{ fireScrollEvent() method
5183
private void fireScrollEvent(boolean vertical)
5184    {
5185        Object JavaDoc[] listeners = listenerList.getListenerList();
5186        for(int i = listeners.length - 2; i >= 0; i--)
5187        {
5188            if(listeners[i] == ScrollListener.class)
5189            {
5190                try
5191                {
5192                    if(vertical)
5193                        ((ScrollListener)listeners[i+1]).scrolledVertically(this);
5194                    else
5195                        ((ScrollListener)listeners[i+1]).scrolledHorizontally(this);
5196                }
5197                catch(Throwable JavaDoc t)
5198                {
5199                    Log.log(Log.ERROR,this,t);
5200                }
5201            }
5202        }
5203    } //}}}
5204

5205    //{{{ fireStatusChanged() method
5206
private void fireStatusChanged(int flag, boolean value)
5207    {
5208        Object JavaDoc[] listeners = listenerList.getListenerList();
5209        for(int i = listeners.length - 2; i >= 0; i--)
5210        {
5211            if(listeners[i] == StatusListener.class)
5212            {
5213                try
5214                {
5215                    ((StatusListener)listeners[i+1])
5216                        .statusChanged(this,flag,value);
5217                }
5218                catch(Throwable JavaDoc t)
5219                {
5220                    Log.log(Log.ERROR,this,t);
5221                }
5222            }
5223        }
5224    } //}}}
5225

5226    //{{{ fireBracketSelected() method
5227
private void fireBracketSelected(int line, String JavaDoc text)
5228    {
5229        Object JavaDoc[] listeners = listenerList.getListenerList();
5230        for(int i = listeners.length - 2; i >= 0; i--)
5231        {
5232            if(listeners[i] == StatusListener.class)
5233            {
5234                try
5235                {
5236                    ((StatusListener)listeners[i+1])
5237                        .bracketSelected(this,line,text);
5238                }
5239                catch(Throwable JavaDoc t)
5240                {
5241                    Log.log(Log.ERROR,this,t);
5242                }
5243            }
5244        }
5245    } //}}}
5246

5247    //{{{ _changeLine() method
5248
private void _changeLine(boolean select, int newCaret)
5249    {
5250        if(select)
5251        {
5252            RectParams params = getRectParams(caret,newCaret);
5253            int extraStartVirt;
5254            int extraEndVirt;
5255            if(params == null)
5256            {
5257                extraStartVirt = 0;
5258                extraEndVirt = 0;
5259            }
5260            else
5261            {
5262                extraStartVirt = params.extraStartVirt;
5263                extraEndVirt = params.extraEndVirt;
5264                newCaret = params.newCaret;
5265            }
5266            extendSelection(caret,newCaret,extraStartVirt,extraEndVirt);
5267        }
5268        else if(!multi)
5269            selectNone();
5270
5271        moveCaretPosition(newCaret);
5272    }//}}}
5273

5274    
5275    /**
5276     * Check if the line contains only spaces and tabs.
5277     *
5278     * @param lineIndex the line index
5279     * @return <code>true</code> if the line contains only spaces and tabs
5280     */

5281    private boolean lineContainsSpaceAndTabs(int lineIndex)
5282    {
5283        getLineText(lineIndex,lineSegment);
5284
5285        for(int j = 0; j < lineSegment.count; j++)
5286        {
5287            switch(lineSegment.array[lineSegment.offset + j])
5288            {
5289            case ' ':
5290            case '\t':
5291                break;
5292            default:
5293                return false;
5294            }
5295        }
5296        return true;
5297    }
5298
5299    //{{{ insert() method
5300
protected void insert(String JavaDoc str, boolean indent)
5301    {
5302        try
5303        {
5304            // Don't overstrike if we're on the end of
5305
// the line
5306
if(overwrite || indent)
5307                buffer.beginCompoundEdit();
5308
5309            if(overwrite)
5310            {
5311                int caretLineEnd = getLineEndOffset(caretLine);
5312                if(caretLineEnd - caret > 1)
5313                    buffer.remove(caret,1);
5314            }
5315
5316            buffer.insert(caret,str);
5317
5318            if(indent)
5319                buffer.indentLine(caretLine,true);
5320        }
5321        finally
5322        {
5323            if(overwrite || indent)
5324                buffer.endCompoundEdit();
5325        }
5326    } //}}}
5327

5328    //{{{ insertTab() method
5329
private void insertTab()
5330    {
5331        int tabSize = buffer.getTabSize();
5332        if(buffer.getBooleanProperty("noTabs"))
5333        {
5334            int lineStart = getLineStartOffset(caretLine);
5335
5336            String JavaDoc line = getText(lineStart,caret - lineStart);
5337
5338            int pos = 0;
5339
5340            for(int i = 0; i < line.length(); i++)
5341            {
5342                switch(line.charAt(pos))
5343                {
5344                case '\t':
5345                    pos = 0;
5346                    break;
5347                default:
5348                    if(++pos >= tabSize)
5349                        pos = 0;
5350                    break;
5351                }
5352            }
5353
5354            replaceSelection(StandardUtilities.createWhiteSpace(
5355                tabSize - pos,0));
5356        }
5357        else
5358            replaceSelection("\t");
5359    } //}}}
5360

5361    //{{{ userInputTab() method
5362
protected void userInputTab()
5363    {
5364        if(getSelectionCount() == 1)
5365        {
5366            Selection sel = getSelection(0);
5367            if(sel instanceof Selection.Rect ||
5368                (sel.startLine == sel.endLine
5369                && (sel.start != buffer.getLineStartOffset(sel.startLine)
5370                || sel.end != buffer.getLineEndOffset(sel.startLine) - 1)))
5371            {
5372                insertTab();
5373            }
5374            else
5375                shiftIndentRight();
5376        }
5377        else if(getSelectionCount() != 0)
5378            shiftIndentRight();
5379        else
5380            insertTab();
5381    } //}}}
5382

5383    //{{{ doWordWrap() method
5384
/**
5385     * Does hard wrap.
5386     */

5387    protected boolean doWordWrap(boolean spaceInserted)
5388    {
5389        if(!hardWrap || maxLineLen <= 0)
5390            return false;
5391
5392        buffer.getLineText(caretLine,lineSegment);
5393
5394        int start = getLineStartOffset(caretLine);
5395        int end = getLineEndOffset(caretLine);
5396        int len = end - start - 1;
5397
5398        int caretPos = caret - start;
5399
5400        // only wrap if we're at the end of a line, or the rest of the
5401
// line text is whitespace
5402
for(int i = caretPos; i < len; i++)
5403        {
5404            char ch = lineSegment.array[lineSegment.offset + i];
5405            if(ch != ' ' && ch != '\t')
5406                return false;
5407        }
5408
5409        int tabSize = buffer.getTabSize();
5410
5411        String JavaDoc wordBreakChars = buffer.getStringProperty("wordBreakChars");
5412
5413        int lastInLine = 0; // last character before wrap
5414
int logicalLength = 0; // length with tabs expanded
5415
int lastWordOffset = -1;
5416        boolean lastWasSpace = true;
5417        for(int i = 0; i < caretPos; i++)
5418        {
5419            char ch = lineSegment.array[lineSegment.offset + i];
5420            if(ch == '\t')
5421            {
5422                logicalLength += tabSize - (logicalLength % tabSize);
5423                if(!lastWasSpace && logicalLength <= maxLineLen)
5424                {
5425                    lastInLine = i;
5426                    lastWordOffset = i;
5427                    lastWasSpace = true;
5428                }
5429            }
5430            else if(ch == ' ')
5431            {
5432                logicalLength++;
5433                if(!lastWasSpace &&
5434                    logicalLength <= maxLineLen + 1)
5435                {
5436                    lastInLine = i;
5437                    lastWordOffset = i;
5438                    lastWasSpace = true;
5439                }
5440            }
5441            else if(wordBreakChars != null && wordBreakChars.indexOf(ch) != -1)
5442            {
5443                logicalLength++;
5444                if(!lastWasSpace && logicalLength <= maxLineLen)
5445                {
5446                    lastInLine = i;
5447                    lastWordOffset = i;
5448                    lastWasSpace = true;
5449                }
5450            }
5451            else
5452            {
5453                lastInLine = i;
5454                logicalLength++;
5455                lastWasSpace = false;
5456            }
5457        }
5458
5459        boolean returnValue;
5460
5461        int insertNewLineAt;
5462        if(spaceInserted && logicalLength == maxLineLen
5463            && lastInLine == caretPos - 1)
5464        {
5465            insertNewLineAt = caretPos;
5466            returnValue = true;
5467        }
5468        else if(logicalLength >= maxLineLen && lastWordOffset != -1)
5469        {
5470            insertNewLineAt = lastWordOffset;
5471            returnValue = false;
5472        }
5473        else
5474            return false;
5475
5476        try
5477        {
5478            buffer.beginCompoundEdit();
5479            buffer.insert(start + insertNewLineAt,"\n");
5480            // caretLine would have been incremented
5481
// since insertNewLineAt <= caretPos
5482
buffer.indentLine(caretLine,true);
5483        }
5484        finally
5485        {
5486            buffer.endCompoundEdit();
5487        }
5488
5489        /* only ever return true if space was pressed
5490         * with logicalLength == maxLineLen */

5491        return returnValue;
5492    } //}}}
5493

5494    //{{{ updateStructureHighlightWithDelay() method
5495
private static void updateBracketHighlightWithDelay()
5496    {
5497        structureTimer.stop();
5498        structureTimer.start();
5499    } //}}}
5500

5501    //{{{ updateStructureHighlight() method
5502
private void updateStructureHighlight()
5503    {
5504        if(!painter.isStructureHighlightEnabled()
5505            && !gutter.isStructureHighlightEnabled())
5506            return;
5507
5508        for (StructureMatcher matcher : structureMatchers)
5509        {
5510            match = matcher.getMatch(this);
5511            if(match != null)
5512                break;
5513        }
5514
5515        if(match != null)
5516        {
5517            if(caretLine < match.startLine)
5518                invalidateLineRange(caretLine,match.endLine);
5519            else
5520                invalidateLineRange(match.startLine,caretLine);
5521
5522            if(!displayManager.isLineVisible(match.startLine)
5523                || chunkCache.getScreenLineOfOffset(
5524                match.startLine,match.start - getLineStartOffset(match.startLine))
5525                == -1)
5526            {
5527                showStructureStatusMessage(match.startLine < caretLine);
5528            }
5529        }
5530    } //}}}
5531

5532    //{{{ showStructureStatusMessage() method
5533
private void showStructureStatusMessage(boolean backward)
5534    {
5535        String JavaDoc text = buffer.getLineText(match.startLine).trim();
5536        if(backward && match.startLine != 0 && text.length() == 1)
5537        {
5538            switch(text.charAt(0))
5539            {
5540            case '{': case '}':
5541            case '[': case ']':
5542            case '(': case ')':
5543                text = buffer.getLineText(match.startLine - 1)
5544                    .trim() + ' ' + text;
5545                break;
5546            }
5547        }
5548
5549        // get rid of embedded tabs not removed by trim()
5550
fireBracketSelected(match.startLine + 1,text.replace('\t',' '));
5551    } //}}}
5552

5553    //{{{ recalculateLastPhysicalLine() method
5554
void recalculateLastPhysicalLine()
5555    {
5556        int oldScreenLastLine = screenLastLine;
5557        for(int i = visibleLines - 1; i >= 0; i--)
5558        {
5559            ChunkCache.LineInfo info = chunkCache.getLineInfo(i);
5560            if(info.physicalLine != -1)
5561            {
5562                physLastLine = info.physicalLine;
5563                screenLastLine = i;
5564                break;
5565            }
5566        }
5567        invalidateScreenLineRange(oldScreenLastLine,screenLastLine);
5568    } //}}}
5569

5570    //{{{ getRectParams() method
5571
static class RectParams
5572    {
5573        final int extraStartVirt;
5574        final int extraEndVirt;
5575        final int newCaret;
5576
5577        RectParams(int extraStartVirt, int extraEndVirt, int newCaret)
5578        {
5579            this.extraStartVirt = extraStartVirt;
5580            this.extraEndVirt = extraEndVirt;
5581            this.newCaret = newCaret;
5582        }
5583    }
5584
5585    /**
5586     * Used when doing S+UP/DOWN to simplify dealing with virtual space.
5587     */

5588    private RectParams getRectParams(int caret, int newCaret)
5589    {
5590        Selection s = getSelectionAtOffset(caret);
5591        int virtualWidth;
5592        if(s instanceof Selection.Rect)
5593        {
5594            if(caret == s.end)
5595            {
5596                virtualWidth = buffer.getVirtualWidth(
5597                    s.endLine,s.end - getLineStartOffset(
5598                    s.endLine)) + ((Selection.Rect)s).extraEndVirt;
5599            }
5600            else
5601            {
5602                virtualWidth = buffer.getVirtualWidth(
5603                    s.startLine,s.start - getLineStartOffset(
5604                    s.startLine)) + ((Selection.Rect)s).extraStartVirt;
5605            }
5606        }
5607        else if(rectangularSelectionMode)
5608        {
5609            virtualWidth = buffer.getVirtualWidth(
5610                caretLine,caret - buffer.getLineStartOffset(caretLine));
5611        }
5612        else
5613            return null;
5614
5615        int newLine = getLineOfOffset(newCaret);
5616        int[] totalVirtualWidth = new int[1];
5617        int newOffset = buffer.getOffsetOfVirtualColumn(newLine,
5618            virtualWidth,totalVirtualWidth);
5619        if(newOffset == -1)
5620        {
5621            int extraVirt = virtualWidth - totalVirtualWidth[0];
5622            newCaret = getLineEndOffset(newLine) - 1;
5623
5624            boolean bias;
5625            if(s == null)
5626                bias = newCaret < caret;
5627            else if(s.start == caret)
5628                bias = newCaret <= s.end;
5629            else if(s.end == caret)
5630                bias = newCaret <= s.start;
5631            else
5632                bias = false;
5633
5634            RectParams returnValue;
5635            if(bias)
5636                returnValue = new RectParams(extraVirt,0,newCaret);
5637            else
5638                returnValue = new RectParams(0,extraVirt,newCaret);
5639            return returnValue;
5640        }
5641        else
5642        {
5643            return new RectParams(0,0,getLineStartOffset(newLine)
5644                + newOffset);
5645        }
5646    } //}}}
5647

5648    //{{{ delete() method
5649
private void delete(boolean forward)
5650    {
5651        if(!buffer.isEditable())
5652        {
5653            getToolkit().beep();
5654            return;
5655        }
5656
5657        if(getSelectionCount() != 0)
5658        {
5659            Selection[] selections = getSelection();
5660            for(int i = 0; i < selections.length; i++)
5661            {
5662                Selection s = selections[i];
5663                if(s instanceof Selection.Rect)
5664                {
5665                    Selection.Rect r = (Selection.Rect)s;
5666                    int startColumn = r.getStartColumn(buffer);
5667                    if(startColumn == r.getEndColumn(buffer))
5668                    {
5669                        if(!forward && startColumn == 0)
5670                            getToolkit().beep();
5671                        else
5672                            tallCaretDelete(r,forward);
5673                    }
5674                    else
5675                        setSelectedText(s,null);
5676                }
5677                else
5678                    setSelectedText(s,null);
5679            }
5680        }
5681        else if(forward)
5682        {
5683            if(caret == buffer.getLength())
5684            {
5685                getToolkit().beep();
5686                return;
5687            }
5688
5689            buffer.remove(caret,1);
5690        }
5691        else
5692        {
5693            if(caret == 0)
5694            {
5695                getToolkit().beep();
5696                return;
5697            }
5698
5699            buffer.remove(caret - 1,1);
5700        }
5701    } //}}}
5702

5703    //{{{ tallCaretDelete() method
5704
private void tallCaretDelete(Selection.Rect s, boolean forward)
5705    {
5706        try
5707        {
5708            buffer.beginCompoundEdit();
5709
5710            int[] width = new int[1];
5711
5712            int startCol = s.getStartColumn(buffer);
5713            int startLine = s.startLine;
5714            int endLine = s.endLine;
5715            for(int i = startLine; i <= endLine; i++)
5716            {
5717                int offset = buffer.getOffsetOfVirtualColumn(
5718                    i,startCol,width);
5719                if(offset == -1)
5720                {
5721                    if(width[0] == startCol)
5722                        offset = getLineLength(i);
5723                    else
5724                    {
5725                        if(i == startLine && !forward)
5726                            shiftTallCaretLeft(s);
5727                        continue;
5728                    }
5729                }
5730                offset += buffer.getLineStartOffset(i);
5731                if(forward)
5732                {
5733                    if(offset != buffer.getLineEndOffset(i) - 1)
5734                        buffer.remove(offset,1);
5735                }
5736                else
5737                    buffer.remove(offset-1,1);
5738            }
5739        }
5740        finally
5741        {
5742            buffer.endCompoundEdit();
5743        }
5744    } //}}}
5745

5746    //{{{ shiftTallCaretLeft() method
5747
private void shiftTallCaretLeft(Selection.Rect s)
5748    {
5749        removeFromSelection(s);
5750        addToSelection(new Selection.Rect(
5751            buffer,
5752            s.getStartLine(),s.getStartColumn(buffer) - 1,
5753            s.getEndLine(),s.getEndColumn(buffer) - 1));
5754    } //}}}
5755

5756    //{{{ setMaxLineLength() method
5757
private void setMaxLineLength(int maxLineLen)
5758    {
5759        this.maxLineLen = maxLineLen;
5760
5761        if(maxLineLen <= 0)
5762        {
5763            if(softWrap)
5764            {
5765                wrapToWidth = true;
5766                wrapMargin = painter.getWidth() - charWidth * 3;
5767            }
5768            else
5769            {
5770                wrapToWidth = false;
5771                wrapMargin = 0;
5772            }
5773        }
5774        else
5775        {
5776            // stupidity
5777
char[] foo = new char[maxLineLen];
5778            for(int i = 0; i < foo.length; i++)
5779            {
5780                foo[i] = ' ';
5781            }
5782            wrapToWidth = false;
5783            wrapMargin = (int)painter.getFont().getStringBounds(
5784                foo,0,foo.length,
5785                painter.getFontRenderContext())
5786                .getWidth();
5787        }
5788    } //}}}
5789

5790    //{{{ addExplicitFold() method
5791
/**
5792     * Add an explicit fold.
5793     * You should call this method inside a compoundEdit in the buffer.
5794     * You must also check if the buffer fold mode is explicit before
5795     * calling this method.
5796     *
5797     * @param caretStart the starting offset
5798     * @param caretEnd the end offset
5799     * @param lineStart the start line
5800     * @param lineEnd the end line
5801     * @since jEdit 4.3pre3
5802     */

5803    protected int addExplicitFold(int caretStart, int caretEnd, int lineStart, int lineEnd)
5804    {
5805        // need to "fix" the caret position so that we get the right rule.
5806
// taking the start offset one char ahead and the end offset one char
5807
// behing makes sure we get the right rule for the text being
5808
// wrapped (tricky around mode boundaries, e.g., php code embedded
5809
// in HTML code)
5810
int startCaret = caretStart < buffer.getLength() ? caretStart + 1 : caretStart;
5811        int endCaret = caretEnd > 0 ? caretEnd - 1 : caretEnd;
5812
5813        String JavaDoc startLineComment = buffer.getContextSensitiveProperty(startCaret,"lineComment");
5814        String JavaDoc startCommentStart = buffer.getContextSensitiveProperty(startCaret,"commentStart");
5815        String JavaDoc startCommentEnd = buffer.getContextSensitiveProperty(startCaret,"commentEnd");
5816        String JavaDoc endLineComment = buffer.getContextSensitiveProperty(endCaret,"lineComment");
5817        String JavaDoc endCommentStart = buffer.getContextSensitiveProperty(endCaret,"commentStart");
5818        String JavaDoc endCommentEnd = buffer.getContextSensitiveProperty(endCaret,"commentEnd");
5819
5820        String JavaDoc start;
5821        int caretBack = 1;
5822        if(startLineComment != null)
5823            start = startLineComment + "{{{ ";
5824        else if(startCommentStart != null && startCommentEnd != null)
5825        {
5826            start = startCommentStart + "{{{ " + startCommentEnd;
5827            caretBack = 1 + startCommentStart.length();
5828        }
5829        else
5830            start = "{{{ ";
5831
5832        if (startLineComment != null) {
5833            // add a new line if there's text after the comment
5834
// we're inserting
5835
int nextLineOffset = buffer.getLineStartOffset(lineStart+1);
5836            if (nextLineOffset - caretStart != 1)
5837                start += "\n";
5838        }
5839        else
5840        {
5841            // always insert a new line if there's no comment character.
5842
start += "\n";
5843        }
5844
5845        String JavaDoc end;
5846        if(endLineComment != null)
5847            end = endLineComment + "}}}";
5848        else if(endCommentStart != null && endCommentEnd != null)
5849            end = endCommentStart + "}}}" + endCommentEnd;
5850        else
5851            end = "}}}";
5852
5853        String JavaDoc line = buffer.getLineText(lineStart);
5854        String JavaDoc whitespace = line.substring(0,
5855            StandardUtilities.getLeadingWhiteSpace(line));
5856
5857        if (endLineComment != null)
5858        {
5859            // if we're inserting a line comment into a non-empty
5860
// line, we'll need to add a line break so we don't
5861
// comment out existing code.
5862
int nextLineOffset = buffer.getLineStartOffset(lineEnd+1);
5863            if (nextLineOffset - caretEnd != 1)
5864                end += "\n";
5865        }
5866        else
5867        {
5868            // always insert a new line if there's no comment character.
5869
end += "\n";
5870        }
5871
5872        if(caretEnd == buffer.getLineStartOffset(lineEnd))
5873            buffer.insert(caretEnd,end);
5874        else
5875        {
5876            String JavaDoc lineText = buffer.getText(caretEnd - 1, 1);
5877            if (Character.isWhitespace(lineText.charAt(0)))
5878                buffer.insert(caretEnd, end);
5879            else
5880                buffer.insert(caretEnd,' ' + end);
5881        }
5882
5883        buffer.insert(caretStart,start + whitespace);
5884
5885        return caretBack;
5886    } //}}}
5887
//}}}
5888

5889    //{{{ Inner classes
5890

5891    //{{{ CaretBlinker class
5892
static class CaretBlinker implements ActionListener
5893    {
5894        //{{{ actionPerformed() method
5895
public void actionPerformed(ActionEvent evt)
5896        {
5897            if(focusedComponent != null && focusedComponent.hasFocus())
5898                focusedComponent.blinkCaret();
5899        } //}}}
5900
} //}}}
5901

5902    //{{{ MutableCaretEvent class
5903
class MutableCaretEvent extends CaretEvent JavaDoc
5904    {
5905        //{{{ MutableCaretEvent constructor
5906
MutableCaretEvent()
5907        {
5908            super(TextArea.this);
5909        } //}}}
5910

5911        //{{{ getDot() method
5912
public int getDot()
5913        {
5914            return getCaretPosition();
5915        } //}}}
5916

5917        //{{{ getMark() method
5918
public int getMark()
5919        {
5920            return getMarkPosition();
5921        } //}}}
5922
} //}}}
5923

5924    //{{{ AdjustHandler class
5925
class AdjustHandler implements AdjustmentListener
5926    {
5927        //{{{ adjustmentValueChanged() method
5928
public void adjustmentValueChanged(AdjustmentEvent evt)
5929        {
5930            if(!scrollBarsInitialized)
5931                return;
5932
5933            if(evt.getAdjustable() == vertical)
5934                setFirstLine(vertical.getValue());
5935            else
5936                setHorizontalOffset(-horizontal.getValue());
5937        } //}}}
5938
} //}}}
5939

5940    //{{{ FocusHandler class
5941
class FocusHandler implements FocusListener
5942    {
5943        //{{{ focusGained() method
5944
public void focusGained(FocusEvent evt)
5945        {
5946            if(bufferChanging)
5947                return;
5948
5949            if(match != null)
5950            {
5951                if(caretLine < match.startLine)
5952                    invalidateLineRange(caretLine,match.endLine);
5953                else
5954                    invalidateLineRange(match.startLine,caretLine);
5955            }
5956            else
5957                invalidateLine(caretLine);
5958
5959            focusedComponent = TextArea.this;
5960        } //}}}
5961

5962        //{{{ focusLost() method
5963
public void focusLost(FocusEvent evt)
5964        {
5965            if(!isShowing())
5966                return;
5967
5968            if(match != null)
5969            {
5970                if(caretLine < match.startLine)
5971                    invalidateLineRange(caretLine,match.endLine);
5972                else
5973                    invalidateLineRange(match.startLine,caretLine);
5974            }
5975            else
5976                invalidateLine(caretLine);
5977        } //}}}
5978
} //}}}
5979

5980    //{{{ MouseWheelHandler class
5981
class MouseWheelHandler implements MouseWheelListener
5982    {
5983        public void mouseWheelMoved(MouseWheelEvent e)
5984        {
5985            /****************************************************
5986             * move caret depending on pressed control-keys:
5987             * - Alt: move cursor, do not select
5988             * - Alt+(shift or control): move cursor, select
5989             * - shift: scroll page
5990             * - control: scroll single line
5991             * - <else>: scroll 3 lines
5992             ****************************************************/

5993            if(e.isAltDown())
5994            {
5995                boolean select = e.isShiftDown()
5996                    || e.isControlDown();
5997                if(e.getWheelRotation() < 0)
5998                    goToPrevLine(select);
5999                else
6000                    goToNextLine(select);
6001            }
6002            else if(e.isShiftDown())
6003            {
6004                if(e.getWheelRotation() > 0)
6005                    scrollDownPage();
6006                else
6007                    scrollUpPage();
6008            }
6009            else if(e.isControlDown())
6010            {
6011                setFirstLine(getFirstLine()
6012                    + e.getWheelRotation());
6013            }
6014            else if(e.getScrollType()
6015                == MouseWheelEvent.WHEEL_UNIT_SCROLL)
6016            {
6017                setFirstLine(getFirstLine()
6018                    + e.getUnitsToScroll());
6019            }
6020            else
6021            {
6022                setFirstLine(getFirstLine()
6023                    + 3 * e.getWheelRotation());
6024            }
6025        }
6026    } //}}}
6027

6028    //}}}
6029

6030    //{{{ Class initializer
6031
static
6032    {
6033        caretTimer = new Timer JavaDoc(500,new CaretBlinker());
6034        caretTimer.setInitialDelay(500);
6035        caretTimer.start();
6036
6037        structureTimer = new Timer JavaDoc(100,new ActionListener()
6038        {
6039            public void actionPerformed(ActionEvent evt)
6040            {
6041                if(focusedComponent != null)
6042                    focusedComponent.updateStructureHighlight();
6043            }
6044        });
6045        structureTimer.setInitialDelay(100);
6046        structureTimer.setRepeats(false);
6047    } //}}}
6048

6049    //{{{ main() method
6050
public static void main(String JavaDoc[] args)
6051    {
6052        JFrame frame = new JFrame();
6053        TextArea text = new TextArea();
6054        frame.getContentPane().add(text);
6055        frame.pack();
6056        frame.setVisible(true);
6057    } //}}}
6058
}
6059
Popular Tags