001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: *
041: * Contributor(s): Ivan Soleimanipour.
042: */
043:
044: /*
045: * "Screen.java"
046: * Screen.java 1.9 01/07/26
047: */
048:
049: package org.netbeans.lib.terminalemulator;
050:
051: import java.awt.*;
052: import java.awt.event.*;
053:
054: import javax.swing.*; // import javax.swing.FocusManager; // override java.awt.FocusManager if present
055: import javax.accessibility.*;
056: import javax.swing.text.AttributeSet;
057: import javax.swing.text.SimpleAttributeSet;
058: import javax.swing.text.MutableAttributeSet;
059: import javax.swing.text.StyleConstants;
060:
061: // We can _almost_ inherit from awt.Canvas, except that then we lose various
062: // very handy abilities:
063: // - Tool tips end up not working right since the AWT Canvas won't cooperate
064: // with our containing JRootPane.
065: // - We loose the ability to use DebugGraphics.
066: // - JComponent does double-buffering for us, so we need not reimplement it.
067: // (there's still an issue of whose double buffering is quicker).
068:
069: class Screen extends JComponent implements Accessible {
070: private Term term; // back pointer
071:
072: private static final boolean debug = false;
073:
074: public Screen(Term term, int dx, int dy) {
075: this .term = term;
076: Dimension dim = new Dimension(dx, dy);
077: setSize(dim);
078: setPreferredSize(dim);
079: // setOpaque(true); // see comment in Term.repaint()
080:
081: setGrabTab(true);
082:
083: if (debug) {
084: // Just turning our double buffering isn't enough, need to
085: // turn it off everywhere.
086: RepaintManager repaintManager = RepaintManager
087: .currentManager(this );
088: repaintManager.setDoubleBufferingEnabled(false);
089:
090: setDebugGraphicsOptions(DebugGraphics.FLASH_OPTION
091: | DebugGraphics.BUFFERED_OPTION
092: | DebugGraphics.LOG_OPTION);
093: }
094: }
095:
096: // debugging hooks:
097:
098: /* TMP
099: public void setSize(int width, int height) {
100: super.setSize(width, height);
101: }
102: public void setSize(Dimension dim) {
103: super.setSize(dim);
104: }
105: */
106:
107: // When under NB we sometime get resizes when validate() happens
108: // while Term !isShowing() so the sizes are <= 0.
109: // The result is a 1-column output.
110: // In the following we discard "bad" resizes as a workaround.
111: public void setBounds(int x, int y, int width, int height) {
112: if (width <= 0 || height <= 0)
113: return;
114: super .setBounds(x, y, width, height);
115: }
116:
117: public void setBounds(Rectangle r) {
118: super .setBounds(r);
119: }
120:
121: /**
122: * Allow Tab's to come through to us.
123: * This is deprecated under 1.4 but works runtime-wise.
124: * Once we shipt to building under 1.4 should do things as the comment
125: * below suggests.
126: */
127: public boolean OLD_isManagingFocus() {
128: return true;
129: }
130:
131: /* LATER
132:
133: This code to be used when we want Term to only see Tab but not CtrlTab.
134: This is easily accomplished by overriding isManagingFocus (in Screen).
135: But that function is deprecated and when we switch to 1.4 as a base
136: we'll have to use the below methodology. There are a lot of new 1.4
137: classes involved so I haven't bothered writing it introspectively.
138:
139: Also the below way isn't the best. It assumes that all java
140: implementations will follow the Swing guides for focus traversal keys
141: and that no container of us will modify the set. A better way would be to
142: use a read-modify-write approach where we "subtract" out the keystrokes
143: we want to see. Note though that the Set returned by
144: getFocusTraversalKeys is immutable and you'll need to achieve the
145: subtraction through copying instead of deleting.
146: */
147:
148: private java.util.Set original_fwd_keys = null;
149: private java.util.Set original_bwd_keys = null;
150:
151: private void setGrabTab(boolean grabTab) {
152:
153: if (original_fwd_keys == null) {
154: original_fwd_keys = new java.util.HashSet();
155: original_fwd_keys.add(AWTKeyStroke.getAWTKeyStroke(
156: KeyEvent.VK_TAB, InputEvent.CTRL_MASK));
157: original_bwd_keys = new java.util.HashSet();
158: original_bwd_keys.add(AWTKeyStroke.getAWTKeyStroke(
159: KeyEvent.VK_TAB, InputEvent.CTRL_MASK
160: | InputEvent.SHIFT_MASK));
161: }
162:
163: if (grabTab) {
164: // install our simplified version allowing Ctrl-TAB to traverse
165: setFocusTraversalKeys(
166: KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
167: original_fwd_keys);
168: setFocusTraversalKeys(
169: KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
170: original_bwd_keys);
171:
172: } else {
173: // revert to default
174: setFocusTraversalKeys(
175: KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
176: setFocusTraversalKeys(
177: KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);
178: }
179: }
180:
181: public void paint(Graphics g) {
182:
183: // No need to double buffer as our caller, the repaint manager,
184: // already does so.
185:
186: if (debug) {
187: // HACK, normally, by the time we get the Graphics
188: // the components getComponentGraphics() should've retrieved
189: // a DebugGraphics for us, _but_ it only does that if we have
190: // a 'ui' which we don't, so I have to do this myself.
191: g = new DebugGraphics(g, this );
192: }
193:
194: // Let the term do the actual work of rendering
195: term.do_paint(g);
196: }
197:
198: public Dimension getMaximumSize() {
199: return new Dimension(1000, 1000);
200: }
201:
202: //..........................................................................
203: // Accessibility stuff is all here
204: //..........................................................................
205:
206: private AccessibleContext accessible_context;
207:
208: private AccessibleScreenText accessible_screen_text;
209:
210: public AccessibleContext getAccessibleContext() {
211: if (accessible_context == null) {
212: accessible_context = new AccessibleScreen();
213: }
214: return accessible_context;
215: }
216:
217: protected class AccessibleScreenText implements AccessibleText {
218: AccessibleScreenText() {
219: }
220:
221: public int getCaretPosition() {
222: return term.CoordToPosition(term.getCursorCoord());
223: }
224:
225: // cache for getCharacterAttribute
226: private int last_attr;
227: private MutableAttributeSet last_as;
228:
229: public AttributeSet getCharacterAttribute(int index) {
230: Coord c = term.PositionToCoord(index);
231: if (c == null)
232: return null;
233: BCoord b = c.toBCoord(term.firsta);
234: int attr = 0;
235: try {
236: Line l = term.buf.lineAt(b.row);
237: int[] attrs = l.attrArray();
238: attr = attrs[b.col];
239: } catch (Exception x) {
240: ;
241: }
242:
243: if (attr == last_attr)
244: return last_as;
245:
246: MutableAttributeSet as = new SimpleAttributeSet();
247:
248: if ((attr & Attr.UNDERSCORE) == Attr.UNDERSCORE)
249: as.addAttribute(StyleConstants.Underline, Boolean.TRUE);
250: if ((attr & Attr.BRIGHT) == Attr.BRIGHT)
251: as.addAttribute(StyleConstants.Bold, Boolean.TRUE);
252:
253: boolean reverse = ((attr & Attr.REVERSE) == Attr.REVERSE);
254:
255: Color color;
256: if ((color = term.foregroundColor(reverse, attr)) != Color.black)
257: as.addAttribute(StyleConstants.Foreground, color);
258:
259: if ((color = term.backgroundColor(reverse, attr)) != null)
260: as.addAttribute(StyleConstants.Background, color);
261:
262: last_attr = attr;
263: last_as = as;
264:
265: return as;
266: }
267:
268: public Rectangle getCharacterBounds(int index) {
269: return term.getCharacterBounds(term.PositionToCoord(index));
270: }
271:
272: public int getCharCount() {
273: return term.getCharCount();
274: }
275:
276: public int getSelectionStart() {
277: Extent x = term.getSelectionExtent();
278: if (x == null)
279: return getCaretPosition();
280: return term.CoordToPosition(x.begin);
281: }
282:
283: public int getSelectionEnd() {
284: Extent x = term.getSelectionExtent();
285: if (x == null)
286: return getCaretPosition();
287: return term.CoordToPosition(x.end);
288: }
289:
290: public String getSelectedText() {
291: return term.getSelectedText();
292: }
293:
294: private String getHelper(int part, BCoord b) {
295: if (b == null)
296: return null;
297:
298: Line l = term.buf.lineAt(b.row);
299:
300: switch (part) {
301: case CHARACTER:
302: return new String(l.charArray(), b.col, 1);
303: case WORD:
304: BExtent bword = term.buf.find_word(
305: term.word_delineator, b);
306: Extent word = bword.toExtent(term.firsta);
307: return term.textWithin(word.begin, word.end);
308: case SENTENCE:
309: return new String(l.charArray());
310: }
311: return null;
312: }
313:
314: public String getAfterIndex(int part, int index) {
315: Coord c = term.PositionToCoord(index);
316: if (c == null)
317: return null;
318: BCoord b = c.toBCoord(term.firsta);
319: b = term.buf.advance(b);
320: return getHelper(part, b);
321: }
322:
323: public String getAtIndex(int part, int index) {
324: Coord c = term.PositionToCoord(index);
325: if (c == null)
326: return null;
327: BCoord b = c.toBCoord(term.firsta);
328: return getHelper(part, b);
329: }
330:
331: public String getBeforeIndex(int part, int index) {
332: Coord c = term.PositionToCoord(index);
333: if (c == null)
334: return null;
335: BCoord b = c.toBCoord(term.firsta);
336: b = term.buf.backup(b);
337: return getHelper(part, b);
338: }
339:
340: public int getIndexAtPoint(Point p) {
341: BCoord v = term.toViewCoord(p);
342: BCoord b = term.toBufCoords(v);
343: return term.CoordToPosition(new Coord(b, term.firsta));
344: }
345: }
346:
347: protected class AccessibleScreen extends AccessibleJComponent {
348: public String getAccessibleDescription() {
349: return "Terminal emulator"; // NOI18N
350: }
351:
352: public AccessibleRole getAccessibleRole() {
353: return AccessibleRole.TEXT;
354: }
355:
356: public AccessibleText getAccessibleText() {
357: if (accessible_screen_text == null)
358: accessible_screen_text = new AccessibleScreenText();
359: return accessible_screen_text;
360: }
361: }
362:
363: private int oldPos;
364:
365: void possiblyUpdateCaretText() {
366: /*
367: * Called from Term.putc_work().
368: *
369: * This is very crude. It works on the assumption that Term is just
370: * getting regular characters and on each one we modify the text and
371: * the cursor advances so we fire both simultaneously.
372: */
373:
374: // don't bother with this stuff if no-one cares
375: if (accessible_screen_text == null)
376: return;
377:
378: int pos = term.CoordToPosition(term.getCursorCoord());
379:
380: accessible_context.firePropertyChange(
381: AccessibleContext.ACCESSIBLE_TEXT_PROPERTY, null,
382: new Integer(pos));
383: // sending null, pos is how JTextComponent does it.
384:
385: accessible_context.firePropertyChange(
386: AccessibleContext.ACCESSIBLE_CARET_PROPERTY,
387: new Integer(pos), new Integer(oldPos));
388:
389: oldPos = pos;
390: }
391: }
|