KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > terminalemulator > Term


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is Terminal Emulator.
16  * The Initial Developer of the Original Software is Sun Microsystems, Inc..
17  * Portions created by Sun Microsystems, Inc. are Copyright (C) 2001-2004.
18  * All Rights Reserved.
19  *
20  * Contributor(s): Ivan Soleimanipour.
21  */

22
23 package org.netbeans.lib.terminalemulator;
24
25 import java.awt.*;
26 import java.awt.event.*;
27 import java.awt.datatransfer.*;
28 import javax.swing.*;
29 import javax.accessibility.*;
30
31 import java.awt.font.*;
32 import java.awt.geom.Point2D JavaDoc;
33
34 import java.util.HashSet JavaDoc;
35 import java.util.Date JavaDoc;
36 import java.util.LinkedList JavaDoc;
37 import java.util.ListIterator JavaDoc;
38
39 /**
40 Term is a pure Java multi-purpose terminal emulator.
41 <p>
42 It has the following generic features:
43 <ul>
44     <li>All "dumb" operations. Basically putting characters on a screen and
45     processing keyboard input.
46     <li>ANSI mode "smart" operations. Cursor control etc.
47     <li>Character attributes like color, reverse-video etc.
48     <li>Selection service in character, word and line modes matching xterm
49     with configurable word boundary detection.
50     <li>History buffer.
51     <li>Facilities to iterate through logical lines, to implement search for
52     example.
53     <li>Support for nested pickable regions in order to support hyperlinked
54     views or more complex active text utilities.
55     <li>Support for double-width Oriental characters.
56 </ul>
57 <p>
58 <h2>Coordinate systems</h2>
59 The following coordinate systems are used with Term.
60 They are all cartesian and have their origin at the top left.
61 All but the first are 0-origin.
62 But they differ in all other respects:
63 <dl>
64
65 <dt>ANSI Screen coordinates
66     <dd>
67     Address only the visible portion of the screen.
68     They are 1-origin and extend thru the width and height of the visible
69     portion of the screen per getColumns() and getRows().
70     <p>
71     This is how an application (like 'vi' etc) views the screen.
72     This coordinate system primarily comes into play in the cursor addressing
73     directive, op_cm() and otherwise is not really used in the implementation.
74 <p>
75
76 <dt>Cell coordinates
77     <dd>
78     Each character usually takes one cell, and all placement on the screen
79     is in terms of a grid of cells getColumns() wide This cellular nature
80     is why fixed font is "required". In some locales some characters may
81     be double-width.
82     Japanese characters are like this, so they take up two cells.
83     There are no double-height characters (that I know of).
84     <p>
85     Cursor motion is in cell coordinates, so to move past a Japanese character
86     you need the cursor to move right twice. A cursor can also be placed on
87     the second cell of a double-width character.
88     <p>
89     Note that this is strictly an internal coordinate system. For example
90     Term.getCursorCol() and getCursorCoord() return buffer coordinates.
91     <p>
92     The main purpose of this coordinate system is to capture logical columns.
93     In the vertical direction sometimes it extends only the height of the
94     screen and sometimes the height of the buffer.
95 <p>
96
97 <dt>Buffer coordinates ...
98     <dd>
99     ... address the whole history character buffer.
100     These are 0-origin and extend thru the width
101     of the screen per getColumns(), or more if horizontal scrolling is
102     enabled, and the whole history, that is, getHistorySize()+getRows().
103     <p>
104     The BCoord class captures the value of such coordinates.
105     It is more akin to the 'int offset' used in the Java text package
106     as opposed to javax.swing.text.Position.
107     <p>
108     If there are no double-width characters the buffer coords pretty much
109     overlap with cell coords. If double-width characters are added then
110     the buffer column and cell column will have a larger skew the more right
111     you go.
112 <p>
113 <dt>Absolute coordinates ...
114     <dd>
115     ... are like Buffer coordinates in the horizontal direction.
116     In the vertical direction their origin is the first line that was
117     sent to the terminal. This line might have scrolled out of history and
118     might no longer be in the buffer. In effect each line ever printed by
119     Term gets a unique Absolute row.
120     <p>
121     What good is this? The ActiveRegion mechanism maintains coordinates
122     for its' boundaries. As text scrolls out of history buffer row coordinates
123     have to shift and all ActiveRegions' coords need to be relocated. This
124     can get expensive because as soon as the history buffer becomes full
125     each newline will require a relocation. This is the approach that
126     javax.swing.text.Position implements and it's justified there because
127     no Swing component has a "history buffer".
128     However, if you use absolute coordinates you'll never have to
129     relocate anything! Simple and effective.
130     <p>
131     Well almost. What happens when you reach Integer.MAX_VALUE? You wrap and
132     that can confuse everything. What are the chances of this happening?
133     Suppose term can process 4000 lines per second. A runaway process will
134     produce Integer.MAX_VALUE lines in about 4 days. That's too close
135     for comfort, so Term does detect the wrap and only then goes and
136     relocates stuff. This, however, causes a secondary problem with
137     testability since no-one wants to wait 4 days for a single wrap.
138     So what I've done is periodically set Term.modulo to something
139     smaller and tested stuff.
140     <p>
141     I'm indebted to Alan Kostinsky for this bit of lateral thinking.
142 </dl>
143
144
145 <p>
146 <h2>Modes of use</h2>
147 There are three ways Term can be used.
148 These modes aren't explicit they are just a convenient way of discussing
149 functionality.
150 <dl>
151 <dt>Screen mode
152     <dd>
153     This represents the traditional terminal without a history buffer.
154     Applications
155     running under the terminal assume they are dealing with a fixed size
156     screen and interact with it in the screen coordinate system through
157     escape sequence (ANSI or otherwise). The most common application which
158     uses terminals in this ways is the screen editor, like vi or emacs.
159     <p>
160     Term will convert keystrokes to an output stream and will process
161     characters in an input stream and render them unto the screen.
162     What and how these streams are connected to is up to the client of Term,
163     since it is usually highly platform dependent. For example on unixes
164     the streams may be connected to partially-JNI-based "pty" streams.
165     <p>
166     This mode works correctly even if there is history and you see a
167     scrollbar, just as it does under XTerm and it's derivatives.
168
169 <p>
170 <dt>Buffer/Interactive mode
171     <dd>
172     This is the primary facility that XTerm and other derivatives provide. The
173     screen has a history buffer in the vertical dimension.
174     <p>
175     Because of limited history active regions can scroll out of history and
176     while the coordinate invalidation problem is not addressed by absolute
177     coordiantes sometimes we don't want stuff to wink out.
178     <br>
179     Which is why we have ...
180
181 <p>
182 <dt>Page mode
183     <dd>
184     It is possible to "anchor" a location in the buffer and prevent it
185     from going out of history. This can be helpful in having the
186     client of Term make sure that crucial output doesn't get lost due to
187     short-sighted history settings on the part of the user.
188     <p>
189     To use Term
190     in this mode you can use setText() or appendText() instead of
191     connecting to Terms streams.
192     This mode is called page mode because the most common use of it
193     would be as something akin to a hypertext browser.
194     To that end
195     Term supports nestable ActiveRegions and mapping of coordinates
196     to regions. ActiveTerm puts all of this together in a comprehensive
197     subclass.
198 </dl>
199
200 <p>
201 <h2>What Term is not</h2>
202 <ul>
203 <li>
204 While there is an internal Buffer class, and while it behaves like a
205 document in that it can
206 be implicitly "edited" and character attributes explicitly changed,
207 Term is not a document editing widget.
208 <p>
209 <li>
210 Term is also not a command line processor in the sense that a MS Windows
211 console is. Its shuttling of keyboard events to an output stream and
212 rendering of characters from the input stream unto the screen are completely
213 independent activities.
214 <p>
215 This is due to Terms unix heritage where shells (ksh, bash etc) do their own
216 cmdline and history editing, but if you're so inclined the LineDiscipline
217 may be used for experimentation with indigenous cmdline processing.
218 </ul>
219  */

220
221 public class Term extends JComponent implements Accessible {
222     private State st = new State();
223     private Sel sel = new Sel(this, st);
224     private Ops ops = new OpsImpl();
225
226     private int top_margin = 0; // 0 means default (see topMargin())
227
private int bot_margin = 0;
228
229     // Stuff to control how often RegionManager.cull() gets called
230
private int cull_count = 0;
231     private static final int cull_frequency = 50;
232
233     // 'firsta' is the absolute line number of the line at 'lines[0]'.
234
protected int firsta = 0;
235
236     // chars gone by in lines that winked out of history
237
// 'firsta' ~= 'linesInPrehistory'
238
private int charsInPrehistory = 0;
239
240     private static final int modulo = Integer.MAX_VALUE/2;
241
242     private Screen screen;
243     private JScrollBar vscroll_bar;
244     private ScrollWrapper hscroll_wrapper;
245     private JScrollBar hscroll_bar;
246     private boolean has_focus;
247
248     // statistics
249
private int n_putchar;
250     private int n_putchars;
251     private int n_linefeeds;
252     private int n_repaint;
253     private int n_paint;
254
255     MyFontMetrics metrics = null;
256
257     Buffer buf = new Buffer(80);
258
259     private RegionManager region_manager = new RegionManager();
260
261     // 'left_down_point' remembers where the left button came down as a
262
// workaround for the flakey mouseDragged event. The flakiness has to with
263
// the fact that the mousePressed coord is not delivered in the first drag
264
// event, so if you proess and drag very quickly, the first drag coord
265
// will be quite far from the initial press location.
266

267     private Point left_down_point;
268
269     // getSystemSelection() wasn't available on Java prior to 1.4
270
private Clipboard systemClipboard = getToolkit().getSystemClipboard();
271     private Clipboard systemSelection = getToolkit().getSystemSelection();
272
273
274     /**
275      * ScrollWrapper is a HACK that allows us to make pairs of scrollbars
276      * look nice.
277      * <p>
278      * A JScrollPane, or more specifically ScrollPaneLayout, arranges
279      * a pair of vertical and horizontal scrollbars as follows:
280      * | |
281      * | |
282      * |v|
283      * -------------
284      * >|
285      * -------------
286      * ... so that here is a nice square corner.
287      *
288      * But ScrollPaneLayout insists that it's viewport is a JViewport and that
289      * it's container is a JScrollPane. It is probably possible to make the
290      * screen be a JViewPort and use a JScrollPane to contain the screen, but
291      * it's very tricky. (For that matter it should be possible to avoid doing
292      * our own scrolling altogether and use JScrollPane functionality, but
293      * it's also tricky).
294      *
295      * Since we're using a BorderLayout putting the horizontal SB in the SOUTH
296      * portion yields something like this:
297      * | |
298      * | |
299      * |v|
300      * ----------------
301      * >|
302      * ----------------
303      * Soooo, to make things look right, we use ScrollWrapper to control the
304      * sizing of the horizontal scrollbar. It basically uses a GridBagLayout
305      * and GridBagConstraints.insets to create the square corner.
306      */

307
308     private class ScrollWrapper extends JComponent implements Accessible {
309     public JScrollBar scroll_bar;
310
311     public ScrollWrapper(JScrollBar scroll_bar) {
312         GridBagLayout gbl = new GridBagLayout();
313         setLayout(gbl);
314
315         GridBagConstraints gbc = new GridBagConstraints();
316         gbc.anchor = GridBagConstraints.WEST;
317         gbc.fill = GridBagConstraints.BOTH;
318         gbc.gridwidth = 1;
319         gbc.gridheight = 1;
320         gbc.weightx = 1.0f;
321         gbc.weighty = 1.0f;
322         int slop = vscroll_bar.getMaximumSize().width;
323         gbc.insets = new Insets(0, 0, 0, slop);
324         add(scroll_bar, gbc);
325
326         this.scroll_bar = scroll_bar;
327     }
328
329     protected void paintComponent(Graphics g) {
330         // If we don't do this, the square corner will end getting filled
331
// with random grot.
332
Dimension sz = getSize();
333         g.clearRect(0, 0, sz.width, sz.height);
334     }
335
336     //......................................................................
337
// Accessibility stuff is all here
338
//......................................................................
339

340     public AccessibleContext getAccessibleContext() {
341         if (accessible_context == null) {
342          accessible_context = new AccessibleScrollWrapper();
343         }
344         return accessible_context;
345     }
346     private AccessibleContext accessible_context;
347
348     protected class AccessibleScrollWrapper extends AccessibleJComponent {
349         public AccessibleRole getAccessibleRole() {
350         return AccessibleRole.PANEL;
351         }
352     }
353     }
354
355
356     private class BaseTermStream extends TermStream {
357     public void flush() {
358         repaint(true);
359     }
360
361     public void putChar(char c) {
362         /*
363          * echoes a character unto the screen
364          */

365         ckEventDispatchThread();
366         // OLD NPE-x synchronized(Term.this)
367
{
368         n_putchar++;
369         putc_work(c);
370         }
371         possibly_repaint(true);
372
373         // pavel.buzek@czech.sun.com put this as a fix to speed up
374
// StreamTerm on windows. This will make raw mode not work,
375
// Instead now LineDiscipline properly buffers incoming characters.
376
// so should be considered temporary.
377
// repaint(c == '\n');
378
}
379
380     public void putChars(char buf[], int offset, int count) {
381         ckEventDispatchThread();
382         // OLD NPE-x synchronized (Term.this)
383
{
384         n_putchars++;
385         for (int bx = 0; bx < count; bx++) {
386             putc_work(buf[offset+bx]);
387         }
388         }
389         possibly_repaint(true);
390     }
391
392     public void sendChar(char c) {
393         fireChar(c);
394     }
395
396     public void sendChars(char buf[], int offset, int count) {
397         fireChars(buf, offset, count);
398     }
399     }
400
401     // head is closer to Term
402
// pushes extend tail
403
private TermStream base_stream = new BaseTermStream();
404     private TermStream dce_end = base_stream;
405     private TermStream dte_end = base_stream;
406
407     /**
408      *
409      */

410     public void pushStream(TermStream stream) {
411     // Term will send keystrokes by calling dte_end.sendChar
412
// Characters sent via Term.putChar will be sent down dce_end.
413
//
414
// The base stream is strange in that on the first push it will get
415
// split into two parts, one sticking at the end of the dce chain,
416
// the other at the end of the dte chain. Hence the special case
417
// treatment
418

419     if (dce_end == base_stream) {
420         // towards dce
421
dte_end = stream;
422         stream.setToDCE(base_stream);
423
424         // towards dte
425
stream.setToDTE(base_stream);
426         dce_end = stream;
427
428     } else {
429         // towards dce
430
dte_end.setToDCE(stream);
431         stream.setToDCE(base_stream);
432
433         // towards dte
434
stream.setToDTE(dce_end);
435         dce_end = stream;
436     }
437
438     stream.setTerm(this);
439     }
440
441     /*
442      * Debugging utilities
443      */

444     public static final int DEBUG_OPS = 1<<0;
445     public static final int DEBUG_KEYS = 1<<1;
446     public static final int DEBUG_INPUT = 1<<2;
447     public static final int DEBUG_OUTPUT = 1<<3;
448     public static final int DEBUG_WRAP = 1<<4;
449     public static final int DEBUG_MARGINS = 1<<5;
450
451     private int debug_gutter_width = 0;
452
453     public void setDebugFlags(int flags) {
454     debug = flags;
455     }
456
457     private int debug = /* DEBUG_OPS|DEBUG_KEYS|DEBUG_INPUT|DEBUG_OUTPUT | */ 0;
458
459     private boolean debugOps() {
460     return (debug & DEBUG_OPS) == DEBUG_OPS;
461     }
462     private boolean debugKeys() {
463     return (debug & DEBUG_KEYS) == DEBUG_KEYS;
464     }
465     private boolean debugWrap() {
466     return (debug & DEBUG_WRAP) == DEBUG_WRAP;
467     }
468     private boolean debugMargins() {
469     return true;
470     /* TMP
471     return (debug & DEBUG_MARGINS) == DEBUG_MARGINS;
472     */

473     }
474     /**
475      * Return true if DEBUG_INPUT flag has been set.
476      */

477     protected boolean debugInput() {
478     return (debug & DEBUG_INPUT) == DEBUG_INPUT;
479     }
480     /**
481      * Return true if DEBUG_OUTPUT flag has been set.
482      */

483     protected boolean debugOutput() {
484     return (debug & DEBUG_OUTPUT) == DEBUG_OUTPUT;
485     }
486
487
488     /*
489      * Top and bottom margins are stored as 1-origin values, with 0
490      * denoting default.
491      *
492      * topMargin() & botMargin() return 0-origin values which is what we
493      * use for screen coordinates.
494      *
495      * The margin lines are inclusive, that is, lines on the margin lines
496      * participate in scrolling.
497      */

498
499     private int topMargin() {
500     return (top_margin == 0)? 0: top_margin-1;
501     }
502     private int botMargin() {
503     return (bot_margin == 0)? st.rows-1: bot_margin-1;
504     }
505
506     /**
507      * beginx is row lines above the bottom.
508      * It's used for all cursor motion calculations and cursor relative
509      * line operations.
510      * It's used instead of firstx because firstx changes as we scroll.
511      * This allows us to restrict screen editing to the last chunk of
512      * the buffer.
513      */

514     private int beginx() {
515     return buf.nlines - st.rows;
516     }
517
518     private Line cursor_line() {
519     return buf.lineAt(st.cursor.row);
520     }
521
522     /**
523      * Set/unset WordDelineator.
524      * Passing a null sets it to the default WordDelineator.
525      */

526     public void setWordDelineator(WordDelineator word_delineator) {
527     if (word_delineator == null)
528         this.word_delineator = default_word_delineator;
529     else
530         this.word_delineator = word_delineator;
531     }
532     /**
533      * Get the WordDelineator used by this.
534      */

535     public WordDelineator getWordDelineator() {
536     return this.word_delineator;
537     }
538     private WordDelineator default_word_delineator = new WordDelineator();
539     WordDelineator word_delineator = default_word_delineator;
540
541     
542     /**
543      * Set/unset input listener.
544      * Entered text gets sent via this listener
545      *
546      * @deprecated Replaced by {@link #addInputListener(TermInputListener)}.
547      */

548     public void setInputListener(TermInputListener l) {
549     addInputListener(l);
550     }
551
552     /**
553      * Add an input listener to this.
554      * <p>
555      * Text entered via the keyboard gets sent via this listener.
556      */

557     public void addInputListener(TermInputListener l) {
558     input_listeners.add(l);
559     }
560
561     public void removeInputListener(TermInputListener l) {
562     input_listeners.remove(l);
563     }
564
565     private void fireChar(char c) {
566     ListIterator JavaDoc iter = input_listeners.listIterator();
567     while (iter.hasNext()) {
568         TermInputListener l = (TermInputListener) iter.next();
569         l.sendChar(c);
570     }
571     }
572
573     private void fireChars(char buf[], int offset, int count) {
574     ListIterator JavaDoc iter = input_listeners.listIterator();
575     while (iter.hasNext()) {
576         TermInputListener l = (TermInputListener) iter.next();
577         l.sendChars(buf, offset, count);
578     }
579     }
580
581     private LinkedList JavaDoc input_listeners = new LinkedList JavaDoc();
582
583
584     /**
585      * Set/unset misc listener.
586      * The following events gets sent via this listener:
587      * window size changes
588      *
589      * @deprecated Replaced by{@link #addListener(TermListener)}.
590      */

591     public void setListener(TermListener l) {
592     addListener(l);
593     }
594
595     /**
596      * Add a TermListener to this.
597      */

598     public void addListener(TermListener l) {
599     listeners.add(l);
600     }
601
602     /**
603      * Remove the given TermListener from this.
604      */

605     public void removeListener(TermListener l) {
606     listeners.remove(l);
607     }
608
609
610     private void fireSizeChanged(Dimension cells, Dimension pixels) {
611     ListIterator JavaDoc iter = listeners.listIterator();
612     while (iter.hasNext()) {
613         TermListener l = (TermListener) iter.next();
614         l.sizeChanged(cells, pixels);
615     }
616     }
617
618     private LinkedList JavaDoc listeners = new LinkedList JavaDoc();
619
620     /**
621      * Set/unset focus policy.
622      * <br>
623      * When set, the Term screen will grab focus when clicked on, otherwise
624      * it will grab focus when the mouse moves into it.
625      */

626     public void setClickToType(boolean click_to_type) {
627     this.click_to_type = click_to_type;
628     }
629     public boolean isClickToType() {
630     return click_to_type;
631     }
632     private boolean click_to_type = true;
633
634
635
636     /**
637      * Control whether keystrokes are ignored.
638      */

639     public void setReadOnly(boolean read_only) {
640     this.read_only = read_only;
641     }
642
643     /**
644      * Return whether keystrokes are ignored.
645      */

646     public boolean isReadOnly() {
647     return read_only;
648     }
649
650     private boolean read_only = false;
651
652
653
654
655     /**
656      * Clear the visible portion of screen
657      */

658     public void clear() {
659     for (int row = 0; row < st.rows; row++) {
660         Line l = buf.lineAt(beginx() + row);
661         l.reset();
662     }
663         regionManager().reset();
664     }
665
666     /**
667      * Clear all of the history without repainting the screen.
668      * <p>
669      * This is useful if you want to avoid flicker.
670      */

671     public void clearHistoryNoRefresh() {
672     sel.cancel(true);
673
674     int old_cols = buf.visibleCols();
675     buf = new Buffer(old_cols);
676
677     firsta = 0;
678     charsInPrehistory = 0;
679
680     st.firstx = 0;
681     st.firsty = 0;
682     st.cursor.row = 0;
683     st.cursor.col = 0;
684     st.attr = 0;
685     st.saveCursor(); // This clobbers the saved cursor value
686
st.restoreCursor(); // release reference to saved cursor object
687

688     adjust_lines(st.rows);
689
690     st.firstx = 0;
691     st.firsty = 0;
692         
693         regionManager().reset();
694
695     screen.possiblyUpdateCaretText();
696     }
697
698     /**
699      * Clear all of the history, including any visible portion of it.
700      * <p>
701      * Use {@link #clearHistoryNoRefresh()} if you find that clearHistory
702      * causes flickering.
703      */

704     public void clearHistory() {
705     clearHistoryNoRefresh();
706     repaint(true);
707     }
708
709     /**
710      * Return the RegionManager associated with this Term
711      */

712     public RegionManager regionManager() {
713     return region_manager;
714     }
715
716     public String JavaDoc textWithin(Coord begin, Coord end) {
717     if (begin == null || end == null)
718         return null;
719
720     final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
721
722     visitLines(begin, end, false, new LineVisitor() {
723         public boolean visit(Line l, int row, int bcol, int ecol) {
724         buf.append(l.charArray(), bcol, ecol-bcol+1);
725         return true;
726         }
727     } );
728     return buf.toString();
729     }
730     
731     public String JavaDoc getRowText(int row) {
732         Line line = buf.lineAt( row );
733         if ( line == null )
734             return null;
735         return line.stringBuffer().toString();
736     }
737
738     /**
739      * Get KeyStroke set.
740      * <p>
741      * Be default Term consumes all keystrokes.
742      * Any KeyStroke added to this set will be passed through and not consumed.
743      * <p>
744      * Be careful with control characters you need to create the keystroke
745      * as follows (note the - 64):
746      * <pre>
747      * KeyStroke.getKeyStroke(new Character((char)('T'-64)), Event.CTRL_MASK)
748      * </pre>
749      */

750     public HashSet JavaDoc getKeyStrokeSet() {
751     return keystroke_set;
752     }
753
754     /*
755      * Set the KeyStroke set.
756      * <p>
757      * While Term has a KeyStroke set set up by default, often many Terms
758      * share the same keystroke. This method allows this sharing.
759      */

760     public void setKeyStrokeSet(HashSet JavaDoc keystroke_set) {
761     this.keystroke_set = keystroke_set;
762
763     /* DEBUG
764     System.out.println("-----------------------------------------");//NOI18N
765     java.util.Iterator i = keystroke_set.iterator();
766     while (i.hasNext()) {
767         KeyStroke ks = (KeyStroke) i.next();
768         System.out.println("--- " + ks); // NOI18N
769     }
770     */

771     }
772
773     private HashSet JavaDoc keystroke_set = new HashSet JavaDoc();
774
775     // attempted partial fix for IZ 17337
776
// 'keystroke_set' is a collection of KeyStrokes in the form:
777
// ks3 = getKeyStroke(VK_C, CTRL_MASK)
778
// we use maybeConsume() in keyPressed and keyTyped events. During
779
// keyTyped the event->KS gives us
780
// ks2 = getKeyStroke((char) ('c'-64), CTRL_MASK)
781
// ks2 and ks3 while logically equivalent don't hash to the same so
782
// maybeConsume() says yes to ks2 and the Ctrl-C gets passed on.
783
//
784
// So to detect whether something in 'keystroke_set' needs to be dropped
785
// we need to check at keyPress time but take action at keyTyped time.
786
// 'passOn' helps us do that.
787

788     private boolean passOn = true;
789
790
791     /**
792      * Return true (and consume it) if 'e' is allowed to be consumed by us.
793      *
794      * If our owner is interested in some keys they will put someting into
795      * keystroke_set.
796      */

797     private boolean maybeConsume(KeyEvent e) {
798
799     if (e.isConsumed())
800         return false;
801
802     KeyStroke ks = KeyStroke.getKeyStrokeForEvent(e);
803
804     /* DEBUG
805     System.out.println("Term.maybeConsume(" + e + ")"); // NOI18N
806     System.out.println("\tKS = " + ks); // NOI18N
807     System.out.println("\tcontained = " + keystroke_set.contains(ks)); // NOI18N
808     */

809
810     if (keystroke_set == null || ! keystroke_set.contains(ks)) {
811         e.consume();
812         return true;
813     }
814     return false;
815     }
816
817     
818     /**
819      * Visit the physical lines from begin, through 'end'.
820      * <p>
821      * If 'newlines' is set, the passed 'ecol' is set to the actual
822      * number of columns in the view to signify that the newline is included.
823      * This way of doing it helps with rendering of a whole-line selection.
824      * Also Line knows about this and will tack on a "\n" when Line.text()
825      * is asked for.
826      */

827     void visitLines(Coord begin, Coord end, boolean newlines,
828             LineVisitor visitor) {
829     buf.visitLines(begin.toBCoord(firsta), end.toBCoord(firsta), newlines, visitor);
830     }
831
832     /**
833      * Visit logical lines from begin through end.
834      * <p>
835      * If begin is null, then the start of the buffer is assumed.
836      * If end is null, then the end of the buffer is assumed.
837      */

838     public void visitLogicalLines(Coord begin, Coord end,
839                   final LogicalLineVisitor llv) {
840
841     // Create a trampoline visitor
842
LineVisitor tramp = new LineVisitor() {
843         String JavaDoc text = ""; // NOI18N
844
int lineno = 0;
845         Coord begin = null;
846         Coord end = null;
847
848         public boolean visit(Line l, int brow, int bcol, int ecol) {
849
850         if (l.isWrapped()) {
851             if (begin == null)
852             begin = new Coord(new BCoord(brow, bcol), firsta);
853             text += l.text(bcol, ecol);
854
855         } else {
856             if (begin == null)
857             begin = new Coord(new BCoord(brow, bcol), firsta);
858             end = new Coord(new BCoord(brow, ecol), firsta);
859             text += l.text(bcol, ecol);
860
861             if (!llv.visit(lineno, begin, end, text))
862             return false;
863
864             lineno++;
865             text = ""; // NOI18N
866
begin = null;
867             end = null;
868         }
869
870         return true;
871         }
872     } ;
873
874     if (begin == null)
875         begin = new Coord();
876
877     if (end == null) {
878         int lastrow = buf.nlines-1;
879         Line l = buf.lineAt(lastrow);
880         end = new Coord(new BCoord(lastrow, l.length()-1), firsta);
881     }
882
883     if (begin.compareTo(end) > 0)
884         return;
885
886     buf.visitLines(begin.toBCoord(firsta), end.toBCoord(firsta), false, tramp);
887     }
888
889     /**
890      * Visit logical lines in reverse from end through begin.
891      * <p>
892      * If begin is null, then the start of the buffer is assumed.
893      * If end is null, then the end of the buffer is assumed.
894      */

895     public void reverseVisitLogicalLines(Coord begin, Coord end,
896                   final LogicalLineVisitor llv) {
897
898     // Create a trampoline visitor
899
LineVisitor tramp = new LineVisitor() {
900         String JavaDoc text = ""; // NOI18N
901
int lineno = 0;
902         Coord begin = null;
903         Coord end = null;
904
905         public boolean visit(Line l, int brow, int bcol, int ecol) {
906
907         boolean line_is_continuation = false;
908         if (brow > 0 && buf.lineAt(brow-1).isWrapped())
909             line_is_continuation = true;
910
911         if (line_is_continuation) {
912             if (end == null)
913             end = new Coord(new BCoord(brow, ecol), firsta);
914             text = l.text(bcol, ecol) + text;
915
916         } else {
917             if (end == null)
918             end = new Coord(new BCoord(brow, ecol), firsta);
919             begin = new Coord(new BCoord(brow, bcol), firsta);
920             text = l.text(bcol, ecol) + text;
921
922             if (!llv.visit(lineno, begin, end, text))
923             return false;
924
925             lineno++;
926             text = ""; // NOI18N
927
begin = null;
928             end = null;
929         }
930
931         return true;
932         }
933     } ;
934
935     if (begin == null)
936         begin = new Coord();
937
938     if (end == null) {
939         int lastrow = buf.nlines-1;
940         Line l = buf.lineAt(lastrow);
941         end = new Coord(new BCoord(lastrow, l.length()-1), firsta);
942     }
943
944     if (begin.compareTo(end) > 0)
945         return;
946
947     buf.reverseVisitLines(begin.toBCoord(firsta), end.toBCoord(firsta), false, tramp);
948     }
949
950     /**
951      * Convert offsets in logical lines into physical coordinates.
952      * <p>
953      * Usually called from within a LogicalLineVisitor.visit().
954      * 'begin' should be the 'begin' Coord passed to visit. 'offset' is an
955      * offset into the logical line, the 'text' argument passed to visit().
956      * <p>
957      * Note that 'offset' is relative to begin.col!
958      * <p>
959      * Following is an example of a line "aaaaaxxxxxxxxxXXXxxxxxxxx" which
960      * got wrapped twice. Suppose you're searching for "XXX" and you
961      * began your search from the first 'x' on row 2.
962      * <pre>
963         row | columns |
964         ----+----------+
965         0 |0123456789|
966         1 | |
967         2 |aaaaaxxxxx| wrap
968         3 |xxxxXXXxxx| wrap
969         4 |xxxxx |
970         5 | |
971
972      * </pre>
973      * The visitor will be called with 'begin' pointing at the first 'x',
974      * 'end' pointing at the last 'x' and 'text' containing the above line.
975      * Let's say you use String.indexOf("XXX") and get an offset of 9.
976      * Passing the 'begin' through and the 9 as an offset and 3 as the
977      * length will yield an Extent (3,4) - (3,7) which youcan forward to
978      * setSelectionExtent();
979      * <p>
980      * The implementation of this function is not snappy (it calls
981      * Term.advance() in a loop) but the assumption is that his function
982      * will not be called in a tight loop.
983      */

984     public Extent extentInLogicalLine(Coord begin, int offset, int length) {
985
986     // SHOULD factor the A/B Coord conversion out
987

988     Coord from = (Coord) begin.clone();
989     while (offset-- > 0) {
990         from = new Coord(buf.advance(from.toBCoord(firsta)), firsta);
991     }
992
993     Coord to = (Coord) from.clone();
994     while (--length > 0) {
995         to = new Coord(buf.advance(to.toBCoord(firsta)), firsta);
996     }
997
998     return new Extent(from, to);
999     }
1000
1001    private boolean cursor_was_visible() {
1002    return st.cursor.row-1 >= st.firstx &&
1003           st.cursor.row-1 < st.firstx + st.rows;
1004    }
1005
1006    /**
1007     * Ensure that the given coordinate is visible.
1008     * <p>
1009     * If the given coordinate is visible within the screen nothing happens.
1010     * Otherwise the screen is scrolled so that the given target ends up
1011     * approximately mid-sceeen.
1012     */

1013    public void possiblyNormalize(Coord target) {
1014    if (target == null)
1015        return;
1016
1017    BCoord btarget = target.toBCoord(firsta);
1018    if (btarget.row >= st.firstx && btarget.row < st.firstx + st.rows)
1019        return;
1020
1021    st.firstx = btarget.row - (st.rows/2);
1022    if (st.firstx < 0)
1023        st.firstx = 0;
1024    else if (st.firstx + st.rows > buf.nlines)
1025        st.firstx = buf.nlines - st.rows;
1026
1027    repaint(true);
1028    }
1029    
1030    /**
1031     * Ensure that maximum of the given region is visible.
1032     * <p>
1033     * If the given region is visible within the screen nothing happens.
1034     * If the region is bigger than the screen height, first line of the region
1035     * will be displayed in first line of screen and end of region
1036     * won't be displayed.
1037     * If the region is bigger than the half of screen height, last line
1038     * of the region will be displayed in the last line of the screen.
1039     * Otherwise the region will start approximately in mid-sceeen
1040     */

1041    public void possiblyNormalize(ActiveRegion region) {
1042    if (region == null)
1043        return;
1044
1045        BCoord bbegin = region.begin.toBCoord(firsta);
1046    BCoord bend = region.end.toBCoord(firsta);
1047    if ( bbegin.row >= st.firstx && bend.row < st.firstx + st.rows )
1048        return;
1049
1050        if ( bend.row - bbegin.row + 1 >= st.rows )
1051            // region has more rows then screen
1052
st.firstx = bbegin.row;
1053        else {
1054            st.firstx = bbegin.row - (st.rows/2);
1055            if (st.firstx < 0)
1056                st.firstx = 0;
1057            else if (st.firstx + st.rows > buf.nlines)
1058                st.firstx = buf.nlines - st.rows;
1059            else if (st.firstx + st.rows <= bend.row)
1060                // display also end of region
1061
st.firstx = bend.row - st.rows + 1;
1062        }
1063        
1064    repaint(true);
1065    }
1066
1067    /**
1068     * If the given coordinate is visible within the screen returns true,
1069     * otherwise returns false.
1070     */

1071    public boolean isCoordVisible(Coord target) {
1072    BCoord btarget = target.toBCoord(firsta);
1073    if (btarget.row >= st.firstx && btarget.row < st.firstx + st.rows)
1074        return true;
1075        else
1076            return false;
1077    }
1078    
1079    private void possiblyScrollOnInput() {
1080    if (!scroll_on_input)
1081        return;
1082
1083    // vertical (row) dimension
1084
if (st.cursor.row >= st.firstx && st.cursor.row < st.firstx + st.rows) {
1085        ;
1086    } else {
1087        st.firstx = buf.nlines - st.rows;
1088        repaint(true);
1089    }
1090    }
1091
1092    /*
1093     * Horizontal scrolling to track the cursor.
1094     *
1095     * The view/buffer etc calculations, performed by possiblyHScroll(), are not
1096     * terribly complex, but they do depend on up-to-date cursor position.
1097     * If we were to do all this work on every character received we would
1098     * get some slowdown, but worse if say a large file with long lines is
1099     * being 'cat'ed Term will end up scrolling left and right. It would be
1100     * rather annoying.
1101     * But attempting to do the scrolling on typed input isn't going to do
1102     * because (because of non-local echoing) the cursor isn't yet updated when
1103     * a key has been pressed.
1104     * Additionally, a key may result in more than character being echoed.
1105     * for example, an ENTER might result in a "\n\r", a TAB in 8 ' 's,
1106     * and a '\b' in a "\b \b" sequence so a single flag won't do.
1107     * Instead we use a count, 'hscroll_count' which says: attempt
1108     * possiblyHScroll() on the next <n> received characters. It may happen
1109     * there there is a lot of keyboard input and very little output. In
1110     * such cases 'hscroll_count' will start accumulating a defecit. To
1111     * counter this we reset the count to 8 on "newline" type stuff.
1112     */

1113
1114    private int hscroll_count = 0;
1115
1116    private void hscrollReset(char c) {
1117    ckEventDispatchThread();
1118    // OLD NPE-x synchronized(hscroll_lock)
1119
{
1120        if (c == '\n' || c == '\r')
1121        hscroll_count = 8;
1122        else
1123        hscroll_count += 8;
1124    }
1125    }
1126
1127    private void possiblyHScroll() {
1128
1129    // decide if we actually need to do it
1130

1131    if (!horizontally_scrollable)
1132        return;
1133
1134    ckEventDispatchThread();
1135    // OLD NPE-x synchronized(hscroll_lock)
1136
{
1137        if (hscroll_count > 0) {
1138        hscroll_count--;
1139        } else {
1140        return;
1141        }
1142    }
1143
1144    /* DEBUG
1145    System.out.println("Checking hscroll cursor.col " + st.cursor.col + // NOI18N
1146                       " firsty " + st.firsty // NOI18N
1147               " visibleCols " + buf.visibleCols()); // NOI18N
1148    */

1149
1150    // horizontal (col) dimension
1151
if (st.cursor.col >= st.firsty + buf.visibleCols()) {
1152        /* DEBUG
1153        System.out.println("Need to scroll right"); // NOI18N
1154        */

1155        st.firsty = st.cursor.col - buf.visibleCols() + 1;
1156
1157        // Expand our idea of column count so that if the cursor is
1158
// at the end the scrollbar allows us to scroll to it.
1159
// This is a bit unusual in that if we type stuff right up to the
1160
// last visible column and return the hscrollbar will display this
1161
// one extra column even though nothing was ever typed in it.
1162
// This is particularly annoying in 'vi' with right-butting lines.
1163
// In the future SHOULD think up of something "smart".
1164
buf.noteColumn(st.cursor.col+1);
1165
1166        repaint(true);
1167
1168    } else if (st.cursor.col - buf.visibleCols() < st.firsty) {
1169        /* DEBUG
1170        System.out.println("Need to scroll left"); // NOI18N
1171        */

1172        st.firsty = st.cursor.col - buf.visibleCols() + 1;
1173        if (st.firsty < 0)
1174        st.firsty = 0;
1175        else
1176        repaint(true);
1177    }
1178    }
1179
1180
1181    /**
1182     * Set the display attribute for characters printed from now on.
1183     * <p>
1184     * Unrecognized values silently ignored.
1185     */

1186
1187    public void setAttribute(int value) {
1188    st.attr = Attr.setAttribute(st.attr, value);
1189    }
1190
1191    /**
1192     * Set or unset the display attribute for characters from 'begin' to 'end'
1193     * inclusive to 'value' depending on the value of 'on'.
1194     * <p>
1195     * Will cause a repaint.
1196     */

1197    public void setCharacterAttribute(Coord begin, Coord end,
1198                      final int value, final boolean on ) {
1199
1200    visitLines(begin, end, false, new LineVisitor() {
1201        public boolean visit(Line l, int brow, int bcol, int ecol) {
1202        l.setCharacterAttribute(bcol, ecol, value, on);
1203        return true;
1204        }
1205    } );
1206
1207    repaint(false);
1208    }
1209
1210    /*
1211     * Set the glyph and line background colors for the line the cursor is on.
1212     * <p>
1213     * Will not repaint.
1214     */

1215    public void setGlyph(int glyph_id, int background_id) {
1216    Line l = cursor_line();
1217    l.glyph_glyph = glyph_id;
1218    l.glyph_rendition = background_id;
1219    }
1220
1221    /*
1222     * Set the glyph and line background colors for the give line.
1223     * <p>
1224     * Will repaint.
1225     */

1226    public void setRowGlyph(int row, int glyph_id, int background_id) {
1227    Coord c = new Coord();
1228    c.row = row;
1229    c.col = 0;
1230    BCoord b = c.toBCoord(firsta);
1231    Line l = buf.lineAt(b.row);
1232    if (l == null)
1233        return;
1234    l.glyph_glyph = glyph_id;
1235    l.glyph_rendition = background_id;
1236
1237    possibly_repaint(false);
1238    }
1239
1240
1241    /**
1242     * Adjust lines when the widget is resized (and also at construction time)
1243     * <p>
1244     * When growing makes sure that everything in the screen is backed up
1245     * by a buffer Line. When shrinking removes lines from the top or the
1246     * bottom as appropriate.
1247     * <p>
1248     * In other words implements the xterm resizeGravity=SouthWest semantics.
1249     * which roughly says: When resizing keep the line that was at the bottom,
1250     * _at_ the bottom.
1251     */

1252
1253    private void adjust_lines(int delta_row) {
1254
1255    if (delta_row > 0) {
1256        // attempt to scroll
1257
st.firstx -= delta_row;
1258
1259        // SHOULD eliminate the loop and move the work to Buffer
1260
while (st.firstx < 0) {
1261        buf.appendLine();
1262        st.firstx++;
1263        }
1264
1265    } else if (delta_row < 0) {
1266        // we shrunk
1267
int orows = st.rows - delta_row; // reconstruct orows
1268

1269        // First attempt to remove lines from the bottom of the buffer.
1270
// This comes into play mostly when you have just started Term
1271
// and have but few lines near the top and nothing in history
1272
// so you really can't scroll.
1273

1274        // How many lines can we trim at the bottom before we eat
1275
// into the cursor?
1276
// Really weird I seem to get the same results regardless of
1277
// whether I use orows or buf.nlines. SHOULD investigate more.
1278

1279        int allowed = buf.nlines - st.cursor.row - 1;
1280
1281        if (allowed < 0)
1282        allowed = 0;
1283
1284        int delete_from_bottom;
1285        if (allowed > -delta_row) {
1286        // no need to scroll, we accomodate the whole resize by
1287
// removing lines from the bottom
1288
delete_from_bottom = -delta_row;
1289        } else {
1290        // delete as much as we're allowed ...
1291
delete_from_bottom = allowed;
1292        // ... and scroll for the rest
1293
st.firstx += (-delta_row - allowed);
1294        }
1295
1296        // SHOULD eliminate the loop and move the work to Buffer
1297
while (delete_from_bottom-- > 0) {
1298        buf.removeLineAt(buf.nlines-1);
1299        }
1300
1301
1302    }
1303    // printStats("From delta_row of " + delta_row); // NOI18N
1304

1305    adjust_scrollbar();
1306    }
1307
1308    /**
1309     * returns curent history size of buffer.
1310     */

1311    public int getHistoryBuffSize () {
1312        return buf.nlines - st.rows;
1313    }
1314    
1315    private void limit_lines() {
1316
1317    /*
1318     * Make sure we don't exceed the buffer size limit historySize.
1319     * This implements the vanishing of lines from the beginning of history.
1320     */

1321
1322    if (anchored)
1323        return;
1324
1325    int history = buf.nlines - st.rows;
1326    if (history < 0)
1327        history = 0;
1328
1329    int toremove = history - history_size;
1330    if (toremove > 0) {
1331        int charsRemoved = buf.removeLinesAt(0, toremove);
1332
1333        charsInPrehistory += charsRemoved;
1334
1335        // relocate all row indices
1336
st.adjust(-toremove);
1337
1338        firsta += toremove;
1339
1340        // cull any regions that are no longer in history
1341
if (++cull_count % cull_frequency == 0) {
1342        /* DEBUG
1343        System.out.println("Culling regions ..."); // NOI18N
1344        */

1345        region_manager.cull(firsta);
1346        }
1347
1348        // our absolute coordinates are about to wrap
1349
if (firsta + buf.nlines >= modulo) {
1350        int old_firsta = firsta;
1351        firsta = 0;
1352
1353        sel.relocate(old_firsta, firsta);
1354        region_manager.relocate(old_firsta, firsta);
1355        }
1356    }
1357
1358    // deal with selection moving out of the buffer
1359
sel.adjust(firsta, 0, firsta + buf.nlines);
1360
1361    adjust_scrollbar();
1362    }
1363
1364    /**
1365     * Scroller is used to implement selection auto-scrolling.
1366     * <p>
1367     * When a selection drag moves out of the window a scroller object/thread
1368     * is started to periodically scroll and extend the selection.
1369     */

1370    private class Scroller extends Thread JavaDoc {
1371    public final static int UP = 1<<1;
1372    public final static int DOWN = 1<<2;
1373    public final static int LEFT = 1<<3;
1374    public final static int RIGHT = 1<<4;
1375
1376    private int direction;
1377
1378    public Scroller(int direction) {
1379        this.direction = direction;
1380    }
1381
1382    private boolean extend() {
1383
1384        ckEventDispatchThread();
1385        // OLD NPE-x synchronized (Term.this)
1386
{
1387
1388        // Selection might wink out while we're auto scrolling.
1389
// Since we use 'sel.sel_extent' further below we
1390
// synchronize.
1391
if (sel.sel_extent == null)
1392            return false;
1393
1394        BCoord x = sel.sel_extent.toBCoord(firsta);
1395        BCoord v = toViewCoord(x);
1396        int r = v.row;
1397        int c = v.col;
1398
1399        if ((direction & DOWN) == DOWN) {
1400            lineDown(1);
1401            r = st.rows-1;
1402            c = buf.totalCols();
1403        } else if ((direction & UP) == UP) {
1404            lineUp(1);
1405            r = 0;
1406            c = 0;
1407        } else {
1408            BCoord vc2 = toViewCoord(drag_point);
1409            r = vc2.row;
1410            c = vc2.col;
1411        }
1412
1413
1414        if ((direction & LEFT) == LEFT) {
1415            st.firsty --;
1416            if (st.firsty < 0)
1417            st.firsty = 0;
1418            c = 0;
1419        } else if ((direction & RIGHT) == RIGHT) {
1420            st.firsty ++;
1421            int limit = buf.totalCols() - buf.visibleCols();
1422            if (limit < 0)
1423            limit = 0;
1424            if (st.firsty > limit)
1425            st.firsty = limit;
1426            c = st.firsty + buf.visibleCols();
1427        }
1428
1429        BCoord vc = new BCoord(r, c);
1430        BCoord bc = toBufCoords(vc);
1431        sel.track(new Coord(bc, firsta));
1432        repaint(true);
1433        return true;
1434        }
1435    }
1436
1437    public void run() {
1438        while (true) {
1439
1440        /* DEBUG
1441        System.out.print("Scrolling "); // NOI18N
1442        if ((direction & UP) == UP)
1443            System.out.print("UP "); // NOI18N
1444        if ((direction & DOWN) == DOWN)
1445            System.out.print("DOWN "); // NOI18N
1446        if ((direction & LEFT) == LEFT)
1447            System.out.print("LEFT "); // NOI18N
1448        if ((direction & RIGHT) == RIGHT)
1449            System.out.print("RIGHT "); // NOI18N
1450        System.out.println();
1451        */

1452
1453        extend();
1454
1455        try {
1456            // See issue 36404
1457
sleep(10);
1458        } catch (InterruptedException JavaDoc x) {
1459            break;
1460        }
1461        }
1462        /* DEBUG
1463        System.out.println("Done with Scrolling"); // NOI18N
1464        */

1465    }
1466    }
1467
1468    private Scroller scroller;
1469    private Point drag_point;
1470
1471    private int scrolling_direction = 0;
1472
1473
1474    private void scroll_to(int direction, MouseEvent e) {
1475    if (direction == scrolling_direction) {
1476        if (direction == 0) {
1477        // We're moving inside the view
1478
BCoord bc = toBufCoords(toViewCoord(e.getPoint()));
1479        sel.track(new Coord(bc, firsta));
1480        repaint(false);
1481        }
1482        return;
1483    }
1484
1485    // we changed direction
1486

1487    // get rid of the old scroller
1488
if (scroller != null) {
1489        scroller.interrupt();
1490        scroller = null;
1491    }
1492
1493    if (direction == 0) {
1494        BCoord bc = toBufCoords(toViewCoord(e.getPoint()));
1495        sel.track(new Coord(bc, firsta));
1496        repaint(false);
1497    } else {
1498        scroller = new Scroller(direction);
1499        scroller.start();
1500    }
1501
1502    scrolling_direction = direction;
1503    }
1504
1505
1506    /**
1507     * Constructor
1508     */

1509    public Term() {
1510    st.rows = 25;
1511    st.firstx = 0;
1512    st.firsty = 0;
1513
1514    standard_color[0] = Color.black;
1515    standard_color[1] = Color.red;
1516    standard_color[2] = Color.green;
1517    standard_color[3] = Color.yellow;
1518    standard_color[4] = Color.blue;
1519    standard_color[5] = Color.magenta;
1520    standard_color[6] = Color.cyan;
1521    standard_color[7] = Color.white;
1522
1523    custom_color[0] = Color.black;
1524    custom_color[1] = Color.black;
1525    custom_color[2] = Color.black;
1526    custom_color[3] = Color.black;
1527    custom_color[4] = Color.black;
1528    custom_color[5] = Color.black;
1529    custom_color[6] = Color.black;
1530    custom_color[7] = Color.black;
1531
1532        Font f = UIManager.getFont ("TextArea.font"); //NOI18N
1533
if (f == null) {
1534        // on, e.g., GTK L&F
1535
f = UIManager.getFont ("controlFont"); //NOI18N
1536
}
1537
1538        if (f != null) {
1539            setFont(new Font("Monospaced", Font.PLAIN, f.getSize() + 1)); // NOI18N
1540
} else {
1541            setFont (new Font("Monospaced", Font.PLAIN, 12)); // NOI18N
1542
}
1543
1544    BorderLayout layout = new BorderLayout();
1545    setLayout(layout);
1546    screen = new Screen(this,
1547                (buf.visibleCols() * metrics.width +
1548                 glyph_gutter_width +
1549                 debug_gutter_width),
1550                st.rows * metrics.height);
1551
1552        add(BorderLayout.CENTER, screen);
1553        screen.setBackground(null); // inherit from this
1554
screen.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
1555
1556    adjust_lines(st.rows);
1557    st.cursor.row = 0;
1558
1559    vscroll_bar = new JScrollBar(JScrollBar.VERTICAL);
1560        add(BorderLayout.EAST, vscroll_bar);
1561        vscroll_bar.setValues(st.firstx, st.rows-1, 0, st.rows);
1562        vscroll_bar.setUnitIncrement(1);
1563        vscroll_bar.setEnabled(true);
1564        vscroll_bar.setFocusable(false);
1565
1566    vscroll_bar.addAdjustmentListener(new AdjustmentListener() {
1567
1568        public void adjustmentValueChanged(AdjustmentEvent e) {
1569        JScrollBar sb = (JScrollBar) e.getAdjustable();
1570        switch(e.getAdjustmentType()) {
1571            case AdjustmentEvent.TRACK:
1572
1573            int pos = e.getValue();
1574
1575            // adjustmentValueChanged() will get called when we
1576
// call setValues() on the scrollbar.
1577
// This test sort-of filters that out
1578
if (pos == st.firstx)
1579                break;
1580
1581            // deal with the user moving the thumb
1582
st.firstx = pos;
1583            /* DEBUG
1584            if (st.firstx + st.rows > buf.nlines) {
1585                Thread.dumpStack();
1586                printStats("bad scroll value"); // NOI18N
1587            }
1588            */

1589            repaint(false);
1590            break;
1591
1592            default:
1593            /* DEBUG
1594            System.out.println("adjustmentValueChanged: " + e); // NOI18N
1595            */

1596            break;
1597        }
1598        }
1599    } );
1600
1601
1602    hscroll_bar = new JScrollBar(JScrollBar.HORIZONTAL);
1603        hscroll_bar.setValues(st.firsty, buf.totalCols()-1, 0, buf.totalCols());
1604        hscroll_bar.setUnitIncrement(1);
1605        hscroll_bar.setEnabled(true);
1606        hscroll_bar.setFocusable(false);
1607
1608    hscroll_wrapper = new ScrollWrapper(hscroll_bar);
1609    add(BorderLayout.SOUTH, hscroll_wrapper);
1610
1611    hscroll_bar.addAdjustmentListener(new AdjustmentListener() {
1612
1613        public void adjustmentValueChanged(AdjustmentEvent e) {
1614        JScrollBar sb = (JScrollBar) e.getAdjustable();
1615        switch(e.getAdjustmentType()) {
1616            case AdjustmentEvent.TRACK:
1617
1618            int pos = e.getValue();
1619
1620            // adjustmentValueChanged() will get called when we
1621
// call setValues() on the scrollbar.
1622
// This test sort-of filters that out
1623
if (pos == st.firsty)
1624                break;
1625
1626            // deal with the user moving the thumb
1627
st.firsty = pos;
1628            repaint(false);
1629            break;
1630
1631            default:
1632            /* DEBUG
1633            System.out.println("adjustmentValueChanged: " + e); // NOI18N
1634            */

1635            break;
1636        }
1637        }
1638    } );
1639
1640    screen.addComponentListener(new ComponentAdapter() {
1641
1642        public void componentResized(ComponentEvent e) {
1643        Dimension size = screen.getSize();
1644        sizeChanged(size.width, size.height);
1645
1646        // workaround for java bug 4711314 where a repaint()
1647
// appears before componentResized() causing issue 25313
1648
repaint(true);
1649        }
1650        
1651    } );
1652
1653    screen.addKeyListener(new KeyListener() {
1654
1655        // We consume all events so no additional side-effects take place.
1656
// Examples:
1657
// - We don't want TAB to move focus away. (see more below)
1658
// - We don't want ^p to bring up a printer diaolog
1659
// - etc
1660

1661        // HACK. Java maps [Return] to 10 as opposed to 13. This is due
1662
// to it's Windows-chauvinism, a 'reality' pointed out to me by
1663
// one of the AWT people.
1664
// The keycode doesn't make it to keyTyped, so for now we detect
1665
// a Return by capturing press/releases of VK_ENTER and
1666
// use a flag.
1667
boolean saw_return;
1668
1669        public void keyTyped(KeyEvent e) {
1670        char c = e.getKeyChar();
1671
1672        if (debugKeys())
1673            System.out.println("term: keyTyped: " + e); // NOI18N
1674

1675        if (read_only)
1676            return;
1677
1678        if (c == 10 && saw_return) {
1679            saw_return = false;
1680            c = (char) 13;
1681        }
1682
1683        if (passOn && maybeConsume(e)) {
1684            on_char(c);
1685            possiblyScrollOnInput();
1686        }
1687
1688        hscrollReset(c);
1689        }
1690
1691        public void keyPressed(KeyEvent e) {
1692        /* DEBUG
1693        System.out.println("keyPressed " + e); // NOI18N
1694        */

1695
1696        switch (e.getKeyCode()) {
1697            case KeyEvent.VK_COPY:
1698            copyToClipboard();
1699            break;
1700            case KeyEvent.VK_PASTE:
1701            pasteFromClipboard();
1702            break;
1703            case KeyEvent.VK_ENTER:
1704            saw_return = true;
1705            break;
1706            case KeyEvent.VK_PAGE_UP:
1707            if ( e.getModifiers() == Event.CTRL_MASK ) {
1708                pageLeft(1);
1709            } else {
1710                pageUp(1);
1711            }
1712            break;
1713            case KeyEvent.VK_PAGE_DOWN:
1714            if ( e.getModifiers() == Event.CTRL_MASK ) {
1715                pageRight(1);
1716            } else {
1717                pageDown(1);
1718            }
1719            break;
1720
1721            case KeyEvent.VK_UP:
1722            if (e.getModifiers() == Event.CTRL_MASK) {
1723                lineUp(1);
1724            }
1725            break;
1726            case KeyEvent.VK_DOWN:
1727            if (e.getModifiers() == Event.CTRL_MASK) {
1728                lineDown(1);
1729            }
1730            break;
1731        }
1732        passOn = maybeConsume(e);
1733        }
1734
1735        public void keyReleased(KeyEvent e) {
1736        /* DEBUG
1737        System.out.println("keyReleased"); // NOI18N
1738        */

1739
1740        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
1741            /* DEBUG
1742            System.out.println("keyReleased VK_ENTER"); // NOI18N
1743            */

1744            saw_return = false;
1745        }
1746        maybeConsume(e);
1747        }
1748    });
1749
1750    screen.addMouseMotionListener(new MouseMotionListener() {
1751
1752        public void mouseDragged(MouseEvent e) {
1753        /* DEBUG
1754        System.out.println("mouseDragged"); // NOI18N
1755        */

1756                
1757                // akemr - fix of #25648
1758
if ( SwingUtilities.isRightMouseButton( e ) )
1759                    return;
1760                
1761        if (left_down_point != null) {
1762            BCoord bc = toBufCoords(toViewCoord(left_down_point));
1763            sel.track(new Coord(bc, firsta));
1764            left_down_point = null;
1765        }
1766        drag_point = e.getPoint();
1767        /* DEBUG
1768        System.out.println("mouseDrag: " + drag_point); // NOI18N
1769        */

1770
1771        int scroll_direction = 0;
1772
1773        if (drag_point.y < 0) {
1774            scroll_direction |= Scroller.UP;
1775        } else if (drag_point.y > screen.getSize().height) {
1776            scroll_direction |= Scroller.DOWN;
1777        }
1778
1779        if (drag_point.x < 0) {
1780            scroll_direction |= Scroller.LEFT;
1781        } else if (drag_point.x > screen.getSize().width) {
1782            scroll_direction |= Scroller.RIGHT;
1783        }
1784
1785        scroll_to(scroll_direction, e);
1786        }
1787
1788        public void mouseMoved(MouseEvent e) {
1789        /* DEBUG
1790        Point p = (Point) e.getPoint().clone();
1791        BCoord bc = toBufCoords(toViewCoord(p));
1792        Coord c = new Coord(bc, firsta);
1793        Extent x = sel.getExtent();
1794        if (x == null) {
1795            System.out.println("sel intersect: no extent"); // NOI18N
1796        } else {
1797            System.out.println("sel intersect: " + // NOI18N
1798            (x.intersects(c.row, c.col)? "intersects" // NOI18N
1799                         "doesn't intersect")); // NOI18N
1800        }
1801        */

1802        }
1803    });
1804
1805        addMouseWheelHandler( screen, vscroll_bar );
1806
1807    screen.addMouseListener(new MouseListener() {
1808
1809        public void mouseClicked(MouseEvent e) {
1810        BCoord bcoord = toBufCoords(toViewCoord(e.getPoint()));
1811
1812        if (SwingUtilities.isLeftMouseButton(e)) {
1813            /* DEBUG
1814            System.out.println("LEFT click"); // NOI18N
1815            */

1816            if (click_to_type)
1817            requestFocus();
1818
1819        } else if ( SwingUtilities.isMiddleMouseButton(e)) {
1820            /* DEBUG
1821            System.out.println("MIDDLE click"); // NOI18N
1822            System.out.println("Selection: '" + sel.sel_get() + "'"); // NOI18N
1823            */

1824            pasteFromSelection();
1825        }
1826        }
1827
1828        public void mousePressed(MouseEvent e) {
1829        /* DEBUG
1830        System.out.println("mousePressed "+e.getModifiers()); // NOI18N
1831        */

1832
1833        if (SwingUtilities.isLeftMouseButton(e)) {
1834
1835            if (e.isShiftDown()) {
1836            // JLF/dtterm selection extension
1837
// Actually it's _addition_ so SHOULD enhance Sel
1838
// to do that instead.
1839
BCoord bc = toBufCoords(toViewCoord(e.getPoint()));
1840            if (sel.extend(new Coord(bc, firsta))) {
1841                fireSelectionExtentChanged();
1842                repaint(false);
1843            }
1844            return;
1845            }
1846
1847            if (sel.cancel(false))
1848            repaint(false);
1849
1850            if (e.getClickCount() == 1) {
1851            left_down_point = (Point) e.getPoint().clone();
1852
1853            } else if (e.getClickCount() == 2) {
1854            BCoord bcoord = toBufCoords(toViewCoord(e.getPoint()));
1855            BExtent word = buf.find_word(word_delineator, bcoord);
1856            sel.select_word(word.toExtent(firsta));
1857            repaint(false);
1858
1859            } else if (e.getClickCount() == 3) {
1860            BCoord bcoord = toBufCoords(toViewCoord(e.getPoint()));
1861            sel.select_line(new Coord(bcoord, firsta));
1862            repaint(false);
1863
1864            }
1865
1866            fireSelectionExtentChanged();
1867        }
1868        }
1869        public void mouseReleased(MouseEvent e) {
1870        /* DEBUG
1871        System.out.println("mouseReleased"); // NOI18N
1872        */

1873        if (SwingUtilities.isLeftMouseButton(e)) {
1874
1875            if (e.isShiftDown()) {
1876            // we're extending
1877
return;
1878            }
1879
1880            if (scroller != null) {
1881            scroller.interrupt();
1882            scroller = null;
1883            }
1884
1885            // Don't put in the selection if we didn't move
1886
// When left button goes down 'left_down_point' is set.
1887
// As soon as we move the mouse it gets reset.
1888
if (left_down_point == null)
1889            sel.done( /* OLD false */);
1890            left_down_point = null;
1891        }
1892        }
1893
1894
1895        /*
1896         * Implement follow-mouse focus
1897         */

1898        public void mouseEntered(MouseEvent e) {
1899        /* DEBUG
1900        System.out.println("mouseEntered"); // NOI18N
1901        */

1902        if (!click_to_type) {
1903            requestFocus();
1904        }
1905        }
1906        public void mouseExited(MouseEvent e) {
1907        /* DEBUG
1908        System.out.println("mouseExited"); // NOI18N
1909        */

1910        }
1911
1912    });
1913
1914    screen.addFocusListener(new FocusListener() {
1915        public void focusGained(FocusEvent e) {
1916        /* DEBUG
1917        System.out.println("Focus gained >>>>>>>>>>>>>>>>>>>>>>>"); // NOI18N
1918        */

1919        has_focus = true;
1920        repaint(false);
1921        }
1922
1923        public void focusLost(FocusEvent e) {
1924        /* DEBUG
1925        Component o = e.getOppositeComponent();
1926        System.out.println("Focus lost <<<<<<<<<<<<<<<<<<<<<<<" + o);
1927        */

1928
1929        has_focus = false;
1930        repaint(false);
1931        }
1932    } );
1933    }
1934
1935
1936    /*
1937     * Print interesting statistics and facts about this Term
1938     */

1939    public void printStats(String JavaDoc message) {
1940    if (message != null)
1941        System.out.println(message);
1942
1943    if (message != null)
1944        System.out.print("\t"); // NOI18N
1945
buf.printStats();
1946
1947    if (message != null)
1948        System.out.print("\t"); // NOI18N
1949
System.out.println("rows " + st.rows + // NOI18N
1950
" v cols " + buf.visibleCols() + // NOI18N
1951
" t cols " + buf.totalCols() + // NOI18N
1952
" history " + history_size + // NOI18N
1953
" firstx " + st.firstx + // NOI18N
1954
" firsty " + st.firsty + // NOI18N
1955
" firsta " + firsta + // NOI18N
1956
" gutter " + glyph_gutter_width); // NOI18N
1957

1958    if (message != null)
1959        System.out.print("\t"); // NOI18N
1960
System.out.println("Cursor " + st.cursor + // NOI18N
1961
" topMargin " + topMargin() + // NOI18N
1962
" botMargin " + botMargin()); // NOI18N
1963

1964    if (message != null)
1965        System.out.print("\t"); // NOI18N
1966
System.out.println("putChar " + n_putchar + // NOI18N
1967
" putChars " + n_putchars + // NOI18N
1968
" linefeeds " + n_linefeeds + // NOI18N
1969
" repaint " + n_repaint + // NOI18N
1970
" paint " + n_paint); // NOI18N
1971
}
1972
1973    public void paste() {
1974    pasteFromClipboard();
1975    }
1976
1977    private void pasteHelp(Clipboard cb) {
1978    if (read_only)
1979        return;
1980
1981    Transferable contents = cb.getContents(screen);
1982    if (contents == null)
1983        return;
1984
1985    if (!contents.isDataFlavorSupported(DataFlavor.stringFlavor))
1986        return;
1987
1988    try {
1989        String JavaDoc string;
1990        string = (String JavaDoc) contents.getTransferData(DataFlavor.stringFlavor);
1991        /* DEBUG
1992        System.out.println("System selection contains '" + string + "'"); // NOI18N
1993        */

1994        char ca[] = string.toCharArray();
1995        sendChars(ca, 0, ca.length);
1996    } catch (Exception JavaDoc e) {
1997        ;
1998    }
1999    }
2000
2001    /**
2002     * Transfer contents of selection to Terms input stream.
2003     * <p>
2004     * The pasted content is sent through sendChars.
2005     * <br>
2006     * The paste will silently fail if:
2007     * <ul>
2008     * <li> The selection has null contents.
2009     * <li> The selections data flavor is not string.
2010     * </ul>
2011     */

2012    public void pasteFromSelection() {
2013    /* DEBUG
2014    System.out.println("Term: pasteFromSelection()"); // NOI18N
2015    */

2016    if (systemSelection == null)
2017        return;
2018    pasteHelp(systemSelection);
2019    }
2020
2021    /**
2022     * Transfer contents of clipboard to Terms input stream.
2023     * <p>
2024     * The pasted content is sent through sendChars.
2025     * <br>
2026     * The paste will silently fail if:
2027     * <ul>
2028     * <li> The selection has null contents.
2029     * <li> The selections data flavor is not string.
2030     * </ul>
2031     */

2032    public void pasteFromClipboard() {
2033    pasteHelp(systemClipboard);
2034    }
2035
2036    /**
2037     * Transfer selected text into clipboard.
2038     */

2039    public void copy() {
2040    copyToClipboard();
2041    }
2042
2043    /**
2044     * Transfer selected text into clipboard.
2045     */

2046    public void copyToClipboard() {
2047    String JavaDoc text = sel.getSelection();
2048    if (text != null) {
2049        StringSelection ss = new StringSelection(text);
2050        systemClipboard.setContents(ss, sel);
2051    }
2052    }
2053
2054    /**
2055     * Transfer selected text into selection.
2056     */

2057    public void copyToSelection() {
2058    /* DEBUG
2059    System.out.println("Term: copyToSelection()"); // NOI18N
2060    */

2061
2062    if (systemSelection == null)
2063        return;
2064    String JavaDoc text = sel.getSelection();
2065    StringSelection ss = new StringSelection(text); // null 'text' is OK
2066
systemSelection.setContents(ss, sel);
2067    }
2068
2069    private static Extent old_extent = null;
2070
2071    void fireSelectionExtentChanged() {
2072    Extent new_extent = getSelectionExtent();
2073    firePropertyChange("selectionExtent", old_extent, new_extent);
2074    old_extent = new_extent;
2075    }
2076
2077    /**
2078     * Set the number of character rows in the screen.
2079     * <p>
2080     * See setRowsColumns() for some additional important information.
2081     */

2082    public void setRows(int rows) {
2083    if (old_rows == -1)
2084        old_rows = st.rows;
2085    st.rows = rows;
2086
2087    updateScreenSize();
2088    }
2089
2090    /**
2091     * Get the number of character rows in the screen
2092     */

2093    public int getRows() {
2094    return st.rows;
2095    }
2096
2097    /**
2098     * Set the number of character columns in the screen.
2099     * <p>
2100     * See setRowsColumns() for some additional important information.
2101     */

2102    public void setColumns(int cols) {
2103    buf.setVisibleCols(cols);
2104    updateScreenSize();
2105    }
2106
2107    /**
2108     * Trampoline from Line.ensureCapacity() to Buffer.noteColumn()
2109     */

2110    void noteColumn(Line l, int capacity) {
2111    int vcapacity = l.bufToCell(metrics, capacity);
2112    buf.noteColumn(vcapacity);
2113    }
2114
2115    /**
2116     * Trampoline from Line to MyFontMetrics.checkForMultiCell()
2117     */

2118    void checkForMultiCell(char c) {
2119    metrics.checkForMultiCell(c);
2120    }
2121
2122    /**
2123     * Get the number of character columns in the screen
2124     */

2125    public int getColumns() {
2126    return buf.visibleCols();
2127    }
2128
2129    /**
2130     * Simultaneously set the number of character rows and columns.
2131     * <p>
2132     * A Term is a composite widget made of a contained screen (getScreen())
2133     * and a scrollbar. It is not a JScrollPane. You're actually setting
2134     * the screen size here.
2135     * <p>
2136     * Setting the column size also sets the width of the buffer. This doesn't
2137     * alter the length (column at which lines were wrapped) of past lines,
2138     * but only new additional lines. For example, if you set columns to
2139     * 20, print a bunch of lines that wrap, then resize to 80 columns, all
2140     * the lines that were wrapped to 20 will stay wrapped that way. This is
2141     * consistent with xterm behaviour.
2142     * <p>
2143     * If this Term is embedded in a component with a layout manager that is
2144     * set up to accept child resizes gracefully this widget will be resized
2145     * as expected.
2146     * <p>
2147     * Alternatively if this Term is embedded in a Window (like JFrame)
2148     * the window will need to be re-pack()ed as it does not accomodate it's
2149     * childrens size changes. This has to be done by the application using
2150     * Term. The best way to do this is to add a TermListener() and call pack()
2151     * on the appropriate window when a resize notification is fired.
2152     */

2153
2154    public void setRowsColumns(int rows, int columns) {
2155
2156    // Combine code in setRows() and setColumns() so we factor
2157
// the calls to updateScreenSize().
2158

2159    if (old_rows == -1)
2160        old_rows = st.rows;
2161    st.rows = rows;
2162    buf.setVisibleCols(columns);
2163
2164    updateScreenSize();
2165    }
2166
2167    /**
2168     * Governs whether Term will round down resize requests to character
2169     * cell size.
2170     * <p>
2171     * Sizes are usually set by containers' layout managers. If rounding is
2172     * enabled Term will attempt to adjust the size to an even multiple
2173     * of the character cell size.
2174     * <p>
2175     * The layout manager might not neccesarily honor the rounded size.
2176     * The situation can be somewhat improved by making sure that the ultimate
2177     * container is re-packed as described in {@link #setRowsColumns(int, int)}.
2178     */

2179    public void setSizeRounded(boolean size_rounded) {
2180    this.size_rounded = size_rounded;
2181    updateScreenSize();
2182    }
2183
2184    /**
2185     * Returns true if Term will round down resize requests to character
2186     * cell size.
2187     * <p>
2188     * See {@link #setSizeRounded(boolean)} for more info.
2189     */

2190    public boolean isSizeRounded() {
2191    return size_rounded;
2192    }
2193
2194    private boolean size_rounded = true;
2195
2196    /*
2197     * Used to extract the dimensions of the terminal.
2198     * SHOULD replace with getRowsCols and getScreenSize
2199     */

2200    public void fillSizeInfo(Dimension cells, Dimension pixels) {
2201    cells.height = st.rows;
2202    cells.width = buf.visibleCols();
2203    Dimension cpixels = screen.getSize();
2204    pixels.width = cpixels.width - glyph_gutter_width - debug_gutter_width;
2205    pixels.height = cpixels.height;
2206    }
2207
2208    /**
2209     * Once the terminal is connected to something, use this function to
2210     * send all Term Listener notifications.
2211     */

2212    protected void updateTtySize() {
2213    if (screen != null) {
2214        Dimension cells = new Dimension(buf.visibleCols(), st.rows);
2215        Dimension pixels = screen.getSize();
2216        fireSizeChanged(cells, pixels);
2217    }
2218    }
2219
2220    /*
2221     * various coordinate conversion functions
2222     */

2223
2224    BCoord toViewCoord(BCoord b) {
2225    /*
2226     * Convert from buffer coords to view coords
2227     */

2228        Line l = buf.lineAt(b.row);
2229        if (l != null) { //XXX Hotfix for issue 40189 - Buffer.lineAt() will
2230
//catch an AIOBEE and return null. Probably related
2231
//to Ivan's notes about clipping in Term.paint()
2232
int vc = buf.lineAt(b.row).bufToCell(metrics, b.col);
2233            BCoord v = new BCoord(b.row - st.firstx, vc - st.firsty);
2234            return v;
2235        } else {
2236            return null;
2237        }
2238    }
2239
2240    Point toPixel(BCoord v) {
2241    /*
2242     * Convert from view coords to pixel coords
2243     */

2244    Point p = new Point(v.col * metrics.width +
2245                glyph_gutter_width +
2246                debug_gutter_width,
2247                v.row * metrics.height);
2248    return p;
2249    }
2250    
2251    /**
2252     * Convert row/column coords to pixel coords within the widgets
2253     * coordinate system.
2254     * It returns the pixel of the upper left corner of the target cell.
2255     */

2256    public Point toPixel(Coord target) {
2257        BCoord btarget = target.toBCoord(firsta);
2258    return toPixel( btarget );
2259    }
2260
2261    /**
2262     * Convert pixel coords to view row/column coords (0/0-origin)
2263     */

2264    BCoord toViewCoord(Point p) {
2265    BCoord v = new BCoord(p.y / metrics.height,
2266                 (p.x - glyph_gutter_width - debug_gutter_width) / metrics.width);
2267    v.clip(st.rows, buf.visibleCols());
2268    /* DEBUG
2269    System.out.println("toViewCoord() -> " + v); // NOI18N
2270    */

2271    return v;
2272    }
2273
2274    BCoord toBufCoords(BCoord v) {
2275    /*
2276     * Convert view row/column coords to buffer row/column coords.
2277     * If the buffer is smaller than the view, map to the last line.
2278     */

2279    int brow = st.firstx + v.row;
2280    if (brow >= buf.nlines)
2281        brow = buf.nlines-1;
2282    int bc = buf.lineAt(brow).cellToBuf(metrics, st.firsty + v.col);
2283    BCoord b = new BCoord(brow, bc);
2284    /* DEBUG
2285    System.out.println("toBufCoords(" + v + ") -> " + b); // NOI18N
2286    */

2287    return b;
2288    }
2289
2290    /**
2291     * Convert pixel coords to view (visible area) row/column coords (both
2292     * o-origin).
2293     * <p>
2294     * In the returned Point, x represents the column, y the row.
2295     */

2296    public Point mapToViewRowCol(Point p) {
2297    BCoord c = toViewCoord(p);
2298    return new Point(c.col, c.row);
2299    }
2300
2301    /**
2302     * Convert pixel coords to (history) buffer row/column coords (both
2303     * 0-origin).
2304     * <p>
2305     * In the returned Point, x represents the column, y the row.
2306     */

2307    public Point mapToBufRowCol(Point p) {
2308    BCoord c = toBufCoords(toViewCoord(p));
2309    return new Point(c.col, c.row);
2310    }
2311
2312    private Color rendition_to_color(int rendition) {
2313    switch (rendition) {
2314        case 40: return standard_color[0];
2315        case 41: return standard_color[1];
2316        case 42: return standard_color[2];
2317        case 43: return standard_color[3];
2318        case 44: return standard_color[4];
2319        case 45: return standard_color[5];
2320        case 46: return standard_color[6];
2321        case 47: return standard_color[7];
2322
2323        case 58: return custom_color[0];
2324        case 59: return custom_color[1];
2325        case 60: return custom_color[2];
2326        case 61: return custom_color[3];
2327        case 62: return custom_color[4];
2328        case 63: return custom_color[5];
2329        case 64: return custom_color[6];
2330        case 65: return custom_color[7];
2331
2332        default: return null;
2333    }
2334    }
2335
2336
2337    private Color actual_foreground;
2338    private Color actual_background;
2339    private boolean check_selection;
2340    private int totcols;
2341
2342    private void do_run(Graphics g, int yoff, int xoff, int baseline,
2343            int brow, char buf[], Line l,
2344            int attr, int rbegin, int rend) {
2345
2346    /* DEBUG
2347    System.out.println("do_run(" + rbegin + ", " + rend + ")"); // NOI18N
2348    */

2349
2350    int x;
2351    int rlength;
2352    int xlength;
2353
2354    if (metrics.isMultiCell()) {
2355        int vbegin = l.bufToCell(metrics, rbegin);
2356        int vend = l.bufToCell(metrics, rend+1)-1;
2357        x = xoff + (vbegin - st.firsty) * metrics.width;
2358        int vlength = vend - vbegin + 1;
2359        if (vlength <= 0) {
2360        /* DEBUG
2361        System.out.println("do_run(" + rbegin + ", " + rend + ")"); // NOI18N
2362        */

2363        return;
2364        }
2365        rlength = rend - rbegin + 1;
2366        xlength = vlength * metrics.width;
2367
2368    } else {
2369        x = xoff + (rbegin - st.firsty) * metrics.width;
2370        rlength = rend - rbegin + 1;
2371        if (rlength <= 0) {
2372        /* DEBUG
2373        System.out.println("do_run(" + rbegin + ", " + rend + ")"); // NOI18N
2374        */

2375        return;
2376        }
2377        xlength = rlength * metrics.width;
2378    }
2379
2380    boolean reverse = ((attr & Attr.REVERSE) == Attr.REVERSE);
2381    boolean active = ((attr & Attr.ACTIVE) == Attr.ACTIVE);
2382
2383    // choose background color
2384
Color bg = null;
2385
2386    if (active) {
2387        bg = active_color;
2388    } else {
2389        bg = backgroundColor(reverse, attr);
2390    }
2391
2392    if (bg != null) {
2393        // Draw any background
2394
g.setColor(bg);
2395        g.fillRect(x, yoff, xlength, metrics.height - metrics.leading);
2396    }
2397
2398    // Set foreground color
2399
Color fg = foregroundColor(reverse, attr);
2400    g.setColor(fg);
2401
2402    // draw any underscores
2403
if ( (attr & Attr.UNDERSCORE) == Attr.UNDERSCORE) {
2404        int h = metrics.height - metrics.leading - 1;
2405        g.drawLine(x, yoff+h, x + xlength, yoff+h);
2406    }
2407
2408    // Draw the foreground character glyphs
2409
myDrawChars(g, buf, l, rbegin, rlength, x, baseline);
2410
2411    // Draw fake bold characters by redrawing one pixel to the right
2412
if ( (attr & Attr.BRIGHT) == Attr.BRIGHT) {
2413        myDrawChars(g, buf, l, rbegin, rlength, x+1, baseline);
2414    }
2415    }
2416
2417
2418
2419    private final Point newp = new Point();
2420
2421    /*
2422     * Tweak glyph X positions so they fall on cell/grid/column boundries.
2423     */

2424    private void massage_glyphs(GlyphVector gv, int start, int n, Line l) {
2425    Point2D JavaDoc pos0 = gv.getGlyphPosition(0);
2426
2427    // There's one big assumption here that in a monospaced font all the
2428
// Y placements are identical. So we use the placement for the first
2429
// glyph only.
2430
newp.y = (int) pos0.getY();
2431
2432    int col = (int) pos0.getX();
2433    for (int gx = 0; gx < n; gx++) {
2434        newp.x = col;
2435        gv.setGlyphPosition(gx, newp);
2436        col += l.width(metrics, start + gx) * metrics.width;
2437    }
2438    }
2439
2440    /**
2441     * Draw characters in cells.
2442     *
2443     * Fixed width or monospaced fonts implies that the glyphs of all characters
2444     * have the same width. Some non-latin characters (japanese) might have
2445     * glyph widths that are an _integer multiple_ of the latin glyphs. Thus
2446     * cellular (grid based) text widget like this termulator can still place
2447     * all characters nicely. There is a 'C' function wcwidth() which
2448     * ... determines the number of _column_ positions ... and CDE's DtTrem
2449     * ultimately depends on it to place things. (See also Tuthill & Smallberg,
2450     * "Creating worldwide software" PrenticeHall 2nd ed. p98)
2451     *
2452     * Unfortunately the fonts used by Java, even the "monospaced" fonts, do
2453     * not abide by the above convention. I measured a 10pt ja locale latin
2454     * character at 7 pixels wide and a japanese character at 12 pixels wide,
2455     * instead of 14. A similar problem existed with respect to the "unprintbale"
2456     * placeholder square. Until Java 1.4 it used to be 9 or 10 pixels wide!
2457     * The square is fixed, but I"m not sure the above will be anytime soon.
2458     *
2459     * What this means is that Graphics.drawString() when given a mix and match
2460     * of latin and japanese characters will not place them right. Selection
2461     * doesn't work etc.
2462     *
2463     * Nor does Java provide anything resembling wcwidth() so we're rolling
2464     * our own here. That's done in Line.width().
2465     *
2466     * So one approach would be to place each character individually, but it's
2467     * rather slow. Fortunately Java provides a GlyphVector class that allows
2468     * us to tweak the positions of the glyphs. The timing I"ve gotten are
2469     * 50 for one drawChars() per charactr. (SLOWER below)
2470     * 15 using the GlyphVector technique
2471     * 8 using plain drawChars
2472     * Unfortunately GlyphVector's interface leaves a bit to be desired.
2473     * - It does not take a (char [], offset, length) triple and depends
2474     * on the length of the char array passed in. Since our Line char arrays
2475     * have some slop in them we can't pass them directly. Hence the
2476     * "new char[]" and the "System.arraycopy".
2477     * - The interface for getting and setting positions is also a bit
2478     * awkward as you may notice from massage_glyphs().
2479     *
2480     * We SHOULD fall back on plain drawChars() if the host charset is an
2481     * 8 bit encoding like ASCII or ISO 8859. This encoding is available
2482     * via System.getProperty("file.encoding") but there are so many aliases
2483     * for each that I"m wary of hardcoding tests. See
2484     * http://www.iana.org/assignments/character-sets
2485     * Java 1.4 has class Charset that helps with the aliases but we can't
2486     * yet lock into 1.4.
2487     */

2488
2489    private void myDrawChars(Graphics g, char buf[], Line l,
2490                 int start, int howmany, int xoff, int baseline) {
2491    if (metrics.isMultiCell()) {
2492        // slow way
2493
// This looks expensive but it is in fact a whole lot faster
2494
// than issuing a g.drawChars() _per_ character
2495

2496        Graphics2D g2 = (Graphics2D) g;
2497        FontRenderContext frc = g2.getFontRenderContext();
2498        // Gaaah, why doesn't createGlyphVector() take a (char[],offset,len)
2499
// triple?
2500
char[] tmp = new char[howmany];
2501        System.arraycopy(buf, start, tmp, 0, howmany);
2502        GlyphVector gv = getFont().createGlyphVector(frc, tmp);
2503        massage_glyphs(gv, start, howmany, l);
2504        g2.drawGlyphVector(gv, xoff, baseline);
2505    } else {
2506        // fast way
2507
g.drawChars(buf, start, howmany, xoff, baseline);
2508    }
2509    }
2510
2511    /*
2512     * Render one line
2513     * Draw the line on this brow (buffer row 0-origin)
2514     */

2515
2516    private void paint_line_new(Graphics g, Line l, int brow,
2517               int xoff, int yoff, int baseline,
2518               Extent selx) {
2519
2520    int length = l.length();
2521    if (length == 0)
2522        return;
2523
2524    int lastcol;
2525    int firstcol;
2526
2527    if (metrics.isMultiCell()) {
2528
2529        // Figure what buffer column is the first visible one (moral
2530
// equivalent of st.firsty)
2531

2532        // SHOULD replace with something that does cellToBuf/bufToCell
2533
// all at once. There are a couple of other occurances of this
2534
// pattern.
2535

2536        firstcol = l.cellToBuf(metrics, st.firsty);
2537        int inverse_firstcol = l.bufToCell(metrics, firstcol);
2538        int delta = st.firsty - inverse_firstcol;
2539        if (delta > 0) {
2540        /* This is what to do if we want to draw the right half of the
2541         * glyph. However the left half of it will end up in the glyph
2542         * gutter and to compensate for thet we'll need to tweak the
2543         * clip region. For now taking the easy way out>
2544
2545        int pdelta = delta * metrics.width; // pixel delta
2546        xoff -= pdelta;
2547
2548        */

2549
2550        firstcol++;
2551        int pdelta = delta * metrics.width; // pixel delta
2552
xoff += pdelta;
2553        }
2554
2555        lastcol = l.cellToBuf(metrics, st.firsty + buf.visibleCols() - 1);
2556
2557        /* DEBUG
2558        System.out.print
2559        ("firstcol = " + firstcol + " for firsty " + st.firsty); // NOI18N
2560        System.out.print
2561        (" delta = " + delta); // NOI18N
2562        System.out.println
2563        (" lastcol = " + lastcol + // NOI18N
2564         " for visibleCols " + buf.visibleCols()); // NOI18N
2565        */

2566
2567    } else {
2568        lastcol = st.firsty + buf.visibleCols() - 1;
2569        firstcol = st.firsty;
2570    }
2571
2572
2573    lastcol = Math.min(lastcol, length-1);
2574    if (firstcol > lastcol)
2575        return;
2576    int howmany = lastcol - firstcol + 1;
2577
2578
2579    // 'length' is not used from here on down
2580

2581    char buf[] = l.charArray();
2582
2583    if (! l.hasAttributes()) {
2584
2585        if (debugWrap()) {
2586        if (l.isWrapped() && l.isAboutToWrap())
2587            g.setColor(Color.red); // not a good state to be in
2588
else if (l.isAboutToWrap())
2589            g.setColor(Color.orange);
2590        else if (l.isWrapped())
2591            g.setColor(Color.magenta);
2592        }
2593
2594        myDrawChars(g, buf, l, firstcol, howmany, xoff, baseline);
2595
2596
2597        return;
2598    }
2599
2600    int attrs[] = l.attrArray();
2601
2602    // find the extent of the selection on this line
2603
int sbegin = -1;
2604    int send = -1;
2605    if (check_selection && selx != null) {
2606        int arow = firsta + brow;
2607        Coord b = selx.begin;
2608        Coord e = selx.end;
2609        if (b.row <= arow && e.row >= arow) {
2610        if (b.row == e.row) {
2611            sbegin = b.col;
2612            send = e.col;
2613        } else if (arow == b.row) {
2614            sbegin = b.col;
2615            send = totcols;
2616        } else if (arow == e.row) {
2617            sbegin = 0;
2618            send = e.col;
2619        } else {
2620            sbegin = 0;
2621            send = totcols;
2622        }
2623        }
2624    }
2625
2626    // iterate through runs
2627

2628    int rbegin = firstcol;
2629    int rend = rbegin;
2630
2631    while (true) {
2632
2633        // find a "run"
2634
// A run, as in run-length-encoding, is a set of characters with
2635
// the same attributes.
2636

2637        int attr = attrs[rbegin];
2638        rend = rbegin+1;
2639        while (rend <= lastcol) {
2640        if (attrs[rend] != attr)
2641            break;
2642        rend++;
2643        }
2644        rend--;
2645
2646        // render the run
2647
// need to do this with awareness of the selection
2648
// parts that fall under the selection are rendered with an
2649
// alternative attribute set.
2650

2651        int alt_attr = (attr & ~ (Attr.BGCOLOR|Attr.FGCOLOR|Attr.REVERSE|Attr.ACTIVE));
2652        if (sbegin == -1 || send < rbegin || sbegin > rend) {
2653        // run is not in selection
2654
do_run(g, yoff, xoff,
2655               baseline, brow, buf, l, attr, rbegin, rend);
2656
2657        } else if (sbegin <= rbegin && send >= rend) {
2658        // run entirely in selection
2659
/* DEBUG
2660        System.out.println("run entirely in selection"); // NOI18N
2661        */

2662        do_run(g, yoff, xoff,
2663               baseline, brow, buf, l, alt_attr, rbegin, rend);
2664
2665        } else if (sbegin > rbegin && send < rend) {
2666        // selection fully within run
2667
// split into three parts
2668
/* DEBUG
2669        System.out.println("run selection fully within run"); // NOI18N
2670        */

2671        do_run(g, yoff, xoff,
2672               baseline, brow, buf, l, attr, rbegin, sbegin-1);
2673        do_run(g, yoff, xoff,
2674               baseline, brow, buf, l, alt_attr, sbegin, send);
2675        do_run(g, yoff, xoff,
2676               baseline, brow, buf, l, attr, send+1, rend);
2677
2678        } else if (sbegin <= rbegin) {
2679        // selection covers left portion of run
2680
/* DEBUG
2681        System.out.println("selection covers left portion of run"); // NOI18N
2682        */

2683        // split into two parts
2684
do_run(g, yoff, xoff,
2685               baseline, brow, buf, l, alt_attr, rbegin, send);
2686        do_run(g, yoff, xoff,
2687               baseline, brow, buf, l, attr, send+1, rend);
2688
2689        } else if (send >= rend) {
2690        // selection covers right portion of run
2691
// split into two parts
2692
/* DEBUG
2693        System.out.println("selection covers right portion of run"); // NOI18N
2694        */

2695        do_run(g, yoff, xoff,
2696               baseline, brow, buf, l, attr, rbegin, sbegin-1);
2697        do_run(g, yoff, xoff,
2698               baseline, brow, buf, l, alt_attr, sbegin, rend);
2699
2700        } else {
2701        /* DEBUG
2702        System.out.println("Odd run/selection overlap"); // NOI18N
2703        */

2704        }
2705
2706        if (rend+1 >= lastcol)
2707        break;
2708
2709        // shift
2710
rbegin = rend + 1;
2711    }
2712    }
2713
2714    /* OLD NPE-x synchronized */ void do_paint(Graphics g) {
2715    ckEventDispatchThread();
2716
2717    /*
2718     * Render the buffer unto the screen
2719     * SHOULD try theses:
2720     * - use drawChars?
2721     * - make passes first the glyphs and BG, then chars, then cursor
2722     * - use drawString(AttributedCharacterIterator iterator, ...)
2723     * - precompute any metrics related stuff
2724     */

2725    if (st.firstx == -1) {
2726        /* DEBUG
2727        System.out.println("Term.paint() no lines"); // NOI18N
2728        */

2729        return;
2730    }
2731
2732    /* DEBUG
2733    long paint_start_time = System.currentTimeMillis();
2734    */

2735
2736    // If Screen is opaque it seems that there is a bug in Swing where
2737
// the Graphics that we get here ends up with fonts other than what
2738
// we assigned to Term. So we make doubly sure here.
2739
g.setFont(getFont());
2740
2741    n_paint++;
2742
2743    if (reverse_video) {
2744        actual_foreground = getBackground();
2745        actual_background = getForeground();
2746    } else {
2747        actual_foreground = getForeground();
2748        actual_background = getBackground();
2749    }
2750
2751    // clear the screen
2752
g.setColor(actual_background);
2753    g.fillRect(0, 0, screen.getSize().width, screen.getSize().height);
2754
2755    // draw any BG stripes
2756
// do this before the selection
2757

2758
2759    int xoff = debug_gutter_width + glyph_gutter_width;
2760
2761    int lx = st.firstx;
2762
2763    for (int vrow = 0; vrow < st.rows; vrow++) {
2764        Line l = buf.lineAt(lx);
2765        if (l == null)
2766        break; // don't make a big fuss the loop below will
2767

2768        int yoff = metrics.height*vrow;
2769
2770        Color background = rendition_to_color(l.glyph_rendition);
2771        if (background != null) {
2772        int rect_height = metrics.height - metrics.leading;
2773        g.setColor(background);
2774        g.fillRect(xoff, yoff, screen.getWidth(), rect_height);
2775        }
2776
2777        lx++;
2778    }
2779
2780
2781    if (!selection_xor) {
2782        // The screen is clear, draw any selections
2783
//
2784
// If most of the lines are w/o attributes then the text just gets
2785
// draw over this. Lines that are attributed end up doing some
2786
// redundant work repainting.
2787

2788        sel.paint(g);
2789    }
2790
2791
2792    g.setColor(actual_foreground);
2793
2794    Extent selx = sel.getExtent();
2795    check_selection = (selx != null && !selection_xor);
2796    totcols = buf.totalCols();
2797
2798    /* DEBUG
2799    System.out.println("=========================================="); // NOI18N
2800    */

2801    lx = st.firstx;
2802
2803    for (int vrow = 0; vrow < st.rows; vrow++) {
2804        Line l = buf.lineAt(lx);
2805        if (l == null) {
2806        /* DEBUG
2807        System.out.println("vrow " + vrow + " lx " + lx); // NOI18N
2808        */

2809        printStats(null);
2810        break;
2811        }
2812
2813        xoff = 0;
2814        int yoff = metrics.height*vrow;
2815        int baseline = yoff + metrics.ascent;
2816
2817        if (debug_gutter_width > 0) {
2818        String JavaDoc buf = "" + (firsta + st.firstx + vrow); // NOI18N
2819
g.drawString(buf, xoff, baseline);
2820        }
2821        xoff += debug_gutter_width;
2822
2823        // draw any glyphs that we might have
2824
if (glyph_gutter_width > 0) {
2825        Image image = glyph_images[l.glyph_glyph];
2826        if (image != null) {
2827            // xy passed to drawImage() is the top-left of the image
2828
int gyoff = yoff;
2829            g.drawImage(image, xoff, gyoff, Color.white, null);
2830        }
2831        }
2832        xoff += glyph_gutter_width;
2833
2834        paint_line_new(g, l, vrow + st.firstx, xoff, yoff, baseline, selx);
2835
2836        // restore 'g' to something reasonable
2837
g.setColor(actual_foreground);
2838
2839        lx++;
2840    }
2841
2842    paint_cursor(g);
2843
2844    if (selection_xor)
2845        sel.paint(g);
2846
2847    if (debugMargins())
2848        paint_margins(g);
2849
2850    /* DEBUG
2851    long paint_stop_time = System.currentTimeMillis();
2852    long paint_time = paint_stop_time - paint_start_time;
2853    System.out.println("paint_time = " + paint_time); // NOI18N
2854    */

2855    }
2856
2857    private void paint_margins(Graphics g) {
2858    }
2859
2860    private void paint_cursor(Graphics g) {
2861    if (!cursor_visible)
2862        return;
2863
2864    // figure what row the cursor is on
2865
if (st.cursor.row == -1) {
2866        // System.out.println("Term.paint_cursor: " + // NOI18N
2867
// "cursor doesn't point to any line"); // NOI18N
2868
return;
2869    }
2870
2871    int cursor_row = st.cursor.row - st.firstx;
2872    if (cursor_row >= st.rows) {
2873        return; // cursor not visible
2874
}
2875
2876    int cursor_col = st.cursor.col - st.firsty;
2877    if (cursor_col >= buf.visibleCols()) {
2878        return; // cursor not visible
2879
} else if (cursor_col < 0) {
2880        return; // cursor not visible
2881
}
2882
2883    g.setXORMode(actual_background);
2884    int rect_x = cursor_col * metrics.width +
2885             glyph_gutter_width +
2886             debug_gutter_width;
2887    int rect_y = cursor_row * metrics.height;
2888    // we _don't_ make cursor as wide as underlying character
2889
int rect_width = metrics.width;
2890    int rect_height = metrics.height - metrics.leading;
2891    if (has_focus)
2892        g.fillRect(rect_x, rect_y, rect_width, rect_height);
2893    else
2894        g.drawRect(rect_x, rect_y, rect_width, rect_height);
2895    }
2896
2897    private final boolean do_margins = true;
2898
2899    private boolean possiblyScrollDown() {
2900    /*
2901     * If cursor has moved below the scrollable region scroll down.
2902     * Buffer manipulation is partly done here or at the callsite if
2903     * 'true' is returned.
2904     */

2905
2906    if (!do_margins) {
2907        if (st.cursor.row >= st.firstx + st.rows) {
2908        // scroll down
2909
st.firstx++;
2910        return true;
2911        }
2912        return false;
2913    } else {
2914        // new margin based scrolling
2915
if (st.cursor.row >= st.firstx + botMargin() + 1) {
2916        // scroll down
2917
if (topMargin() == 0) {
2918            if (scroll_on_output || cursor_was_visible() && track_cursor)
2919            st.firstx++;
2920            return true;
2921        } else {
2922            st.cursor.row = st.firstx + botMargin();
2923            Line l = buf.moveLineFromTo(st.firstx + topMargin(),
2924                       st.cursor.row);
2925            l.reset();
2926            return false;
2927        }
2928        }
2929        return false;
2930    }
2931    }
2932
2933    /**
2934     * Send a character to the terminal screen.
2935     */

2936    public void putChar(char c) {
2937    dce_end.putChar(c);
2938    }
2939
2940    /**
2941     * Send several characters to the terminal screen.
2942     * <br>
2943     * While 'buf' will have a size it may not be fully filled, hence
2944     * the explicit 'nchar'.
2945     */

2946    public void putChars(char buf[], int offset, int count) {
2947    dce_end.putChars(buf, offset, count);
2948    }
2949
2950    /**
2951     * Force a repaint.
2952     * <p>
2953     * Normally a putChar() or putChars() will call repaint, unless ...
2954     * setRepaintEnabled() has been called with false. This function
2955     * allows for some flexibility wrt to buffering and flushing:
2956     * <pre>
2957     * term.setRefreshEnabled(false);
2958     * for (cx = 0; cx < buf.length; cx++) {
2959     * term.putChar(c);
2960     * if (c % 10 == 0)
2961     * term.flush();
2962     * }
2963     * term.setRefreshEnabled(true);
2964     * </pre>
2965     */

2966    public void flush() {
2967    dce_end.flush();
2968    }
2969
2970    /**
2971     * Send a message back to DCE.
2972     * <p>
2973     * Perhaps SHOULD lock out sendChar() so user input doesn't interfere.
2974     */

2975    private void reply(String JavaDoc str) {
2976    /* DEBUG
2977    System.out.println("replying " + str); // NOI18N
2978    */

2979    for (int sx = 0; sx < str.length(); sx++)
2980        sendChar(str.charAt(sx));
2981    }
2982
2983
2984    /*
2985     * Term used to implement Ops but then that forces Ops to be public
2986     * which we don't want. So we do it this way.
2987     */

2988    private class OpsImpl implements Ops {
2989
2990    public void op_pause() {
2991
2992        // This yields slighlty more reasonable results.
2993
Thread.currentThread().yield();
2994
2995        /*
2996
2997        DtTerm sends ~240 NUL's between reverse video switching to
2998        simulate a flash. The resolution of Thread.sleep() isn't
2999        really one millisecond so the flash ends up being too long.
3000
3001        try {
3002        Thread.currentThread().sleep(0, 500);
3003        } catch (InterruptedException x) {
3004        ;
3005        }
3006        */

3007    }
3008
3009
3010    public void op_char(char c) {
3011        if (debugOps())
3012        System.out.println("op_char('" + c + "') = " + (int) c); // NOI18N
3013

3014        // generic character printing
3015
Line l = cursor_line();
3016
3017        int insertion_col = l.cellToBuf(metrics, st.cursor.col);
3018        if (debugOps()) {
3019        System.out.println("op_char(): st.cursor.col " + st.cursor.col + // NOI18N
3020
" insertion_col " + insertion_col); // NOI18N
3021
}
3022        if (!st.overstrike) {
3023        // This just shifts stuff the actual character gets put in below.
3024
l.insertCharAt(Term.this, ' ', insertion_col, st.attr);
3025        }
3026
3027        int cwidth = metrics.wcwidth(c);
3028        if (l.isAboutToWrap() ||
3029        (cwidth > 1 &&
3030         st.cursor.col + cwidth > buf.visibleCols() &&
3031         !horizontally_scrollable)) {
3032
3033        // 'wrap' the line
3034
if (debugOps())
3035            System.out.println("\twrapping it"); // NOI18N
3036
l.setWrapped(true);
3037        l.setAboutToWrap(false);
3038        op_line_feed();
3039        op_carriage_return();
3040        l = cursor_line();
3041        insertion_col = 0;
3042        // Fall thru
3043
}
3044
3045        l.setCharAt(Term.this, c, insertion_col, st.attr); // overstrike
3046
st.cursor.col += cwidth;
3047
3048        if (st.cursor.col >= buf.visibleCols() && !horizontally_scrollable) {
3049        if (debugOps())
3050            System.out.println("\tabout to wrap"); // NOI18N
3051
l.setAboutToWrap(true);
3052        st.cursor.col -= cwidth;
3053        }
3054    }
3055
3056    public void op_attr(int attr) {
3057        if (debugOps())
3058        System.out.println("op_attr(" + attr + ")"); // NOI18N
3059
setAttribute(attr);
3060    }
3061
3062    public void op_bel() {
3063        // ring the bell
3064
// SHOULD implement
3065
}
3066
3067    public void op_back_space() {
3068        // back-space
3069
if (debugOps())
3070        System.out.println("op_back_space"); // NOI18N
3071

3072        if (st.cursor.col > 0) {
3073        if (! cursor_line().isAboutToWrap()) {
3074            st.cursor.col--;
3075        }
3076        cursor_line().setAboutToWrap(false);
3077
3078        // If we' backed up to column 0, maybe we need to consider
3079
// whether the previous line was wrapped. Older xterms aren't
3080
// this clever, newer ones (Solaris 8+?) are.
3081

3082        if (st.cursor.col == 0) {
3083            if (st.cursor.row > 0) {
3084            // maybe we had wrapped on the previous line?
3085
if (debugOps())
3086                System.out.println("\tchecking if prev is wrapped"); // NOI18N
3087
Line prev = buf.lineAt(st.cursor.row-1);
3088            if (prev.isWrapped()) {
3089                if (debugOps())
3090                System.out.println("\tit is"); // NOI18N
3091
st.cursor.row--;
3092
3093                // The below is done in a roundabout way because BS doesn't
3094
// really reduce length. So, suppose we went to the end with
3095
// latin chars that makes the line 80 long. Then we backspace
3096
// to column 78 and enter one 2-cell japanese character. Now
3097
// the line is conceptually 79 long, but it still remembers
3098
// the 80. So we don't use 'prev.length()' directly.
3099

3100                // st.cursor.col = prev.bufToCell(metrics, prev.length()-1);
3101

3102                int last_col = prev.cellToBuf(metrics, buf.visibleCols()-1);
3103                st.cursor.col = prev.bufToCell(metrics, last_col);
3104
3105                prev.setWrapped(false);
3106
3107                // The following isn't entirely correct when we backspaced
3108
// over a multi-celled character. SHOULD either note
3109
// what we BS'ed over or note the slop at the end of the line.
3110
prev.setAboutToWrap(true);
3111            }
3112            }
3113        }
3114        }
3115    }
3116
3117    public void op_line_feed() {
3118        // NL line feed ctrl-J
3119
// move cursor down one line and if goes past the screen
3120
// add a new line.
3121
if (debugOps())
3122        System.out.println("op_line_feed"); // NOI18N
3123
Line last_line = cursor_line();
3124        /* DEBUG
3125        if (last_line == null) {
3126        Thread.dumpStack();
3127        printStats("last_line == null in op_line_feed()");// NOI18N
3128        }
3129        */

3130        st.cursor.row++;
3131        if (possiblyScrollDown()) {
3132        buf.addLineAt(st.cursor.row);
3133        limit_lines();
3134        if (debugOps())
3135            System.out.println("op_line_feed ADJUSTED"); // NOI18N
3136
}
3137        // have new line inherit cursorAtEnd
3138
boolean atw = last_line.isAboutToWrap();
3139        cursor_line().setAboutToWrap(atw);
3140        last_line.setAboutToWrap(false);
3141
3142        n_linefeeds++;
3143
3144        // See repaint() for an explanation of this.
3145
// repaint(false);
3146
}
3147
3148    public void op_tab() {
3149        // TAB/HT
3150
// SHOULD do something better with tabs near the end of the line
3151
// On the other hand, that's how ANSI terminals are supposed
3152
// to behave
3153

3154        if (debugOps())
3155        System.out.println("op_tab"); // NOI18N
3156

3157        if (st.cursor.col == buf.visibleCols()-1 && !horizontally_scrollable)
3158        return;
3159
3160        Line l = cursor_line();
3161        int insert_col = l.cellToBuf(metrics, st.cursor.col);
3162        l.setCharAt(Term.this, ' ', insert_col, st.attr);
3163        st.cursor.col++;
3164        insert_col++;
3165        // no need to re-apply cellToBuf to cursor since we're only adding 1-wide ' '
3166
while ((st.cursor.col < buf.visibleCols()-1 || horizontally_scrollable) &&
3167           (st.cursor.col % tab_size) != 0) {
3168        cursor_line().setCharAt(Term.this, ' ', insert_col, st.attr);
3169        st.cursor.col++;
3170        insert_col++;
3171        }
3172    }
3173
3174    public void op_carriage_return() {
3175        if (debugOps())
3176        System.out.println("op_carriage_return"); // NOI18N
3177
st.cursor.col = 0;
3178        cursor_line().setAboutToWrap(false);
3179    }
3180
3181    public void op_al(int count) {
3182        // add new blank line
3183
if (debugOps())
3184        System.out.println("op_al(" + count + ")"); // NOI18N
3185

3186        Line l;
3187        while (count-- > 0) {
3188        boolean old_atw = cursor_line().setAboutToWrap(false);
3189
3190        // reverse of op_dl()
3191
// Rotate a line from bottom to top
3192
if (!do_margins) {
3193            l = buf.moveLineFromTo(buf.nlines-1, st.cursor.row);
3194        } else {
3195            l = buf.moveLineFromTo(st.firstx + botMargin(), st.cursor.row);
3196        }
3197        l.reset();
3198
3199        cursor_line().setAboutToWrap(old_atw);
3200        }
3201
3202        switch(sel.intersection(st.cursor.row - 1)) {
3203        case Sel.INT_NONE:
3204        case Sel.INT_ABOVE:
3205        case Sel.INT_ON:
3206            // nothing to do
3207
break;
3208        case Sel.INT_STRADDLES:
3209            sel.cancel(true); // DtTerm behaviour
3210
break;
3211        case Sel.INT_BELOW:
3212            sel.adjust(firsta, +1, firsta + buf.nlines);
3213            break;
3214        }
3215    }
3216
3217    public void op_bc(int count) {
3218        // back cursor/column
3219
if (debugOps())
3220        System.out.println("op_bc(" + count + ")"); // NOI18N
3221

3222        while (count-- > 0) {
3223        if (st.cursor.col <= 0)
3224            return;
3225        st.cursor.col--;
3226        }
3227        cursor_line().setAboutToWrap(false);
3228    }
3229
3230    public void op_cm(int row, int col) {
3231        // cursor motion row and col come in as 1-origin)
3232
if (debugOps())
3233        System.out.println("op_cm(row " + row + ", col " + col + ")"); // NOI18N
3234

3235        // "xemacs -nw" seems to overflow and underflow often.
3236

3237        // 0 is allowed
3238
if (row == 0)
3239        row = 1;
3240        if (col == 0)
3241        col = 1;
3242
3243        // deal with overflow
3244
if (row > st.rows)
3245        row = st.rows;
3246        if (col > buf.visibleCols())
3247        col = buf.visibleCols();
3248
3249        cursor_line().setAboutToWrap(false);
3250        st.cursor.row = beginx() + row - 1;
3251        st.cursor.col = col - 1;
3252        // Maybe SHOULD setAboutToWrap(true) if on last column?
3253
}
3254
3255    public void op_cl() {
3256        // clear screen and home cursor
3257
if (debugOps())
3258        System.out.println("op_cl"); // NOI18N
3259
cursor_line().setAboutToWrap(false);
3260        clear();
3261        st.cursor.row = beginx();
3262        st.cursor.col = 0;
3263    }
3264
3265    public void op_ce() {
3266        // clear to end of line
3267
if (debugOps())
3268        System.out.println("op_ce"); // NOI18N
3269

3270        Line l = cursor_line();
3271        l.clearToEndFrom(Term.this, l.cellToBuf(metrics, st.cursor.col));
3272
3273        switch(sel.intersection(st.cursor.row)) {
3274        case Sel.INT_NONE:
3275        case Sel.INT_ABOVE:
3276        case Sel.INT_BELOW:
3277            // nothing to do
3278
break;
3279        case Sel.INT_ON:
3280        case Sel.INT_STRADDLES:
3281            sel.cancel(true); // DtTerm behaviour
3282
break;
3283        }
3284    }
3285
3286    public void op_cd() {
3287        // clear to end of screen
3288
if (debugOps())
3289        System.out.println("op_cd -- clear to end of screen"); // NOI18N
3290

3291        for (int lx = st.cursor.row; lx < beginx() + st.rows; lx++) {
3292        Line l = buf.lineAt(lx);
3293        l.reset();
3294        }
3295
3296        switch(sel.intersection(st.cursor.row)) {
3297        case Sel.INT_NONE:
3298        case Sel.INT_ABOVE:
3299            // nothing to do
3300
break;
3301        case Sel.INT_BELOW:
3302        case Sel.INT_ON:
3303        case Sel.INT_STRADDLES:
3304            sel.cancel(true); // DtTerm behaviour
3305
break;
3306        }
3307    }
3308
3309    public void op_dc(int count) {
3310        // delete character
3311
if (debugOps())
3312        System.out.println("op_dc(" + count + ")"); // NOI18N
3313
if (count == 0)
3314        count = 1;
3315        Line l = cursor_line();
3316        while (count-- > 0)
3317        l.deleteCharAt(l.cellToBuf(metrics, st.cursor.col));
3318    }
3319
3320    public void op_dl(int count) {
3321        // delete line
3322
// and scroll everything under it up
3323
if (debugOps())
3324        System.out.println("op_dl(" + count + ")"); // NOI18N
3325

3326        Line l;
3327        while (count-- > 0) {
3328        boolean old_atw = cursor_line().setAboutToWrap(false);
3329
3330        // reverse of op_al()
3331
// Rotate a line from top to bottom
3332
if (!do_margins) {
3333            l = buf.moveLineFromTo(st.cursor.row,
3334                       (beginx()+st.rows-1)-1);
3335        } else {
3336            l = buf.moveLineFromTo(st.cursor.row,
3337                       (beginx()+botMargin())-1);
3338        }
3339        l.reset();
3340
3341        cursor_line().setAboutToWrap(old_atw);
3342        }
3343
3344        switch(sel.intersection(st.cursor.row)) {
3345        case Sel.INT_NONE:
3346        case Sel.INT_ABOVE:
3347            // nothing to do
3348
break;
3349        case Sel.INT_ON:
3350        case Sel.INT_STRADDLES:
3351            sel.cancel(true); // DtTerm behaviour
3352
break;
3353        case Sel.INT_BELOW:
3354            sel.adjust(firsta, -1, firsta + buf.nlines);
3355            break;
3356        }
3357    }
3358
3359    public void op_do(int count) {
3360        // down count lines
3361
// SHOULD add a mode: {scroll, warp, stay} for cases where
3362
// cursor is on the bottom line.
3363

3364        if (debugOps())
3365        System.out.println("op_do(" + count + ") -- down"); // NOI18N
3366

3367        boolean old_atw = cursor_line().setAboutToWrap(false);
3368
3369        while (count-- > 0) {
3370        st.cursor.row++;
3371        if (st.cursor.row >= buf.nlines) {
3372
3373            // equivalent of op_newline:
3374
if (possiblyScrollDown()) {
3375            buf.addLineAt(st.cursor.row);
3376            limit_lines();
3377            if (debugOps())
3378                System.out.println("op_do ADJUSTED"); // NOI18N
3379
}
3380        }
3381        }
3382        cursor_line().setAboutToWrap(old_atw);
3383    }
3384
3385    public void op_ho() {
3386        // cursor home (upper left of the screen)
3387
if (debugOps())
3388        System.out.println("op_ho -- home"); // NOI18N
3389
cursor_line().setAboutToWrap(false);
3390        st.cursor.row = beginx();
3391        st.cursor.col = 0;
3392    }
3393
3394    public void op_ic(int count) {
3395        // insert character
3396
if (debugOps())
3397        System.out.println("op_ic(" + count + ")"); // NOI18N
3398

3399        Line l = cursor_line();
3400        int insertion_col = l.cellToBuf(metrics, st.cursor.col);
3401        while (count-- > 0) {
3402        l.insertCharAt(Term.this, ' ', insertion_col, st.attr);
3403        }
3404        // SHOULD worry about line wrapping
3405
}
3406
3407    public void op_nd(int count) {
3408        // cursor right (non-destructive space)
3409
if (debugOps())
3410        System.out.println("op_nd(" + count + ")"); // NOI18N
3411

3412        int vc = st.cursor.col;
3413        while (count-- > 0) {
3414        vc++;
3415        if (vc >= buf.visibleCols()) {
3416            if (debugOps())
3417            System.out.println("\tbailing out at count " + count); // NOI18N
3418
vc--;
3419            break;
3420        }
3421        }
3422        st.cursor.col = vc;
3423    }
3424
3425    public void op_up(int count) {
3426        // cursor up
3427
if (debugOps())
3428        System.out.println("op_up(" + count + ")"); // NOI18N
3429

3430        boolean old_atw = cursor_line().setAboutToWrap(false);
3431        Line l;
3432        while (count-- > 0) {
3433        st.cursor.row--;
3434        if (st.cursor.row < st.firstx) {
3435            st.cursor.row = st.firstx;
3436            // scroll down, Rotate a line from bottom to top
3437
if (!do_margins) {
3438            l = buf.moveLineFromTo(buf.nlines-1, st.cursor.row);
3439            } else {
3440            l = buf.moveLineFromTo(st.firstx + botMargin(), st.cursor.row);
3441            }
3442            l.reset();
3443            // SHOULD note and do something about the selection?
3444
}
3445        }
3446        cursor_line().setAboutToWrap(old_atw);
3447    }
3448
3449    public void op_sc() {
3450        // save cursor position
3451
if (debugOps())
3452        System.out.println("op_sc()"); // NOI18N
3453
st.saveCursor();
3454        // SHOULD defeat repaint?
3455
}
3456
3457    public void op_rc() {
3458        // restore saved cursor position
3459
if (debugOps())
3460        System.out.println("op_rc()"); // NOI18N
3461
st.restoreCursor();
3462    }
3463
3464    public void op_glyph(int glyph, int rendition) {
3465        if (debugOps()) {
3466        System.out.println("op_glyph(glyph " + glyph + // NOI18N
3467
", rendition " + rendition + ")"); // NOI18N
3468
}
3469        setGlyph(glyph, rendition);
3470    }
3471
3472    public void op_reverse(boolean reverse_video) {
3473        setReverseVideo(reverse_video);
3474    }
3475
3476    public void op_cursor_visible(boolean visible) {
3477        setCursorVisible(visible);
3478    }
3479
3480    public void op_margin(int from, int to) {
3481        if (debugOps()) {
3482        System.out.println("op_margin(" + from + ", " + // NOI18N
3483
to + ")"); // NOI18N
3484
}
3485
3486        if (from < 0)
3487        top_margin = 0;
3488        else if (from > st.rows)
3489        top_margin = st.rows;
3490        else
3491        top_margin = from;
3492
3493        if (to < 0)
3494        bot_margin = 0;
3495        else if (to > st.rows)
3496        bot_margin = st.rows;
3497        else
3498        bot_margin = to;
3499
3500        if (top_margin > bot_margin) {
3501        int tmp = top_margin;
3502        top_margin = bot_margin;
3503        bot_margin = tmp;
3504        }
3505    }
3506
3507    long last_time = System.currentTimeMillis();
3508
3509    public void op_time(boolean repaint) {
3510        long time = System.currentTimeMillis();
3511        long elapsed = time - last_time;
3512        Date JavaDoc d = new Date JavaDoc(time);
3513        String JavaDoc date_str = d.toString();
3514        String JavaDoc elapsed_str = "" + elapsed/1000 + "." + elapsed%1000;// NOI18N
3515
String JavaDoc output1 = date_str + " Elapsed (sec): " + elapsed_str;// NOI18N
3516
String JavaDoc output2 = "putChar " + n_putchar + // NOI18N
3517
" putChars " + n_putchars + // NOI18N
3518
" linefeeds " + n_linefeeds + // NOI18N
3519
" repaint " + n_repaint + // NOI18N
3520
" paint " + n_paint; // NOI18N
3521

3522        setAttribute(41); // Red Bg
3523

3524        // can't use appendText from within ops.
3525

3526        for (int sx = 0; sx < output1.length(); sx++)
3527        op_char(output1.charAt(sx));
3528        op_line_feed();
3529        op_carriage_return();
3530        for (int sx = 0; sx < output2.length(); sx++)
3531        op_char(output2.charAt(sx));
3532
3533        setAttribute(0);
3534
3535        last_time = time;
3536        n_putchar = 0;
3537        n_putchars = 0;
3538        n_linefeeds = 0;
3539        n_paint = 0;
3540        n_repaint = 0;
3541
3542        repaint(true);
3543        // TMP setRefreshEnabled(repaint);
3544
}
3545
3546    public int op_get_width() {
3547        return horizontally_scrollable? buf.totalCols(): buf.visibleCols();
3548    }
3549
3550    public int op_get_column() {
3551        return st.cursor.col;
3552    }
3553
3554    public void op_soft_reset() {
3555        st.overstrike = true;
3556        top_margin = 0; // 0 means default (see topMargin())
3557
bot_margin = 0;
3558        st.attr = 0;
3559        repaint(false);
3560    }
3561
3562    public void op_full_reset() {
3563        op_soft_reset();
3564        op_cl(); // clear screen, home cursor
3565
reverse_video = false;
3566        repaint(false);
3567    }
3568
3569    public void op_set_mode(int mode) {
3570        switch (mode) {
3571        case 4: // insert mode
3572
st.overstrike = false;
3573            break;
3574        case 2: // keyboard lock
3575
case 12: // local echo off
3576
case 20: // newline
3577
// Currently unsupported
3578
break;
3579        }
3580    }
3581
3582    public void op_reset_mode(int mode) {
3583        switch (mode) {
3584        case 4: // replace mode
3585
st.overstrike = true;
3586            break;
3587        case 2: // keyboard unlock
3588
case 12: // local echo on
3589
case 20: // newline
3590
// Currently unsupported
3591
break;
3592        }
3593    }
3594    public void op_status_report(int code) {
3595        switch (code) {
3596        case 5:
3597            reply((char)27 + "[0n"); // NOI18N
3598
break;
3599        case 6:
3600            reply((char)27 + "[" + // NOI18N
3601
(st.cursor.row - st.firstx) + ";" + // NOI18N
3602
st.cursor.col + "R"); // NOI18N
3603
break;
3604        }
3605    }
3606    }
3607
3608    private void putc_work(char c) {
3609    interp.processChar(c);
3610    possiblyHScroll();
3611    screen.possiblyUpdateCaretText();
3612    }
3613
3614    private void on_char(char c) {
3615    sendChar(c);
3616    }
3617
3618    private void sendChars(char c[], int offset, int count) {
3619    dte_end.sendChars(c, offset, count);
3620    }
3621
3622    private void sendChar(char c) {
3623    dte_end.sendChar(c);
3624    }
3625
3626    /**
3627     * Adjust vertical scrollbar range
3628     */

3629    private void adjust_scrollbar() {
3630
3631    // JScrollBar is weird.
3632
// The visible range is 1 (for value) + extent.
3633
// So extent has to be set to visible-range - 1:
3634

3635    adjust_scrollbar_impl();
3636
3637    /* OLD NPE-x
3638    // It's important that we do this from within the AWT event thread.
3639
3640        if (SwingUtilities.isEventDispatchThread()) {
3641            adjust_scrollbar_impl();
3642        }
3643        else {
3644            SwingUtilities.invokeLater(new Runnable() {
3645                public void run() {
3646                    adjust_scrollbar_impl();
3647                }
3648            });
3649        }
3650    */

3651    }
3652
3653    private void adjust_scrollbar_impl() {
3654    if (vscroll_bar != null) {
3655        int value = st.firstx;
3656        int extent = st.rows-1;
3657        int min = 0;
3658        int max;
3659        if (buf.nlines <= st.rows)
3660        max = st.rows - 1;
3661        else
3662        max = buf.nlines - 1;
3663        vscroll_bar.setValues(value, extent, min, max);
3664    }
3665
3666    if (hscroll_bar != null && horizontally_scrollable) {
3667        int value = st.firsty;
3668        int extent = buf.visibleCols()-1;
3669        int min = 0;
3670        int max;
3671        if (buf.totalCols() <= buf.visibleCols())
3672        max = buf.visibleCols() - 1;
3673        else
3674        max = buf.totalCols() - 1;
3675
3676        /* DEBUG
3677        System.out.println("HSCROLL " + min + " <= " + value + // NOI18N
3678        "[" + extent + "] " + max); // NOI18N
3679        */

3680
3681        hscroll_bar.setValues(value, extent, min, max);
3682
3683        /* DEBUG
3684        System.out.println("HSCROLL " + hscroll_bar.getMinimum() + // NOI18N
3685        " <= " + hscroll_bar.getValue() + // NOI18N
3686        "[" + hscroll_bar.getModel().getExtent() + "] " + hscroll_bar.getMaximum()); // NOI18N
3687        */

3688    }
3689    }
3690
3691    /**
3692     * Figure the pixel size of the screen based on various properties.
3693     */

3694    private Dimension calculateSize() {
3695    int dx = buf.visibleCols() * metrics.width +
3696         glyph_gutter_width +
3697         debug_gutter_width;
3698    int dy = st.rows * metrics.height;
3699    Dimension d = new Dimension(dx, dy);
3700    return d;
3701    }
3702
3703    /**
3704     * To be called as the result of programmatic changes in properties
3705     * that affect the size of the screen: font, rows & columns, glyph
3706     * gutter width etc.
3707     * Applies the newly calculated size via sizeChanged().
3708     *
3709     * We used to call screen.setSize() which would eventually call
3710     * sizeChanged() through the notification mechanism. That worked well
3711     * for row column size changes etc, but not so well for font changes.
3712     * What would happen is that he cause of size changes would be lost
3713     * by the time we got to sizeChanged() and for example the screen
3714     * wouldn't resize as a result of font pt-size changes.
3715     */

3716    private void updateScreenSize() {
3717    /* DEBUG
3718    System.out.println("updateScreenSize("+buf.cols+", "+st.rows+")"); // NOI18N
3719    */

3720    if (screen != null) {
3721        Dimension d = calculateSize();
3722        sizeChanged(d.width, d.height);
3723    }
3724    }
3725
3726    // HACK:
3727
// Helper variable to remember the original value of rows across
3728
// various different control flows.
3729

3730    private int old_rows = -1;
3731
3732
3733    /**
3734     * Called whenver the screens size is to be changed, either by
3735     * us via updateScreenSize(), or thru user action and the Screen
3736     * componentResized() notification.
3737     *
3738     * Adjust the state and buffer, commit to the size by setting
3739     * preferredSize and notify any interested parties.
3740     */

3741
3742    void sizeChanged(int newWidth, int newHeight) {
3743    /* DEBUG
3744    System.out.println("sizeChanged(newheight " + newHeight + // NOI18N
3745        ", newWidth " + newWidth + ")");
3746    */

3747
3748    // Do columns first ... they're easy
3749
int newcols = (newWidth - glyph_gutter_width - debug_gutter_width) /
3750              metrics.width;
3751    buf.setVisibleCols(newcols);
3752
3753
3754    if (old_rows == -1) {
3755        // st.rows hasn't changed yet, so remember it before changing it.
3756
old_rows = st.rows;
3757    }
3758
3759    st.rows = newHeight/metrics.height;
3760        
3761        // akemr - hack to fix #17807
3762
if ( st.rows < 1 )
3763            st.rows = 1;
3764        
3765    /* DEBUG
3766    System.out.println(">>>>>>> rows from "+old_rows+" to "+st.rows); // NOI18N
3767    */

3768
3769    int row_delta = st.rows - old_rows; // negative => we shrunk
3770
old_rows = -1;
3771
3772    adjust_lines(row_delta);
3773
3774    limit_lines();
3775
3776    // Commit to the size
3777
//
3778
// Setting setPreferredSize() is where the commitment is. If we
3779
// don't do it our layout manager containers won't honor the resizing
3780
// and snap us back.
3781

3782    Dimension new_size = isSizeRounded()?
3783                calculateSize():
3784                new Dimension(newWidth, newHeight);
3785
3786    /* DEBUG
3787    System.out.println("but I want "+new_size.height+" "+new_size.width); // NOI18N
3788    */

3789
3790    if (false) {
3791        // Setting size is a bad idea. the potential for getting into
3792
// a looping tug-of-war with our containers' layout manager
3793
// is too high and unpredictable. One nasty example we ran
3794
// into was JTabbedPane.
3795
screen.setSize(new_size);
3796
3797    } else {
3798        screen.setPreferredSize(new_size);
3799
3800        // Do we really need these?
3801
invalidate();
3802        if (getParent() != null)
3803        getParent().validate();
3804    }
3805
3806
3807    // Notify any interested parties.
3808
// Normally we'd inline the code in updateTtySize() here but factoring
3809
// it has it's uses as explained in updateTtySize().
3810
updateTtySize();
3811    }
3812
3813
3814    protected void possibly_repaint(boolean adjust_scrollbar) {
3815    if (!refresh_enabled)
3816        return;
3817    repaint(adjust_scrollbar);
3818    }
3819
3820    /**
3821     * Model and or view settings have changed, redraw everything.
3822     */

3823    protected void repaint(boolean adjust_scrollbar) {
3824
3825    /*
3826     * A long discussion on performance and smooth vs jump vs jerky
3827     * scrolling ... (note: a lot of this is based on experiments with
3828     * Term as a unix terminal emulator application as opposed to
3829     * within the context of NetBeans).
3830     *
3831     * Term spends it's time between collecting and deciphering input
3832     * and repainting the screen. Input processing always goes on, but
3833     * screen repainitng can be done more or less often to trade off
3834     * smoothness of scrolling vs speed.
3835     *
3836     * At one end is so-called smooth scrolling. This is where the
3837     * screen is redrawn on every linefeed. That's a lot of painting.
3838     * To get into that mode use the paintImmediately() below and
3839     * uncomment the call to us in op_line_feed(). Also
3840     * paintImmediately() doesn't really work unless the Screen is
3841     * opaque. I think that is because the paint request comes
3842     * to us and we don't forward it to screen; but it could be a
3843     * Swing bug too. Term is very slow in this. For example I"ve
3844     * time xterm and DtTerm dealing with "cat /etc/termcap" in 2-3
3845     * seconds while Term takes 20-25 seconds. Part of this is
3846     * attributed to the fact that Term doesn't take advantage of
3847     * bitBlitting when it's adding one line at a time and still
3848     * redraws everything. However I'll make a case below that this
3849     * isn't that important.
3850     *
3851     * Then there is so-called jump scrolling. In this regime terminal
3852     * emulators redraw the screen "as time permits". This is in effect
3853     * what the swing repaint manager helps with. Multiple repaint()
3854     * requests translate to one actual paint(). With todays computers
3855     * it's very hard to tell visually that you're jump scrolling
3856     * things go by so fast (yes, even under Swing), so this is the
3857     * preferred setup.
3858     * Here term does a bit better. To deal with a cat'ed 100,000
3859     * line file DtTerm takes 8 seconds, while Term takes 22 seconds.
3860     * (That's 3 times slower vs 8 times). From some measurements
3861     * I've made the number of linefeeds per actual paints has
3862     * ranged from > 100 to upper 30's. These numbers are sufficiently
3863     * high that the whole screen has to be repained everytime.
3864     * I.e. blitting to scroll and drawing only what's new isn't
3865     * going to help here. To get reasonable jump-scrolling, you need
3866     * to make sure that the Screen is opaque because if you don't
3867     * you will get ...
3868     *
3869     * Jerky scrolling. If Term is not opaque, the number of actual
3870     * paints per repaint() requests diminishes drastically. 'cat' of
3871     * etc/termcap (once the code has been warmed up) sometimes causes
3872     * a single refresh at the end in contrast to ~100 when Screen
3873     * is opaque. Naturally Term in this mode can eat up input at
3874     * a rate comparable to dtterm etc, but the jerkiness is very
3875     * ugly.
3876     * Opacity isn't the only criterion. Term, when embeded inside a
3877     * tabbed pane (like it is in NetBeans) will also act as if it's
3878     * opaque and you get more frequent refreshes, as in the
3879     * jump-scrolling regime. But that was way too slow for the
3880     * taste of NB users which is why OutputTab window calls us on a
3881     * timer. That brings it's own jerkiness of a different sort.
3882     *
3883     * There is a third factor that contributes to slowness. If you
3884     * just 'cat' a file you get the numbers I presneted above. But
3885     * if you run an app that actually puts out the 100,000 lines
3886     * some sort of timing interaction forces Term into near smooth
3887     * scrolling and as a result things slow down a lot! For example,
3888     * $ generate_100K_lines > /tmp/bag 00:08 sec
3889     * $ cat /tmp/bag 00:20 sec
3890     * $ generate_100K_lines 03:42 sec (opaque)
3891     * $ generate_100K_lines 01:58 sec (!opaque)
3892     * This happens even if the generating program is a lightweight
3893     * native application. In fact I believe it is this effect that
3894     * forced NB's OutputTab to adopt the timer. I believe there are two
3895     * factors that contrinute to this.
3896     * a) Running applications are line buffered so putChars(), with
3897     * it's attendant repaint(), gets called once per line pushing
3898     * us into the smooth scrolling regime. (But why then doesn't
3899     * DtTerm suffer from this?)
3900     * b) timeslicing gives enough time to the repaint manager such
3901     * that it converts evey repaint() to a paint.
3902     * I know (b) is a factor since if I "simulate" (a) by issueing
3903     * repaints() from op_line_feed() while keeping this function from
3904     * using paintImmediately() I don't get that many paints.
3905     * The combined case has 44 paints per repaint as does simulated (a).
3906     * So ain increased number of paints per repaint doesn't
3907     * explain this.
3908     *
3909     * In the end, currently since jump scrolling is still not very
3910     * fast and since NB has the timer anyway, Screen is not opaque.
3911     *
3912     * A useful quantitative measure is the number of linefeeds vs
3913     * the number of repaint requests vs the number of actual paints.
3914     * All these are collected and can be dumped via op_time() or
3915     * printStats().
3916     */

3917
3918    n_repaint++;
3919
3920    if (adjust_scrollbar)
3921        adjust_scrollbar();
3922
3923
3924    // The following causes Screen.paint() to get called by the Swing
3925
// repaint manager which in turn calls back to term.paint(Graphics).
3926
screen.repaint(20);
3927
3928
3929    // The following should cause an immediate paint. It doesn't
3930
// always though!
3931
// I've found that for it to be effective Screen needs to be opaque.
3932

3933    /*
3934    NOTE: paintImmediately() is probably not the best thing to use.
3935    // RepaintManager.currentManager(screen).paintDirtyRegions();
3936    screen.paintImmediately(0, 0, screen.getWidth(), screen.getHeight());
3937    */

3938    }
3939
3940    /*
3941     * Term-specific properties
3942     */

3943
3944    /**
3945     * Control whether the default foreground and background colors will
3946     * be reversed.
3947     * <p>
3948     * Note: This is independent of characters' reverse attribute.
3949     */

3950    public void setReverseVideo(boolean reverse_video) {
3951    this.reverse_video = reverse_video;
3952    repaint(false);
3953    }
3954
3955    /**
3956     * Return the value set by setReverseVideo().
3957     */

3958    public boolean isReverseVideo() {
3959    return reverse_video;
3960    }
3961
3962    private boolean reverse_video = false;
3963
3964    /**
3965     * Set the color of the hilite (selection) - for non XOR mode
3966     */

3967    public void setHighlightColor(Color color) {
3968    sel.setColor(color);
3969    repaint(false);
3970    }
3971
3972    /**
3973     * Get the color of the hilite (selection) - for non XOR mode
3974     */

3975    public Color getHighlightColor() {
3976    return sel.getColor();
3977    }
3978    
3979    /**
3980     * Set the color of the hilite (selection) - for XOR mode
3981     */

3982    public void setHighlightXORColor(Color color) {
3983    sel.setXORColor(color);
3984    repaint(false);
3985    }
3986
3987    /**
3988     * Get the color of the hilite (selection) - for XOR mode
3989     */

3990    public Color getHighlightXORColor() {
3991    return sel.getXORColor();
3992    }
3993
3994
3995    /**
3996     * Set the feedback color of active regions.
3997     */

3998    public void setActiveColor(Color color) {
3999    // SHOULD check for null color
4000
active_color = color;
4001    repaint(false);
4002    }
4003
4004    /**
4005     * Get the feedback color of active regions.
4006     */

4007    public Color getActiveColor() {
4008    // SHOULD clone? but Color is not clonable and has no simple
4009
// Color(COlor) constructor. What does JComponent do?
4010
return active_color;
4011    }
4012
4013    private Color active_color = Color.lightGray;
4014
4015    /**
4016     * Control whether an anchor is set.
4017     * <p>
4018     * Setting an anchor will automatically cause the buffer to grow, in
4019     * excess of what was set by setHistorySize(), to ensure that whatever
4020     * is displayed and in the current history will still be accessible.
4021     * <p>
4022     * Also, if you're working with Extents, Coords and ActiveRegions, or
4023     * visiting logical lines, you might want to anchor the text so that
4024     * your coordinates don't get invalidated by lines going out of the buffer.
4025     * <p>
4026     * Repeated enabling of the anchor will discard all text that
4027     * doesn't fit in history and start a new anchor.
4028     * <p>
4029     * When anchoring is disabled any text in excess of setHistorySize()
4030     * is trimmed and the given history size comes into effect again.
4031     */

4032    public void setAnchored(boolean anchored) {
4033    ckEventDispatchThread();
4034    // OLD NPE-x synchronized(this)
4035
{
4036    if (anchored) {
4037        this.anchored = false;
4038        limit_lines();
4039        this.anchored = true;
4040    } else {
4041        this.anchored = false;
4042        limit_lines();
4043        repaint(false); // limit_lines() already adjusted scrollbar
4044
}
4045    }
4046    }
4047
4048    /**
4049     * Return true if the text is currently anchored.
4050     */

4051    public boolean isAnchored() {
4052    return anchored;
4053    }
4054
4055    private boolean anchored = false;
4056
4057
4058    /**
4059     * Returns the actual drawing area so events can be interposed upon,
4060     * like context menus.
4061     * @deprecated
4062     */

4063    public JComponent getCanvas() {
4064    return screen;
4065    }
4066
4067    /**
4068     * Returns the actual drawing area so events can be interposed upon,
4069     * like context menus.
4070     */

4071    public JComponent getScreen() {
4072    return screen;
4073    }
4074
4075    /**
4076     * Return the terminal operations implementation.
4077     * <b>WARNING! This is temporary</b>
4078     */

4079    public Ops ops() {
4080    return ops;
4081    }
4082
4083    /**
4084     * Set the Interpreter type by name.
4085     * @see Term#setInterp
4086     */

4087    public void setEmulation(String JavaDoc emulation) {
4088    Interp new_interp = InterpKit.forName(emulation, ops);
4089    if (new_interp == null)
4090        return;
4091    interp = new_interp;
4092    }
4093
4094    /**
4095     * Returns the termcap string that best describes what this Term
4096     * emulates.
4097     */

4098    public String JavaDoc getEmulation() {
4099    return getInterp().name();
4100    }
4101
4102
4103    /**
4104     * Set the emulation interpreter.
4105     * <p>
4106     * It is not advisable to change the emulation after Term has been
4107     * connected to a process, since it's often impossible to advise
4108     * the process of the new terminal type.
4109     */

4110    public void setInterp(Interp interp) {
4111    this.interp = interp;
4112    }
4113
4114    /**
4115     * Return the Interpreter assigned to this.
4116     */

4117    public Interp getInterp() {
4118    return interp;
4119    }
4120
4121    private Interp interp = new InterpDumb(ops); // used to InterpANSI
4122

4123
4124    /**
4125     * Set how many lines of history will be available.
4126     * <p>
4127     * If an anchor is in effect the history size will only have an
4128     * effect when the anchor is reset.
4129     */

4130    public void setHistorySize(int new_size) {
4131    history_size = new_size;
4132    limit_lines();
4133    repaint(true);
4134    }
4135
4136    /**
4137     * Return the number of lines in history
4138     */

4139    public int getHistorySize() {
4140    return history_size;
4141    }
4142    private int history_size = 20;
4143
4144
4145
4146    /**
4147     * Set the width of the glyph gutter in pixels
4148     */

4149    public void setGlyphGutterWidth(int pixels) {
4150
4151    glyph_gutter_width = pixels;
4152
4153    // protect against client mistakes?
4154
if (glyph_gutter_width > 30)
4155        glyph_gutter_width = 30;
4156
4157    updateScreenSize();
4158    }
4159    private int glyph_gutter_width;
4160
4161    /**
4162     * Associate an Image with a glyph id, or clear it if image is null.
4163     *
4164     * Numbering the glyphs is confusing. They start with 48. That is,
4165     * if you register glyph #0 using hbvi/vim :K command the escape
4166     * sequence emitted is 48. 48 is ascii '0'.
4167     */

4168    public void setGlyphImage(int glyph_number, Image image) {
4169    if (glyph_number > 256)
4170        return; // SHOULD throw an exception?
4171
glyph_images[glyph_number] = image;
4172    }
4173    private Image glyph_images[] = new Image[256];;
4174
4175    /**
4176     * Get the usable area for drawing glyphs
4177     *
4178     * This value changes when the gutter width or the font changes
4179     */

4180    public Dimension getGlyphCellSize() {
4181    return new Dimension(glyph_gutter_width, metrics.height);
4182    }
4183
4184    /**
4185     * Register up to 8 new custom colors.
4186     *
4187     * Unlike glyph id's you can start the numbers from 0.
4188     * hbvi/vim's :K command will add a 58 to the number, but that
4189     * is the code we interpret as custom color #0.
4190     */

4191    public void setCustomColor(int number, Color c) {
4192    custom_color[number] = c;
4193    }
4194    private final Color custom_color[] = new Color[8];
4195    private final Color standard_color[] = new Color[8];
4196
4197    /**
4198     * Get cursor row in buffer coordinates (0-origin).
4199     */

4200    public int getCursorRow() {
4201    return st.cursor.row;
4202    }
4203
4204    /**
4205     * Get cursor column in buffer coordinates (0-origin)
4206     */

4207    public int getCursorCol() {
4208    return cursor_line().cellToBuf(metrics, st.cursor.col);
4209    }
4210
4211    /**
4212     * Get (absolute) cursor coordinates.
4213     * <p>
4214     * The returned Coord is newly allocated and need not be cloned.
4215     */

4216    public Coord getCursorCoord() {
4217    Line l = buf.lineAt(st.cursor.row);
4218    return new Coord(new BCoord(st.cursor.row,
4219                    l.cellToBuf(metrics, st.cursor.col)),
4220             firsta);
4221    }
4222
4223    /*
4224     *
4225     * Move the cursor to the given (absolute) coordinates
4226     *
4227     * @deprecated, replaced by{@link #setCursorCoord(Coord)}
4228     */

4229    public void goTo(Coord coord) {
4230    setCursorCoord(coord);
4231    }
4232
4233    /**
4234     * Move the cursor to the given (absolute) coordinates
4235     * SHOULD be setCursorCoord!
4236     */

4237    public void setCursorCoord(Coord coord) {
4238    Coord c = (Coord) coord.clone();
4239    c.clip(st.rows, buf.visibleCols(), firsta);
4240    st.cursor = c.toBCoord(firsta);
4241    st.cursor.col = cursor_line().bufToCell(metrics, st.cursor.col);
4242    repaint(true);
4243    }
4244
4245    /**
4246     * Control whether the cursor is visible or not.
4247     * <p>
4248     * We don't want a visible cursor when we're using Term in
4249     * non-interactve mode.
4250     */

4251    public void setCursorVisible(boolean cursor_visible) {
4252    this.cursor_visible = cursor_visible;
4253    }
4254
4255    /**
4256     * Find out if cursor is visible.
4257     */

4258    public boolean isCursorVisible() {
4259    return cursor_visible;
4260    }
4261
4262    private boolean cursor_visible = true;
4263
4264
4265    /**
4266     * Back up the coordinate by one character and return new Coord.
4267     * <p>
4268     * Travels back over line boundaries
4269     * <br>
4270     * Returns null if 'c' is the first character in the buffer.
4271     */

4272
4273    public Coord backup(Coord c) {
4274        BCoord bRow = buf.backup(c.toBCoord(firsta));
4275        if (bRow == null)
4276            return null;
4277    return new Coord(bRow, firsta);
4278    }
4279
4280    /**
4281     * Advance the coordinate by one charater and return new coord.
4282     * <p>
4283     * Travels forward over line boundaries.
4284     * <br>
4285     * Returns null if 'c' is the last character in the buffer.
4286     */

4287    public Coord advance(Coord c) {
4288    return new Coord(buf.advance(c.toBCoord(firsta)), firsta);
4289    }
4290
4291
4292    /**
4293     * Get contents of current selection.
4294     * <p>
4295     * Returns 'null' if there is no current selection.
4296     */

4297    public String JavaDoc getSelectedText() {
4298    return sel.getSelection();
4299    }
4300
4301    /**
4302     * Get the extent of the current selection.
4303     * <p>
4304     * If there is no selection returns 'null'.
4305     */

4306    public Extent getSelectionExtent() {
4307    return sel.getExtent();
4308    }
4309
4310    /**
4311     * Set the extent of the selection.
4312     */

4313    public void setSelectionExtent(Extent extent) {
4314    extent.begin.clip(buf.nlines, buf.totalCols(), firsta);
4315    extent.end.clip(buf.nlines, buf.totalCols(), firsta);
4316    sel.setExtent(extent);
4317    repaint(false);
4318    }
4319
4320    /**
4321     * Clear the selection.
4322     */

4323    public void clearSelection() {
4324        sel.cancel( true );
4325    repaint(false);
4326    }
4327
4328    /**
4329     * Set whether slections automatically get copied to the systemSelection
4330     * when the selection is completed (the button is released).
4331     * <p>
4332     * This is how xterm and other X-windows selections work.
4333     * <p>
4334     * This property can probably be deprecated. It was neccessary in the
4335     * pre-1.4.2 days when we didn't have a systemSelection and we wanted
4336     * to have the option of not cloberring the systemClipboard on text
4337     * selection.
4338     *
4339     * @deprecated selections now always get copied to systemSelection if
4340     * it exists.
4341     */

4342    public void setAutoCopy(boolean auto_copy) {
4343    // no-op
4344
}
4345
4346    /**
4347     * Return the value set by setAutoCopy()
4348     *
4349     * @deprecated Now always returns 'true'.
4350     */

4351    public boolean isAutoCopy() {
4352    return true;
4353    }
4354
4355    /*
4356     * Control whether refreshes are enabled.
4357     * <p>
4358     * Turn refresh off if you're about to add a lot of text to the
4359     * terminal. Another way is to use appendText("stuff", false)
4360     */

4361    public void setRefreshEnabled(boolean refresh_enabled) {
4362    this.refresh_enabled = refresh_enabled;
4363    if (refresh_enabled)
4364        repaint(true);
4365    }
4366    public boolean isRefreshEnabled() {
4367    return refresh_enabled;
4368    }
4369    private boolean refresh_enabled = true;
4370
4371
4372    /**
4373     * Sets whether the selection highlighting is XOR style or normal
4374     * Swing style.
4375     */

4376    public void setSelectionXOR(boolean selection_xor) {
4377    this.selection_xor = selection_xor;
4378    repaint(false);
4379    }
4380    /*
4381     * If returns 'true' then selections are drawn using the xor mode,
4382     * Otherwise they are drawn in regular swing fashion.
4383     */

4384    public boolean isSelectionXOR() {
4385    return selection_xor;
4386    }
4387
4388    // make accessible to Sel
4389
boolean selection_xor = false;
4390
4391    /**
4392     * Set the TAB size.
4393     * <p>
4394     * The cursor is moved to the next column such that
4395     * <pre>
4396     * column (0-origin) modulo tab_size == 0
4397     * </pre>
4398     * The cursor will not go past the last column.
4399     * <p>
4400     * Note that the conventional assumption of what a tab is, is not
4401     * entirely accurate. ANSI does not define TABs as above but rather
4402     * as a directive to move to the next "tabstop" which has to have been
4403     * set previously. In fact on unixes it is the terminal line discipline
4404     * that expands tabs to spaces in the conventional way. That in,
4405     * turn explains why TAB information doesn't make it into selections and
4406     * why copying and pasting Makefile instructions is liable to lead
4407     * to hard-to-diagnose make rpoblems, which, in turn drove the ANT people
4408     * to reinvent the world.
4409     */

4410    public void setTabSize(int tab_size) {
4411    this.tab_size = tab_size;
4412    }
4413
4414    /**
4415     * Get the TAB size.
4416     */

4417    public int getTabSize() {
4418    return tab_size;
4419    }
4420
4421    private int tab_size = 8;
4422
4423    /**
4424     * Control whether Term scrolls to the bottom on keyboard input.
4425     * <p>
4426     * This is analogous to the xterm -sk/+sk option.
4427     */

4428    public void setScrollOnInput(boolean scroll_on_input) {
4429    this.scroll_on_input = scroll_on_input;
4430    }
4431
4432    /**
4433     * Return whether Term scrolls to the bottom on keyboard input.
4434     */

4435    public boolean isScrollOnInput() {
4436    return scroll_on_input;
4437    }
4438
4439    private boolean scroll_on_input = true;
4440
4441    /**
4442     * Control whether Term scrolls on any output.
4443     * <p>
4444     * When set to false, if the user moves the scrollbar to see some
4445     * text higher up in history, the view will not change even if more
4446     * output is produced. But if the cursor is visible, scrolling will
4447     * happen. This is so that in an interactive session any prompt will
4448     * be visible etc.
4449     * <p>
4450     * However, the tracking of the cursor is controlled by the 'trackCursor'
4451     * property which by default is set to 'true'.
4452     * <p>
4453     * This property is analogous to the xterm -si/+si option.
4454     */

4455    public void setScrollOnOutput(boolean scroll_on_output) {
4456    this.scroll_on_output = scroll_on_output;
4457    }
4458
4459    /**
4460     * Return whether Term scrolls on any output.
4461     */

4462    public boolean isScrollOnOutput() {
4463    return scroll_on_output;
4464    }
4465
4466    private boolean scroll_on_output = true;
4467
4468
4469    /**
4470     * Control whether Term will scroll to track the cursor as text is added.
4471     * <p>
4472     * If set to true, as output is being generated Term will try to keep
4473     * the cursor in view.
4474     * <p>
4475     * This property is only relevant when scrollOnOutput is set to false.
4476     * If scrollOnOutput is true, this property is also implicitly true.
4477     */

4478    public void setTrackCursor(boolean track_cursor) {
4479    this.track_cursor = track_cursor;
4480    }
4481
4482    /**
4483     * Return whether Term will scroll to track the cursor as text is added.
4484     */

4485    public boolean isTrackCursor() {
4486    return track_cursor;
4487    }
4488
4489    private boolean track_cursor = true;
4490
4491
4492    /**
4493     * Controls horizontal scrolling and line wrapping.
4494     * <p>
4495     * When enabled a horizontal scrollbar becomes visible and line-wrapping
4496     * is disabled.
4497     */

4498    public void setHorizontallyScrollable(boolean horizontally_scrollable) {
4499    this.horizontally_scrollable = horizontally_scrollable;
4500    // hscroll_bar.setVisible(horizontally_scrollable);
4501
hscroll_wrapper.setVisible(horizontally_scrollable);
4502    }
4503
4504    /*
4505     * Returns whether horizontal scrolling is enabled.
4506     * @see Term.setHorizontallyScrollable
4507     */

4508    public boolean isHorizontallyScrollable() {
4509    return this.horizontally_scrollable;
4510    }
4511
4512    private boolean horizontally_scrollable = true;
4513
4514
4515    /**
4516     * Clear everything and assign new text.
4517     * <p>
4518     * If the size of the text exceeds history early parts of it will get
4519     * lost, unless an anchor was set using setAnchor().
4520     */

4521    public void setText(String JavaDoc text) {
4522    // SHOULD make a bit more efficient
4523
clearHistoryNoRefresh();
4524    appendText(text, true);
4525    }
4526
4527    /**
4528     * Add new text at the current cursor position.
4529     * <p>
4530     * Doesn't repaint the view unless 'repaint' is set to 'true'.
4531     * <br>
4532     * Doesn't do anything if 'text' is 'null'.
4533     */

4534    public void appendText(String JavaDoc text, boolean repaint) {
4535
4536    if (text == null)
4537        return;
4538
4539    ckEventDispatchThread();
4540    // OLD NPE-x synchronized(this)
4541
{
4542        for (int cx = 0; cx < text.length(); cx++) {
4543        putc_work(text.charAt(cx));
4544        if (text.charAt(cx) == '\n')
4545            putc_work('\r');
4546        }
4547    }
4548    if (repaint)
4549        repaint(true);
4550    }
4551
4552    /**
4553     * Scroll the view 'n' pages up.
4554     * <p>
4555     * A page is the height of the view.
4556     */

4557
4558    public void pageUp(int n) {
4559    ckEventDispatchThread();
4560    // OLD NPE-x synchronized(this)
4561
{
4562        st.firstx -= n * st.rows;
4563        if (st.firstx < 0)
4564        st.firstx = 0;
4565    }
4566    repaint(true);
4567    }
4568
4569    /**
4570     * Scroll the view 'n' pages down.
4571     * <p>
4572     * A page is the height of the view.
4573     */

4574
4575    public void pageDown(int n) {
4576    ckEventDispatchThread();
4577    // OLD NPE-x synchronized(this)
4578
{
4579        st.firstx += n * st.rows;
4580
4581        if (st.firstx + st.rows > buf.nlines)
4582        st.firstx = buf.nlines - st.rows;
4583    }
4584    repaint(true);
4585    }
4586
4587    /**
4588     * Scroll the view 'n' lines up.
4589     */

4590    public void lineUp(int n) {
4591    ckEventDispatchThread();
4592    // OLD NPE-x synchronized(this)
4593
{
4594        st.firstx -= n;
4595        if (st.firstx < 0)
4596        st.firstx = 0;
4597    }
4598    repaint(true);
4599    }
4600
4601    /**
4602     * Scroll the view 'n' lines down.
4603     */

4604    public void lineDown(int n) {
4605    ckEventDispatchThread();
4606    // OLD NPE-x synchronized(this)
4607
{
4608        st.firstx += n;
4609        if (st.firstx + st.rows > buf.nlines)
4610        st.firstx = buf.nlines - st.rows;
4611    }
4612    repaint(true);
4613    }
4614
4615    /**
4616     * Scroll the view 'n' pages to the left.
4617     */

4618    public void pageLeft(int n) {
4619    columnLeft(n * buf.visibleCols());
4620    }
4621
4622    /**
4623     * Scroll the view 'n' pages to the right.
4624     */

4625    public void pageRight(int n) {
4626    columnRight(n * buf.visibleCols());
4627    }
4628
4629    /**
4630     * Scroll the view 'n' columns to the right.
4631     */

4632    public void columnRight(int n) {
4633    ckEventDispatchThread();
4634    // OLD NPE-x synchronized(this)
4635
{
4636        st.firsty += n;
4637        if (st.firsty + buf.visibleCols() > buf.totalCols())
4638        st.firsty = buf.totalCols() - buf.visibleCols();
4639    }
4640    repaint(true);
4641    }
4642
4643    /**
4644     * Scroll the view 'n' columns to the left.
4645     */

4646    public void columnLeft(int n) {
4647    ckEventDispatchThread();
4648    // OLD NPE-x synchronized(this)
4649
{
4650        st.firsty -= n;
4651        if (st.firsty < 0)
4652        st.firsty = 0;
4653    }
4654    repaint(true);
4655    }
4656
4657    /**
4658     * Return the cell width of the given character.
4659     */

4660    public int charWidth(char c) {
4661    return metrics.wcwidth(c);
4662    }
4663
4664    /*
4665     * The following are overrides of JComponent/Component
4666     */

4667
4668    /**
4669     * Override of JComponent.
4670     * <p>
4671     * We absolutely require fixed width fonts, so if the font is changed
4672     * we create a monospaced version of it with the same style and size.
4673     */

4674
4675    public void setFont(Font new_font) {
4676
4677    Font font = new Font("Monospaced", // NOI18N
4678
new_font.getStyle(),
4679                 new_font.getSize());
4680
4681    super.setFont(font); // This should invalidate us, which
4682
// ultimately will cause a repaint
4683

4684    /* DEBUG
4685    System.out.println("Font info:"); // NOI18N
4686    System.out.println("\tlogical name: " + font.getName()); // NOI18N
4687    System.out.println("\tfamily name: " + font.getFamily()); // NOI18N
4688    System.out.println("\tface name: " + font.getFontName()); // NOI18N
4689    */

4690
4691    // cache the metrics
4692
metrics = new MyFontMetrics(this, font);
4693
4694    updateScreenSize();
4695    }
4696
4697
4698    /**
4699     * Override of JComponent.
4700     * <p>
4701     * Pass on the request to the screen where all the actual focus
4702     * management happens.
4703     */

4704    public void requestFocus() {
4705    screen.requestFocus();
4706    }
4707    
4708    public boolean requestFocusInWindow() {
4709        return screen.requestFocusInWindow();
4710    }
4711
4712    /**
4713     * Override of JComponent.
4714     * <p>
4715     * Pass on enabledness to sub-components (scrollbars and screen)
4716     */

4717    public void setEnabled(boolean enabled) {
4718    // This was done as a result of issue 24824
4719

4720    super.setEnabled(enabled);
4721
4722    hscroll_bar.setEnabled(enabled);
4723    vscroll_bar.setEnabled(enabled);
4724    screen.setEnabled(enabled);
4725    }
4726
4727
4728    //..........................................................................
4729
// Accessibility stuff is all here
4730
// Not just the required interfaces but also all the helpers.
4731
//..........................................................................
4732

4733    /**
4734     * Since Term is a composite widget the main accessible JComponent is
4735     * not Term but an internal JComponent. We'll speak of Term accessibility
4736     * when we in fact are referring to the that inner component.
4737     * <p>
4738     * Accessibility for Term is tricky because it doesn't fit into the
4739     * roles delineated by Swing. The closest role is that of TEXT and that
4740     * is too bound to how JTextComponent works. To wit ...
4741     <p>
4742     <dl>
4743     <dt>2D vs 1D coordinates
4744     <dd>
4745     Term has a 2D coordinate system while AccessibleText works with 1D
4746     locations. So Term actually has code which translates between the two.
4747     This code is not exactly efficient but only kicks in when assistive
4748     technology latches on.
4749     <br>
4750     Line breaks ('\n's) count as characters! However we only count
4751     logical line breaks ('\n's appearing in the input stream) as opposed to
4752     wrapped lines!
4753     <p>
4754     The current implementation doesn't cache any of the mappings because
4755     that would require a word per line extra storage for the cumulative
4756     char count. The times actually we're pretty fast with a 4000 line
4757     histroy.
4758
4759     <dt>WORDs and SENTENCEs
4760     <dd>
4761     For AccessibleText.get*Index() functions WORD uses the regular
4762     Term WordDelineator. SENTENCE translates to just a line.
4763
4764     <dt>Character attributes
4765     <dd>
4766     Term uses the ANSI convention of character attributes so when
4767     AccessibleText.getCharacterAttribute() is used a rough translation
4768     is made as follows:
4769     <ul>
4770     <li> ANSI underscore -> StyleConstants.Underline
4771     <li> ANSI bright/bold -> StyleConstants.Bold
4772     <li> Non-black foreground color -> StyleConstants.Foreground
4773     <li> Explicitly set background color -> StyleConstants.Background
4774     </ul>
4775     Font related information is always constant so it is not provided.
4776
4777     <dt>History
4778     <dd>
4779     Term has history and lines wink out. If buffer coordinates were
4780     used to interact with accessibility, caretPosition and charCount
4781     would be dancing around. Fortunately Term has absolute coordinates.
4782     So positions returned via AccessibleText might eventually refer to
4783     text that has gone by.
4784
4785     <dt>Caret and Mark vs Cursor and Selection
4786     <dd>
4787    While Term keeps the selection and cursor coordinates independent,
4788    JTextComponent merges them and AccessibleText inherits this view.
4789    With Term caretPosition is the position of the cursor and selection
4790    ends will not neccessarily match with the caret position.
4791     </dl>
4792     <p>
4793     Currently only notifications of ACCESSIBLE_CARET_PROPERTY and
4794     ACCESSIBLE_TEXT_PROPERTY are fired and that always in pairs.
4795     They are fired on the receipt of any character to be processed.
4796     <p>
4797     IMPORTANT: It is assumed that under assistive technology Term will be
4798     used primarily as a continuous text output device or a readonly document.
4799     Therefore ANSI cursor motion and text editing commands or anything that
4800     mutates the text will completely invalidate all of AccessibleTexts
4801     properties. (Perhaps an exception SHOULD be made for backspace)
4802     */

4803
4804    public AccessibleContext getAccessibleContext() {
4805    if (accessible_context == null) {
4806         accessible_context = new AccessibleTerm();
4807    }
4808    return accessible_context;
4809    }
4810    private AccessibleContext accessible_context;
4811
4812    /*
4813     * Term is really a container. Screen is where things get drawn and
4814     * where focus is set to, so all real accessibility work is done there.
4815     * We just declare us to have a generic role.
4816     */

4817    protected class AccessibleTerm extends AccessibleJComponent {
4818    public AccessibleRole getAccessibleRole() {
4819        return AccessibleRole.PANEL;
4820    }
4821    public void setAccessibleName(String JavaDoc name) {
4822        screen.getAccessibleContext().setAccessibleName(name);
4823    }
4824    }
4825
4826    /**
4827     * [DO NOT USE] Convert a 2D Coord to a 1D linear position.
4828     * <p>
4829     * This function really should be private but I need it to be public for
4830     * unit-testing purposes.
4831     */

4832    public int CoordToPosition(Coord c) {
4833    BCoord b = c.toBCoord(firsta);
4834    int nchars = charsInPrehistory;
4835    for (int r = 0; r < b.row; r++) {
4836        Line l = buf.lineAt(r);
4837        nchars += l.length();
4838        if (!l.isWrapped())
4839        nchars += 1;
4840    }
4841
4842    nchars += c.col;
4843
4844    return nchars;
4845    }
4846
4847    /**
4848     * [DO NOT USE] Convert a 1D linear position to a 2D Coord.
4849     * <p>
4850     * This function really should be private but I need it to be public for
4851     * unit-testing purposes.
4852     */

4853    public Coord PositionToCoord(int position) {
4854    int nchars = charsInPrehistory;
4855    for (int r = 0; r < buf.nlines; r++) {
4856        Line l = buf.lineAt(r);
4857        nchars += l.length();
4858        if (!l.isWrapped())
4859        nchars += 1;
4860        if (nchars > position) {
4861        BCoord b = new BCoord();
4862        b.row = r;
4863        b.col = buf.lineAt(r).length() + 1 - (nchars - position);
4864        return new Coord(b, firsta);
4865        }
4866    }
4867    return null;
4868    }
4869
4870    /**
4871     * Return the number of characters stored.
4872     * <p>
4873     * Include logical newlines for now to match the above conversions.
4874     * Hmm, do we include chars in prehistory?
4875     */

4876
4877    int getCharCount() {
4878    int nchars = charsInPrehistory;
4879    for (int r = 0; r < buf.nlines; r++) {
4880        Line l = buf.lineAt(r);
4881        nchars += l.length();
4882        if (!l.isWrapped())
4883        nchars += 1;
4884    }
4885    return nchars;
4886    }
4887
4888    /**
4889     * Return the bounding rectangle of the character at the given coordinate
4890     */

4891    Rectangle getCharacterBounds(Coord c) {
4892    if (c == null)
4893        return null;
4894    BCoord b = c.toBCoord(firsta);
4895
4896    char ch = '\0';
4897    try {
4898        Line l = buf.lineAt(b.row);
4899        ch = l.charArray()[b.col];
4900    } catch(Exception JavaDoc x) {
4901        ;
4902    }
4903
4904    Point p1 = toPixel(b);
4905    Rectangle rect = new Rectangle();
4906    rect.x = p1.x;
4907    rect.y = p1.y;
4908    rect.width = metrics.width * charWidth(ch);
4909    rect.height = metrics.height;
4910    return rect;
4911    }
4912
4913    Color backgroundColor(boolean reverse, int attr) {
4914    Color bg = null;
4915    if (reverse) {
4916        int fcx = Attr.foregroundColor(attr);
4917        if (fcx != 0 && fcx <= 8) {
4918                bg = standard_color[fcx-1];
4919            } else if (fcx > 8) {
4920                bg = custom_color[fcx-9];
4921        } else {
4922        bg = actual_foreground;
4923        }
4924
4925    } else {
4926        int bcx = Attr.backgroundColor(attr);
4927        if (bcx != 0 && bcx <= 8) {
4928                bg = standard_color[bcx-1];
4929            } else if (bcx > 8) {
4930                bg = custom_color[bcx-9];
4931        }
4932    }
4933    return bg;
4934    }
4935
4936    Color foregroundColor(boolean reverse, int attr) {
4937    Color fg = null;
4938    if (reverse) {
4939        int bcx = Attr.backgroundColor(attr);
4940            if (bcx != 0 && bcx <= 8) {
4941                fg = standard_color[bcx-1];
4942            } else if (bcx > 8) {
4943                fg = custom_color[bcx-9];
4944        } else {
4945        fg = actual_background;
4946        }
4947
4948    } else {
4949        int fcx = Attr.foregroundColor(attr);
4950        if (fcx != 0 && fcx <= 8) {
4951                fg = standard_color[fcx-1];
4952            } else if (fcx > 8) {
4953                fg = custom_color[fcx-9];
4954            } else {
4955        fg = actual_foreground;
4956        }
4957    }
4958    return fg;
4959    }
4960
4961    private static void ckEventDispatchThread() {
4962    /*
4963    if (!SwingUtilities.isEventDispatchThread()) {
4964        System.out.println("term: NOT IN EventDispatchThread");
4965        Thread.dumpStack();
4966    }
4967    */

4968    }
4969
4970    /* attaches MouseWheelHandler to scroll the component
4971     */

4972    private static void addMouseWheelHandler(JComponent comp, JScrollBar bar) {
4973        comp.addMouseWheelListener(new MouseWheelHandler (bar)); // XXX who removes this lsnr?
4974
}
4975    
4976    private static class MouseWheelHandler implements MouseWheelListener {
4977
4978        private JScrollBar scrollbar;
4979        
4980        public MouseWheelHandler (JScrollBar scrollbar) {
4981            this.scrollbar = scrollbar;
4982        }
4983        
4984        public void mouseWheelMoved(MouseWheelEvent e) {
4985            int totalScrollAmount = e.getUnitsToScroll() * scrollbar.getUnitIncrement();
4986            scrollbar.setValue( scrollbar.getValue() + totalScrollAmount );
4987        }
4988        
4989    }
4990}
4991
Popular Tags