Source Code Cross Referenced for Term.java in  » IDE-Netbeans » cvsclient » org » netbeans » lib » terminalemulator » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » cvsclient » org.netbeans.lib.terminalemulator 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         *
0041:         * Contributor(s): Ivan Soleimanipour.
0042:         */
0043:
0044:        package org.netbeans.lib.terminalemulator;
0045:
0046:        import java.awt.*;
0047:        import java.awt.event.*;
0048:        import java.awt.datatransfer.*;
0049:        import javax.swing.*;
0050:        import javax.accessibility.*;
0051:
0052:        import java.awt.font.*;
0053:        import java.awt.geom.Point2D;
0054:
0055:        import java.util.HashSet;
0056:        import java.util.Date;
0057:        import java.util.LinkedList;
0058:        import java.util.ListIterator;
0059:
0060:        /**
0061:         Term is a pure Java multi-purpose terminal emulator.
0062:         <p>
0063:         It has the following generic features:
0064:         <ul>
0065:         <li>All "dumb" operations. Basically putting characters on a screen and
0066:         processing keyboard input.
0067:         <li>ANSI mode "smart" operations. Cursor control etc.
0068:         <li>Character attributes like color, reverse-video etc.
0069:         <li>Selection service in character, word and line modes matching xterm
0070:         with configurable word boundary detection.
0071:         <li>History buffer.
0072:         <li>Facilities to iterate through logical lines, to implement search for
0073:         example.
0074:         <li>Support for nested pickable regions in order to support hyperlinked
0075:         views or more complex active text utilities.
0076:         <li>Support for double-width Oriental characters.
0077:         </ul>
0078:         <p>
0079:         <h2>Coordinate systems</h2>
0080:         The following coordinate systems are used with Term.
0081:         They are all cartesian and have their origin at the top left.
0082:         All but the first are 0-origin.
0083:         But they differ in all other respects:
0084:         <dl>
0085:
0086:         <dt>ANSI Screen coordinates
0087:         <dd>
0088:         Address only the visible portion of the screen.
0089:         They are 1-origin and extend thru the width and height of the visible
0090:         portion of the screen per getColumns() and getRows(). 
0091:         <p>
0092:         This is how an application (like 'vi' etc) views the screen.
0093:         This coordinate system primarily comes into play in the cursor addressing
0094:         directive, op_cm() and otherwise is not really used in the implementation.
0095:         <p>
0096:
0097:         <dt>Cell coordinates
0098:         <dd>
0099:         Each character usually takes one cell, and all placement on the screen
0100:         is in terms of a grid of cells getColumns() wide This cellular nature
0101:         is why fixed font is "required". In some locales some characters may
0102:         be double-width.
0103:         Japanese characters are like this, so they take up two cells.
0104:         There are no double-height characters (that I know of).
0105:         <p>
0106:         Cursor motion is in cell coordinates, so to move past a Japanese character
0107:         you need the cursor to move right twice. A cursor can also be placed on
0108:         the second cell of a double-width character.
0109:         <p>
0110:         Note that this is strictly an internal coordinate system. For example
0111:         Term.getCursorCol() and getCursorCoord() return buffer coordinates.
0112:         <p>
0113:         The main purpose of this coordinate system is to capture logical columns. 
0114:         In the vertical direction sometimes it extends only the height of the
0115:         screen and sometimes the height of the buffer.
0116:         <p>
0117:
0118:         <dt>Buffer coordinates ...
0119:         <dd>
0120:         ... address the whole history character buffer.
0121:         These are 0-origin and extend thru the width 
0122:         of the screen per getColumns(), or more if horizontal scrolling is
0123:         enabled, and the whole history, that is, getHistorySize()+getRows().
0124:         <p>
0125:         The BCoord class captures the value of such coordinates.
0126:         It is more akin to the 'int offset' used in the Java text package
0127:         as opposed to javax.swing.text.Position.
0128:         <p> 
0129:         If there are no double-width characters the buffer coords pretty much
0130:         overlap with cell coords. If double-width characters are added then
0131:         the buffer column and cell column will have a larger skew the more right
0132:         you go.
0133:         <p>
0134:         <dt>Absolute coordinates ...
0135:         <dd>
0136:         ... are like Buffer coordinates in the horizontal direction. 
0137:         In the vertical direction their origin is the first line that was
0138:         sent to the terminal. This line might have scrolled out of history and
0139:         might no longer be in the buffer. In effect each line ever printed by 
0140:         Term gets a unique Absolute row.
0141:         <p>
0142:         What good is this? The ActiveRegion mechanism maintains coordinates
0143:         for its' boundaries. As text scrolls out of history buffer row coordinates
0144:         have to shift and all ActiveRegions' coords need to be relocated. This
0145:         can get expensive because as soon as the history buffer becomes full
0146:         each newline will require a relocation. This is the approach that
0147:         javax.swing.text.Position implements and it's justified there because
0148:         no Swing component has a "history buffer".
0149:         However, if you use absolute coordinates you'll never have to 
0150:         relocate anything! Simple and effective.
0151:         <p>
0152:         Well almost. What happens when you reach Integer.MAX_VALUE? You wrap and
0153:         that can confuse everything. What are the chances of this happening?
0154:         Suppose term can process 4000 lines per second. A runaway process will
0155:         produce Integer.MAX_VALUE lines in about 4 days. That's too close
0156:         for comfort, so Term does detect the wrap and only then goes and 
0157:         relocates stuff. This, however, causes a secondary problem with
0158:         testability since no-one wants to wait 4 days for a single wrap.
0159:         So what I've done is periodically set Term.modulo to something
0160:         smaller and tested stuff.
0161:         <p>
0162:         I'm indebted to Alan Kostinsky for this bit of lateral thinking.
0163:         </dl>
0164:
0165:
0166:         <p>
0167:         <h2>Modes of use</h2>
0168:         There are three ways Term can be used.
0169:         These modes aren't explicit they are just a convenient way of discussing
0170:         functionality.
0171:         <dl>
0172:         <dt>Screen mode
0173:         <dd>
0174:         This represents the traditional terminal without a history buffer.
0175:         Applications 
0176:         running under the terminal assume they are dealing with a fixed size
0177:         screen and interact with it in the screen coordinate system through 
0178:         escape sequence (ANSI or otherwise). The most common application which
0179:         uses terminals in this ways is the screen editor, like vi or emacs.
0180:         <p>
0181:         Term will convert keystrokes to an output stream and will process 
0182:         characters in an input stream and render them unto the screen. 
0183:         What and how these streams are connected to is up to the client of Term,
0184:         since it is usually highly platform dependent. For example on unixes 
0185:         the streams may be connected to partially-JNI-based "pty" streams.
0186:         <p>
0187:         This mode works correctly even if there is history and you see a
0188:         scrollbar, just as it does under XTerm and it's derivatives.
0189:
0190:         <p>
0191:         <dt>Buffer/Interactive mode
0192:         <dd>
0193:         This is the primary facility that XTerm and other derivatives provide. The
0194:         screen has a history buffer in the vertical dimension.
0195:         <p>
0196:         Because of limited history active regions can scroll out of history and
0197:         while the coordinate invalidation problem is not addressed by absolute
0198:         coordiantes sometimes we don't want stuff to wink out.
0199:         <br>
0200:         Which is why we have ...
0201:
0202:         <p>
0203:         <dt>Page mode
0204:         <dd>
0205:         It is possible to "anchor" a location in the buffer and prevent it 
0206:         from going out of history. This can be helpful in having the
0207:         client of Term make sure that crucial output doesn't get lost due to
0208:         short-sighted history settings on the part of the user.
0209:         <p>
0210:         To use Term 
0211:         in this mode you can use setText() or appendText() instead of
0212:         connecting to Terms streams.
0213:         This mode is called page mode because the most common use of it 
0214:         would be as something akin to a hypertext browser.
0215:         To that end
0216:         Term supports nestable ActiveRegions and mapping of coordinates
0217:         to regions. ActiveTerm puts all of this together in a comprehensive
0218:         subclass.
0219:         </dl>
0220:
0221:         <p>
0222:         <h2>What Term is not</h2>
0223:         <ul>
0224:         <li>
0225:         While there is an internal Buffer class, and while it behaves like a
0226:         document in that it can
0227:         be implicitly "edited" and character attributes explicitly changed,
0228:         Term is not a document editing widget.
0229:         <p>
0230:         <li>
0231:         Term is also not a command line processor in the sense that a MS Windows
0232:         console is. Its shuttling of keyboard events to an output stream and
0233:         rendering of characters from the input stream unto the screen are completely
0234:         independent activities.
0235:         <p>
0236:         This is due to Terms unix heritage where shells (ksh, bash etc) do their own
0237:         cmdline and history editing, but if you're so inclined the LineDiscipline
0238:         may be used for experimentation with indigenous cmdline processing.
0239:         </ul>
0240:         */
0241:
0242:        public class Term extends JComponent implements  Accessible {
0243:            private State st = new State();
0244:            private Sel sel = new Sel(this , st);
0245:            private Ops ops = new OpsImpl();
0246:
0247:            private int top_margin = 0; // 0 means default (see topMargin())
0248:            private int bot_margin = 0;
0249:
0250:            // Stuff to control how often RegionManager.cull() gets called
0251:            private int cull_count = 0;
0252:            private static final int cull_frequency = 50;
0253:
0254:            // 'firsta' is the absolute line number of the line at 'lines[0]'.
0255:            protected int firsta = 0;
0256:
0257:            // chars gone by in lines that winked out of history
0258:            // 'firsta' ~= 'linesInPrehistory'
0259:            private int charsInPrehistory = 0;
0260:
0261:            private static final int modulo = Integer.MAX_VALUE / 2;
0262:
0263:            private Screen screen;
0264:            private JScrollBar vscroll_bar;
0265:            private ScrollWrapper hscroll_wrapper;
0266:            private JScrollBar hscroll_bar;
0267:            private boolean has_focus;
0268:
0269:            // statistics
0270:            private int n_putchar;
0271:            private int n_putchars;
0272:            private int n_linefeeds;
0273:            private int n_repaint;
0274:            private int n_paint;
0275:
0276:            MyFontMetrics metrics = null;
0277:
0278:            Buffer buf = new Buffer(80);
0279:
0280:            private RegionManager region_manager = new RegionManager();
0281:
0282:            // 'left_down_point' remembers where the left button came down as a
0283:            // workaround for the flakey mouseDragged event. The flakiness has to with
0284:            // the fact that the mousePressed coord is not delivered in the first drag
0285:            // event, so if you proess and drag very quickly, the first drag coord
0286:            // will be quite far from the initial press location.
0287:
0288:            private Point left_down_point;
0289:
0290:            // getSystemSelection() wasn't available on Java prior to 1.4
0291:            private Clipboard systemClipboard = getToolkit()
0292:                    .getSystemClipboard();
0293:            private Clipboard systemSelection = getToolkit()
0294:                    .getSystemSelection();
0295:
0296:            /**
0297:             * ScrollWrapper is a HACK that allows us to make pairs of scrollbars 
0298:             * look nice.
0299:             * <p>
0300:             * A JScrollPane, or more specifically ScrollPaneLayout, arranges 
0301:             * a pair of vertical and horizontal scrollbars as follows:
0302:             *                   | |
0303:             *                   | |
0304:             *                   |v|
0305:             *      ------------- 
0306:             *                 >|
0307:             *      -------------
0308:             * ... so that here is a nice square corner.
0309:             *
0310:             * But ScrollPaneLayout insists that it's viewport is a JViewport and that
0311:             * it's container is a JScrollPane. It is probably possible to make the
0312:             * screen be a JViewPort and use a JScrollPane to contain the screen, but 
0313:             * it's very tricky. (For that matter it should be possible to avoid doing
0314:             * our own scrolling altogether and use JScrollPane functionality, but
0315:             * it's also tricky).
0316:             *
0317:             * Since we're using a BorderLayout putting the horizontal SB in the SOUTH
0318:             * portion yields something like this:
0319:             *                   | |
0320:             *                   | |
0321:             *                   |v|
0322:             *      ---------------- 
0323:             *                    >|
0324:             *      ----------------
0325:             * Soooo, to make things look right, we use ScrollWrapper to control the
0326:             * sizing of the horizontal scrollbar. It basically uses a GridBagLayout
0327:             * and GridBagConstraints.insets to create the square corner.
0328:             */
0329:
0330:            private class ScrollWrapper extends JComponent implements 
0331:                    Accessible {
0332:                public JScrollBar scroll_bar;
0333:
0334:                public ScrollWrapper(JScrollBar scroll_bar) {
0335:                    GridBagLayout gbl = new GridBagLayout();
0336:                    setLayout(gbl);
0337:
0338:                    GridBagConstraints gbc = new GridBagConstraints();
0339:                    gbc.anchor = GridBagConstraints.WEST;
0340:                    gbc.fill = GridBagConstraints.BOTH;
0341:                    gbc.gridwidth = 1;
0342:                    gbc.gridheight = 1;
0343:                    gbc.weightx = 1.0f;
0344:                    gbc.weighty = 1.0f;
0345:                    int slop = vscroll_bar.getMaximumSize().width;
0346:                    gbc.insets = new Insets(0, 0, 0, slop);
0347:                    add(scroll_bar, gbc);
0348:
0349:                    this .scroll_bar = scroll_bar;
0350:                }
0351:
0352:                protected void paintComponent(Graphics g) {
0353:                    // If we don't do this, the square corner will end getting filled 
0354:                    // with random grot.
0355:                    Dimension sz = getSize();
0356:                    g.clearRect(0, 0, sz.width, sz.height);
0357:                }
0358:
0359:                //......................................................................
0360:                // Accessibility stuff is all here
0361:                //......................................................................
0362:
0363:                public AccessibleContext getAccessibleContext() {
0364:                    if (accessible_context == null) {
0365:                        accessible_context = new AccessibleScrollWrapper();
0366:                    }
0367:                    return accessible_context;
0368:                }
0369:
0370:                private AccessibleContext accessible_context;
0371:
0372:                protected class AccessibleScrollWrapper extends
0373:                        AccessibleJComponent {
0374:                    public AccessibleRole getAccessibleRole() {
0375:                        return AccessibleRole.PANEL;
0376:                    }
0377:                }
0378:            }
0379:
0380:            private class BaseTermStream extends TermStream {
0381:                public void flush() {
0382:                    repaint(true);
0383:                }
0384:
0385:                public void putChar(char c) {
0386:                    /*
0387:                     * echoes a character unto the screen
0388:                     */
0389:                    ckEventDispatchThread();
0390:                    // OLD NPE-x synchronized(Term.this)
0391:                    {
0392:                        n_putchar++;
0393:                        putc_work(c);
0394:                    }
0395:                    possibly_repaint(true);
0396:
0397:                    // pavel.buzek@czech.sun.com put this as a fix to speed up
0398:                    // StreamTerm on windows. This will make raw mode not work,
0399:                    // Instead now LineDiscipline properly buffers incoming characters.
0400:                    // so should be considered temporary.
0401:                    // repaint(c == '\n');
0402:                }
0403:
0404:                public void putChars(char buf[], int offset, int count) {
0405:                    ckEventDispatchThread();
0406:                    // OLD NPE-x synchronized (Term.this)
0407:                    {
0408:                        n_putchars++;
0409:                        for (int bx = 0; bx < count; bx++) {
0410:                            putc_work(buf[offset + bx]);
0411:                        }
0412:                    }
0413:                    possibly_repaint(true);
0414:                }
0415:
0416:                public void sendChar(char c) {
0417:                    fireChar(c);
0418:                }
0419:
0420:                public void sendChars(char buf[], int offset, int count) {
0421:                    fireChars(buf, offset, count);
0422:                }
0423:            }
0424:
0425:            // head is closer to Term
0426:            // pushes extend tail
0427:            private TermStream base_stream = new BaseTermStream();
0428:            private TermStream dce_end = base_stream;
0429:            private TermStream dte_end = base_stream;
0430:
0431:            /**
0432:             * 
0433:             */
0434:            public void pushStream(TermStream stream) {
0435:                // Term will send keystrokes by calling dte_end.sendChar
0436:                // Characters sent via Term.putChar will be sent down dce_end.
0437:                // 
0438:                // The base stream is strange in that on the first push it will get
0439:                // split into two parts, one sticking at the end of the dce chain,
0440:                // the other at the end of the dte chain. Hence the special case 
0441:                // treatment
0442:
0443:                if (dce_end == base_stream) {
0444:                    // towards dce
0445:                    dte_end = stream;
0446:                    stream.setToDCE(base_stream);
0447:
0448:                    // towards dte
0449:                    stream.setToDTE(base_stream);
0450:                    dce_end = stream;
0451:
0452:                } else {
0453:                    // towards dce
0454:                    dte_end.setToDCE(stream);
0455:                    stream.setToDCE(base_stream);
0456:
0457:                    // towards dte
0458:                    stream.setToDTE(dce_end);
0459:                    dce_end = stream;
0460:                }
0461:
0462:                stream.setTerm(this );
0463:            }
0464:
0465:            /*
0466:             * Debugging utilities
0467:             */
0468:            public static final int DEBUG_OPS = 1 << 0;
0469:            public static final int DEBUG_KEYS = 1 << 1;
0470:            public static final int DEBUG_INPUT = 1 << 2;
0471:            public static final int DEBUG_OUTPUT = 1 << 3;
0472:            public static final int DEBUG_WRAP = 1 << 4;
0473:            public static final int DEBUG_MARGINS = 1 << 5;
0474:
0475:            private int debug_gutter_width = 0;
0476:
0477:            public void setDebugFlags(int flags) {
0478:                debug = flags;
0479:            }
0480:
0481:            private int debug = /* DEBUG_OPS|DEBUG_KEYS|DEBUG_INPUT|DEBUG_OUTPUT | */0;
0482:
0483:            private boolean debugOps() {
0484:                return (debug & DEBUG_OPS) == DEBUG_OPS;
0485:            }
0486:
0487:            private boolean debugKeys() {
0488:                return (debug & DEBUG_KEYS) == DEBUG_KEYS;
0489:            }
0490:
0491:            private boolean debugWrap() {
0492:                return (debug & DEBUG_WRAP) == DEBUG_WRAP;
0493:            }
0494:
0495:            private boolean debugMargins() {
0496:                return true;
0497:                /* TMP
0498:                return (debug & DEBUG_MARGINS) == DEBUG_MARGINS;
0499:                 */
0500:            }
0501:
0502:            /**
0503:             * Return true if DEBUG_INPUT flag has been set.
0504:             */
0505:            protected boolean debugInput() {
0506:                return (debug & DEBUG_INPUT) == DEBUG_INPUT;
0507:            }
0508:
0509:            /**
0510:             * Return true if DEBUG_OUTPUT flag has been set.
0511:             */
0512:            protected boolean debugOutput() {
0513:                return (debug & DEBUG_OUTPUT) == DEBUG_OUTPUT;
0514:            }
0515:
0516:            /*
0517:             * Top and bottom margins are stored as 1-origin values, with 0
0518:             * denoting default. 
0519:             *
0520:             * topMargin() & botMargin() return 0-origin values which is what we
0521:             * use for screen coordinates.
0522:             *
0523:             * The margin lines are inclusive, that is, lines on the margin lines 
0524:             * participate in scrolling.
0525:             */
0526:
0527:            private int topMargin() {
0528:                return (top_margin == 0) ? 0 : top_margin - 1;
0529:            }
0530:
0531:            private int botMargin() {
0532:                return (bot_margin == 0) ? st.rows - 1 : bot_margin - 1;
0533:            }
0534:
0535:            /**
0536:             * beginx is row lines above the bottom.
0537:             * It's used for all cursor motion calculations and cursor relative
0538:             * line operations.
0539:             * It's used instead of firstx because firstx changes as we scroll.
0540:             * This allows us to restrict screen editing to the last chunk of 
0541:             * the buffer.
0542:             */
0543:            private int beginx() {
0544:                return buf.nlines - st.rows;
0545:            }
0546:
0547:            private Line cursor_line() {
0548:                return buf.lineAt(st.cursor.row);
0549:            }
0550:
0551:            /**
0552:             * Set/unset WordDelineator.
0553:             * Passing a null sets it to the default WordDelineator.
0554:             */
0555:            public void setWordDelineator(WordDelineator word_delineator) {
0556:                if (word_delineator == null)
0557:                    this .word_delineator = default_word_delineator;
0558:                else
0559:                    this .word_delineator = word_delineator;
0560:            }
0561:
0562:            /**
0563:             * Get the WordDelineator used by this.
0564:             */
0565:            public WordDelineator getWordDelineator() {
0566:                return this .word_delineator;
0567:            }
0568:
0569:            private WordDelineator default_word_delineator = new WordDelineator();
0570:            WordDelineator word_delineator = default_word_delineator;
0571:
0572:            /**
0573:             * Set/unset input listener.
0574:             * Entered text gets sent via this listener
0575:             *
0576:             * @deprecated Replaced by {@link #addInputListener(TermInputListener)}.
0577:             */
0578:            public void setInputListener(TermInputListener l) {
0579:                addInputListener(l);
0580:            }
0581:
0582:            /**
0583:             * Add an input listener to this.
0584:             * <p>
0585:             * Text entered via the keyboard gets sent via this listener.
0586:             */
0587:            public void addInputListener(TermInputListener l) {
0588:                input_listeners.add(l);
0589:            }
0590:
0591:            public void removeInputListener(TermInputListener l) {
0592:                input_listeners.remove(l);
0593:            }
0594:
0595:            private void fireChar(char c) {
0596:                ListIterator iter = input_listeners.listIterator();
0597:                while (iter.hasNext()) {
0598:                    TermInputListener l = (TermInputListener) iter.next();
0599:                    l.sendChar(c);
0600:                }
0601:            }
0602:
0603:            private void fireChars(char buf[], int offset, int count) {
0604:                ListIterator iter = input_listeners.listIterator();
0605:                while (iter.hasNext()) {
0606:                    TermInputListener l = (TermInputListener) iter.next();
0607:                    l.sendChars(buf, offset, count);
0608:                }
0609:            }
0610:
0611:            private LinkedList input_listeners = new LinkedList();
0612:
0613:            /**
0614:             * Set/unset misc listener.
0615:             * The following events gets sent via this listener:
0616:             * window size changes
0617:             *
0618:             * @deprecated Replaced by{@link #addListener(TermListener)}.
0619:             */
0620:            public void setListener(TermListener l) {
0621:                addListener(l);
0622:            }
0623:
0624:            /**
0625:             * Add a TermListener to this.
0626:             */
0627:            public void addListener(TermListener l) {
0628:                listeners.add(l);
0629:            }
0630:
0631:            /**
0632:             * Remove the given TermListener from this.
0633:             */
0634:            public void removeListener(TermListener l) {
0635:                listeners.remove(l);
0636:            }
0637:
0638:            private void fireSizeChanged(Dimension cells, Dimension pixels) {
0639:                ListIterator iter = listeners.listIterator();
0640:                while (iter.hasNext()) {
0641:                    TermListener l = (TermListener) iter.next();
0642:                    l.sizeChanged(cells, pixels);
0643:                }
0644:            }
0645:
0646:            private LinkedList listeners = new LinkedList();
0647:
0648:            /**
0649:             * Set/unset focus policy.
0650:             * <br>
0651:             * When set, the Term screen will grab focus when clicked on, otherwise
0652:             * it will grab focus when the mouse moves into it.
0653:             */
0654:            public void setClickToType(boolean click_to_type) {
0655:                this .click_to_type = click_to_type;
0656:            }
0657:
0658:            public boolean isClickToType() {
0659:                return click_to_type;
0660:            }
0661:
0662:            private boolean click_to_type = true;
0663:
0664:            /**
0665:             * Control whether keystrokes are ignored.
0666:             */
0667:            public void setReadOnly(boolean read_only) {
0668:                this .read_only = read_only;
0669:            }
0670:
0671:            /**
0672:             * Return whether keystrokes are ignored.
0673:             */
0674:            public boolean isReadOnly() {
0675:                return read_only;
0676:            }
0677:
0678:            private boolean read_only = false;
0679:
0680:            /**
0681:             * Clear the visible portion of screen
0682:             */
0683:            public void clear() {
0684:                for (int row = 0; row < st.rows; row++) {
0685:                    Line l = buf.lineAt(beginx() + row);
0686:                    l.reset();
0687:                }
0688:                regionManager().reset();
0689:            }
0690:
0691:            /**
0692:             * Clear all of the history without repainting the screen.
0693:             * <p>
0694:             * This is useful if you want to avoid flicker.
0695:             */
0696:            public void clearHistoryNoRefresh() {
0697:                sel.cancel(true);
0698:
0699:                int old_cols = buf.visibleCols();
0700:                buf = new Buffer(old_cols);
0701:
0702:                firsta = 0;
0703:                charsInPrehistory = 0;
0704:
0705:                st.firstx = 0;
0706:                st.firsty = 0;
0707:                st.cursor.row = 0;
0708:                st.cursor.col = 0;
0709:                st.attr = 0;
0710:                st.saveCursor(); // This clobbers the saved cursor value
0711:                st.restoreCursor(); // release reference to saved cursor object
0712:
0713:                adjust_lines(st.rows);
0714:
0715:                st.firstx = 0;
0716:                st.firsty = 0;
0717:
0718:                regionManager().reset();
0719:
0720:                screen.possiblyUpdateCaretText();
0721:            }
0722:
0723:            /**
0724:             * Clear all of the history, including any visible portion of it.
0725:             * <p>
0726:             * Use {@link #clearHistoryNoRefresh()} if you find that clearHistory 
0727:             * causes flickering.
0728:             */
0729:            public void clearHistory() {
0730:                clearHistoryNoRefresh();
0731:                repaint(true);
0732:            }
0733:
0734:            /**
0735:             * Return the RegionManager associated with this Term
0736:             */
0737:            public RegionManager regionManager() {
0738:                return region_manager;
0739:            }
0740:
0741:            public String textWithin(Coord begin, Coord end) {
0742:                if (begin == null || end == null)
0743:                    return null;
0744:
0745:                final StringBuffer buf = new StringBuffer();
0746:
0747:                visitLines(begin, end, false, new LineVisitor() {
0748:                    public boolean visit(Line l, int row, int bcol, int ecol) {
0749:                        buf.append(l.charArray(), bcol, ecol - bcol + 1);
0750:                        return true;
0751:                    }
0752:                });
0753:                return buf.toString();
0754:            }
0755:
0756:            public String getRowText(int row) {
0757:                Line line = buf.lineAt(row);
0758:                if (line == null)
0759:                    return null;
0760:                return line.stringBuffer().toString();
0761:            }
0762:
0763:            /**
0764:             * Get KeyStroke set.
0765:             * <p>
0766:             * Be default Term consumes all keystrokes. 
0767:             * Any KeyStroke added to this set will be passed through and not consumed.
0768:             * <p>
0769:             * Be careful with control characters you need to create the keystroke
0770:             * as follows (note the - 64):
0771:             * <pre>
0772:             * KeyStroke.getKeyStroke(new Character((char)('T'-64)), Event.CTRL_MASK)
0773:             * </pre>
0774:             */
0775:            public HashSet getKeyStrokeSet() {
0776:                return keystroke_set;
0777:            }
0778:
0779:            /*
0780:             * Set the KeyStroke set.
0781:             * <p>
0782:             * While Term has a KeyStroke set set up by default, often many Terms
0783:             * share the same keystroke. This method allows this sharing.
0784:             */
0785:            public void setKeyStrokeSet(HashSet keystroke_set) {
0786:                this .keystroke_set = keystroke_set;
0787:
0788:                /* DEBUG
0789:                System.out.println("-----------------------------------------");//NOI18N
0790:                java.util.Iterator i = keystroke_set.iterator();
0791:                while (i.hasNext()) {
0792:                    KeyStroke ks = (KeyStroke) i.next();
0793:                    System.out.println("--- " + ks);	// NOI18N
0794:                }
0795:                 */
0796:            }
0797:
0798:            private HashSet keystroke_set = new HashSet();
0799:
0800:            // attempted partial fix for IZ 17337
0801:            // 'keystroke_set' is a collection of KeyStrokes in the form:
0802:            //	ks3 = getKeyStroke(VK_C, CTRL_MASK)
0803:            // we use maybeConsume() in keyPressed and keyTyped events. During
0804:            // keyTyped the event->KS gives us
0805:            //	ks2 = getKeyStroke((char) ('c'-64), CTRL_MASK)
0806:            // ks2 and ks3 while logically equivalent don't hash to the same so
0807:            // maybeConsume() says yes to ks2 and the Ctrl-C gets passed on.
0808:            //
0809:            // So to detect whether something in 'keystroke_set' needs to be dropped 
0810:            // we need to check at keyPress time but take action at keyTyped time.
0811:            // 'passOn' helps us do that.
0812:
0813:            private boolean passOn = true;
0814:
0815:            /**
0816:             * Return true (and consume it) if 'e' is allowed to be consumed by us.
0817:             *
0818:             * If our owner is interested in some keys they will put someting into
0819:             * keystroke_set.
0820:             */
0821:            private boolean maybeConsume(KeyEvent e) {
0822:
0823:                if (e.isConsumed())
0824:                    return false;
0825:
0826:                KeyStroke ks = KeyStroke.getKeyStrokeForEvent(e);
0827:
0828:                /* DEBUG
0829:                System.out.println("Term.maybeConsume(" + e + ")");	// NOI18N
0830:                System.out.println("\tKS = " + ks);	// NOI18N
0831:                System.out.println("\tcontained = " + keystroke_set.contains(ks));	// NOI18N
0832:                 */
0833:
0834:                if (keystroke_set == null || !keystroke_set.contains(ks)) {
0835:                    e.consume();
0836:                    return true;
0837:                }
0838:                return false;
0839:            }
0840:
0841:            /**
0842:             * Visit the physical lines from begin, through 'end'.
0843:             * <p>
0844:             * If 'newlines' is set, the passed 'ecol' is set to the actual
0845:             * number of columns in the view to signify that the newline is included.
0846:             * This way of doing it helps with rendering of a whole-line selection.
0847:             * Also Line knows about this and will tack on a "\n" when Line.text()
0848:             * is asked for.
0849:             */
0850:            void visitLines(Coord begin, Coord end, boolean newlines,
0851:                    LineVisitor visitor) {
0852:                buf.visitLines(begin.toBCoord(firsta), end.toBCoord(firsta),
0853:                        newlines, visitor);
0854:            }
0855:
0856:            /**
0857:             * Visit logical lines from begin through end.
0858:             * <p>
0859:             * If begin is null, then the start of the buffer is assumed.
0860:             * If end is null, then the end of the buffer is assumed.
0861:             */
0862:            public void visitLogicalLines(Coord begin, Coord end,
0863:                    final LogicalLineVisitor llv) {
0864:
0865:                // Create a trampoline visitor
0866:                LineVisitor tramp = new LineVisitor() {
0867:                    String text = ""; // NOI18N
0868:                    int lineno = 0;
0869:                    Coord begin = null;
0870:                    Coord end = null;
0871:
0872:                    public boolean visit(Line l, int brow, int bcol, int ecol) {
0873:
0874:                        if (l.isWrapped()) {
0875:                            if (begin == null)
0876:                                begin = new Coord(new BCoord(brow, bcol),
0877:                                        firsta);
0878:                            text += l.text(bcol, ecol);
0879:
0880:                        } else {
0881:                            if (begin == null)
0882:                                begin = new Coord(new BCoord(brow, bcol),
0883:                                        firsta);
0884:                            end = new Coord(new BCoord(brow, ecol), firsta);
0885:                            text += l.text(bcol, ecol);
0886:
0887:                            if (!llv.visit(lineno, begin, end, text))
0888:                                return false;
0889:
0890:                            lineno++;
0891:                            text = ""; // NOI18N
0892:                            begin = null;
0893:                            end = null;
0894:                        }
0895:
0896:                        return true;
0897:                    }
0898:                };
0899:
0900:                if (begin == null)
0901:                    begin = new Coord();
0902:
0903:                if (end == null) {
0904:                    int lastrow = buf.nlines - 1;
0905:                    Line l = buf.lineAt(lastrow);
0906:                    end = new Coord(new BCoord(lastrow, l.length() - 1), firsta);
0907:                }
0908:
0909:                if (begin.compareTo(end) > 0)
0910:                    return;
0911:
0912:                buf.visitLines(begin.toBCoord(firsta), end.toBCoord(firsta),
0913:                        false, tramp);
0914:            }
0915:
0916:            /**
0917:             * Visit logical lines in reverse from end through begin.
0918:             * <p>
0919:             * If begin is null, then the start of the buffer is assumed.
0920:             * If end is null, then the end of the buffer is assumed.
0921:             */
0922:            public void reverseVisitLogicalLines(Coord begin, Coord end,
0923:                    final LogicalLineVisitor llv) {
0924:
0925:                // Create a trampoline visitor
0926:                LineVisitor tramp = new LineVisitor() {
0927:                    String text = ""; // NOI18N
0928:                    int lineno = 0;
0929:                    Coord begin = null;
0930:                    Coord end = null;
0931:
0932:                    public boolean visit(Line l, int brow, int bcol, int ecol) {
0933:
0934:                        boolean line_is_continuation = false;
0935:                        if (brow > 0 && buf.lineAt(brow - 1).isWrapped())
0936:                            line_is_continuation = true;
0937:
0938:                        if (line_is_continuation) {
0939:                            if (end == null)
0940:                                end = new Coord(new BCoord(brow, ecol), firsta);
0941:                            text = l.text(bcol, ecol) + text;
0942:
0943:                        } else {
0944:                            if (end == null)
0945:                                end = new Coord(new BCoord(brow, ecol), firsta);
0946:                            begin = new Coord(new BCoord(brow, bcol), firsta);
0947:                            text = l.text(bcol, ecol) + text;
0948:
0949:                            if (!llv.visit(lineno, begin, end, text))
0950:                                return false;
0951:
0952:                            lineno++;
0953:                            text = ""; // NOI18N
0954:                            begin = null;
0955:                            end = null;
0956:                        }
0957:
0958:                        return true;
0959:                    }
0960:                };
0961:
0962:                if (begin == null)
0963:                    begin = new Coord();
0964:
0965:                if (end == null) {
0966:                    int lastrow = buf.nlines - 1;
0967:                    Line l = buf.lineAt(lastrow);
0968:                    end = new Coord(new BCoord(lastrow, l.length() - 1), firsta);
0969:                }
0970:
0971:                if (begin.compareTo(end) > 0)
0972:                    return;
0973:
0974:                buf.reverseVisitLines(begin.toBCoord(firsta), end
0975:                        .toBCoord(firsta), false, tramp);
0976:            }
0977:
0978:            /**
0979:             * Convert offsets in logical lines into physical coordinates.
0980:             * <p>
0981:             * Usually called from within a LogicalLineVisitor.visit(). 
0982:             * 'begin' should be the 'begin' Coord passed to visit. 'offset' is an 
0983:             * offset into the logical line, the 'text' argument passed to visit().
0984:             * <p>
0985:             * Note that 'offset' is relative to begin.col!
0986:             * <p>
0987:             * Following is an example of a line "aaaaaxxxxxxxxxXXXxxxxxxxx" which
0988:             * got wrapped twice. Suppose you're searching for "XXX" and you
0989:             * began your search from the first 'x' on row 2.
0990:             * <pre>
0991:                row |  columns |
0992:                ----+----------+
0993:                0	|0123456789|
0994:                1	|          |
0995:                2	|aaaaaxxxxx| wrap
0996:                3	|xxxxXXXxxx| wrap
0997:                4	|xxxxx     |
0998:                5	|	   |
0999:
1000:             * </pre>
1001:             * The visitor will be called with 'begin' pointing at the first 'x', 
1002:             * 'end' pointing at the last 'x' and 'text' containing the above line.
1003:             * Let's say you use String.indexOf("XXX") and get an offset of 9.
1004:             * Passing the 'begin' through and the 9 as an offset and 3 as the
1005:             * length will yield an Extent (3,4) - (3,7) which youcan forward to
1006:             * setSelectionExtent();
1007:             * <p>
1008:             * The implementation of this function is not snappy (it calls
1009:             * Term.advance() in a loop) but the assumption is that his function
1010:             * will not be called in a tight loop.
1011:             */
1012:            public Extent extentInLogicalLine(Coord begin, int offset,
1013:                    int length) {
1014:
1015:                // SHOULD factor the A/B Coord conversion out
1016:
1017:                Coord from = (Coord) begin.clone();
1018:                while (offset-- > 0) {
1019:                    from = new Coord(buf.advance(from.toBCoord(firsta)), firsta);
1020:                }
1021:
1022:                Coord to = (Coord) from.clone();
1023:                while (--length > 0) {
1024:                    to = new Coord(buf.advance(to.toBCoord(firsta)), firsta);
1025:                }
1026:
1027:                return new Extent(from, to);
1028:            }
1029:
1030:            private boolean cursor_was_visible() {
1031:                return st.cursor.row - 1 >= st.firstx
1032:                        && st.cursor.row - 1 < st.firstx + st.rows;
1033:            }
1034:
1035:            /**
1036:             * Ensure that the given coordinate is visible.
1037:             * <p>
1038:             * If the given coordinate is visible within the screen nothing happens.
1039:             * Otherwise the screen is scrolled so that the given target ends up 
1040:             * approximately mid-sceeen.
1041:             */
1042:            public void possiblyNormalize(Coord target) {
1043:                if (target == null)
1044:                    return;
1045:
1046:                BCoord btarget = target.toBCoord(firsta);
1047:                if (btarget.row >= st.firstx
1048:                        && btarget.row < st.firstx + st.rows)
1049:                    return;
1050:
1051:                st.firstx = btarget.row - (st.rows / 2);
1052:                if (st.firstx < 0)
1053:                    st.firstx = 0;
1054:                else if (st.firstx + st.rows > buf.nlines)
1055:                    st.firstx = buf.nlines - st.rows;
1056:
1057:                repaint(true);
1058:            }
1059:
1060:            /**
1061:             * Ensure that maximum of the given region is visible.
1062:             * <p>
1063:             * If the given region is visible within the screen nothing happens.
1064:             * If the region is bigger than the screen height, first line of the region 
1065:             * will be displayed in first line of screen and end of region
1066:             * won't be displayed.
1067:             * If the region is bigger than the half of screen height, last line
1068:             * of the region will be displayed in the last line of the screen.
1069:             * Otherwise the region will start approximately in mid-sceeen
1070:             */
1071:            public void possiblyNormalize(ActiveRegion region) {
1072:                if (region == null)
1073:                    return;
1074:
1075:                BCoord bbegin = region.begin.toBCoord(firsta);
1076:                BCoord bend = region.end.toBCoord(firsta);
1077:                if (bbegin.row >= st.firstx && bend.row < st.firstx + st.rows)
1078:                    return;
1079:
1080:                if (bend.row - bbegin.row + 1 >= st.rows)
1081:                    // region has more rows then screen
1082:                    st.firstx = bbegin.row;
1083:                else {
1084:                    st.firstx = bbegin.row - (st.rows / 2);
1085:                    if (st.firstx < 0)
1086:                        st.firstx = 0;
1087:                    else if (st.firstx + st.rows > buf.nlines)
1088:                        st.firstx = buf.nlines - st.rows;
1089:                    else if (st.firstx + st.rows <= bend.row)
1090:                        // display also end of region
1091:                        st.firstx = bend.row - st.rows + 1;
1092:                }
1093:
1094:                repaint(true);
1095:            }
1096:
1097:            /**
1098:             * If the given coordinate is visible within the screen returns true,
1099:             * otherwise returns false.
1100:             */
1101:            public boolean isCoordVisible(Coord target) {
1102:                BCoord btarget = target.toBCoord(firsta);
1103:                if (btarget.row >= st.firstx
1104:                        && btarget.row < st.firstx + st.rows)
1105:                    return true;
1106:                else
1107:                    return false;
1108:            }
1109:
1110:            private void possiblyScrollOnInput() {
1111:                if (!scroll_on_input)
1112:                    return;
1113:
1114:                // vertical (row) dimension
1115:                if (st.cursor.row >= st.firstx
1116:                        && st.cursor.row < st.firstx + st.rows) {
1117:                    ;
1118:                } else {
1119:                    st.firstx = buf.nlines - st.rows;
1120:                    repaint(true);
1121:                }
1122:            }
1123:
1124:            /*
1125:             * Horizontal scrolling to track the cursor.
1126:             *
1127:             * The view/buffer etc calculations, performed by possiblyHScroll(), are not
1128:             * terribly complex, but they do depend on up-to-date cursor position.
1129:             * If we were to do all this work on every character received we would
1130:             * get some slowdown, but worse if say a large file with long lines is
1131:             * being 'cat'ed Term will end up scrolling left and right. It would be
1132:             * rather annoying.
1133:             * But attempting to do the scrolling on typed input isn't going to do
1134:             * because (because of non-local echoing) the cursor isn't yet updated when
1135:             * a key has been pressed.
1136:             * Additionally, a key may result in more than character being echoed.
1137:             * for example, an ENTER might result in a "\n\r", a TAB in 8 ' 's,
1138:             * and a '\b' in a "\b \b" sequence so a single flag won't do.
1139:             * Instead we use a count, 'hscroll_count' which says: attempt 
1140:             * possiblyHScroll() on the next <n> received characters. It may happen
1141:             * there there is a lot of keyboard input and very little output. In
1142:             * such cases 'hscroll_count' will start accumulating a defecit. To
1143:             * counter this we reset the count to 8 on "newline" type stuff.
1144:             */
1145:
1146:            private int hscroll_count = 0;
1147:
1148:            private void hscrollReset(char c) {
1149:                ckEventDispatchThread();
1150:                // OLD NPE-x synchronized(hscroll_lock)
1151:                {
1152:                    if (c == '\n' || c == '\r')
1153:                        hscroll_count = 8;
1154:                    else
1155:                        hscroll_count += 8;
1156:                }
1157:            }
1158:
1159:            private void possiblyHScroll() {
1160:
1161:                // decide if we actually need to do it
1162:
1163:                if (!horizontally_scrollable)
1164:                    return;
1165:
1166:                ckEventDispatchThread();
1167:                // OLD NPE-x synchronized(hscroll_lock)
1168:                {
1169:                    if (hscroll_count > 0) {
1170:                        hscroll_count--;
1171:                    } else {
1172:                        return;
1173:                    }
1174:                }
1175:
1176:                /* DEBUG
1177:                System.out.println("Checking hscroll cursor.col " + st.cursor.col + 	// NOI18N
1178:                                   " firsty " + st.firsty 	// NOI18N
1179:                		   " visibleCols " + buf.visibleCols());	// NOI18N
1180:                 */
1181:
1182:                // horizontal (col) dimension
1183:                if (st.cursor.col >= st.firsty + buf.visibleCols()) {
1184:                    /* DEBUG
1185:                    System.out.println("Need to scroll right");	// NOI18N
1186:                     */
1187:                    st.firsty = st.cursor.col - buf.visibleCols() + 1;
1188:
1189:                    // Expand our idea of column count so that if the cursor is
1190:                    // at the end the scrollbar allows us to scroll to it.
1191:                    // This is a bit unusual in that if we type stuff right up to the
1192:                    // last visible column and return the hscrollbar will display this
1193:                    // one extra column even though nothing was ever typed in it. 
1194:                    // This is particularly annoying in 'vi' with right-butting lines.
1195:                    // In the future SHOULD think up of something "smart".
1196:                    buf.noteColumn(st.cursor.col + 1);
1197:
1198:                    repaint(true);
1199:
1200:                } else if (st.cursor.col - buf.visibleCols() < st.firsty) {
1201:                    /* DEBUG
1202:                    System.out.println("Need to scroll left");	// NOI18N
1203:                     */
1204:                    st.firsty = st.cursor.col - buf.visibleCols() + 1;
1205:                    if (st.firsty < 0)
1206:                        st.firsty = 0;
1207:                    else
1208:                        repaint(true);
1209:                }
1210:            }
1211:
1212:            /**
1213:             * Set the display attribute for characters printed from now on.
1214:             * <p>
1215:             * Unrecognized values silently ignored.
1216:             */
1217:
1218:            public void setAttribute(int value) {
1219:                st.attr = Attr.setAttribute(st.attr, value);
1220:            }
1221:
1222:            /**
1223:             * Set or unset the display attribute for characters from 'begin' to 'end'
1224:             * inclusive to 'value' depending on the value of 'on'.
1225:             * <p>
1226:             * Will cause a repaint.
1227:             */
1228:            public void setCharacterAttribute(Coord begin, Coord end,
1229:                    final int value, final boolean on) {
1230:
1231:                visitLines(begin, end, false, new LineVisitor() {
1232:                    public boolean visit(Line l, int brow, int bcol, int ecol) {
1233:                        l.setCharacterAttribute(bcol, ecol, value, on);
1234:                        return true;
1235:                    }
1236:                });
1237:
1238:                repaint(false);
1239:            }
1240:
1241:            /*
1242:             * Set the glyph and line background colors for the line the cursor is on.
1243:             * <p>
1244:             * Will not repaint.
1245:             */
1246:            public void setGlyph(int glyph_id, int background_id) {
1247:                Line l = cursor_line();
1248:                l.glyph_glyph = glyph_id;
1249:                l.glyph_rendition = background_id;
1250:            }
1251:
1252:            /*
1253:             * Set the glyph and line background colors for the give line.
1254:             * <p>
1255:             * Will repaint.
1256:             */
1257:            public void setRowGlyph(int row, int glyph_id, int background_id) {
1258:                Coord c = new Coord();
1259:                c.row = row;
1260:                c.col = 0;
1261:                BCoord b = c.toBCoord(firsta);
1262:                Line l = buf.lineAt(b.row);
1263:                if (l == null)
1264:                    return;
1265:                l.glyph_glyph = glyph_id;
1266:                l.glyph_rendition = background_id;
1267:
1268:                possibly_repaint(false);
1269:            }
1270:
1271:            /**
1272:             * Adjust lines when the widget is resized (and also at construction time)
1273:             * <p>
1274:             * When growing makes sure that everything in the screen is backed up
1275:             * by a buffer Line. When shrinking removes lines from the top or the
1276:             * bottom as appropriate.
1277:             * <p>
1278:             * In other words implements the xterm resizeGravity=SouthWest semantics.
1279:             * which roughly says: When resizing keep the line that was at the bottom,
1280:             * _at_ the bottom.
1281:             */
1282:
1283:            private void adjust_lines(int delta_row) {
1284:
1285:                if (delta_row > 0) {
1286:                    // attempt to scroll
1287:                    st.firstx -= delta_row;
1288:
1289:                    // SHOULD eliminate the loop and move the work to Buffer
1290:                    while (st.firstx < 0) {
1291:                        buf.appendLine();
1292:                        st.firstx++;
1293:                    }
1294:
1295:                } else if (delta_row < 0) {
1296:                    // we shrunk
1297:                    int orows = st.rows - delta_row; // reconstruct orows
1298:
1299:                    // First attempt to remove lines from the bottom of the buffer.
1300:                    // This comes into play mostly when you have just started Term
1301:                    // and have but few lines near the top and nothing in history
1302:                    // so you really can't scroll.
1303:
1304:                    // How many lines can we trim at the bottom before we eat
1305:                    // into the cursor?
1306:                    // Really weird I seem to get the same results regardless of
1307:                    // whether I use orows or buf.nlines. SHOULD investigate more.
1308:
1309:                    int allowed = buf.nlines - st.cursor.row - 1;
1310:
1311:                    if (allowed < 0)
1312:                        allowed = 0;
1313:
1314:                    int delete_from_bottom;
1315:                    if (allowed > -delta_row) {
1316:                        // no need to scroll, we accomodate the whole resize by
1317:                        // removing lines from the bottom
1318:                        delete_from_bottom = -delta_row;
1319:                    } else {
1320:                        // delete as much as we're allowed ...
1321:                        delete_from_bottom = allowed;
1322:                        // ... and scroll for the rest
1323:                        st.firstx += (-delta_row - allowed);
1324:                    }
1325:
1326:                    // SHOULD eliminate the loop and move the work to Buffer
1327:                    while (delete_from_bottom-- > 0) {
1328:                        buf.removeLineAt(buf.nlines - 1);
1329:                    }
1330:
1331:                }
1332:                // printStats("From delta_row of " + delta_row);	// NOI18N
1333:
1334:                adjust_scrollbar();
1335:            }
1336:
1337:            /**
1338:             * returns curent history size of buffer.
1339:             */
1340:            public int getHistoryBuffSize() {
1341:                return buf.nlines - st.rows;
1342:            }
1343:
1344:            private void limit_lines() {
1345:
1346:                /*
1347:                 * Make sure we don't exceed the buffer size limit historySize.
1348:                 * This implements the vanishing of lines from the beginning of history.
1349:                 */
1350:
1351:                if (anchored)
1352:                    return;
1353:
1354:                int history = buf.nlines - st.rows;
1355:                if (history < 0)
1356:                    history = 0;
1357:
1358:                int toremove = history - history_size;
1359:                if (toremove > 0) {
1360:                    int charsRemoved = buf.removeLinesAt(0, toremove);
1361:
1362:                    charsInPrehistory += charsRemoved;
1363:
1364:                    // relocate all row indices
1365:                    st.adjust(-toremove);
1366:
1367:                    firsta += toremove;
1368:
1369:                    // cull any regions that are no longer in history
1370:                    if (++cull_count % cull_frequency == 0) {
1371:                        /* DEBUG
1372:                        System.out.println("Culling regions ..."); // NOI18N
1373:                         */
1374:                        region_manager.cull(firsta);
1375:                    }
1376:
1377:                    // our absolute coordinates are about to wrap
1378:                    if (firsta + buf.nlines >= modulo) {
1379:                        int old_firsta = firsta;
1380:                        firsta = 0;
1381:
1382:                        sel.relocate(old_firsta, firsta);
1383:                        region_manager.relocate(old_firsta, firsta);
1384:                    }
1385:                }
1386:
1387:                // deal with selection moving out of the buffer
1388:                sel.adjust(firsta, 0, firsta + buf.nlines);
1389:
1390:                adjust_scrollbar();
1391:            }
1392:
1393:            /**
1394:             * Scroller is used to implement selection auto-scrolling.
1395:             * <p>
1396:             * When a selection drag moves out of the window a scroller object/thread
1397:             * is started to periodically scroll and extend the selection.
1398:             */
1399:            private class Scroller extends Thread {
1400:                public final static int UP = 1 << 1;
1401:                public final static int DOWN = 1 << 2;
1402:                public final static int LEFT = 1 << 3;
1403:                public final static int RIGHT = 1 << 4;
1404:
1405:                private int direction;
1406:
1407:                public Scroller(int direction) {
1408:                    this .direction = direction;
1409:                }
1410:
1411:                private boolean extend() {
1412:
1413:                    ckEventDispatchThread();
1414:                    // OLD NPE-x synchronized (Term.this)
1415:                    {
1416:
1417:                        // Selection might wink out while we're auto scrolling.
1418:                        // Since we use 'sel.sel_extent' further below we
1419:                        // synchronize.
1420:                        if (sel.sel_extent == null)
1421:                            return false;
1422:
1423:                        BCoord x = sel.sel_extent.toBCoord(firsta);
1424:                        BCoord v = toViewCoord(x);
1425:                        int r = v.row;
1426:                        int c = v.col;
1427:
1428:                        if ((direction & DOWN) == DOWN) {
1429:                            lineDown(1);
1430:                            r = st.rows - 1;
1431:                            c = buf.totalCols();
1432:                        } else if ((direction & UP) == UP) {
1433:                            lineUp(1);
1434:                            r = 0;
1435:                            c = 0;
1436:                        } else {
1437:                            BCoord vc2 = toViewCoord(drag_point);
1438:                            r = vc2.row;
1439:                            c = vc2.col;
1440:                        }
1441:
1442:                        if ((direction & LEFT) == LEFT) {
1443:                            st.firsty--;
1444:                            if (st.firsty < 0)
1445:                                st.firsty = 0;
1446:                            c = 0;
1447:                        } else if ((direction & RIGHT) == RIGHT) {
1448:                            st.firsty++;
1449:                            int limit = buf.totalCols() - buf.visibleCols();
1450:                            if (limit < 0)
1451:                                limit = 0;
1452:                            if (st.firsty > limit)
1453:                                st.firsty = limit;
1454:                            c = st.firsty + buf.visibleCols();
1455:                        }
1456:
1457:                        BCoord vc = new BCoord(r, c);
1458:                        BCoord bc = toBufCoords(vc);
1459:                        sel.track(new Coord(bc, firsta));
1460:                        repaint(true);
1461:                        return true;
1462:                    }
1463:                }
1464:
1465:                public void run() {
1466:                    while (true) {
1467:
1468:                        /* DEBUG
1469:                        System.out.print("Scrolling ");	// NOI18N
1470:                        if ((direction & UP) == UP)
1471:                        	System.out.print("UP ");	// NOI18N
1472:                        if ((direction & DOWN) == DOWN)
1473:                        	System.out.print("DOWN ");	// NOI18N
1474:                        if ((direction & LEFT) == LEFT)
1475:                        	System.out.print("LEFT ");	// NOI18N
1476:                        if ((direction & RIGHT) == RIGHT)
1477:                        	System.out.print("RIGHT ");	// NOI18N
1478:                        System.out.println();
1479:                         */
1480:
1481:                        extend();
1482:
1483:                        try {
1484:                            // See issue 36404
1485:                            sleep(10);
1486:                        } catch (InterruptedException x) {
1487:                            break;
1488:                        }
1489:                    }
1490:                    /* DEBUG
1491:                    System.out.println("Done with Scrolling");	// NOI18N
1492:                     */
1493:                }
1494:            }
1495:
1496:            private Scroller scroller;
1497:            private Point drag_point;
1498:
1499:            private int scrolling_direction = 0;
1500:
1501:            private void scroll_to(int direction, MouseEvent e) {
1502:                if (direction == scrolling_direction) {
1503:                    if (direction == 0) {
1504:                        // We're moving inside the view
1505:                        BCoord bc = toBufCoords(toViewCoord(e.getPoint()));
1506:                        sel.track(new Coord(bc, firsta));
1507:                        repaint(false);
1508:                    }
1509:                    return;
1510:                }
1511:
1512:                // we changed direction
1513:
1514:                // get rid of the old scroller
1515:                if (scroller != null) {
1516:                    scroller.interrupt();
1517:                    scroller = null;
1518:                }
1519:
1520:                if (direction == 0) {
1521:                    BCoord bc = toBufCoords(toViewCoord(e.getPoint()));
1522:                    sel.track(new Coord(bc, firsta));
1523:                    repaint(false);
1524:                } else {
1525:                    scroller = new Scroller(direction);
1526:                    scroller.start();
1527:                }
1528:
1529:                scrolling_direction = direction;
1530:            }
1531:
1532:            /**
1533:             * Constructor
1534:             */
1535:            public Term() {
1536:                st.rows = 25;
1537:                st.firstx = 0;
1538:                st.firsty = 0;
1539:
1540:                standard_color[0] = Color.black;
1541:                standard_color[1] = Color.red;
1542:                standard_color[2] = Color.green;
1543:                standard_color[3] = Color.yellow;
1544:                standard_color[4] = Color.blue;
1545:                standard_color[5] = Color.magenta;
1546:                standard_color[6] = Color.cyan;
1547:                standard_color[7] = Color.white;
1548:
1549:                custom_color[0] = Color.black;
1550:                custom_color[1] = Color.black;
1551:                custom_color[2] = Color.black;
1552:                custom_color[3] = Color.black;
1553:                custom_color[4] = Color.black;
1554:                custom_color[5] = Color.black;
1555:                custom_color[6] = Color.black;
1556:                custom_color[7] = Color.black;
1557:
1558:                Font f = UIManager.getFont("TextArea.font"); //NOI18N
1559:                if (f == null) {
1560:                    // on, e.g., GTK L&F
1561:                    f = UIManager.getFont("controlFont"); //NOI18N
1562:                }
1563:
1564:                if (f != null) {
1565:                    setFont(new Font("Monospaced", Font.PLAIN, f.getSize() + 1)); // NOI18N
1566:                } else {
1567:                    setFont(new Font("Monospaced", Font.PLAIN, 12)); // NOI18N
1568:                }
1569:
1570:                BorderLayout layout = new BorderLayout();
1571:                setLayout(layout);
1572:                screen = new Screen(this , (buf.visibleCols() * metrics.width
1573:                        + glyph_gutter_width + debug_gutter_width), st.rows
1574:                        * metrics.height);
1575:
1576:                add(BorderLayout.CENTER, screen);
1577:                screen.setBackground(null); // inherit from this
1578:                screen
1579:                        .setCursor(Cursor
1580:                                .getPredefinedCursor(Cursor.TEXT_CURSOR));
1581:
1582:                adjust_lines(st.rows);
1583:                st.cursor.row = 0;
1584:
1585:                vscroll_bar = new JScrollBar(JScrollBar.VERTICAL);
1586:                add(BorderLayout.EAST, vscroll_bar);
1587:                vscroll_bar.setValues(st.firstx, st.rows - 1, 0, st.rows);
1588:                vscroll_bar.setUnitIncrement(1);
1589:                vscroll_bar.setEnabled(true);
1590:                vscroll_bar.setFocusable(false);
1591:
1592:                vscroll_bar.addAdjustmentListener(new AdjustmentListener() {
1593:
1594:                    public void adjustmentValueChanged(AdjustmentEvent e) {
1595:                        JScrollBar sb = (JScrollBar) e.getAdjustable();
1596:                        switch (e.getAdjustmentType()) {
1597:                        case AdjustmentEvent.TRACK:
1598:
1599:                            int pos = e.getValue();
1600:
1601:                            // adjustmentValueChanged() will get called when we
1602:                            // call setValues() on the scrollbar.
1603:                            // This test sort-of filters that out
1604:                            if (pos == st.firstx)
1605:                                break;
1606:
1607:                            // deal with the user moving the thumb
1608:                            st.firstx = pos;
1609:                            /* DEBUG
1610:                            if (st.firstx + st.rows > buf.nlines) {
1611:                                Thread.dumpStack();
1612:                                printStats("bad scroll value");	// NOI18N
1613:                            }
1614:                             */
1615:                            repaint(false);
1616:                            break;
1617:
1618:                        default:
1619:                            /* DEBUG
1620:                            System.out.println("adjustmentValueChanged: " + e); // NOI18N
1621:                             */
1622:                            break;
1623:                        }
1624:                    }
1625:                });
1626:
1627:                hscroll_bar = new JScrollBar(JScrollBar.HORIZONTAL);
1628:                hscroll_bar.setValues(st.firsty, buf.totalCols() - 1, 0, buf
1629:                        .totalCols());
1630:                hscroll_bar.setUnitIncrement(1);
1631:                hscroll_bar.setEnabled(true);
1632:                hscroll_bar.setFocusable(false);
1633:
1634:                hscroll_wrapper = new ScrollWrapper(hscroll_bar);
1635:                add(BorderLayout.SOUTH, hscroll_wrapper);
1636:
1637:                hscroll_bar.addAdjustmentListener(new AdjustmentListener() {
1638:
1639:                    public void adjustmentValueChanged(AdjustmentEvent e) {
1640:                        JScrollBar sb = (JScrollBar) e.getAdjustable();
1641:                        switch (e.getAdjustmentType()) {
1642:                        case AdjustmentEvent.TRACK:
1643:
1644:                            int pos = e.getValue();
1645:
1646:                            // adjustmentValueChanged() will get called when we
1647:                            // call setValues() on the scrollbar.
1648:                            // This test sort-of filters that out
1649:                            if (pos == st.firsty)
1650:                                break;
1651:
1652:                            // deal with the user moving the thumb
1653:                            st.firsty = pos;
1654:                            repaint(false);
1655:                            break;
1656:
1657:                        default:
1658:                            /* DEBUG
1659:                            System.out.println("adjustmentValueChanged: " + e); // NOI18N
1660:                             */
1661:                            break;
1662:                        }
1663:                    }
1664:                });
1665:
1666:                screen.addComponentListener(new ComponentAdapter() {
1667:
1668:                    public void componentResized(ComponentEvent e) {
1669:                        Dimension size = screen.getSize();
1670:                        sizeChanged(size.width, size.height);
1671:
1672:                        // workaround for java bug 4711314 where a repaint()
1673:                        // appears before componentResized() causing issue 25313
1674:                        repaint(true);
1675:                    }
1676:
1677:                });
1678:
1679:                screen.addKeyListener(new KeyListener() {
1680:
1681:                    // We consume all events so no additional side-effects take place.
1682:                    // Examples:
1683:                    // - We don't want TAB to move focus away. (see more below)
1684:                    // - We don't want ^p to bring up a printer diaolog
1685:                    // - etc
1686:
1687:                    // HACK. Java maps [Return] to 10 as opposed to 13. This is due
1688:                    // to it's Windows-chauvinism, a 'reality' pointed out to me by
1689:                    // one of the AWT people.
1690:                    // The keycode doesn't make it to keyTyped, so for now we detect
1691:                    // a Return by capturing press/releases of VK_ENTER and
1692:                    // use a flag.
1693:                    boolean saw_return;
1694:
1695:                    public void keyTyped(KeyEvent e) {
1696:                        char c = e.getKeyChar();
1697:
1698:                        if (debugKeys())
1699:                            System.out.println("term: keyTyped: " + e); // NOI18N
1700:
1701:                        if (read_only)
1702:                            return;
1703:
1704:                        if (c == 10 && saw_return) {
1705:                            saw_return = false;
1706:                            c = (char) 13;
1707:                        }
1708:
1709:                        if (passOn && maybeConsume(e)) {
1710:                            on_char(c);
1711:                            possiblyScrollOnInput();
1712:                        }
1713:
1714:                        hscrollReset(c);
1715:                    }
1716:
1717:                    public void keyPressed(KeyEvent e) {
1718:                        /* DEBUG
1719:                        System.out.println("keyPressed " + e); // NOI18N
1720:                         */
1721:
1722:                        switch (e.getKeyCode()) {
1723:                        case KeyEvent.VK_COPY:
1724:                            copyToClipboard();
1725:                            break;
1726:                        case KeyEvent.VK_PASTE:
1727:                            pasteFromClipboard();
1728:                            break;
1729:                        case KeyEvent.VK_ENTER:
1730:                            saw_return = true;
1731:                            break;
1732:                        case KeyEvent.VK_PAGE_UP:
1733:                            if (e.getModifiers() == Event.CTRL_MASK) {
1734:                                pageLeft(1);
1735:                            } else {
1736:                                pageUp(1);
1737:                            }
1738:                            break;
1739:                        case KeyEvent.VK_PAGE_DOWN:
1740:                            if (e.getModifiers() == Event.CTRL_MASK) {
1741:                                pageRight(1);
1742:                            } else {
1743:                                pageDown(1);
1744:                            }
1745:                            break;
1746:
1747:                        case KeyEvent.VK_UP:
1748:                            if (e.getModifiers() == Event.CTRL_MASK) {
1749:                                lineUp(1);
1750:                            }
1751:                            break;
1752:                        case KeyEvent.VK_DOWN:
1753:                            if (e.getModifiers() == Event.CTRL_MASK) {
1754:                                lineDown(1);
1755:                            }
1756:                            break;
1757:                        }
1758:                        passOn = maybeConsume(e);
1759:                    }
1760:
1761:                    public void keyReleased(KeyEvent e) {
1762:                        /* DEBUG
1763:                        System.out.println("keyReleased"); // NOI18N
1764:                         */
1765:
1766:                        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
1767:                            /* DEBUG
1768:                            System.out.println("keyReleased VK_ENTER"); // NOI18N
1769:                             */
1770:                            saw_return = false;
1771:                        }
1772:                        maybeConsume(e);
1773:                    }
1774:                });
1775:
1776:                screen.addMouseMotionListener(new MouseMotionListener() {
1777:
1778:                    public void mouseDragged(MouseEvent e) {
1779:                        /* DEBUG
1780:                        System.out.println("mouseDragged"); // NOI18N
1781:                         */
1782:
1783:                        // akemr - fix of #25648
1784:                        if (SwingUtilities.isRightMouseButton(e))
1785:                            return;
1786:
1787:                        if (left_down_point != null) {
1788:                            BCoord bc = toBufCoords(toViewCoord(left_down_point));
1789:                            sel.track(new Coord(bc, firsta));
1790:                            left_down_point = null;
1791:                        }
1792:                        drag_point = e.getPoint();
1793:                        /* DEBUG
1794:                        System.out.println("mouseDrag: " + drag_point); // NOI18N
1795:                         */
1796:
1797:                        int scroll_direction = 0;
1798:
1799:                        if (drag_point.y < 0) {
1800:                            scroll_direction |= Scroller.UP;
1801:                        } else if (drag_point.y > screen.getSize().height) {
1802:                            scroll_direction |= Scroller.DOWN;
1803:                        }
1804:
1805:                        if (drag_point.x < 0) {
1806:                            scroll_direction |= Scroller.LEFT;
1807:                        } else if (drag_point.x > screen.getSize().width) {
1808:                            scroll_direction |= Scroller.RIGHT;
1809:                        }
1810:
1811:                        scroll_to(scroll_direction, e);
1812:                    }
1813:
1814:                    public void mouseMoved(MouseEvent e) {
1815:                        /* DEBUG
1816:                        Point p = (Point) e.getPoint().clone();
1817:                        BCoord bc = toBufCoords(toViewCoord(p));
1818:                        Coord c = new Coord(bc, firsta);
1819:                        Extent x = sel.getExtent();
1820:                        if (x == null) {
1821:                            System.out.println("sel intersect: no extent");	// NOI18N
1822:                        } else {
1823:                            System.out.println("sel intersect: " +	// NOI18N
1824:                            (x.intersects(c.row, c.col)? "intersects"	// NOI18N
1825:                        				 "doesn't intersect"));	// NOI18N
1826:                        }
1827:                         */
1828:                    }
1829:                });
1830:
1831:                addMouseWheelHandler(screen, vscroll_bar);
1832:
1833:                screen.addMouseListener(new MouseListener() {
1834:
1835:                    public void mouseClicked(MouseEvent e) {
1836:                        BCoord bcoord = toBufCoords(toViewCoord(e.getPoint()));
1837:
1838:                        if (SwingUtilities.isLeftMouseButton(e)) {
1839:                            /* DEBUG
1840:                            System.out.println("LEFT click"); // NOI18N
1841:                             */
1842:                            if (click_to_type)
1843:                                requestFocus();
1844:
1845:                        } else if (SwingUtilities.isMiddleMouseButton(e)) {
1846:                            /* DEBUG
1847:                            System.out.println("MIDDLE click"); // NOI18N
1848:                            System.out.println("Selection: '" + sel.sel_get() + "'"); // NOI18N
1849:                             */
1850:                            pasteFromSelection();
1851:                        }
1852:                    }
1853:
1854:                    public void mousePressed(MouseEvent e) {
1855:                        /* DEBUG
1856:                        System.out.println("mousePressed "+e.getModifiers()); // NOI18N
1857:                         */
1858:
1859:                        if (SwingUtilities.isLeftMouseButton(e)) {
1860:
1861:                            if (e.isShiftDown()) {
1862:                                // JLF/dtterm selection extension
1863:                                // Actually it's _addition_ so SHOULD enhance Sel
1864:                                // to do that instead.
1865:                                BCoord bc = toBufCoords(toViewCoord(e
1866:                                        .getPoint()));
1867:                                if (sel.extend(new Coord(bc, firsta))) {
1868:                                    fireSelectionExtentChanged();
1869:                                    repaint(false);
1870:                                }
1871:                                return;
1872:                            }
1873:
1874:                            if (sel.cancel(false))
1875:                                repaint(false);
1876:
1877:                            if (e.getClickCount() == 1) {
1878:                                left_down_point = (Point) e.getPoint().clone();
1879:
1880:                            } else if (e.getClickCount() == 2) {
1881:                                BCoord bcoord = toBufCoords(toViewCoord(e
1882:                                        .getPoint()));
1883:                                BExtent word = buf.find_word(word_delineator,
1884:                                        bcoord);
1885:                                sel.select_word(word.toExtent(firsta));
1886:                                repaint(false);
1887:
1888:                            } else if (e.getClickCount() == 3) {
1889:                                BCoord bcoord = toBufCoords(toViewCoord(e
1890:                                        .getPoint()));
1891:                                sel.select_line(new Coord(bcoord, firsta));
1892:                                repaint(false);
1893:
1894:                            }
1895:
1896:                            fireSelectionExtentChanged();
1897:                        }
1898:                    }
1899:
1900:                    public void mouseReleased(MouseEvent e) {
1901:                        /* DEBUG
1902:                        System.out.println("mouseReleased"); // NOI18N
1903:                         */
1904:                        if (SwingUtilities.isLeftMouseButton(e)) {
1905:
1906:                            if (e.isShiftDown()) {
1907:                                // we're extending
1908:                                return;
1909:                            }
1910:
1911:                            if (scroller != null) {
1912:                                scroller.interrupt();
1913:                                scroller = null;
1914:                            }
1915:
1916:                            // Don't put in the selection if we didn't move
1917:                            // When left button goes down 'left_down_point' is set.
1918:                            // As soon as we move the mouse it gets reset.
1919:                            if (left_down_point == null)
1920:                                sel.done( /* OLD false */);
1921:                            left_down_point = null;
1922:                        }
1923:                    }
1924:
1925:                    /*
1926:                     * Implement follow-mouse focus
1927:                     */
1928:                    public void mouseEntered(MouseEvent e) {
1929:                        /* DEBUG
1930:                        System.out.println("mouseEntered"); // NOI18N
1931:                         */
1932:                        if (!click_to_type) {
1933:                            requestFocus();
1934:                        }
1935:                    }
1936:
1937:                    public void mouseExited(MouseEvent e) {
1938:                        /* DEBUG
1939:                        System.out.println("mouseExited"); // NOI18N
1940:                         */
1941:                    }
1942:
1943:                });
1944:
1945:                screen.addFocusListener(new FocusListener() {
1946:                    public void focusGained(FocusEvent e) {
1947:                        /* DEBUG
1948:                        System.out.println("Focus gained >>>>>>>>>>>>>>>>>>>>>>>"); // NOI18N
1949:                         */
1950:                        has_focus = true;
1951:                        repaint(false);
1952:                    }
1953:
1954:                    public void focusLost(FocusEvent e) {
1955:                        /* DEBUG
1956:                        Component o = e.getOppositeComponent();
1957:                        System.out.println("Focus lost <<<<<<<<<<<<<<<<<<<<<<<" + o);
1958:                         */
1959:
1960:                        has_focus = false;
1961:                        repaint(false);
1962:                    }
1963:                });
1964:            }
1965:
1966:            /*
1967:             * Print interesting statistics and facts about this Term
1968:             */
1969:            public void printStats(String message) {
1970:                if (message != null)
1971:                    System.out.println(message);
1972:
1973:                if (message != null)
1974:                    System.out.print("\t"); // NOI18N
1975:                buf.printStats();
1976:
1977:                if (message != null)
1978:                    System.out.print("\t"); // NOI18N
1979:                System.out.println("rows " + st.rows + // NOI18N
1980:                        "  v cols " + buf.visibleCols() + // NOI18N
1981:                        "  t cols " + buf.totalCols() + // NOI18N
1982:                        "  history " + history_size + // NOI18N
1983:                        "  firstx " + st.firstx + // NOI18N
1984:                        "  firsty " + st.firsty + // NOI18N
1985:                        "  firsta " + firsta + // NOI18N
1986:                        "  gutter " + glyph_gutter_width); // NOI18N
1987:
1988:                if (message != null)
1989:                    System.out.print("\t"); // NOI18N
1990:                System.out.println("Cursor " + st.cursor + // NOI18N
1991:                        "  topMargin " + topMargin() + // NOI18N
1992:                        "  botMargin " + botMargin()); // NOI18N
1993:
1994:                if (message != null)
1995:                    System.out.print("\t"); // NOI18N
1996:                System.out.println("putChar " + n_putchar + // NOI18N
1997:                        "  putChars " + n_putchars + // NOI18N
1998:                        "  linefeeds " + n_linefeeds + // NOI18N
1999:                        "  repaint " + n_repaint + // NOI18N
2000:                        "  paint " + n_paint); // NOI18N
2001:            }
2002:
2003:            public void paste() {
2004:                pasteFromClipboard();
2005:            }
2006:
2007:            private void pasteHelp(Clipboard cb) {
2008:                if (read_only)
2009:                    return;
2010:
2011:                Transferable contents = cb.getContents(screen);
2012:                if (contents == null)
2013:                    return;
2014:
2015:                if (!contents.isDataFlavorSupported(DataFlavor.stringFlavor))
2016:                    return;
2017:
2018:                try {
2019:                    String string;
2020:                    string = (String) contents
2021:                            .getTransferData(DataFlavor.stringFlavor);
2022:                    /* DEBUG
2023:                    System.out.println("System selection contains '" + string + "'"); // NOI18N
2024:                     */
2025:                    char ca[] = string.toCharArray();
2026:                    sendChars(ca, 0, ca.length);
2027:                } catch (Exception e) {
2028:                    ;
2029:                }
2030:            }
2031:
2032:            /**
2033:             * Transfer contents of selection to Terms input stream.
2034:             * <p>
2035:             * The pasted content is sent through sendChars.
2036:             * <br>
2037:             * The paste will silently fail if:
2038:             * <ul>
2039:             * <li> The selection has null contents.
2040:             * <li> The selections data flavor is not string.
2041:             * </ul>
2042:             */
2043:            public void pasteFromSelection() {
2044:                /* DEBUG
2045:                System.out.println("Term: pasteFromSelection()"); // NOI18N
2046:                 */
2047:                if (systemSelection == null)
2048:                    return;
2049:                pasteHelp(systemSelection);
2050:            }
2051:
2052:            /**
2053:             * Transfer contents of clipboard to Terms input stream.
2054:             * <p>
2055:             * The pasted content is sent through sendChars.
2056:             * <br>
2057:             * The paste will silently fail if:
2058:             * <ul>
2059:             * <li> The selection has null contents.
2060:             * <li> The selections data flavor is not string.
2061:             * </ul>
2062:             */
2063:            public void pasteFromClipboard() {
2064:                pasteHelp(systemClipboard);
2065:            }
2066:
2067:            /**
2068:             * Transfer selected text into clipboard.
2069:             */
2070:            public void copy() {
2071:                copyToClipboard();
2072:            }
2073:
2074:            /**
2075:             * Transfer selected text into clipboard.
2076:             */
2077:            public void copyToClipboard() {
2078:                String text = sel.getSelection();
2079:                if (text != null) {
2080:                    StringSelection ss = new StringSelection(text);
2081:                    systemClipboard.setContents(ss, sel);
2082:                }
2083:            }
2084:
2085:            /**
2086:             * Transfer selected text into selection.
2087:             */
2088:            public void copyToSelection() {
2089:                /* DEBUG
2090:                System.out.println("Term: copyToSelection()");	// NOI18N
2091:                 */
2092:
2093:                if (systemSelection == null)
2094:                    return;
2095:                String text = sel.getSelection();
2096:                StringSelection ss = new StringSelection(text); // null 'text' is OK
2097:                systemSelection.setContents(ss, sel);
2098:            }
2099:
2100:            private static Extent old_extent = null;
2101:
2102:            void fireSelectionExtentChanged() {
2103:                Extent new_extent = getSelectionExtent();
2104:                firePropertyChange("selectionExtent", old_extent, new_extent); // NOI18N
2105:                old_extent = new_extent;
2106:            }
2107:
2108:            /**
2109:             * Set the number of character rows in the screen.
2110:             * <p>
2111:             * See setRowsColumns() for some additional important information.
2112:             */
2113:            public void setRows(int rows) {
2114:                if (old_rows == -1)
2115:                    old_rows = st.rows;
2116:                st.rows = rows;
2117:
2118:                updateScreenSize();
2119:            }
2120:
2121:            /**
2122:             * Get the number of character rows in the screen
2123:             */
2124:            public int getRows() {
2125:                return st.rows;
2126:            }
2127:
2128:            /**
2129:             * Set the number of character columns in the screen.
2130:             * <p>
2131:             * See setRowsColumns() for some additional important information.
2132:             */
2133:            public void setColumns(int cols) {
2134:                buf.setVisibleCols(cols);
2135:                updateScreenSize();
2136:            }
2137:
2138:            /**
2139:             * Trampoline from Line.ensureCapacity() to Buffer.noteColumn()
2140:             */
2141:            void noteColumn(Line l, int capacity) {
2142:                int vcapacity = l.bufToCell(metrics, capacity);
2143:                buf.noteColumn(vcapacity);
2144:            }
2145:
2146:            /**
2147:             * Trampoline from Line to MyFontMetrics.checkForMultiCell()
2148:             */
2149:            void checkForMultiCell(char c) {
2150:                metrics.checkForMultiCell(c);
2151:            }
2152:
2153:            /**
2154:             * Get the number of character columns in the screen
2155:             */
2156:            public int getColumns() {
2157:                return buf.visibleCols();
2158:            }
2159:
2160:            /**
2161:             * Simultaneously set the number of character rows and columns.
2162:             * <p>
2163:             * A Term is a composite widget made of a contained screen (getScreen())
2164:             * and a scrollbar. It is not a JScrollPane. You're actually setting
2165:             * the screen size here.
2166:             * <p>
2167:             * Setting the column size also sets the width of the buffer. This doesn't
2168:             * alter the length (column at which lines were wrapped) of past lines,
2169:             * but only new additional lines. For example, if you set columns to
2170:             * 20, print a bunch of lines that wrap, then resize to 80 columns, all
2171:             * the lines that were wrapped to 20 will stay wrapped that way. This is
2172:             * consistent with xterm behaviour.
2173:             * <p>
2174:             * If this Term is embedded in a component with a layout manager that is
2175:             * set up to accept child resizes gracefully this widget will be resized
2176:             * as expected.
2177:             * <p>
2178:             * Alternatively if this Term is embedded in a Window (like JFrame) 
2179:             * the window will need to be re-pack()ed as it does not accomodate it's
2180:             * childrens size changes. This has to be done by the application using 
2181:             * Term. The best way to do this is to add a TermListener() and call pack()
2182:             * on the appropriate window when a resize notification is fired.
2183:             */
2184:
2185:            public void setRowsColumns(int rows, int columns) {
2186:
2187:                // Combine code in setRows() and setColumns() so we factor
2188:                // the calls to updateScreenSize().
2189:
2190:                if (old_rows == -1)
2191:                    old_rows = st.rows;
2192:                st.rows = rows;
2193:                buf.setVisibleCols(columns);
2194:
2195:                updateScreenSize();
2196:            }
2197:
2198:            /**
2199:             * Governs whether Term will round down resize requests to character
2200:             * cell size.
2201:             * <p>
2202:             * Sizes are usually set by containers' layout managers. If rounding is
2203:             * enabled Term will attempt to adjust the size to an even multiple 
2204:             * of the character cell size.
2205:             * <p>
2206:             * The layout manager might not neccesarily honor the rounded size. 
2207:             * The situation can be somewhat improved by making sure that the ultimate
2208:             * container is re-packed as described in {@link #setRowsColumns(int, int)}.
2209:             */
2210:            public void setSizeRounded(boolean size_rounded) {
2211:                this .size_rounded = size_rounded;
2212:                updateScreenSize();
2213:            }
2214:
2215:            /**
2216:             * Returns true if Term will round down resize requests to character
2217:             * cell size.
2218:             * <p>
2219:             * See {@link #setSizeRounded(boolean)} for more info.
2220:             */
2221:            public boolean isSizeRounded() {
2222:                return size_rounded;
2223:            }
2224:
2225:            private boolean size_rounded = true;
2226:
2227:            /*
2228:             * Used to extract the dimensions of the terminal.
2229:             * SHOULD replace with getRowsCols and getScreenSize
2230:             */
2231:            public void fillSizeInfo(Dimension cells, Dimension pixels) {
2232:                cells.height = st.rows;
2233:                cells.width = buf.visibleCols();
2234:                Dimension cpixels = screen.getSize();
2235:                pixels.width = cpixels.width - glyph_gutter_width
2236:                        - debug_gutter_width;
2237:                pixels.height = cpixels.height;
2238:            }
2239:
2240:            /**
2241:             * Once the terminal is connected to something, use this function to
2242:             * send all Term Listener notifications.
2243:             */
2244:            protected void updateTtySize() {
2245:                if (screen != null) {
2246:                    Dimension cells = new Dimension(buf.visibleCols(), st.rows);
2247:                    Dimension pixels = screen.getSize();
2248:                    fireSizeChanged(cells, pixels);
2249:                }
2250:            }
2251:
2252:            /*
2253:             * various coordinate conversion functions
2254:             */
2255:
2256:            BCoord toViewCoord(BCoord b) {
2257:                /*
2258:                 * Convert from buffer coords to view coords
2259:                 */
2260:                Line l = buf.lineAt(b.row);
2261:                if (l != null) { //XXX Hotfix for issue 40189 - Buffer.lineAt() will
2262:                    //catch an AIOBEE and return null.  Probably related
2263:                    //to Ivan's notes about clipping in Term.paint()
2264:                    int vc = buf.lineAt(b.row).bufToCell(metrics, b.col);
2265:                    BCoord v = new BCoord(b.row - st.firstx, vc - st.firsty);
2266:                    return v;
2267:                } else {
2268:                    return null;
2269:                }
2270:            }
2271:
2272:            Point toPixel(BCoord v) {
2273:                /*
2274:                 * Convert from view coords to pixel coords
2275:                 */
2276:                Point p = new Point(v.col * metrics.width + glyph_gutter_width
2277:                        + debug_gutter_width, v.row * metrics.height);
2278:                return p;
2279:            }
2280:
2281:            /**
2282:             * Convert row/column coords to pixel coords within the widgets 
2283:             * coordinate system.
2284:             * It returns the pixel of the upper left corner of the target cell.
2285:             */
2286:            public Point toPixel(Coord target) {
2287:                BCoord btarget = target.toBCoord(firsta);
2288:                return toPixel(btarget);
2289:            }
2290:
2291:            /**
2292:             * Convert pixel coords to view row/column coords (0/0-origin)
2293:             */
2294:            BCoord toViewCoord(Point p) {
2295:                BCoord v = new BCoord(p.y / metrics.height, (p.x
2296:                        - glyph_gutter_width - debug_gutter_width)
2297:                        / metrics.width);
2298:                v.clip(st.rows, buf.visibleCols());
2299:                /* DEBUG
2300:                System.out.println("toViewCoord() -> " + v); // NOI18N
2301:                 */
2302:                return v;
2303:            }
2304:
2305:            BCoord toBufCoords(BCoord v) {
2306:                /*
2307:                 * Convert view row/column coords to buffer row/column coords.
2308:                 * If the buffer is smaller than the view, map to the last line.
2309:                 */
2310:                int brow = st.firstx + v.row;
2311:                if (brow >= buf.nlines)
2312:                    brow = buf.nlines - 1;
2313:                int bc = buf.lineAt(brow).cellToBuf(metrics, st.firsty + v.col);
2314:                BCoord b = new BCoord(brow, bc);
2315:                /* DEBUG
2316:                System.out.println("toBufCoords(" + v + ") -> " + b); // NOI18N
2317:                 */
2318:                return b;
2319:            }
2320:
2321:            /**
2322:             * Convert pixel coords to view (visible area) row/column coords (both
2323:             * o-origin).
2324:             * <p>
2325:             * In the returned Point, x represents the column, y the row.
2326:             */
2327:            public Point mapToViewRowCol(Point p) {
2328:                BCoord c = toViewCoord(p);
2329:                return new Point(c.col, c.row);
2330:            }
2331:
2332:            /**
2333:             * Convert pixel coords to (history) buffer row/column coords (both
2334:             * 0-origin).
2335:             * <p>
2336:             * In the returned Point, x represents the column, y the row.
2337:             */
2338:            public Point mapToBufRowCol(Point p) {
2339:                BCoord c = toBufCoords(toViewCoord(p));
2340:                return new Point(c.col, c.row);
2341:            }
2342:
2343:            private Color rendition_to_color(int rendition) {
2344:                switch (rendition) {
2345:                case 40:
2346:                    return standard_color[0];
2347:                case 41:
2348:                    return standard_color[1];
2349:                case 42:
2350:                    return standard_color[2];
2351:                case 43:
2352:                    return standard_color[3];
2353:                case 44:
2354:                    return standard_color[4];
2355:                case 45:
2356:                    return standard_color[5];
2357:                case 46:
2358:                    return standard_color[6];
2359:                case 47:
2360:                    return standard_color[7];
2361:
2362:                case 58:
2363:                    return custom_color[0];
2364:                case 59:
2365:                    return custom_color[1];
2366:                case 60:
2367:                    return custom_color[2];
2368:                case 61:
2369:                    return custom_color[3];
2370:                case 62:
2371:                    return custom_color[4];
2372:                case 63:
2373:                    return custom_color[5];
2374:                case 64:
2375:                    return custom_color[6];
2376:                case 65:
2377:                    return custom_color[7];
2378:
2379:                default:
2380:                    return null;
2381:                }
2382:            }
2383:
2384:            private Color actual_foreground;
2385:            private Color actual_background;
2386:            private boolean check_selection;
2387:            private int totcols;
2388:
2389:            private void do_run(Graphics g, int yoff, int xoff, int baseline,
2390:                    int brow, char buf[], Line l, int attr, int rbegin, int rend) {
2391:
2392:                /* DEBUG
2393:                System.out.println("do_run(" + rbegin + ", " + rend + ")");	// NOI18N
2394:                 */
2395:
2396:                int x;
2397:                int rlength;
2398:                int xlength;
2399:
2400:                if (metrics.isMultiCell()) {
2401:                    int vbegin = l.bufToCell(metrics, rbegin);
2402:                    int vend = l.bufToCell(metrics, rend + 1) - 1;
2403:                    x = xoff + (vbegin - st.firsty) * metrics.width;
2404:                    int vlength = vend - vbegin + 1;
2405:                    if (vlength <= 0) {
2406:                        /* DEBUG
2407:                        System.out.println("do_run(" + rbegin + ", " + rend + ")");	// NOI18N
2408:                         */
2409:                        return;
2410:                    }
2411:                    rlength = rend - rbegin + 1;
2412:                    xlength = vlength * metrics.width;
2413:
2414:                } else {
2415:                    x = xoff + (rbegin - st.firsty) * metrics.width;
2416:                    rlength = rend - rbegin + 1;
2417:                    if (rlength <= 0) {
2418:                        /* DEBUG
2419:                        System.out.println("do_run(" + rbegin + ", " + rend + ")");	// NOI18N
2420:                         */
2421:                        return;
2422:                    }
2423:                    xlength = rlength * metrics.width;
2424:                }
2425:
2426:                boolean reverse = ((attr & Attr.REVERSE) == Attr.REVERSE);
2427:                boolean active = ((attr & Attr.ACTIVE) == Attr.ACTIVE);
2428:
2429:                // choose background color
2430:                Color bg = null;
2431:
2432:                if (active) {
2433:                    bg = active_color;
2434:                } else {
2435:                    bg = backgroundColor(reverse, attr);
2436:                }
2437:
2438:                if (bg != null) {
2439:                    // Draw any background
2440:                    g.setColor(bg);
2441:                    g.fillRect(x, yoff, xlength, metrics.height
2442:                            - metrics.leading);
2443:                }
2444:
2445:                // Set foreground color
2446:                Color fg = foregroundColor(reverse, attr);
2447:                g.setColor(fg);
2448:
2449:                // draw any underscores
2450:                if ((attr & Attr.UNDERSCORE) == Attr.UNDERSCORE) {
2451:                    int h = metrics.height - metrics.leading - 1;
2452:                    g.drawLine(x, yoff + h, x + xlength, yoff + h);
2453:                }
2454:
2455:                // Draw the foreground character glyphs
2456:                myDrawChars(g, buf, l, rbegin, rlength, x, baseline);
2457:
2458:                // Draw fake bold characters by redrawing one pixel to the right
2459:                if ((attr & Attr.BRIGHT) == Attr.BRIGHT) {
2460:                    myDrawChars(g, buf, l, rbegin, rlength, x + 1, baseline);
2461:                }
2462:            }
2463:
2464:            private final Point newp = new Point();
2465:
2466:            /*
2467:             * Tweak glyph X positions so they fall on cell/grid/column boundries.
2468:             */
2469:            private void massage_glyphs(GlyphVector gv, int start, int n, Line l) {
2470:                Point2D pos0 = gv.getGlyphPosition(0);
2471:
2472:                // There's one big assumption here that in a monospaced font all the
2473:                // Y placements are identical. So we use the placement for the first
2474:                // glyph only.
2475:                newp.y = (int) pos0.getY();
2476:
2477:                int col = (int) pos0.getX();
2478:                for (int gx = 0; gx < n; gx++) {
2479:                    newp.x = col;
2480:                    gv.setGlyphPosition(gx, newp);
2481:                    col += l.width(metrics, start + gx) * metrics.width;
2482:                }
2483:            }
2484:
2485:            /**
2486:             * Draw characters in cells.
2487:             *
2488:             * Fixed width or monospaced fonts implies that the glyphs of all characters
2489:             * have the same width. Some non-latin characters (japanese) might have 
2490:             * glyph widths that are an _integer multiple_ of the latin glyphs. Thus
2491:             * cellular (grid based) text widget like this termulator can still place
2492:             * all characters nicely. There is a 'C' function wcwidth() which
2493:             * ... determines the number of _column_ positions ... and CDE's DtTrem
2494:             * ultimately depends on it to place things. (See also Tuthill & Smallberg,
2495:             * "Creating worldwide software" PrenticeHall 2nd ed. p98)
2496:             *
2497:             * Unfortunately the fonts used by Java, even the "monospaced" fonts, do
2498:             * not abide by the above convention. I measured a 10pt ja locale latin
2499:             * character at 7 pixels wide and a japanese character at 12 pixels wide,
2500:             * instead of 14. A similar problem existed with respect to the "unprintbale"
2501:             * placeholder square. Until Java 1.4 it used to be 9 or 10 pixels wide!
2502:             * The square is fixed, but I"m not sure the above will be anytime soon.
2503:             *
2504:             * What this means is that Graphics.drawString() when given a mix and match
2505:             * of latin and japanese characters will not place them right. Selection
2506:             * doesn't work etc. 
2507:             *
2508:             * Nor does Java provide anything resembling wcwidth() so we're rolling
2509:             * our own here. That's done in Line.width().
2510:             * 
2511:             * So one approach would be to place each character individually, but it's
2512:             * rather slow. Fortunately Java provides a GlyphVector class that allows
2513:             * us to tweak the positions of the glyphs. The timing I"ve gotten are
2514:             *		50	for one drawChars() per charactr. (SLOWER below)
2515:             *		15	using the GlyphVector technique
2516:             *		8	using plain drawChars
2517:             * Unfortunately GlyphVector's interface leaves a bit to be desired.
2518:             * - It does not take a (char [], offset, length) triple and depends 
2519:             *   on the length of the char array passed in. Since our Line char arrays
2520:             *   have some slop in them we can't pass them directly. Hence the 
2521:             *	 "new char[]" and the "System.arraycopy".
2522:             * - The interface for getting and setting positions is also a bit
2523:             *	 awkward as you may notice from massage_glyphs().
2524:             *
2525:             * We SHOULD fall back on plain drawChars() if the host charset is an
2526:             * 8 bit encoding like ASCII or ISO 8859. This encoding is available
2527:             * via System.getProperty("file.encoding") but there are so many aliases
2528:             * for each that I"m wary of hardcoding tests. See
2529:             * http://www.iana.org/assignments/character-sets 
2530:             * Java 1.4 has class Charset that helps with the aliases but we can't
2531:             * yet lock into 1.4.
2532:             */
2533:
2534:            private void myDrawChars(Graphics g, char buf[], Line l, int start,
2535:                    int howmany, int xoff, int baseline) {
2536:                if (metrics.isMultiCell()) {
2537:                    // slow way
2538:                    // This looks expensive but it is in fact a whole lot faster
2539:                    // than issuing a g.drawChars() _per_ character
2540:
2541:                    Graphics2D g2 = (Graphics2D) g;
2542:                    FontRenderContext frc = g2.getFontRenderContext();
2543:                    // Gaaah, why doesn't createGlyphVector() take a (char[],offset,len)
2544:                    // triple?
2545:                    char[] tmp = new char[howmany];
2546:                    System.arraycopy(buf, start, tmp, 0, howmany);
2547:                    GlyphVector gv = getFont().createGlyphVector(frc, tmp);
2548:                    massage_glyphs(gv, start, howmany, l);
2549:                    g2.drawGlyphVector(gv, xoff, baseline);
2550:                } else {
2551:                    // fast way
2552:                    g.drawChars(buf, start, howmany, xoff, baseline);
2553:                }
2554:            }
2555:
2556:            /*
2557:             * Render one line
2558:             * Draw the line on this brow (buffer row 0-origin)
2559:             */
2560:
2561:            private void paint_line_new(Graphics g, Line l, int brow, int xoff,
2562:                    int yoff, int baseline, Extent selx) {
2563:
2564:                int length = l.length();
2565:                if (length == 0)
2566:                    return;
2567:
2568:                int lastcol;
2569:                int firstcol;
2570:
2571:                if (metrics.isMultiCell()) {
2572:
2573:                    // Figure what buffer column is the first visible one (moral
2574:                    // equivalent of st.firsty)
2575:
2576:                    // SHOULD replace with something that does cellToBuf/bufToCell
2577:                    // all at once. There are a couple of other occurances of this
2578:                    // pattern.
2579:
2580:                    firstcol = l.cellToBuf(metrics, st.firsty);
2581:                    int inverse_firstcol = l.bufToCell(metrics, firstcol);
2582:                    int delta = st.firsty - inverse_firstcol;
2583:                    if (delta > 0) {
2584:                        /* This is what to do if we want to draw the right half of the
2585:                         * glyph. However the left half of it will end up in the glyph
2586:                         * gutter and to compensate for thet we'll need to tweak the
2587:                         * clip region. For now taking the easy way out>
2588:
2589:                        int pdelta = delta * metrics.width;	// pixel delta
2590:                        xoff -= pdelta;
2591:
2592:                         */
2593:
2594:                        firstcol++;
2595:                        int pdelta = delta * metrics.width; // pixel delta
2596:                        xoff += pdelta;
2597:                    }
2598:
2599:                    lastcol = l.cellToBuf(metrics, st.firsty
2600:                            + buf.visibleCols() - 1);
2601:
2602:                    /* DEBUG
2603:                    System.out.print
2604:                    ("firstcol = " + firstcol + " for firsty " + st.firsty); // NOI18N
2605:                    System.out.print
2606:                    (" delta = " + delta); // NOI18N
2607:                    System.out.println
2608:                    (" lastcol = " + lastcol +	// NOI18N
2609:                     " for visibleCols " + buf.visibleCols()); // NOI18N
2610:                     */
2611:
2612:                } else {
2613:                    lastcol = st.firsty + buf.visibleCols() - 1;
2614:                    firstcol = st.firsty;
2615:                }
2616:
2617:                lastcol = Math.min(lastcol, length - 1);
2618:                if (firstcol > lastcol)
2619:                    return;
2620:                int howmany = lastcol - firstcol + 1;
2621:
2622:                // 'length' is not used from here on down
2623:
2624:                char buf[] = l.charArray();
2625:
2626:                if (!l.hasAttributes()) {
2627:
2628:                    if (debugWrap()) {
2629:                        if (l.isWrapped() && l.isAboutToWrap())
2630:                            g.setColor(Color.red); // not a good state to be in
2631:                        else if (l.isAboutToWrap())
2632:                            g.setColor(Color.orange);
2633:                        else if (l.isWrapped())
2634:                            g.setColor(Color.magenta);
2635:                    }
2636:
2637:                    myDrawChars(g, buf, l, firstcol, howmany, xoff, baseline);
2638:
2639:                    return;
2640:                }
2641:
2642:                int attrs[] = l.attrArray();
2643:
2644:                // find the extent of the selection on this line
2645:                int sbegin = -1;
2646:                int send = -1;
2647:                if (check_selection && selx != null) {
2648:                    int arow = firsta + brow;
2649:                    Coord b = selx.begin;
2650:                    Coord e = selx.end;
2651:                    if (b.row <= arow && e.row >= arow) {
2652:                        if (b.row == e.row) {
2653:                            sbegin = b.col;
2654:                            send = e.col;
2655:                        } else if (arow == b.row) {
2656:                            sbegin = b.col;
2657:                            send = totcols;
2658:                        } else if (arow == e.row) {
2659:                            sbegin = 0;
2660:                            send = e.col;
2661:                        } else {
2662:                            sbegin = 0;
2663:                            send = totcols;
2664:                        }
2665:                    }
2666:                }
2667:
2668:                // iterate through runs
2669:
2670:                int rbegin = firstcol;
2671:                int rend = rbegin;
2672:
2673:                while (true) {
2674:
2675:                    // find a "run"
2676:                    // A run, as in run-length-encoding, is a set of characters with 
2677:                    // the same attributes.
2678:
2679:                    int attr = attrs[rbegin];
2680:                    rend = rbegin + 1;
2681:                    while (rend <= lastcol) {
2682:                        if (attrs[rend] != attr)
2683:                            break;
2684:                        rend++;
2685:                    }
2686:                    rend--;
2687:
2688:                    // render the run 
2689:                    // need to do this with awareness of the selection
2690:                    // parts that fall under the selection are rendered with an
2691:                    // alternative attribute set.
2692:
2693:                    int alt_attr = (attr & ~(Attr.BGCOLOR | Attr.FGCOLOR
2694:                            | Attr.REVERSE | Attr.ACTIVE));
2695:                    if (sbegin == -1 || send < rbegin || sbegin > rend) {
2696:                        // run is not in selection
2697:                        do_run(g, yoff, xoff, baseline, brow, buf, l, attr,
2698:                                rbegin, rend);
2699:
2700:                    } else if (sbegin <= rbegin && send >= rend) {
2701:                        // run entirely in selection
2702:                        /* DEBUG
2703:                        System.out.println("run entirely in selection");	// NOI18N
2704:                         */
2705:                        do_run(g, yoff, xoff, baseline, brow, buf, l, alt_attr,
2706:                                rbegin, rend);
2707:
2708:                    } else if (sbegin > rbegin && send < rend) {
2709:                        // selection fully within run
2710:                        // split into three parts
2711:                        /* DEBUG
2712:                        System.out.println("run selection fully within run");	// NOI18N
2713:                         */
2714:                        do_run(g, yoff, xoff, baseline, brow, buf, l, attr,
2715:                                rbegin, sbegin - 1);
2716:                        do_run(g, yoff, xoff, baseline, brow, buf, l, alt_attr,
2717:                                sbegin, send);
2718:                        do_run(g, yoff, xoff, baseline, brow, buf, l, attr,
2719:                                send + 1, rend);
2720:
2721:                    } else if (sbegin <= rbegin) {
2722:                        // selection covers left portion of run
2723:                        /* DEBUG
2724:                        System.out.println("selection covers left portion of run");	// NOI18N
2725:                         */
2726:                        // split into two parts
2727:                        do_run(g, yoff, xoff, baseline, brow, buf, l, alt_attr,
2728:                                rbegin, send);
2729:                        do_run(g, yoff, xoff, baseline, brow, buf, l, attr,
2730:                                send + 1, rend);
2731:
2732:                    } else if (send >= rend) {
2733:                        // selection covers right portion of run
2734:                        // split into two parts
2735:                        /* DEBUG
2736:                        System.out.println("selection covers right portion of run");	// NOI18N
2737:                         */
2738:                        do_run(g, yoff, xoff, baseline, brow, buf, l, attr,
2739:                                rbegin, sbegin - 1);
2740:                        do_run(g, yoff, xoff, baseline, brow, buf, l, alt_attr,
2741:                                sbegin, rend);
2742:
2743:                    } else {
2744:                        /* DEBUG
2745:                        System.out.println("Odd run/selection overlap");	// NOI18N
2746:                         */
2747:                    }
2748:
2749:                    if (rend + 1 >= lastcol)
2750:                        break;
2751:
2752:                    // shift
2753:                    rbegin = rend + 1;
2754:                }
2755:            }
2756:
2757:            /* OLD NPE-x synchronized */void do_paint(Graphics g) {
2758:                ckEventDispatchThread();
2759:
2760:                /*
2761:                 * Render the buffer unto the screen
2762:                 * SHOULD try theses:
2763:                 *	- use drawChars?
2764:                 *	- make passes first the glyphs and BG, then chars, then cursor
2765:                 *	- use drawString(AttributedCharacterIterator iterator, ...)
2766:                 *	- precompute any metrics related stuff
2767:                 */
2768:                if (st.firstx == -1) {
2769:                    /* DEBUG
2770:                    System.out.println("Term.paint() no lines"); // NOI18N
2771:                     */
2772:                    return;
2773:                }
2774:
2775:                /* DEBUG
2776:                long paint_start_time = System.currentTimeMillis();
2777:                 */
2778:
2779:                // If Screen is opaque it seems that there is a bug in Swing where 
2780:                // the Graphics that we get here ends up with fonts other than what
2781:                // we assigned to Term. So we make doubly sure here.
2782:                g.setFont(getFont());
2783:
2784:                n_paint++;
2785:
2786:                if (reverse_video) {
2787:                    actual_foreground = getBackground();
2788:                    actual_background = getForeground();
2789:                } else {
2790:                    actual_foreground = getForeground();
2791:                    actual_background = getBackground();
2792:                }
2793:
2794:                // clear the screen
2795:                g.setColor(actual_background);
2796:                g.fillRect(0, 0, screen.getSize().width,
2797:                        screen.getSize().height);
2798:
2799:                // draw any BG stripes 
2800:                // do this before the selection
2801:
2802:                int xoff = debug_gutter_width + glyph_gutter_width;
2803:
2804:                int lx = st.firstx;
2805:
2806:                for (int vrow = 0; vrow < st.rows; vrow++) {
2807:                    Line l = buf.lineAt(lx);
2808:                    if (l == null)
2809:                        break; // don't make a big fuss the loop below will
2810:
2811:                    int yoff = metrics.height * vrow;
2812:
2813:                    Color background = rendition_to_color(l.glyph_rendition);
2814:                    if (background != null) {
2815:                        int rect_height = metrics.height - metrics.leading;
2816:                        g.setColor(background);
2817:                        g.fillRect(xoff, yoff, screen.getWidth(), rect_height);
2818:                    }
2819:
2820:                    lx++;
2821:                }
2822:
2823:                if (!selection_xor) {
2824:                    // The screen is clear, draw any selections
2825:                    //
2826:                    // If most of the lines are w/o attributes then the text just gets
2827:                    // draw over this. Lines that are attributed end up doing some
2828:                    // redundant work repainting.
2829:
2830:                    sel.paint(g);
2831:                }
2832:
2833:                g.setColor(actual_foreground);
2834:
2835:                Extent selx = sel.getExtent();
2836:                check_selection = (selx != null && !selection_xor);
2837:                totcols = buf.totalCols();
2838:
2839:                /* DEBUG
2840:                System.out.println("=========================================="); // NOI18N
2841:                 */
2842:                lx = st.firstx;
2843:
2844:                for (int vrow = 0; vrow < st.rows; vrow++) {
2845:                    Line l = buf.lineAt(lx);
2846:                    if (l == null) {
2847:                        /* DEBUG
2848:                        System.out.println("vrow " + vrow + "  lx " + lx); // NOI18N
2849:                         */
2850:                        printStats(null);
2851:                        break;
2852:                    }
2853:
2854:                    xoff = 0;
2855:                    int yoff = metrics.height * vrow;
2856:                    int baseline = yoff + metrics.ascent;
2857:
2858:                    if (debug_gutter_width > 0) {
2859:                        String buf = "" + (firsta + st.firstx + vrow); // NOI18N
2860:                        g.drawString(buf, xoff, baseline);
2861:                    }
2862:                    xoff += debug_gutter_width;
2863:
2864:                    // draw any glyphs that we might have
2865:                    if (glyph_gutter_width > 0) {
2866:                        Image image = glyph_images[l.glyph_glyph];
2867:                        if (image != null) {
2868:                            // xy passed to drawImage() is the top-left of the image
2869:                            int gyoff = yoff;
2870:                            g.drawImage(image, xoff, gyoff, Color.white, null);
2871:                        }
2872:                    }
2873:                    xoff += glyph_gutter_width;
2874:
2875:                    paint_line_new(g, l, vrow + st.firstx, xoff, yoff,
2876:                            baseline, selx);
2877:
2878:                    // restore 'g' to something reasonable
2879:                    g.setColor(actual_foreground);
2880:
2881:                    lx++;
2882:                }
2883:
2884:                paint_cursor(g);
2885:
2886:                if (selection_xor)
2887:                    sel.paint(g);
2888:
2889:                if (debugMargins())
2890:                    paint_margins(g);
2891:
2892:                /* DEBUG
2893:                long paint_stop_time = System.currentTimeMillis();
2894:                long paint_time = paint_stop_time - paint_start_time;
2895:                System.out.println("paint_time = " + paint_time);	// NOI18N
2896:                 */
2897:            }
2898:
2899:            private void paint_margins(Graphics g) {
2900:            }
2901:
2902:            private void paint_cursor(Graphics g) {
2903:                if (!cursor_visible)
2904:                    return;
2905:
2906:                // figure what row the cursor is on
2907:                if (st.cursor.row == -1) {
2908:                    // System.out.println("Term.paint_cursor: " + // NOI18N
2909:                    //		       "cursor doesn't point to any line");	// NOI18N
2910:                    return;
2911:                }
2912:
2913:                int cursor_row = st.cursor.row - st.firstx;
2914:                if (cursor_row >= st.rows) {
2915:                    return; // cursor not visible
2916:                }
2917:
2918:                int cursor_col = st.cursor.col - st.firsty;
2919:                if (cursor_col >= buf.visibleCols()) {
2920:                    return; // cursor not visible
2921:                } else if (cursor_col < 0) {
2922:                    return; // cursor not visible
2923:                }
2924:
2925:                g.setXORMode(actual_background);
2926:                int rect_x = cursor_col * metrics.width + glyph_gutter_width
2927:                        + debug_gutter_width;
2928:                int rect_y = cursor_row * metrics.height;
2929:                // we _don't_ make cursor as wide as underlying character
2930:                int rect_width = metrics.width;
2931:                int rect_height = metrics.height - metrics.leading;
2932:                if (has_focus)
2933:                    g.fillRect(rect_x, rect_y, rect_width, rect_height);
2934:                else
2935:                    g.drawRect(rect_x, rect_y, rect_width, rect_height);
2936:            }
2937:
2938:            private final boolean do_margins = true;
2939:
2940:            private boolean possiblyScrollDown() {
2941:                /*
2942:                 * If cursor has moved below the scrollable region scroll down.
2943:                 * Buffer manipulation is partly done here or at the callsite if 
2944:                 * 'true' is returned.
2945:                 */
2946:
2947:                if (!do_margins) {
2948:                    if (st.cursor.row >= st.firstx + st.rows) {
2949:                        // scroll down
2950:                        st.firstx++;
2951:                        return true;
2952:                    }
2953:                    return false;
2954:                } else {
2955:                    // new margin based scrolling
2956:                    if (st.cursor.row >= st.firstx + botMargin() + 1) {
2957:                        // scroll down
2958:                        if (topMargin() == 0) {
2959:                            if (scroll_on_output || cursor_was_visible()
2960:                                    && track_cursor)
2961:                                st.firstx++;
2962:                            return true;
2963:                        } else {
2964:                            st.cursor.row = st.firstx + botMargin();
2965:                            Line l = buf.moveLineFromTo(
2966:                                    st.firstx + topMargin(), st.cursor.row);
2967:                            l.reset();
2968:                            return false;
2969:                        }
2970:                    }
2971:                    return false;
2972:                }
2973:            }
2974:
2975:            /**
2976:             * Send a character to the terminal screen.
2977:             */
2978:            public void putChar(char c) {
2979:                dce_end.putChar(c);
2980:            }
2981:
2982:            /**
2983:             * Send several characters to the terminal screen.
2984:             * <br>
2985:             * While 'buf' will have a size it may not be fully filled, hence
2986:             * the explicit 'nchar'.
2987:             */
2988:            public void putChars(char buf[], int offset, int count) {
2989:                dce_end.putChars(buf, offset, count);
2990:            }
2991:
2992:            /**
2993:             * Force a repaint.
2994:             * <p>
2995:             * Normally a putChar() or putChars() will call repaint, unless ...
2996:             * setRepaintEnabled() has been called with false. This function 
2997:             * allows for some flexibility wrt to buffering and flushing:
2998:             * <pre>
2999:             *		term.setRefreshEnabled(false);
3000:             *		for (cx = 0; cx < buf.length; cx++) {
3001:             *			term.putChar(c);
3002:             *			if (c % 10 == 0)
3003:             *				term.flush();
3004:             *		}
3005:             *		term.setRefreshEnabled(true);
3006:             * </pre>
3007:             */
3008:            public void flush() {
3009:                dce_end.flush();
3010:            }
3011:
3012:            /**
3013:             * Send a message back to DCE.
3014:             * <p>
3015:             * Perhaps SHOULD lock out sendChar() so user input doesn't interfere.
3016:             */
3017:            private void reply(String str) {
3018:                /* DEBUG
3019:                System.out.println("replying " + str);	// NOI18N
3020:                 */
3021:                for (int sx = 0; sx < str.length(); sx++)
3022:                    sendChar(str.charAt(sx));
3023:            }
3024:
3025:            /*
3026:             * Term used to implement Ops but then that forces Ops to be public
3027:             * which we don't want. So we do it this way.
3028:             */
3029:            private class OpsImpl implements  Ops {
3030:
3031:                public void op_pause() {
3032:
3033:                    // This yields slighlty more reasonable results.
3034:                    Thread.currentThread().yield();
3035:
3036:                    /*
3037:
3038:                    DtTerm sends ~240 NUL's between reverse video switching to
3039:                    simulate a flash. The resolution of Thread.sleep() isn't
3040:                    really one millisecond so the flash ends up being too long.
3041:
3042:                    try {
3043:                    Thread.currentThread().sleep(0, 500);
3044:                    } catch (InterruptedException x) {
3045:                    ;
3046:                    } 
3047:                     */
3048:                }
3049:
3050:                public void op_char(char c) {
3051:                    if (debugOps())
3052:                        System.out.println("op_char('" + c + "') = " + (int) c); // NOI18N
3053:
3054:                    // generic character printing
3055:                    Line l = cursor_line();
3056:
3057:                    int insertion_col = l.cellToBuf(metrics, st.cursor.col);
3058:                    if (debugOps()) {
3059:                        System.out.println("op_char(): st.cursor.col "
3060:                                + st.cursor.col + // NOI18N
3061:                                " insertion_col " + insertion_col); // NOI18N
3062:                    }
3063:                    if (!st.overstrike) {
3064:                        // This just shifts stuff the actual character gets put in below.
3065:                        l.insertCharAt(Term.this , ' ', insertion_col, st.attr);
3066:                    }
3067:
3068:                    int cwidth = metrics.wcwidth(c);
3069:                    if (l.isAboutToWrap()
3070:                            || (cwidth > 1
3071:                                    && st.cursor.col + cwidth > buf
3072:                                            .visibleCols() && !horizontally_scrollable)) {
3073:
3074:                        // 'wrap' the line
3075:                        if (debugOps())
3076:                            System.out.println("\twrapping it"); // NOI18N
3077:                        l.setWrapped(true);
3078:                        l.setAboutToWrap(false);
3079:                        op_line_feed();
3080:                        op_carriage_return();
3081:                        l = cursor_line();
3082:                        insertion_col = 0;
3083:                        // Fall thru
3084:                    }
3085:
3086:                    l.setCharAt(Term.this , c, insertion_col, st.attr); // overstrike
3087:                    st.cursor.col += cwidth;
3088:
3089:                    if (st.cursor.col >= buf.visibleCols()
3090:                            && !horizontally_scrollable) {
3091:                        if (debugOps())
3092:                            System.out.println("\tabout to wrap"); // NOI18N
3093:                        l.setAboutToWrap(true);
3094:                        st.cursor.col -= cwidth;
3095:                    }
3096:                }
3097:
3098:                public void op_attr(int attr) {
3099:                    if (debugOps())
3100:                        System.out.println("op_attr(" + attr + ")"); // NOI18N
3101:                    setAttribute(attr);
3102:                }
3103:
3104:                public void op_bel() {
3105:                    // ring the bell
3106:                    // SHOULD implement
3107:                }
3108:
3109:                public void op_back_space() {
3110:                    // back-space
3111:                    if (debugOps())
3112:                        System.out.println("op_back_space"); // NOI18N
3113:
3114:                    if (st.cursor.col > 0) {
3115:                        if (!cursor_line().isAboutToWrap()) {
3116:                            st.cursor.col--;
3117:                        }
3118:                        cursor_line().setAboutToWrap(false);
3119:
3120:                        // If we' backed up to column 0, maybe we need to consider
3121:                        // whether the previous line was wrapped. Older xterms aren't
3122:                        // this clever, newer ones (Solaris 8+?) are.
3123:
3124:                        if (st.cursor.col == 0) {
3125:                            if (st.cursor.row > 0) {
3126:                                // maybe we had wrapped on the previous line?
3127:                                if (debugOps())
3128:                                    System.out
3129:                                            .println("\tchecking if prev is wrapped"); // NOI18N
3130:                                Line prev = buf.lineAt(st.cursor.row - 1);
3131:                                if (prev.isWrapped()) {
3132:                                    if (debugOps())
3133:                                        System.out.println("\tit is"); // NOI18N
3134:                                    st.cursor.row--;
3135:
3136:                                    // The below is done in a roundabout way because BS doesn't
3137:                                    // really reduce length. So, suppose we went to the end with
3138:                                    // latin chars that makes the line 80 long. Then we backspace
3139:                                    // to column 78 and enter one 2-cell japanese character. Now
3140:                                    // the line is conceptually 79 long, but it still remembers
3141:                                    // the 80. So we don't use 'prev.length()' directly.
3142:
3143:                                    // st.cursor.col = prev.bufToCell(metrics, prev.length()-1);
3144:
3145:                                    int last_col = prev.cellToBuf(metrics, buf
3146:                                            .visibleCols() - 1);
3147:                                    st.cursor.col = prev.bufToCell(metrics,
3148:                                            last_col);
3149:
3150:                                    prev.setWrapped(false);
3151:
3152:                                    // The following isn't entirely correct when we backspaced
3153:                                    // over a multi-celled character. SHOULD either note
3154:                                    // what we BS'ed over or note the slop at the end of the line.
3155:                                    prev.setAboutToWrap(true);
3156:                                }
3157:                            }
3158:                        }
3159:                    }
3160:                }
3161:
3162:                public void op_line_feed() {
3163:                    // NL line feed ctrl-J
3164:                    // move cursor down one line and if goes past the screen
3165:                    // add a new line.
3166:                    if (debugOps())
3167:                        System.out.println("op_line_feed"); // NOI18N
3168:                    Line last_line = cursor_line();
3169:                    /* DEBUG
3170:                    if (last_line == null) {
3171:                    Thread.dumpStack();
3172:                    printStats("last_line == null in op_line_feed()");// NOI18N
3173:                    }
3174:                     */
3175:                    st.cursor.row++;
3176:                    if (possiblyScrollDown()) {
3177:                        buf.addLineAt(st.cursor.row);
3178:                        limit_lines();
3179:                        if (debugOps())
3180:                            System.out.println("op_line_feed ADJUSTED"); // NOI18N
3181:                    }
3182:                    // have new line inherit cursorAtEnd
3183:                    boolean atw = last_line.isAboutToWrap();
3184:                    cursor_line().setAboutToWrap(atw);
3185:                    last_line.setAboutToWrap(false);
3186:
3187:                    n_linefeeds++;
3188:
3189:                    // See repaint() for an explanation of this.
3190:                    // repaint(false);
3191:                }
3192:
3193:                public void op_tab() {
3194:                    // TAB/HT
3195:                    // SHOULD do something better with tabs near the end of the line
3196:                    // On the other hand, that's how ANSI terminals are supposed
3197:                    // to behave
3198:
3199:                    if (debugOps())
3200:                        System.out.println("op_tab"); // NOI18N
3201:
3202:                    if (st.cursor.col == buf.visibleCols() - 1
3203:                            && !horizontally_scrollable)
3204:                        return;
3205:
3206:                    Line l = cursor_line();
3207:                    int insert_col = l.cellToBuf(metrics, st.cursor.col);
3208:                    l.setCharAt(Term.this , ' ', insert_col, st.attr);
3209:                    st.cursor.col++;
3210:                    insert_col++;
3211:                    // no need to re-apply cellToBuf to cursor since we're only adding 1-wide ' '
3212:                    while ((st.cursor.col < buf.visibleCols() - 1 || horizontally_scrollable)
3213:                            && (st.cursor.col % tab_size) != 0) {
3214:                        cursor_line().setCharAt(Term.this , ' ', insert_col,
3215:                                st.attr);
3216:                        st.cursor.col++;
3217:                        insert_col++;
3218:                    }
3219:                }
3220:
3221:                public void op_carriage_return() {
3222:                    if (debugOps())
3223:                        System.out.println("op_carriage_return"); // NOI18N
3224:                    st.cursor.col = 0;
3225:                    cursor_line().setAboutToWrap(false);
3226:                }
3227:
3228:                public void op_al(int count) {
3229:                    // add new blank line
3230:                    if (debugOps())
3231:                        System.out.println("op_al(" + count + ")"); // NOI18N
3232:
3233:                    Line l;
3234:                    while (count-- > 0) {
3235:                        boolean old_atw = cursor_line().setAboutToWrap(false);
3236:
3237:                        // reverse of op_dl()
3238:                        // Rotate a line from bottom to top
3239:                        if (!do_margins) {
3240:                            l = buf.moveLineFromTo(buf.nlines - 1,
3241:                                    st.cursor.row);
3242:                        } else {
3243:                            l = buf.moveLineFromTo(st.firstx + botMargin(),
3244:                                    st.cursor.row);
3245:                        }
3246:                        l.reset();
3247:
3248:                        cursor_line().setAboutToWrap(old_atw);
3249:                    }
3250:
3251:                    switch (sel.intersection(st.cursor.row - 1)) {
3252:                    case Sel.INT_NONE:
3253:                    case Sel.INT_ABOVE:
3254:                    case Sel.INT_ON:
3255:                        // nothing to do
3256:                        break;
3257:                    case Sel.INT_STRADDLES:
3258:                        sel.cancel(true); // DtTerm behaviour
3259:                        break;
3260:                    case Sel.INT_BELOW:
3261:                        sel.adjust(firsta, +1, firsta + buf.nlines);
3262:                        break;
3263:                    }
3264:                }
3265:
3266:                public void op_bc(int count) {
3267:                    // back cursor/column
3268:                    if (debugOps())
3269:                        System.out.println("op_bc(" + count + ")"); // NOI18N
3270:
3271:                    while (count-- > 0) {
3272:                        if (st.cursor.col <= 0)
3273:                            return;
3274:                        st.cursor.col--;
3275:                    }
3276:                    cursor_line().setAboutToWrap(false);
3277:                }
3278:
3279:                public void op_cm(int row, int col) {
3280:                    // cursor motion row and col come in as 1-origin)
3281:                    if (debugOps())
3282:                        System.out.println("op_cm(row " + row + ", col " + col
3283:                                + ")"); // NOI18N
3284:
3285:                    // "xemacs -nw" seems to overflow and underflow often.
3286:
3287:                    // 0 is allowed
3288:                    if (row == 0)
3289:                        row = 1;
3290:                    if (col == 0)
3291:                        col = 1;
3292:
3293:                    // deal with overflow
3294:                    if (row > st.rows)
3295:                        row = st.rows;
3296:                    if (col > buf.visibleCols())
3297:                        col = buf.visibleCols();
3298:
3299:                    cursor_line().setAboutToWrap(false);
3300:                    st.cursor.row = beginx() + row - 1;
3301:                    st.cursor.col = col - 1;
3302:                    // Maybe SHOULD setAboutToWrap(true) if on last column?
3303:                }
3304:
3305:                public void op_cl() {
3306:                    // clear screen and home cursor
3307:                    if (debugOps())
3308:                        System.out.println("op_cl"); // NOI18N
3309:                    cursor_line().setAboutToWrap(false);
3310:                    clear();
3311:                    st.cursor.row = beginx();
3312:                    st.cursor.col = 0;
3313:                }
3314:
3315:                public void op_ce() {
3316:                    // clear to end of line
3317:                    if (debugOps())
3318:                        System.out.println("op_ce"); // NOI18N
3319:
3320:                    Line l = cursor_line();
3321:                    l.clearToEndFrom(Term.this , l.cellToBuf(metrics,
3322:                            st.cursor.col));
3323:
3324:                    switch (sel.intersection(st.cursor.row)) {
3325:                    case Sel.INT_NONE:
3326:                    case Sel.INT_ABOVE:
3327:                    case Sel.INT_BELOW:
3328:                        // nothing to do
3329:                        break;
3330:                    case Sel.INT_ON:
3331:                    case Sel.INT_STRADDLES:
3332:                        sel.cancel(true); // DtTerm behaviour
3333:                        break;
3334:                    }
3335:                }
3336:
3337:                public void op_cd() {
3338:                    // clear to end of screen
3339:                    if (debugOps())
3340:                        System.out.println("op_cd -- clear to end of screen"); // NOI18N
3341:
3342:                    for (int lx = st.cursor.row; lx < beginx() + st.rows; lx++) {
3343:                        Line l = buf.lineAt(lx);
3344:                        l.reset();
3345:                    }
3346:
3347:                    switch (sel.intersection(st.cursor.row)) {
3348:                    case Sel.INT_NONE:
3349:                    case Sel.INT_ABOVE:
3350:                        // nothing to do
3351:                        break;
3352:                    case Sel.INT_BELOW:
3353:                    case Sel.INT_ON:
3354:                    case Sel.INT_STRADDLES:
3355:                        sel.cancel(true); // DtTerm behaviour
3356:                        break;
3357:                    }
3358:                }
3359:
3360:                public void op_dc(int count) {
3361:                    // delete character
3362:                    if (debugOps())
3363:                        System.out.println("op_dc(" + count + ")"); // NOI18N
3364:                    if (count == 0)
3365:                        count = 1;
3366:                    Line l = cursor_line();
3367:                    while (count-- > 0)
3368:                        l.deleteCharAt(l.cellToBuf(metrics, st.cursor.col));
3369:                }
3370:
3371:                public void op_dl(int count) {
3372:                    // delete line
3373:                    // and scroll everything under it up
3374:                    if (debugOps())
3375:                        System.out.println("op_dl(" + count + ")"); // NOI18N
3376:
3377:                    Line l;
3378:                    while (count-- > 0) {
3379:                        boolean old_atw = cursor_line().setAboutToWrap(false);
3380:
3381:                        // reverse of op_al()
3382:                        // Rotate a line from top to bottom
3383:                        if (!do_margins) {
3384:                            l = buf.moveLineFromTo(st.cursor.row, (beginx()
3385:                                    + st.rows - 1) - 1);
3386:                        } else {
3387:                            l = buf.moveLineFromTo(st.cursor.row,
3388:                                    (beginx() + botMargin()) - 1);
3389:                        }
3390:                        l.reset();
3391:
3392:                        cursor_line().setAboutToWrap(old_atw);
3393:                    }
3394:
3395:                    switch (sel.intersection(st.cursor.row)) {
3396:                    case Sel.INT_NONE:
3397:                    case Sel.INT_ABOVE:
3398:                        // nothing to do
3399:                        break;
3400:                    case Sel.INT_ON:
3401:                    case Sel.INT_STRADDLES:
3402:                        sel.cancel(true); // DtTerm behaviour
3403:                        break;
3404:                    case Sel.INT_BELOW:
3405:                        sel.adjust(firsta, -1, firsta + buf.nlines);
3406:                        break;
3407:                    }
3408:                }
3409:
3410:                public void op_do(int count) {
3411:                    // down count lines
3412:                    // SHOULD add a mode: {scroll, warp, stay} for cases where
3413:                    // cursor is on the bottom line.
3414:
3415:                    if (debugOps())
3416:                        System.out.println("op_do(" + count + ") -- down"); // NOI18N
3417:
3418:                    boolean old_atw = cursor_line().setAboutToWrap(false);
3419:
3420:                    while (count-- > 0) {
3421:                        st.cursor.row++;
3422:                        if (st.cursor.row >= buf.nlines) {
3423:
3424:                            // equivalent of op_newline:
3425:                            if (possiblyScrollDown()) {
3426:                                buf.addLineAt(st.cursor.row);
3427:                                limit_lines();
3428:                                if (debugOps())
3429:                                    System.out.println("op_do ADJUSTED"); // NOI18N
3430:                            }
3431:                        }
3432:                    }
3433:                    cursor_line().setAboutToWrap(old_atw);
3434:                }
3435:
3436:                public void op_ho() {
3437:                    // cursor home (upper left of the screen)
3438:                    if (debugOps())
3439:                        System.out.println("op_ho -- home"); // NOI18N
3440:                    cursor_line().setAboutToWrap(false);
3441:                    st.cursor.row = beginx();
3442:                    st.cursor.col = 0;
3443:                }
3444:
3445:                public void op_ic(int count) {
3446:                    // insert character
3447:                    if (debugOps())
3448:                        System.out.println("op_ic(" + count + ")"); // NOI18N
3449:
3450:                    Line l = cursor_line();
3451:                    int insertion_col = l.cellToBuf(metrics, st.cursor.col);
3452:                    while (count-- > 0) {
3453:                        l.insertCharAt(Term.this , ' ', insertion_col, st.attr);
3454:                    }
3455:                    // SHOULD worry about line wrapping
3456:                }
3457:
3458:                public void op_nd(int count) {
3459:                    // cursor right (non-destructive space)
3460:                    if (debugOps())
3461:                        System.out.println("op_nd(" + count + ")"); // NOI18N
3462:
3463:                    int vc = st.cursor.col;
3464:                    while (count-- > 0) {
3465:                        vc++;
3466:                        if (vc >= buf.visibleCols()) {
3467:                            if (debugOps())
3468:                                System.out.println("\tbailing out at count "
3469:                                        + count); // NOI18N
3470:                            vc--;
3471:                            break;
3472:                        }
3473:                    }
3474:                    st.cursor.col = vc;
3475:                }
3476:
3477:                public void op_up(int count) {
3478:                    // cursor up
3479:                    if (debugOps())
3480:                        System.out.println("op_up(" + count + ")"); // NOI18N
3481:
3482:                    boolean old_atw = cursor_line().setAboutToWrap(false);
3483:                    Line l;
3484:                    while (count-- > 0) {
3485:                        st.cursor.row--;
3486:                        if (st.cursor.row < st.firstx) {
3487:                            st.cursor.row = st.firstx;
3488:                            // scroll down, Rotate a line from bottom to top
3489:                            if (!do_margins) {
3490:                                l = buf.moveLineFromTo(buf.nlines - 1,
3491:                                        st.cursor.row);
3492:                            } else {
3493:                                l = buf.moveLineFromTo(st.firstx + botMargin(),
3494:                                        st.cursor.row);
3495:                            }
3496:                            l.reset();
3497:                            // SHOULD note and do something about the selection?
3498:                        }
3499:                    }
3500:                    cursor_line().setAboutToWrap(old_atw);
3501:                }
3502:
3503:                public void op_sc() {
3504:                    // save cursor position
3505:                    if (debugOps())
3506:                        System.out.println("op_sc()"); // NOI18N
3507:                    st.saveCursor();
3508:                    // SHOULD defeat repaint?
3509:                }
3510:
3511:                public void op_rc() {
3512:                    // restore saved cursor position
3513:                    if (debugOps())
3514:                        System.out.println("op_rc()"); // NOI18N
3515:                    st.restoreCursor();
3516:                }
3517:
3518:                public void op_glyph(int glyph, int rendition) {
3519:                    if (debugOps()) {
3520:                        System.out.println("op_glyph(glyph " + glyph + // NOI18N
3521:                                ", rendition " + rendition + ")"); // NOI18N
3522:                    }
3523:                    setGlyph(glyph, rendition);
3524:                }
3525:
3526:                public void op_reverse(boolean reverse_video) {
3527:                    setReverseVideo(reverse_video);
3528:                }
3529:
3530:                public void op_cursor_visible(boolean visible) {
3531:                    setCursorVisible(visible);
3532:                }
3533:
3534:                public void op_margin(int from, int to) {
3535:                    if (debugOps()) {
3536:                        System.out.println("op_margin(" + from + ", " + // NOI18N
3537:                                to + ")"); // NOI18N
3538:                    }
3539:
3540:                    if (from < 0)
3541:                        top_margin = 0;
3542:                    else if (from > st.rows)
3543:                        top_margin = st.rows;
3544:                    else
3545:                        top_margin = from;
3546:
3547:                    if (to < 0)
3548:                        bot_margin = 0;
3549:                    else if (to > st.rows)
3550:                        bot_margin = st.rows;
3551:                    else
3552:                        bot_margin = to;
3553:
3554:                    if (top_margin > bot_margin) {
3555:                        int tmp = top_margin;
3556:                        top_margin = bot_margin;
3557:                        bot_margin = tmp;
3558:                    }
3559:                }
3560:
3561:                long last_time = System.currentTimeMillis();
3562:
3563:                public void op_time(boolean repaint) {
3564:                    long time = System.currentTimeMillis();
3565:                    long elapsed = time - last_time;
3566:                    Date d = new Date(time);
3567:                    String date_str = d.toString();
3568:                    String elapsed_str = "" + elapsed / 1000 + "." + elapsed
3569:                            % 1000;// NOI18N
3570:                    String output1 = date_str + " Elapsed (sec): "
3571:                            + elapsed_str;// NOI18N
3572:                    String output2 = "putChar " + n_putchar + // NOI18N
3573:                            "  putChars " + n_putchars + // NOI18N
3574:                            "  linefeeds " + n_linefeeds + // NOI18N
3575:                            "  repaint " + n_repaint + // NOI18N
3576:                            "  paint " + n_paint; // NOI18N
3577:
3578:                    setAttribute(41); // Red Bg
3579:
3580:                    // can't use appendText from within ops.
3581:
3582:                    for (int sx = 0; sx < output1.length(); sx++)
3583:                        op_char(output1.charAt(sx));
3584:                    op_line_feed();
3585:                    op_carriage_return();
3586:                    for (int sx = 0; sx < output2.length(); sx++)
3587:                        op_char(output2.charAt(sx));
3588:
3589:                    setAttribute(0);
3590:
3591:                    last_time = time;
3592:                    n_putchar = 0;
3593:                    n_putchars = 0;
3594:                    n_linefeeds = 0;
3595:                    n_paint = 0;
3596:                    n_repaint = 0;
3597:
3598:                    repaint(true);
3599:                    // TMP setRefreshEnabled(repaint);
3600:                }
3601:
3602:                public int op_get_width() {
3603:                    return horizontally_scrollable ? buf.totalCols() : buf
3604:                            .visibleCols();
3605:                }
3606:
3607:                public int op_get_column() {
3608:                    return st.cursor.col;
3609:                }
3610:
3611:                public void op_soft_reset() {
3612:                    st.overstrike = true;
3613:                    top_margin = 0; // 0 means default (see topMargin())
3614:                    bot_margin = 0;
3615:                    st.attr = 0;
3616:                    repaint(false);
3617:                }
3618:
3619:                public void op_full_reset() {
3620:                    op_soft_reset();
3621:                    op_cl(); // clear screen, home cursor
3622:                    reverse_video = false;
3623:                    repaint(false);
3624:                }
3625:
3626:                public void op_set_mode(int mode) {
3627:                    switch (mode) {
3628:                    case 4: // insert mode
3629:                        st.overstrike = false;
3630:                        break;
3631:                    case 2: // keyboard lock
3632:                    case 12: // local echo off
3633:                    case 20: // newline
3634:                        // Currently unsupported
3635:                        break;
3636:                    }
3637:                }
3638:
3639:                public void op_reset_mode(int mode) {
3640:                    switch (mode) {
3641:                    case 4: // replace mode
3642:                        st.overstrike = true;
3643:                        break;
3644:                    case 2: // keyboard unlock
3645:                    case 12: // local echo on
3646:                    case 20: // newline
3647:                        // Currently unsupported
3648:                        break;
3649:                    }
3650:                }
3651:
3652:                public void op_status_report(int code) {
3653:                    switch (code) {
3654:                    case 5:
3655:                        reply((char) 27 + "[0n"); // NOI18N
3656:                        break;
3657:                    case 6:
3658:                        reply((char) 27 + "[" + // NOI18N
3659:                                (st.cursor.row - st.firstx) + ";" + // NOI18N
3660:                                st.cursor.col + "R"); // NOI18N
3661:                        break;
3662:                    }
3663:                }
3664:            }
3665:
3666:            private void putc_work(char c) {
3667:                interp.processChar(c);
3668:                possiblyHScroll();
3669:                screen.possiblyUpdateCaretText();
3670:            }
3671:
3672:            private void on_char(char c) {
3673:                sendChar(c);
3674:            }
3675:
3676:            private void sendChars(char c[], int offset, int count) {
3677:                dte_end.sendChars(c, offset, count);
3678:            }
3679:
3680:            private void sendChar(char c) {
3681:                dte_end.sendChar(c);
3682:            }
3683:
3684:            /**
3685:             * Adjust vertical scrollbar range
3686:             */
3687:            private void adjust_scrollbar() {
3688:
3689:                // JScrollBar is weird.
3690:                // The visible range is 1 (for value) + extent.
3691:                // So extent has to be set to visible-range - 1:
3692:
3693:                adjust_scrollbar_impl();
3694:
3695:                /* OLD NPE-x
3696:                // It's important that we do this from within the AWT event thread.
3697:
3698:                    if (SwingUtilities.isEventDispatchThread()) {
3699:                        adjust_scrollbar_impl();
3700:                    }
3701:                    else {
3702:                        SwingUtilities.invokeLater(new Runnable() {
3703:                            public void run() {
3704:                                adjust_scrollbar_impl();
3705:                            }
3706:                        });
3707:                    }
3708:                 */
3709:            }
3710:
3711:            private void adjust_scrollbar_impl() {
3712:                if (vscroll_bar != null) {
3713:                    int value = st.firstx;
3714:                    int extent = st.rows - 1;
3715:                    int min = 0;
3716:                    int max;
3717:                    if (buf.nlines <= st.rows)
3718:                        max = st.rows - 1;
3719:                    else
3720:                        max = buf.nlines - 1;
3721:                    vscroll_bar.setValues(value, extent, min, max);
3722:                }
3723:
3724:                if (hscroll_bar != null && horizontally_scrollable) {
3725:                    int value = st.firsty;
3726:                    int extent = buf.visibleCols() - 1;
3727:                    int min = 0;
3728:                    int max;
3729:                    if (buf.totalCols() <= buf.visibleCols())
3730:                        max = buf.visibleCols() - 1;
3731:                    else
3732:                        max = buf.totalCols() - 1;
3733:
3734:                    /* DEBUG
3735:                    System.out.println("HSCROLL " + min + " <= " + value  + 	// NOI18N
3736:                    "[" + extent + "] " + max);	// NOI18N
3737:                     */
3738:
3739:                    hscroll_bar.setValues(value, extent, min, max);
3740:
3741:                    /* DEBUG
3742:                    System.out.println("HSCROLL " + hscroll_bar.getMinimum() +	// NOI18N
3743:                    " <= " + hscroll_bar.getValue()  + 	// NOI18N
3744:                    "[" + hscroll_bar.getModel().getExtent() + "] " + hscroll_bar.getMaximum());	// NOI18N
3745:                     */
3746:                }
3747:            }
3748:
3749:            /**
3750:             * Figure the pixel size of the screen based on various properties.
3751:             */
3752:            private Dimension calculateSize() {
3753:                int dx = buf.visibleCols() * metrics.width + glyph_gutter_width
3754:                        + debug_gutter_width;
3755:                int dy = st.rows * metrics.height;
3756:                Dimension d = new Dimension(dx, dy);
3757:                return d;
3758:            }
3759:
3760:            /**
3761:             * To be called as the result of programmatic changes in properties
3762:             * that affect the size of the screen: font, rows & columns, glyph
3763:             * gutter width etc.
3764:             * Applies the newly calculated size via sizeChanged().
3765:             * 
3766:             * We used to call screen.setSize() which would eventually call
3767:             * sizeChanged() through the notification mechanism. That worked well
3768:             * for row column size changes etc, but not so well for font changes.
3769:             * What would happen is that he cause of size changes would be lost
3770:             * by the time we got to sizeChanged() and for example the screen
3771:             * wouldn't resize as a result of font pt-size changes.
3772:             */
3773:            private void updateScreenSize() {
3774:                /* DEBUG
3775:                System.out.println("updateScreenSize("+buf.cols+", "+st.rows+")"); // NOI18N
3776:                 */
3777:                if (screen != null) {
3778:                    Dimension d = calculateSize();
3779:                    sizeChanged(d.width, d.height);
3780:                }
3781:            }
3782:
3783:            // HACK:
3784:            // Helper variable to remember the original value of rows across
3785:            // various different control flows.
3786:
3787:            private int old_rows = -1;
3788:
3789:            /**
3790:             * Called whenver the screens size is to be changed, either by
3791:             * us via updateScreenSize(), or thru user action and the Screen
3792:             * componentResized() notification.
3793:             *
3794:             * Adjust the state and buffer, commit to the size by setting
3795:             * preferredSize and notify any interested parties.
3796:             */
3797:
3798:            void sizeChanged(int newWidth, int newHeight) {
3799:                /* DEBUG
3800:                System.out.println("sizeChanged(newheight " + newHeight + // NOI18N
3801:                    ", newWidth " + newWidth + ")");
3802:                 */
3803:
3804:                // Do columns first ... they're easy
3805:                int newcols = (newWidth - glyph_gutter_width - debug_gutter_width)
3806:                        / metrics.width;
3807:                buf.setVisibleCols(newcols);
3808:
3809:                if (old_rows == -1) {
3810:                    // st.rows hasn't changed yet, so remember it before changing it.
3811:                    old_rows = st.rows;
3812:                }
3813:
3814:                st.rows = newHeight / metrics.height;
3815:
3816:                // akemr - hack to fix #17807
3817:                if (st.rows < 1)
3818:                    st.rows = 1;
3819:
3820:                /* DEBUG
3821:                System.out.println(">>>>>>> rows from "+old_rows+" to "+st.rows); // NOI18N
3822:                 */
3823:
3824:                int row_delta = st.rows - old_rows; // negative => we shrunk
3825:                old_rows = -1;
3826:
3827:                adjust_lines(row_delta);
3828:
3829:                limit_lines();
3830:
3831:                // Commit to the size
3832:                // 
3833:                // Setting setPreferredSize() is where the commitment is. If we
3834:                // don't do it our layout manager containers won't honor the resizing
3835:                // and snap us back.
3836:
3837:                Dimension new_size = isSizeRounded() ? calculateSize()
3838:                        : new Dimension(newWidth, newHeight);
3839:
3840:                /* DEBUG
3841:                System.out.println("but I want "+new_size.height+" "+new_size.width); // NOI18N
3842:                 */
3843:
3844:                if (false) {
3845:                    // Setting size is a bad idea. the potential for getting into 
3846:                    // a looping tug-of-war with our containers' layout manager
3847:                    // is too high and unpredictable. One nasty example we ran
3848:                    // into was JTabbedPane.
3849:                    screen.setSize(new_size);
3850:
3851:                } else {
3852:                    screen.setPreferredSize(new_size);
3853:
3854:                    // Do we really need these?
3855:                    invalidate();
3856:                    if (getParent() != null)
3857:                        getParent().validate();
3858:                }
3859:
3860:                // Notify any interested parties.
3861:                // Normally we'd inline the code in updateTtySize() here but factoring 
3862:                // it has it's uses as explained in updateTtySize().
3863:                updateTtySize();
3864:            }
3865:
3866:            protected void possibly_repaint(boolean adjust_scrollbar) {
3867:                if (!refresh_enabled)
3868:                    return;
3869:                repaint(adjust_scrollbar);
3870:            }
3871:
3872:            /**
3873:             * Model and or view settings have changed, redraw everything.
3874:             */
3875:            protected void repaint(boolean adjust_scrollbar) {
3876:
3877:                /*
3878:                 * A long discussion on performance and smooth vs jump vs jerky
3879:                 * scrolling ... (note: a lot of this is based on experiments with
3880:                 * Term as a unix terminal emulator application as opposed to
3881:                 * within the context of NetBeans).
3882:                 *
3883:                 * Term spends it's time between collecting and deciphering input
3884:                 * and repainting the screen. Input processing always goes on, but
3885:                 * screen repainitng can be done more or less often to trade off
3886:                 * smoothness of scrolling vs speed.
3887:                 *
3888:                 * At one end is so-called smooth scrolling. This is where the
3889:                 * screen is redrawn on every linefeed. That's a lot of painting.
3890:                 * To get into that mode use the paintImmediately() below and
3891:                 * uncomment the call to us in op_line_feed(). Also
3892:                 * paintImmediately() doesn't really work unless the Screen is
3893:                 * opaque. I think that is because the paint request comes
3894:                 * to us and we don't forward it to screen; but it could be a 
3895:                 * Swing bug too. Term is very slow in this. For example I"ve
3896:                 * time xterm and DtTerm dealing with "cat /etc/termcap" in 2-3
3897:                 * seconds while Term takes 20-25 seconds. Part of this is
3898:                 * attributed to the fact that Term doesn't take advantage of 
3899:                 * bitBlitting when it's adding one line at a time and still
3900:                 * redraws everything. However I'll make a case below that this
3901:                 * isn't that important.
3902:                 *
3903:                 * Then there is so-called jump scrolling. In this regime terminal
3904:                 * emulators redraw the screen "as time permits". This is in effect
3905:                 * what the swing repaint manager helps with. Multiple repaint()
3906:                 * requests translate to one actual paint(). With todays computers
3907:                 * it's very hard to tell visually that you're jump scrolling
3908:                 * things go by so fast (yes, even under Swing), so this is the
3909:                 * preferred setup.
3910:                 * Here term does a bit better. To deal with a cat'ed 100,000
3911:                 * line file DtTerm takes 8 seconds, while Term takes 22 seconds.
3912:                 * (That's 3 times slower vs 8 times). From some measurements
3913:                 * I've made the number of linefeeds per actual paints has
3914:                 * ranged from > 100 to upper 30's. These numbers are sufficiently
3915:                 * high that the whole screen has to be repained everytime.
3916:                 * I.e. blitting to scroll and drawing only what's new isn't
3917:                 * going to help here. To get reasonable jump-scrolling, you need
3918:                 * to make sure that the Screen is opaque because if you don't 
3919:                 * you will get ...
3920:                 *
3921:                 * Jerky scrolling. If Term is not opaque, the number of actual
3922:                 * paints per repaint() requests diminishes drastically. 'cat' of
3923:                 * etc/termcap (once the code has been warmed up) sometimes causes
3924:                 * a single refresh at the end in contrast to ~100 when Screen
3925:                 * is opaque. Naturally Term in this mode can eat up input at
3926:                 * a rate comparable to dtterm etc, but the jerkiness is very
3927:                 * ugly.
3928:                 * Opacity isn't the only criterion. Term, when embeded inside a 
3929:                 * tabbed pane (like it is in NetBeans) will also act as if it's
3930:                 * opaque and you get more frequent refreshes, as in the
3931:                 * jump-scrolling regime. But that was way too slow for the
3932:                 * taste of NB users which is why OutputTab window calls us on a
3933:                 * timer. That brings it's own jerkiness of a different sort.
3934:                 *
3935:                 * There is a third factor that contributes to slowness. If you 
3936:                 * just 'cat' a file you get the numbers I presneted above. But
3937:                 * if you run an app that actually puts out the 100,000 lines
3938:                 * some sort of timing interaction forces Term into near smooth
3939:                 * scrolling and as a result things slow down a lot! For example,
3940:                 * 	$ generate_100K_lines > /tmp/bag	00:08 sec
3941:                 *	$ cat /tmp/bag				00:20 sec
3942:                 *	$ generate_100K_lines			03:42 sec (opaque)
3943:                 *	$ generate_100K_lines			01:58 sec (!opaque)
3944:                 * This happens even if the generating program is a lightweight 
3945:                 * native application. In fact I believe it is this effect that
3946:                 * forced NB's OutputTab to adopt the timer. I believe there are two
3947:                 * factors that contrinute to this. 
3948:                 * a) Running applications are line buffered so putChars(), with
3949:                 *    it's attendant repaint(), gets called once per line pushing
3950:                 *    us into the smooth scrolling regime. (But why then doesn't
3951:                 *    DtTerm suffer from this?)
3952:                 * b) timeslicing gives enough time to the repaint manager such
3953:                 *    that it converts evey repaint() to a paint.
3954:                 * I know (b) is a factor since if I "simulate" (a) by issueing 
3955:                 * repaints() from op_line_feed() while keeping this function from
3956:                 * using paintImmediately() I don't get that many paints. 
3957:                 * The combined case has 44 paints per repaint as does simulated (a).
3958:                 * So ain increased number of paints per repaint doesn't
3959:                 * explain this.
3960:                 *
3961:                 * In the end, currently since jump scrolling is still not very
3962:                 * fast and since NB has the timer anyway, Screen is not opaque.
3963:                 *
3964:                 * A useful quantitative measure is the number of linefeeds vs
3965:                 * the number of repaint requests vs the number of actual paints.
3966:                 * All these are collected and can be dumped via op_time() or 
3967:                 * printStats().
3968:                 */
3969:
3970:                n_repaint++;
3971:
3972:                if (adjust_scrollbar)
3973:                    adjust_scrollbar();
3974:
3975:                // The following causes Screen.paint() to get called by the Swing
3976:                // repaint manager which in turn calls back to term.paint(Graphics).
3977:                screen.repaint(20);
3978:
3979:                // The following should cause an immediate paint. It doesn't
3980:                // always though!
3981:                // I've found that for it to be effective Screen needs to be opaque.
3982:
3983:                /*
3984:                NOTE: paintImmediately() is probably not the best thing to use.
3985:                // RepaintManager.currentManager(screen).paintDirtyRegions();
3986:                screen.paintImmediately(0, 0, screen.getWidth(), screen.getHeight());
3987:                 */
3988:            }
3989:
3990:            /*
3991:             * Term-specific properties
3992:             */
3993:
3994:            /**
3995:             * Control whether the default foreground and background colors will
3996:             * be reversed.
3997:             * <p>
3998:             * Note: This is independent of characters' reverse attribute.
3999:             */
4000:            public void setReverseVideo(boolean reverse_video) {
4001:                this .reverse_video = reverse_video;
4002:                repaint(false);
4003:            }
4004:
4005:            /**
4006:             * Return the value set by setReverseVideo().
4007:             */
4008:            public boolean isReverseVideo() {
4009:                return reverse_video;
4010:            }
4011:
4012:            private boolean reverse_video = false;
4013:
4014:            /**
4015:             * Set the color of the hilite (selection) - for non XOR mode
4016:             */
4017:            public void setHighlightColor(Color color) {
4018:                sel.setColor(color);
4019:                repaint(false);
4020:            }
4021:
4022:            /**
4023:             * Get the color of the hilite (selection) - for non XOR mode
4024:             */
4025:            public Color getHighlightColor() {
4026:                return sel.getColor();
4027:            }
4028:
4029:            /**
4030:             * Set the color of the hilite (selection) - for XOR mode
4031:             */
4032:            public void setHighlightXORColor(Color color) {
4033:                sel.setXORColor(color);
4034:                repaint(false);
4035:            }
4036:
4037:            /**
4038:             * Get the color of the hilite (selection) - for XOR mode
4039:             */
4040:            public Color getHighlightXORColor() {
4041:                return sel.getXORColor();
4042:            }
4043:
4044:            /**
4045:             * Set the feedback color of active regions.
4046:             */
4047:            public void setActiveColor(Color color) {
4048:                // SHOULD check for null color
4049:                active_color = color;
4050:                repaint(false);
4051:            }
4052:
4053:            /**
4054:             * Get the feedback color of active regions.
4055:             */
4056:            public Color getActiveColor() {
4057:                // SHOULD clone? but Color is not clonable and has no simple
4058:                // Color(COlor) constructor. What does JComponent do?
4059:                return active_color;
4060:            }
4061:
4062:            private Color active_color = Color.lightGray;
4063:
4064:            /**
4065:             * Control whether an anchor is set.
4066:             * <p>
4067:             * Setting an anchor will automatically cause the buffer to grow, in
4068:             * excess of what was set by setHistorySize(), to ensure that whatever
4069:             * is displayed and in the current history will still be accessible.
4070:             * <p> 
4071:             * Also, if you're working with Extents, Coords and ActiveRegions, or
4072:             * visiting logical lines, you might want to anchor the text so that
4073:             * your coordinates don't get invalidated by lines going out of the buffer.
4074:             * <p>
4075:             * Repeated enabling of the anchor will discard all text that 
4076:             * doesn't fit in history and start a new anchor.
4077:             * <p>
4078:             * When anchoring is disabled any text in excess of setHistorySize()
4079:             * is trimmed and the given history size comes into effect again.
4080:             */
4081:            public void setAnchored(boolean anchored) {
4082:                ckEventDispatchThread();
4083:                // OLD NPE-x synchronized(this)
4084:                {
4085:                    if (anchored) {
4086:                        this .anchored = false;
4087:                        limit_lines();
4088:                        this .anchored = true;
4089:                    } else {
4090:                        this .anchored = false;
4091:                        limit_lines();
4092:                        repaint(false); // limit_lines() already adjusted scrollbar
4093:                    }
4094:                }
4095:            }
4096:
4097:            /**
4098:             * Return true if the text is currently anchored.
4099:             */
4100:            public boolean isAnchored() {
4101:                return anchored;
4102:            }
4103:
4104:            private boolean anchored = false;
4105:
4106:            /**
4107:             * Returns the actual drawing area so events can be interposed upon,
4108:             * like context menus.
4109:             * @deprecated
4110:             */
4111:            public JComponent getCanvas() {
4112:                return screen;
4113:            }
4114:
4115:            /**
4116:             * Returns the actual drawing area so events can be interposed upon,
4117:             * like context menus.
4118:             */
4119:            public JComponent getScreen() {
4120:                return screen;
4121:            }
4122:
4123:            /**
4124:             * Return the terminal operations implementation.
4125:             * <b>WARNING! This is temporary</b>
4126:             */
4127:            public Ops ops() {
4128:                return ops;
4129:            }
4130:
4131:            /**
4132:             * Set the Interpreter type by name.
4133:             * @see Term#setInterp
4134:             */
4135:            public void setEmulation(String emulation) {
4136:                Interp new_interp = InterpKit.forName(emulation, ops);
4137:                if (new_interp == null)
4138:                    return;
4139:                interp = new_interp;
4140:            }
4141:
4142:            /**
4143:             * Returns the termcap string that best describes what this Term
4144:             * emulates.
4145:             */
4146:            public String getEmulation() {
4147:                return getInterp().name();
4148:            }
4149:
4150:            /**
4151:             * Set the emulation interpreter.
4152:             * <p>
4153:             * It is not advisable to change the emulation after Term has been
4154:             * connected to a process, since it's often impossible to advise 
4155:             * the process of the new terminal type.
4156:             */
4157:            void setInterp(Interp interp) {
4158:                this .interp = interp;
4159:            }
4160:
4161:            /**
4162:             * Return the Interpreter assigned to this.
4163:             */
4164:            Interp getInterp() {
4165:                return interp;
4166:            }
4167:
4168:            private Interp interp = new InterpDumb(ops); // used to InterpANSI
4169:
4170:            /**
4171:             * Set how many lines of history will be available.
4172:             * <p>
4173:             * If an anchor is in effect the history size will only have an
4174:             * effect when the anchor is reset.
4175:             */
4176:            public void setHistorySize(int new_size) {
4177:                history_size = new_size;
4178:                limit_lines();
4179:                repaint(true);
4180:            }
4181:
4182:            /**
4183:             * Return the number of lines in history
4184:             */
4185:            public int getHistorySize() {
4186:                return history_size;
4187:            }
4188:
4189:            private int history_size = 20;
4190:
4191:            /**
4192:             * Set the width of the glyph gutter in pixels
4193:             */
4194:            public void setGlyphGutterWidth(int pixels) {
4195:
4196:                glyph_gutter_width = pixels;
4197:
4198:                // protect against client mistakes?
4199:                if (glyph_gutter_width > 30)
4200:                    glyph_gutter_width = 30;
4201:
4202:                updateScreenSize();
4203:            }
4204:
4205:            private int glyph_gutter_width;
4206:
4207:            /**
4208:             * Associate an Image with a glyph id, or clear it if image is null.
4209:             * 
4210:             * Numbering the glyphs is confusing. They start with 48. That is, 
4211:             * if you register glyph #0 using hbvi/vim :K command the escape
4212:             * sequence emitted is 48. 48 is ascii '0'.
4213:             */
4214:            public void setGlyphImage(int glyph_number, Image image) {
4215:                if (glyph_number > 256)
4216:                    return; // SHOULD throw an exception?
4217:                glyph_images[glyph_number] = image;
4218:            }
4219:
4220:            private Image glyph_images[] = new Image[256];;
4221:
4222:            /**
4223:             * Get the usable area for drawing glyphs
4224:             *
4225:             * This value changes when the gutter width or the font changes
4226:             */
4227:            public Dimension getGlyphCellSize() {
4228:                return new Dimension(glyph_gutter_width, metrics.height);
4229:            }
4230:
4231:            /**
4232:             * Register up to 8 new custom colors.
4233:             *
4234:             * Unlike glyph id's you can start the numbers from 0. 
4235:             * hbvi/vim's :K command will add a 58 to the number, but that
4236:             * is the code we interpret as custom color #0.
4237:             */
4238:            public void setCustomColor(int number, Color c) {
4239:                custom_color[number] = c;
4240:            }
4241:
4242:            private final Color custom_color[] = new Color[8];
4243:            private final Color standard_color[] = new Color[8];
4244:
4245:            /**
4246:             * Get cursor row in buffer coordinates (0-origin).
4247:             */
4248:            public int getCursorRow() {
4249:                return st.cursor.row;
4250:            }
4251:
4252:            /**
4253:             * Get cursor column in buffer coordinates (0-origin)
4254:             */
4255:            public int getCursorCol() {
4256:                return cursor_line().cellToBuf(metrics, st.cursor.col);
4257:            }
4258:
4259:            /**
4260:             * Get (absolute) cursor coordinates.
4261:             * <p>
4262:             * The returned Coord is newly allocated and need not be cloned.
4263:             */
4264:            public Coord getCursorCoord() {
4265:                Line l = buf.lineAt(st.cursor.row);
4266:                return new Coord(new BCoord(st.cursor.row, l.cellToBuf(metrics,
4267:                        st.cursor.col)), firsta);
4268:            }
4269:
4270:            /*
4271:             * 
4272:             * Move the cursor to the given (absolute) coordinates
4273:             *
4274:             * @deprecated, replaced by{@link #setCursorCoord(Coord)}
4275:             */
4276:            public void goTo(Coord coord) {
4277:                setCursorCoord(coord);
4278:            }
4279:
4280:            /**
4281:             * Move the cursor to the given (absolute) coordinates
4282:             * SHOULD be setCursorCoord!
4283:             */
4284:            public void setCursorCoord(Coord coord) {
4285:                Coord c = (Coord) coord.clone();
4286:                c.clip(st.rows, buf.visibleCols(), firsta);
4287:                st.cursor = c.toBCoord(firsta);
4288:                st.cursor.col = cursor_line().bufToCell(metrics, st.cursor.col);
4289:                repaint(true);
4290:            }
4291:
4292:            /**
4293:             * Control whether the cursor is visible or not.
4294:             * <p>
4295:             * We don't want a visible cursor when we're using Term in
4296:             * non-interactve mode.
4297:             */
4298:            public void setCursorVisible(boolean cursor_visible) {
4299:                this .cursor_visible = cursor_visible;
4300:            }
4301:
4302:            /**
4303:             * Find out if cursor is visible.
4304:             */
4305:            public boolean isCursorVisible() {
4306:                return cursor_visible;
4307:            }
4308:
4309:            private boolean cursor_visible = true;
4310:
4311:            /**
4312:             * Back up the coordinate by one character and return new Coord.
4313:             * <p>
4314:             * Travels back over line boundaries
4315:             * <br>
4316:             * Returns null if 'c' is the first character in the buffer.
4317:             */
4318:
4319:            public Coord backup(Coord c) {
4320:                BCoord bRow = buf.backup(c.toBCoord(firsta));
4321:                if (bRow == null)
4322:                    return null;
4323:                return new Coord(bRow, firsta);
4324:            }
4325:
4326:            /**
4327:             * Advance the coordinate by one charater and return new coord.
4328:             * <p> 
4329:             * Travels forward over line boundaries.
4330:             * <br>
4331:             * Returns null if 'c' is the last character in the buffer.
4332:             */
4333:            public Coord advance(Coord c) {
4334:                return new Coord(buf.advance(c.toBCoord(firsta)), firsta);
4335:            }
4336:
4337:            /**
4338:             * Get contents of current selection.
4339:             * <p>
4340:             * Returns 'null' if there is no current selection.
4341:             */
4342:            public String getSelectedText() {
4343:                return sel.getSelection();
4344:            }
4345:
4346:            /**
4347:             * Get the extent of the current selection.
4348:             * <p>
4349:             * If there is no selection returns 'null'.
4350:             */
4351:            public Extent getSelectionExtent() {
4352:                return sel.getExtent();
4353:            }
4354:
4355:            /**
4356:             * Set the extent of the selection.
4357:             */
4358:            public void setSelectionExtent(Extent extent) {
4359:                extent.begin.clip(buf.nlines, buf.totalCols(), firsta);
4360:                extent.end.clip(buf.nlines, buf.totalCols(), firsta);
4361:                sel.setExtent(extent);
4362:                repaint(false);
4363:            }
4364:
4365:            /**
4366:             * Clear the selection.
4367:             */
4368:            public void clearSelection() {
4369:                sel.cancel(true);
4370:                repaint(false);
4371:            }
4372:
4373:            /**
4374:             * Set whether slections automatically get copied to the systemSelection
4375:             * when the selection is completed (the button is released).
4376:             * <p>
4377:             * This is how xterm and other X-windows selections work.
4378:             * <p>
4379:             * This property can probably be deprecated. It was neccessary in the
4380:             * pre-1.4.2 days when we didn't have a systemSelection and we wanted
4381:             * to have the option of not cloberring the systemClipboard on text
4382:             * selection.
4383:             *
4384:             * @deprecated selections now always get copied to systemSelection if
4385:             * it exists.
4386:             */
4387:            public void setAutoCopy(boolean auto_copy) {
4388:                // no-op
4389:            }
4390:
4391:            /**
4392:             * Return the value set by setAutoCopy()
4393:             *
4394:             * @deprecated Now always returns 'true'.
4395:             */
4396:            public boolean isAutoCopy() {
4397:                return true;
4398:            }
4399:
4400:            /*
4401:             * Control whether refreshes are enabled.
4402:             * <p>
4403:             * Turn refresh off if you're about to add a lot of text to the
4404:             * terminal. Another way is to use appendText("stuff", false) 
4405:             */
4406:            public void setRefreshEnabled(boolean refresh_enabled) {
4407:                this .refresh_enabled = refresh_enabled;
4408:                if (refresh_enabled)
4409:                    repaint(true);
4410:            }
4411:
4412:            public boolean isRefreshEnabled() {
4413:                return refresh_enabled;
4414:            }
4415:
4416:            private boolean refresh_enabled = true;
4417:
4418:            /**
4419:             * Sets whether the selection highlighting is XOR style or normal
4420:             * Swing style.
4421:             */
4422:            public void setSelectionXOR(boolean selection_xor) {
4423:                this .selection_xor = selection_xor;
4424:                repaint(false);
4425:            }
4426:
4427:            /*
4428:             * If returns 'true' then selections are drawn using the xor mode,
4429:             * Otherwise they are drawn in regular swing fashion.
4430:             */
4431:            public boolean isSelectionXOR() {
4432:                return selection_xor;
4433:            }
4434:
4435:            // make accessible to Sel
4436:            boolean selection_xor = false;
4437:
4438:            /**
4439:             * Set the TAB size.
4440:             * <p>
4441:             * The cursor is moved to the next column such that
4442:             * <pre>
4443:             * column (0-origin) modulo tab_size == 0
4444:             * </pre>
4445:             * The cursor will not go past the last column.
4446:             * <p>
4447:             * Note that the conventional assumption of what a tab is, is not
4448:             * entirely accurate. ANSI does not define TABs as above but rather
4449:             * as a directive to move to the next "tabstop" which has to have been
4450:             * set previously. In fact on unixes it is the terminal line discipline
4451:             * that expands tabs to spaces  in the conventional way. That in,
4452:             * turn explains why TAB information doesn't make it into selections and
4453:             * why copying and pasting Makefile instructions is liable to lead
4454:             * to hard-to-diagnose make rpoblems, which, in turn drove the ANT people
4455:             * to reinvent the world.
4456:             */
4457:            public void setTabSize(int tab_size) {
4458:                this .tab_size = tab_size;
4459:            }
4460:
4461:            /**
4462:             * Get the TAB size.
4463:             */
4464:            public int getTabSize() {
4465:                return tab_size;
4466:            }
4467:
4468:            private int tab_size = 8;
4469:
4470:            /**
4471:             * Control whether Term scrolls to the bottom on keyboard input.
4472:             * <p>
4473:             * This is analogous to the xterm -sk/+sk option.
4474:             */
4475:            public void setScrollOnInput(boolean scroll_on_input) {
4476:                this .scroll_on_input = scroll_on_input;
4477:            }
4478:
4479:            /**
4480:             * Return whether Term scrolls to the bottom on keyboard input.
4481:             */
4482:            public boolean isScrollOnInput() {
4483:                return scroll_on_input;
4484:            }
4485:
4486:            private boolean scroll_on_input = true;
4487:
4488:            /**
4489:             * Control whether Term scrolls on any output.
4490:             * <p>
4491:             * When set to false, if the user moves the scrollbar to see some
4492:             * text higher up in history, the view will not change even if more
4493:             * output is produced. But if the cursor is visible, scrolling will
4494:             * happen. This is so that in an interactive session any prompt will
4495:             * be visible etc.
4496:             * <p>
4497:             * However, the tracking of the cursor is controlled by the 'trackCursor'
4498:             * property which by default is set to 'true'.
4499:             * <p>
4500:             * This property is analogous to the xterm -si/+si option.
4501:             */
4502:            public void setScrollOnOutput(boolean scroll_on_output) {
4503:                this .scroll_on_output = scroll_on_output;
4504:            }
4505:
4506:            /**
4507:             * Return whether Term scrolls on any output.
4508:             */
4509:            public boolean isScrollOnOutput() {
4510:                return scroll_on_output;
4511:            }
4512:
4513:            private boolean scroll_on_output = true;
4514:
4515:            /**
4516:             * Control whether Term will scroll to track the cursor as text is added.
4517:             * <p>
4518:             * If set to true, as output is being generated Term will try to keep
4519:             * the cursor in view.
4520:             * <p>
4521:             * This property is only relevant when scrollOnOutput is set to false.
4522:             * If scrollOnOutput is true, this property is also implicitly true.
4523:             */
4524:            public void setTrackCursor(boolean track_cursor) {
4525:                this .track_cursor = track_cursor;
4526:            }
4527:
4528:            /**
4529:             * Return whether Term will scroll to track the cursor as text is added.
4530:             */
4531:            public boolean isTrackCursor() {
4532:                return track_cursor;
4533:            }
4534:
4535:            private boolean track_cursor = true;
4536:
4537:            /**
4538:             * Controls horizontal scrolling and line wrapping.
4539:             * <p>
4540:             * When enabled a horizontal scrollbar becomes visible and line-wrapping
4541:             * is disabled.
4542:             */
4543:            public void setHorizontallyScrollable(
4544:                    boolean horizontally_scrollable) {
4545:                this .horizontally_scrollable = horizontally_scrollable;
4546:                // hscroll_bar.setVisible(horizontally_scrollable);
4547:                hscroll_wrapper.setVisible(horizontally_scrollable);
4548:            }
4549:
4550:            /*
4551:             * Returns whether horizontal scrolling is enabled.
4552:             * @see Term.setHorizontallyScrollable
4553:             */
4554:            public boolean isHorizontallyScrollable() {
4555:                return this .horizontally_scrollable;
4556:            }
4557:
4558:            private boolean horizontally_scrollable = true;
4559:
4560:            /**
4561:             * Clear everything and assign new text.
4562:             * <p>
4563:             * If the size of the text exceeds history early parts of it will get
4564:             * lost, unless an anchor was set using setAnchor(). 
4565:             */
4566:            public void setText(String text) {
4567:                // SHOULD make a bit more efficient
4568:                clearHistoryNoRefresh();
4569:                appendText(text, true);
4570:            }
4571:
4572:            /**
4573:             * Add new text at the current cursor position.
4574:             * <p>
4575:             * Doesn't repaint the view unless 'repaint' is set to 'true'.
4576:             * <br>
4577:             * Doesn't do anything if 'text' is 'null'.
4578:             */
4579:            public void appendText(String text, boolean repaint) {
4580:
4581:                if (text == null)
4582:                    return;
4583:
4584:                ckEventDispatchThread();
4585:                // OLD NPE-x synchronized(this)
4586:                {
4587:                    for (int cx = 0; cx < text.length(); cx++) {
4588:                        putc_work(text.charAt(cx));
4589:                        if (text.charAt(cx) == '\n')
4590:                            putc_work('\r');
4591:                    }
4592:                }
4593:                if (repaint)
4594:                    repaint(true);
4595:            }
4596:
4597:            /**
4598:             * Scroll the view 'n' pages up.
4599:             * <p>
4600:             * A page is the height of the view.
4601:             */
4602:
4603:            public void pageUp(int n) {
4604:                ckEventDispatchThread();
4605:                // OLD NPE-x synchronized(this)
4606:                {
4607:                    st.firstx -= n * st.rows;
4608:                    if (st.firstx < 0)
4609:                        st.firstx = 0;
4610:                }
4611:                repaint(true);
4612:            }
4613:
4614:            /**
4615:             * Scroll the view 'n' pages down.
4616:             * <p>
4617:             * A page is the height of the view.
4618:             */
4619:
4620:            public void pageDown(int n) {
4621:                ckEventDispatchThread();
4622:                // OLD NPE-x synchronized(this)
4623:                {
4624:                    st.firstx += n * st.rows;
4625:
4626:                    if (st.firstx + st.rows > buf.nlines)
4627:                        st.firstx = buf.nlines - st.rows;
4628:                }
4629:                repaint(true);
4630:            }
4631:
4632:            /**
4633:             * Scroll the view 'n' lines up.
4634:             */
4635:            public void lineUp(int n) {
4636:                ckEventDispatchThread();
4637:                // OLD NPE-x synchronized(this)
4638:                {
4639:                    st.firstx -= n;
4640:                    if (st.firstx < 0)
4641:                        st.firstx = 0;
4642:                }
4643:                repaint(true);
4644:            }
4645:
4646:            /**
4647:             * Scroll the view 'n' lines down.
4648:             */
4649:            public void lineDown(int n) {
4650:                ckEventDispatchThread();
4651:                // OLD NPE-x synchronized(this)
4652:                {
4653:                    st.firstx += n;
4654:                    if (st.firstx + st.rows > buf.nlines)
4655:                        st.firstx = buf.nlines - st.rows;
4656:                }
4657:                repaint(true);
4658:            }
4659:
4660:            /**
4661:             * Scroll the view 'n' pages to the left.
4662:             */
4663:            public void pageLeft(int n) {
4664:                columnLeft(n * buf.visibleCols());
4665:            }
4666:
4667:            /**
4668:             * Scroll the view 'n' pages to the right.
4669:             */
4670:            public void pageRight(int n) {
4671:                columnRight(n * buf.visibleCols());
4672:            }
4673:
4674:            /**
4675:             * Scroll the view 'n' columns to the right.
4676:             */
4677:            public void columnRight(int n) {
4678:                ckEventDispatchThread();
4679:                // OLD NPE-x synchronized(this)
4680:                {
4681:                    st.firsty += n;
4682:                    if (st.firsty + buf.visibleCols() > buf.totalCols())
4683:                        st.firsty = buf.totalCols() - buf.visibleCols();
4684:                }
4685:                repaint(true);
4686:            }
4687:
4688:            /**
4689:             * Scroll the view 'n' columns to the left.
4690:             */
4691:            public void columnLeft(int n) {
4692:                ckEventDispatchThread();
4693:                // OLD NPE-x synchronized(this)
4694:                {
4695:                    st.firsty -= n;
4696:                    if (st.firsty < 0)
4697:                        st.firsty = 0;
4698:                }
4699:                repaint(true);
4700:            }
4701:
4702:            /**
4703:             * Return the cell width of the given character.
4704:             */
4705:            public int charWidth(char c) {
4706:                return metrics.wcwidth(c);
4707:            }
4708:
4709:            /*
4710:             * The following are overrides of JComponent/Component
4711:             */
4712:
4713:            /**
4714:             * Override of JComponent.
4715:             * <p>
4716:             * We absolutely require fixed width fonts, so if the font is changed
4717:             * we create a monospaced version of it with the same style and size.
4718:             */
4719:
4720:            public void setFont(Font new_font) {
4721:
4722:                Font font = new Font("Monospaced", // NOI18N
4723:                        new_font.getStyle(), new_font.getSize());
4724:
4725:                super .setFont(font); // This should invalidate us, which
4726:                // ultimately will cause a repaint
4727:
4728:                /* DEBUG
4729:                System.out.println("Font info:"); // NOI18N
4730:                System.out.println("\tlogical name: " + font.getName()); // NOI18N
4731:                System.out.println("\tfamily name: " + font.getFamily()); // NOI18N
4732:                System.out.println("\tface name: " + font.getFontName()); // NOI18N
4733:                 */
4734:
4735:                // cache the metrics
4736:                metrics = new MyFontMetrics(this , font);
4737:
4738:                updateScreenSize();
4739:            }
4740:
4741:            /**
4742:             * Override of JComponent.
4743:             * <p>
4744:             * Pass on the request to the screen where all the actual focus
4745:             * management happens.
4746:             */
4747:            public void requestFocus() {
4748:                screen.requestFocus();
4749:            }
4750:
4751:            public boolean requestFocusInWindow() {
4752:                return screen.requestFocusInWindow();
4753:            }
4754:
4755:            /**
4756:             * Override of JComponent.
4757:             * <p>
4758:             * Pass on enabledness to sub-components (scrollbars and screen)
4759:             */
4760:            public void setEnabled(boolean enabled) {
4761:                // This was done as a result of issue 24824
4762:
4763:                super .setEnabled(enabled);
4764:
4765:                hscroll_bar.setEnabled(enabled);
4766:                vscroll_bar.setEnabled(enabled);
4767:                screen.setEnabled(enabled);
4768:            }
4769:
4770:            //..........................................................................
4771:            // Accessibility stuff is all here
4772:            // Not just the required interfaces but also all the helpers.
4773:            //..........................................................................
4774:
4775:            /**
4776:             * Since Term is a composite widget the main accessible JComponent is
4777:             * not Term but an internal JComponent. We'll speak of Term accessibility
4778:             * when we in fact are referring to the that inner component.
4779:             * <p>
4780:             * Accessibility for Term is tricky because it doesn't fit into the 
4781:             * roles delineated by Swing. The closest role is that of TEXT and that
4782:             * is too bound to how JTextComponent works. To wit ...
4783:             <p>
4784:             <dl>
4785:             <dt>2D vs 1D coordinates
4786:             <dd>
4787:             Term has a 2D coordinate system while AccessibleText works with 1D
4788:             locations. So Term actually has code which translates between the two.
4789:             This code is not exactly efficient but only kicks in when assistive
4790:             technology latches on.
4791:             <br>
4792:             Line breaks ('\n's) count as characters! However we only count 
4793:             logical line breaks ('\n's appearing in the input stream) as opposed to
4794:             wrapped lines!
4795:             <p>
4796:             The current implementation doesn't cache any of the mappings because
4797:             that would require a word per line extra storage for the cumulative
4798:             char count. The times actually we're pretty fast with a 4000 line
4799:             histroy.
4800:
4801:             <dt>WORDs and SENTENCEs
4802:             <dd>
4803:             For AccessibleText.get*Index() functions WORD uses the regular 
4804:             Term WordDelineator. SENTENCE translates to just a line.
4805:
4806:             <dt>Character attributes
4807:             <dd>
4808:             Term uses the ANSI convention of character attributes so when 
4809:             AccessibleText.getCharacterAttribute() is used a rough translation
4810:             is made as follows:
4811:             <ul>
4812:             <li> ANSI underscore -> StyleConstants.Underline
4813:             <li> ANSI bright/bold -> StyleConstants.Bold
4814:             <li> Non-black foreground color -> StyleConstants.Foreground
4815:             <li> Explicitly set background color -> StyleConstants.Background
4816:             </ul>
4817:             Font related information is always constant so it is not provided.
4818:
4819:             <dt>History
4820:             <dd>
4821:             Term has history and lines wink out. If buffer coordinates were 
4822:             used to interact with accessibility, caretPosition and charCount
4823:             would be dancing around. Fortunately Term has absolute coordinates.
4824:             So positions returned via AccessibleText might eventually refer to 
4825:             text that has gone by.
4826:
4827:             <dt>Caret and Mark vs Cursor and Selection
4828:             <dd>
4829:            While Term keeps the selection and cursor coordinates independent, 
4830:            JTextComponent merges them and AccessibleText inherits this view. 
4831:            With Term caretPosition is the position of the cursor and selection
4832:            ends will not neccessarily match with the caret position.
4833:             </dl>
4834:             <p>
4835:             Currently only notifications of ACCESSIBLE_CARET_PROPERTY and
4836:             ACCESSIBLE_TEXT_PROPERTY are fired and that always in pairs.
4837:             They are fired on the receipt of any character to be processed.
4838:             <p>
4839:             IMPORTANT: It is assumed that under assistive technology Term will be
4840:             used primarily as a continuous text output device or a readonly document.
4841:             Therefore ANSI cursor motion and text editing commands or anything that
4842:             mutates the text will completely invalidate all of AccessibleTexts
4843:             properties. (Perhaps an exception SHOULD be made for backspace)
4844:             */
4845:
4846:            public AccessibleContext getAccessibleContext() {
4847:                if (accessible_context == null) {
4848:                    accessible_context = new AccessibleTerm();
4849:                }
4850:                return accessible_context;
4851:            }
4852:
4853:            private AccessibleContext accessible_context;
4854:
4855:            /*
4856:             * Term is really a container. Screen is where things get drawn and 
4857:             * where focus is set to, so all real accessibility work is done there.
4858:             * We just declare us to have a generic role.
4859:             */
4860:            protected class AccessibleTerm extends AccessibleJComponent {
4861:                public AccessibleRole getAccessibleRole() {
4862:                    return AccessibleRole.PANEL;
4863:                }
4864:
4865:                public void setAccessibleName(String name) {
4866:                    screen.getAccessibleContext().setAccessibleName(name);
4867:                }
4868:            }
4869:
4870:            /**
4871:             * [DO NOT USE] Convert a 2D Coord to a 1D linear position.
4872:             * <p>
4873:             * This function really should be private but I need it to be public for 
4874:             * unit-testing purposes.
4875:             */
4876:            public int CoordToPosition(Coord c) {
4877:                BCoord b = c.toBCoord(firsta);
4878:                int nchars = charsInPrehistory;
4879:                for (int r = 0; r < b.row; r++) {
4880:                    Line l = buf.lineAt(r);
4881:                    nchars += l.length();
4882:                    if (!l.isWrapped())
4883:                        nchars += 1;
4884:                }
4885:
4886:                nchars += c.col;
4887:
4888:                return nchars;
4889:            }
4890:
4891:            /**
4892:             * [DO NOT USE] Convert a 1D linear position to a 2D Coord.
4893:             * <p>
4894:             * This function really should be private but I need it to be public for 
4895:             * unit-testing purposes.
4896:             */
4897:            public Coord PositionToCoord(int position) {
4898:                int nchars = charsInPrehistory;
4899:                for (int r = 0; r < buf.nlines; r++) {
4900:                    Line l = buf.lineAt(r);
4901:                    nchars += l.length();
4902:                    if (!l.isWrapped())
4903:                        nchars += 1;
4904:                    if (nchars > position) {
4905:                        BCoord b = new BCoord();
4906:                        b.row = r;
4907:                        b.col = buf.lineAt(r).length() + 1
4908:                                - (nchars - position);
4909:                        return new Coord(b, firsta);
4910:                    }
4911:                }
4912:                return null;
4913:            }
4914:
4915:            /**
4916:             * Return the number of characters stored.
4917:             * <p>
4918:             * Include logical newlines for now to match the above conversions.
4919:             * Hmm, do we include chars in prehistory?
4920:             */
4921:
4922:            int getCharCount() {
4923:                int nchars = charsInPrehistory;
4924:                for (int r = 0; r < buf.nlines; r++) {
4925:                    Line l = buf.lineAt(r);
4926:                    nchars += l.length();
4927:                    if (!l.isWrapped())
4928:                        nchars += 1;
4929:                }
4930:                return nchars;
4931:            }
4932:
4933:            /**
4934:             * Return the bounding rectangle of the character at the given coordinate
4935:             */
4936:            Rectangle getCharacterBounds(Coord c) {
4937:                if (c == null)
4938:                    return null;
4939:                BCoord b = c.toBCoord(firsta);
4940:
4941:                char ch = '\0';
4942:                try {
4943:                    Line l = buf.lineAt(b.row);
4944:                    ch = l.charArray()[b.col];
4945:                } catch (Exception x) {
4946:                    ;
4947:                }
4948:
4949:                Point p1 = toPixel(b);
4950:                Rectangle rect = new Rectangle();
4951:                rect.x = p1.x;
4952:                rect.y = p1.y;
4953:                rect.width = metrics.width * charWidth(ch);
4954:                rect.height = metrics.height;
4955:                return rect;
4956:            }
4957:
4958:            Color backgroundColor(boolean reverse, int attr) {
4959:                Color bg = null;
4960:                if (reverse) {
4961:                    int fcx = Attr.foregroundColor(attr);
4962:                    if (fcx != 0 && fcx <= 8) {
4963:                        bg = standard_color[fcx - 1];
4964:                    } else if (fcx > 8) {
4965:                        bg = custom_color[fcx - 9];
4966:                    } else {
4967:                        bg = actual_foreground;
4968:                    }
4969:
4970:                } else {
4971:                    int bcx = Attr.backgroundColor(attr);
4972:                    if (bcx != 0 && bcx <= 8) {
4973:                        bg = standard_color[bcx - 1];
4974:                    } else if (bcx > 8) {
4975:                        bg = custom_color[bcx - 9];
4976:                    }
4977:                }
4978:                return bg;
4979:            }
4980:
4981:            Color foregroundColor(boolean reverse, int attr) {
4982:                Color fg = null;
4983:                if (reverse) {
4984:                    int bcx = Attr.backgroundColor(attr);
4985:                    if (bcx != 0 && bcx <= 8) {
4986:                        fg = standard_color[bcx - 1];
4987:                    } else if (bcx > 8) {
4988:                        fg = custom_color[bcx - 9];
4989:                    } else {
4990:                        fg = actual_background;
4991:                    }
4992:
4993:                } else {
4994:                    int fcx = Attr.foregroundColor(attr);
4995:                    if (fcx != 0 && fcx <= 8) {
4996:                        fg = standard_color[fcx - 1];
4997:                    } else if (fcx > 8) {
4998:                        fg = custom_color[fcx - 9];
4999:                    } else {
5000:                        fg = actual_foreground;
5001:                    }
5002:                }
5003:                return fg;
5004:            }
5005:
5006:            private static void ckEventDispatchThread() {
5007:                /* 
5008:                if (!SwingUtilities.isEventDispatchThread()) {
5009:                    System.out.println("term: NOT IN EventDispatchThread");
5010:                    Thread.dumpStack();
5011:                }
5012:                 */
5013:            }
5014:
5015:            /* attaches MouseWheelHandler to scroll the component
5016:             */
5017:            private static void addMouseWheelHandler(JComponent comp,
5018:                    JScrollBar bar) {
5019:                comp.addMouseWheelListener(new MouseWheelHandler(bar)); // XXX who removes this lsnr?
5020:            }
5021:
5022:            private static class MouseWheelHandler implements 
5023:                    MouseWheelListener {
5024:
5025:                private JScrollBar scrollbar;
5026:
5027:                public MouseWheelHandler(JScrollBar scrollbar) {
5028:                    this .scrollbar = scrollbar;
5029:                }
5030:
5031:                public void mouseWheelMoved(MouseWheelEvent e) {
5032:                    int totalScrollAmount = e.getUnitsToScroll()
5033:                            * scrollbar.getUnitIncrement();
5034:                    scrollbar
5035:                            .setValue(scrollbar.getValue() + totalScrollAmount);
5036:                }
5037:
5038:            }
5039:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.