null
.
35 | * @see Cloneable
36 | */
37 | ICursor clone();
38 |
39 | /**
40 | * Returns the blinking rate of this cursor.
41 | *
42 | * @return the current blinking rate, in milliseconds.
43 | * @see #setBlinkRate(int)
44 | */
45 | int getBlinkRate();
46 |
47 | /**
48 | * Returns the X-position of the cursor.
49 | *
50 | * @return a X-position, >= 0.
51 | */
52 | int getX();
53 |
54 | /**
55 | * Returns the Y-position of the cursor.
56 | *
57 | * @return a Y-position, >= 0.
58 | */
59 | int getY();
60 |
61 | /**
62 | * Returns whether or not this cursor is visible on screen.
63 | *
64 | * @return true
if this cursor is currently visible,
65 | * false
otherwise.
66 | */
67 | boolean isVisible();
68 |
69 | /**
70 | * Sets the blinking rate of this cursor.
71 | *
72 | * @param rate
73 | * a blinking rate, in milliseconds. A rate of 0 means no blinking.
74 | */
75 | void setBlinkRate( int rate );
76 |
77 | /**
78 | * Sets the visibility of the cursor.
79 | *
80 | * @param visible
81 | * true
to make the cursor visible, false
82 | * to hide the cursor.
83 | */
84 | void setVisible( boolean visible );
85 | }
86 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/ITabulator.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal;
22 |
23 |
24 | /**
25 | * Provides a tabulator that keeps track of the tab stops of a terminal.
26 | */
27 | public interface ITabulator
28 | {
29 | // METHODS
30 |
31 | /**
32 | * Clears the tab stop at the given position.
33 | *
34 | * @param position
35 | * the column position used to determine the next tab stop, > 0.
36 | */
37 | void clear( int position );
38 |
39 | /**
40 | * Clears all tab stops.
41 | */
42 | void clearAll();
43 |
44 | /**
45 | * Returns the width of the tab stop that is at or after the given position.
46 | *
47 | * @param position
48 | * the column position used to determine the next tab stop, >= 0.
49 | * @return the next tab stop width, >= 0.
50 | */
51 | int getNextTabWidth( int position );
52 |
53 | /**
54 | * Returns the width of the tab stop that is before the given position.
55 | *
56 | * @param position
57 | * the column position used to determine the previous tab stop, >= 0.
58 | * @return the previous tab stop width, >= 0.
59 | */
60 | int getPreviousTabWidth( int position );
61 |
62 | /**
63 | * Returns the next tab stop that is at or after the given position.
64 | *
65 | * @param position
66 | * the column position used to determine the next tab stop, >= 0.
67 | * @return the next tab stop, >= 0.
68 | */
69 | int nextTab( int position );
70 |
71 | /**
72 | * Returns the previous tab stop that is before the given position.
73 | *
74 | * @param position
75 | * the column position used to determine the previous tab stop, >= 0.
76 | * @return the previous tab stop, >= 0.
77 | */
78 | int previousTab( int position );
79 |
80 | /**
81 | * Sets the tab stop to the given position.
82 | *
83 | * @param position
84 | * the position of the (new) tab stop, > 0.
85 | */
86 | void set( int position );
87 | }
88 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/ITerminal.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal;
22 |
23 |
24 | import java.awt.event.*;
25 | import java.io.*;
26 |
27 |
28 | /**
29 | * Denotes a terminal, which is a text area of fixed dimensions (width and
30 | * height).
31 | */
32 | public interface ITerminal extends Closeable
33 | {
34 | // INNER TYPES
35 |
36 | /**
37 | * Used to translate keyboard codes to this terminal.
38 | */
39 | public static interface IKeyMapper
40 | {
41 | // METHODS
42 |
43 | /**
44 | * Maps a given keycode + modifiers mask into a character sequence that can
45 | * be send from this terminal.
46 | *
47 | * @param aKeyCode
48 | * the key code, as defined in {@link KeyEvent} (VK_*);
49 | * @param aModifiers
50 | * the bit mask with key modifiers, as defined in
51 | * {@link InputEvent} (*_MASK).
52 | * @return a string that maps the given key code and modifiers in a sequence
53 | * that is native to this terminal, or null
if no
54 | * mapping could be made.
55 | */
56 | String map( int aKeyCode, int aModifiers );
57 | }
58 |
59 | /**
60 | * Denotes a single 'cell' which contains a character and mark up attributes.
61 | */
62 | public static interface ITextCell
63 | {
64 | // METHODS
65 |
66 | /**
67 | * @return the background color index, >= 0 && < 32. A value of 0 means the
68 | * default background color.
69 | */
70 | int getBackground();
71 |
72 | /**
73 | * @return the contents of this cell, as UTF-8 character.
74 | */
75 | char getChar();
76 |
77 | /**
78 | * @return the foreground color index, >= 0 && < 32. A value of 0 means the
79 | * default background color.
80 | */
81 | int getForeground();
82 |
83 | /**
84 | * @return true
if the text should be presented in bold,
85 | * false
otherwise.
86 | */
87 | boolean isBold();
88 |
89 | /**
90 | * @return true
if the text should be presented in italic,
91 | * false
otherwise.
92 | */
93 | boolean isItalic();
94 |
95 | /**
96 | * @return true
if the contents of this cell are protected from
97 | * erasure, false
otherwise.
98 | */
99 | boolean isProtected();
100 |
101 | /**
102 | * @return true
if the text should be presented underlined,
103 | * false
otherwise.
104 | */
105 | boolean isUnderline();
106 |
107 | /**
108 | * @return true
if the foreground and background color should
109 | * be swapped, false
otherwise.
110 | */
111 | boolean isReverse();
112 |
113 | /**
114 | * @return true
if the text should be hidden,
115 | * false
otherwise.
116 | */
117 | boolean isHidden();
118 |
119 | }
120 |
121 | // METHODS
122 |
123 | /**
124 | * Closes this terminal and frees all of its resources.
125 | * 126 | * After a terminal has been closed, it should not be used any more. Doing 127 | * this might result in unexpected behavior. 128 | *
129 | * 130 | * @throws IOException 131 | * in case of I/O problems closing this terminal. 132 | */ 133 | void close() throws IOException; 134 | 135 | /** 136 | * Returns the cursor of this terminal, denoting the current write-position 137 | * is. 138 | * 139 | * @return the current cursor, nevernull
.
140 | */
141 | ICursor getCursor();
142 |
143 | /**
144 | * Returns the terminal front end, responsible for representing the contents
145 | * of the terminal visually.
146 | *
147 | * @return the terminal front end, can be null
if not set.
148 | */
149 | ITerminalFrontend getFrontend();
150 |
151 | /**
152 | * Returns the height of this terminal.
153 | *
154 | * @return a height, in lines.
155 | */
156 | int getHeight();
157 |
158 | /**
159 | * Returns a mapper that translated key codes.
160 | *
161 | * @return a key mapper, never null
.
162 | */
163 | IKeyMapper getKeyMapper();
164 |
165 | /**
166 | * Returns the tabulator for this terminal.
167 | *
168 | * @return the tabulator, never null
.
169 | */
170 | ITabulator getTabulator();
171 |
172 | /**
173 | * Returns the width of this terminal.
174 | *
175 | * @return a width, in columns.
176 | */
177 | int getWidth();
178 |
179 | /**
180 | * Handles the given character sequence as text that should be handled be this
181 | * terminal, for example, by regarding it as literal text, or interpreting in
182 | * some other way.
183 | *
184 | * @param chars
185 | * the character sequence to handle, cannot be null
.
186 | * @return the index until which the given character sequence is handled.
187 | * @throws IOException
188 | * in case of I/O exceptions handling the output.
189 | * @see #getCursor()
190 | */
191 | int read( CharSequence chars ) throws IOException;
192 |
193 | /**
194 | * Resets this terminal to its initial values, meaning that its content will
195 | * be cleared, the cursor will be placed in the first (upper left) position
196 | * and all implementation specific options will be reset to their default
197 | * values.
198 | */
199 | void reset();
200 |
201 | /**
202 | * Sets the terminal front end to use.
203 | *
204 | * @param frontend
205 | * the front end to use, cannot be null
.
206 | * @throws IllegalArgumentException
207 | * in case the given front end was null
.
208 | * @see #getFrontend()
209 | */
210 | void setFrontend( ITerminalFrontend frontend );
211 |
212 | /**
213 | * Handles the given character sequence as response from this terminal.
214 | *
215 | * @param chars
216 | * the character sequence to handle, cannot be null
.
217 | * @return the index until which the given character sequence is handled.
218 | * @throws IOException
219 | * in case of I/O exceptions handling the input.
220 | */
221 | int write( CharSequence chars ) throws IOException;
222 | }
223 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/ITerminalColorScheme.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal;
22 |
23 |
24 | import java.awt.*;
25 |
26 |
27 | /**
28 | * Provides the terminal colors.
29 | */
30 | public interface ITerminalColorScheme
31 | {
32 | // METHODS
33 |
34 | /**
35 | * Returns the background color.
36 | *
37 | * @return the background color, never null
.
38 | * @see #setInverted(boolean)
39 | */
40 | Color getBackgroundColor();
41 |
42 | /**
43 | * Returns the foreground color by its numeric index. There are supposed to be
44 | * 8 different foreground colors.
45 | *
46 | * @param aIndex
47 | * the index of the color to return, >= 0 && < 8.
48 | * @return a foreground color, never null
.
49 | */
50 | Color getColorByIndex( int aIndex );
51 |
52 | /**
53 | * Returns the foreground color.
54 | *
55 | * @return the plain text color, never null
.
56 | * @see #setInverted(boolean)
57 | */
58 | Color getTextColor();
59 |
60 | /**
61 | * Sets whether or not the foreground and background color are to be swapped.
62 | *
63 | * @param inverted
64 | * true
if the background color should become the
65 | * foreground color and the other way around, false
66 | * otherwise.
67 | */
68 | void setInverted( boolean inverted );
69 | }
70 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/ITerminalFrontend.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal;
22 |
23 |
24 | import java.awt.*;
25 | import java.io.*;
26 | import java.util.*;
27 |
28 | import nl.lxtreme.jvt220.terminal.ITerminal.ITextCell;
29 |
30 |
31 | /**
32 | * Denotes a front end for a terminal, which is responsible for representing the
33 | * contents of a terminal visually, e.g, by means of an GUI.
34 | * 35 | * A terminal front end is considered to be responsible for handling the I/O 36 | * between user and terminal. 37 | *
38 | */ 39 | public interface ITerminalFrontend 40 | { 41 | // METHODS 42 | 43 | /** 44 | * Connects this front end to the given input and output streams. 45 | *46 | * This method will start a background thread to read continuously from the 47 | * given input stream. 48 | *
49 | * 50 | * @param inputStream 51 | * the input stream to read the data that should be passed to the 52 | * terminal, cannot benull
;
53 | * @param outputStream
54 | * the output stream to write the data back coming from the terminal,
55 | * cannot be null
.
56 | * @throws IOException
57 | * in case of I/O problems connecting the given input and output
58 | * streams.
59 | */
60 | void connect( InputStream inputStream, OutputStream outputStream ) throws IOException;
61 |
62 | /**
63 | * Connects this frontend to a given output stream.
64 | * 65 | * NOTE: when using this method, you need to explicitly call 66 | * {@link #writeCharacters(Integer...)} yourself in order to let anything 67 | * appear on the terminal. 68 | *
69 | * 70 | * @param outputStream 71 | * the output stream to write the data back coming from the terminal, 72 | * cannot benull
.
73 | * @throws IOException
74 | * in case of I/O problems connecting the given output streams.
75 | */
76 | void connect( OutputStream outputStream ) throws IOException;
77 |
78 | /**
79 | * Disconnects this front end from a connected input and output stream.
80 | *
81 | * @throws IOException
82 | * in case of I/O problems disconnecting.
83 | */
84 | void disconnect() throws IOException;
85 |
86 | /**
87 | * Returns the maximum possible size of the terminal in columns and lines to
88 | * fit on this frontend.
89 | *
90 | * @return the maximum dimensions, never null
.
91 | */
92 | Dimension getMaximumTerminalSize();
93 |
94 | /**
95 | * Returns the width and height of the terminal in pixels.
96 | *
97 | * @return the current dimensions, never null
.
98 | */
99 | Dimension getSize();
100 |
101 | /**
102 | * Returns the {@link Writer} to write responses from the terminal to.
103 | *
104 | * @return a {@link Writer}, can be null
in case this front end
105 | * is not connected yet, or disconnected.
106 | */
107 | Writer getWriter();
108 |
109 | /**
110 | * @return true
if this frontend is able to listen to changes,
111 | * false
if not.
112 | */
113 | boolean isListening();
114 |
115 | /**
116 | * Sets whether or not the foreground and background colors should be
117 | * reversed.
118 | *
119 | * @param reverse
120 | * true
to reverse the foreground and background colors,
121 | * false
otherwise.
122 | */
123 | void setReverse( boolean reverse );
124 |
125 | /**
126 | * Sets the terminal dimensions in pixels.
127 | *
128 | * @param width
129 | * the new width in pixels, > 0;
130 | * @param height
131 | * the new height in pixels, > 0.
132 | */
133 | void setSize( int width, int height );
134 |
135 | /**
136 | * Sets the terminal for this frontend.
137 | *
138 | * @param terminal
139 | * the terminal to connect, cannot be null
.
140 | */
141 | void setTerminal( ITerminal terminal );
142 |
143 | /**
144 | * Called by {@link ITerminal} to notify this frontend that it has changed.
145 | *
146 | * @param cells
147 | * the array with text cells representing the contents of the
148 | * terminal, never null
;
149 | * @param heatMap
150 | * the "heat" map, representing all changed cells of the terminal,
151 | * never null
.
152 | */
153 | void terminalChanged( ITextCell[] cells, BitSet heatMap );
154 |
155 | /**
156 | * Called by {@link ITerminal} to notify the dimensions of the terminal have
157 | * changed.
158 | *
159 | * @param columns
160 | * the new number of columns, > 0;
161 | * @param alines
162 | * the new number of lines, > 0.
163 | */
164 | void terminalSizeChanged( int columns, int alines );
165 |
166 | /**
167 | * Writes the given array of characters directly to the terminal, similar as
168 | * writing to the standard output.
169 | *
170 | * @param chars
171 | * the array with characters to write, cannot be null
.
172 | * @throws IOException
173 | * in case of I/O problems writing to the terminal.
174 | */
175 | void writeCharacters( Integer... chars ) throws IOException;
176 |
177 | /**
178 | * Writes the given sequence of characters directly to the terminal, similar
179 | * as writing to the standard output.
180 | *
181 | * @param chars
182 | * the sequence of characters to write, cannot be null
.
183 | * @throws IOException
184 | * in case of I/O problems writing to the terminal.
185 | */
186 | void writeCharacters( CharSequence chars ) throws IOException;
187 | }
188 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/packageinfo:
--------------------------------------------------------------------------------
1 | version 1.1
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/swing/CharBuffer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal.swing;
22 |
23 |
24 | import java.util.*;
25 | import java.util.concurrent.atomic.*;
26 |
27 |
28 | /**
29 | * Provides a thread-safe character buffer which can append a list of
30 | * characters, or remove a number of characters from the front.
31 | * 32 | * This buffer is designed for atomicity regarding the appending and removal of 33 | * items. There are no ordering guarantees when multiple threads are 34 | * concurrently appending, the only thing that is guaranteed is that the append 35 | * itself is performed atomicly. 36 | *
37 | */ 38 | final class CharBuffer implements CharSequence 39 | { 40 | // INNER TYPES 41 | 42 | /** 43 | * Contains the state of the char buffer itself. 44 | */ 45 | private static class CharBufferState 46 | { 47 | // VARIABLES 48 | 49 | final Integer[] chars; 50 | final int appendPos; 51 | 52 | // CONSTRUCTORS 53 | 54 | /** 55 | * Creates a new {@link CharBufferState} instance. 56 | */ 57 | public CharBufferState( final Integer[] aChars, final int aAppendPos ) 58 | { 59 | this.chars = aChars; 60 | this.appendPos = aAppendPos; 61 | } 62 | } 63 | 64 | // VARIABLES 65 | 66 | private final AtomicReferencenull
.
83 | * @throws IllegalArgumentException
84 | * in case the given list was null
.
85 | */
86 | public CharBuffer( final Integer... aInitialValue )
87 | {
88 | if ( aInitialValue == null )
89 | {
90 | throw new IllegalArgumentException( "InitialValue cannot be null!" );
91 | }
92 |
93 | Integer[] initialValue = Arrays.copyOf( aInitialValue, aInitialValue.length + 10 );
94 | int appendPos = aInitialValue.length;
95 |
96 | this.stateRef = new AtomicReferencenull
.
106 | * @throws IllegalArgumentException
107 | * in case the given list was null
.
108 | */
109 | public void append( final Integer... aChars )
110 | {
111 | if ( aChars == null )
112 | {
113 | throw new IllegalArgumentException( "Chars cannot be null!" );
114 | }
115 |
116 | // Two options for concurrency while we're appending:
117 | // 1) another thread removed data -> append still is valid;
118 | // 2) another thread also appended new data -> append is still valid.
119 |
120 | CharBufferState curState, newState;
121 |
122 | do
123 | {
124 | curState = this.stateRef.get();
125 | Integer[] curArray = curState.chars;
126 | int curAppendPos = curState.appendPos;
127 |
128 | if ( ( curAppendPos + aChars.length ) >= curArray.length )
129 | {
130 | // Enlarge array...
131 | curArray = Arrays.copyOf( curArray, curArray.length + aChars.length + 10 );
132 | }
133 |
134 | System.arraycopy( aChars, 0, curArray, curAppendPos, aChars.length );
135 |
136 | newState = new CharBufferState( curArray, curAppendPos + aChars.length );
137 | }
138 | while ( !this.stateRef.compareAndSet( curState, newState ) );
139 | }
140 |
141 | /**
142 | * {@inheritDoc}
143 | */
144 | @Override
145 | public char charAt( final int aIndex )
146 | {
147 | CharBufferState state = this.stateRef.get();
148 | Integer[] chars = state.chars;
149 | int appendPos = state.appendPos;
150 |
151 | if ( ( aIndex < 0 ) || ( aIndex >= appendPos ) )
152 | {
153 | throw new IndexOutOfBoundsException();
154 | }
155 |
156 | final Integer integer = chars[aIndex];
157 | return ( char )( ( integer == null ) ? 0 : integer.intValue() );
158 | }
159 |
160 | /**
161 | * {@inheritDoc}
162 | */
163 | @Override
164 | public int length()
165 | {
166 | CharBufferState state = this.stateRef.get();
167 | return state.appendPos;
168 | }
169 |
170 | /**
171 | * Removes all characters until (but not including) the given position.
172 | *
173 | * @param aPosition
174 | * the position until which the characters should be removed, with 0
175 | * meaning nothing will be removed, 1 meaning the first character
176 | * will be removed, and so on.
177 | */
178 | public void removeUntil( final int aPosition )
179 | {
180 | CharBufferState curState, newState;
181 | int oldAppendPos = -1;
182 |
183 | // Two options for concurrency while we're removing:
184 | // __ 1) another thread appends new data -> remove still is valid;
185 | // __ 2) another thread also removes data:
186 | // ____ a) it removed data up to our position -> remove still is valid;
187 | // ____ b) it removed data beyond our position -> our no longer holds.
188 |
189 | do
190 | {
191 | curState = this.stateRef.get();
192 |
193 | int position = aPosition;
194 | int curAppendPos = curState.appendPos;
195 | Integer[] curArray = curState.chars;
196 |
197 | if ( oldAppendPos >= 0 )
198 | {
199 | // CAS failed, find out what the resolution should be...
200 | if ( oldAppendPos > curAppendPos )
201 | {
202 | // Data is removed, do only remove the difference in positions...
203 | position -= ( oldAppendPos - curAppendPos );
204 | }
205 |
206 | // In case oldAppendPos < appendPos, there's data appended, which makes
207 | // our remove still valid. In case oldAppendPos == appendPos, then
208 | // something strange is going on, as we've lost our CAS, but neither an
209 | // add, nor an remove is performed. In that case, simply proceed as
210 | // normal...
211 | }
212 | else
213 | {
214 | // First time we're in this loop; do a simple bounds check...
215 | if ( ( position < 0 ) || ( position > curAppendPos ) )
216 | {
217 | throw new IndexOutOfBoundsException( "Position cannot be negative or beyond the length of this buffer!" );
218 | }
219 | }
220 |
221 | // Initially, position cannot be less than zero (is checked in the above
222 | // else condition), however, if the CAS fails, we might have updated the
223 | // position thereby making it possible to let it be negative...
224 | if ( position <= 0 )
225 | {
226 | // Nothing to do...
227 | return;
228 | }
229 |
230 | // Perform the actual removal...
231 | int newSize = Math.max( 0, curArray.length - position );
232 | Integer[] newArray = new Integer[newSize];
233 | System.arraycopy( curArray, position, newArray, 0, newSize );
234 |
235 | int newAppendPos = Math.max( 0, curAppendPos - position );
236 |
237 | newState = new CharBufferState( newArray, newAppendPos );
238 |
239 | oldAppendPos = curAppendPos;
240 | }
241 | while ( !this.stateRef.compareAndSet( curState, newState ) );
242 | }
243 |
244 | /**
245 | * {@inheritDoc}
246 | */
247 | @Override
248 | public CharSequence subSequence( final int aStart, final int aEnd )
249 | {
250 | throw new UnsupportedOperationException();
251 | }
252 |
253 | /**
254 | * {@inheritDoc}
255 | */
256 | @Override
257 | public String toString()
258 | {
259 | CharBufferState state = this.stateRef.get();
260 | StringBuilder sb = new StringBuilder();
261 | for ( int i = 0; i < state.appendPos; i++ )
262 | {
263 | int c = state.chars[i].intValue();
264 | if ( c >= ' ' && c <= '~' )
265 | {
266 | sb.append( ( char )c );
267 | }
268 | else
269 | {
270 | sb.append( "<" ).append( c ).append( ">" );
271 | }
272 | }
273 | return sb.toString();
274 | }
275 | }
276 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/swing/SwingFrontend.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal.swing;
22 |
23 |
24 | import java.awt.*;
25 | import java.awt.event.*;
26 | import java.awt.font.*;
27 | import java.awt.image.*;
28 | import java.io.*;
29 | import java.text.*;
30 | import java.util.*;
31 | import java.util.List;
32 |
33 | import javax.swing.*;
34 |
35 | import nl.lxtreme.jvt220.terminal.*;
36 | import nl.lxtreme.jvt220.terminal.ITerminal.ITextCell;
37 |
38 |
39 | /**
40 | * Provides a Swing frontend for {@link ITerminal}.
41 | */
42 | public class SwingFrontend extends JComponent implements ITerminalFrontend
43 | {
44 | // INNER TYPES
45 |
46 | /**
47 | * Small container for the width, height and line spacing of a single
48 | * character.
49 | */
50 | static final class CharacterDimensions
51 | {
52 | final int m_height;
53 | final int m_width;
54 | final int m_lineSpacing;
55 |
56 | /**
57 | * Creates a new {@link CharacterDimensions} instance.
58 | *
59 | * @param width
60 | * the width of a single character, in pixels;
61 | * @param height
62 | * the height of a single character, in pixels;
63 | * @param lineSpacing
64 | * the spacing to use between two lines with characters, in pixels.
65 | */
66 | public CharacterDimensions( int width, int height, int lineSpacing )
67 | {
68 | m_width = width;
69 | m_height = height;
70 | m_lineSpacing = lineSpacing;
71 | }
72 | }
73 |
74 | /**
75 | * Asynchronous worker that reads data from an input stream and passes this to
76 | * the terminal backend.
77 | */
78 | final class InputStreamWorker extends SwingWorkernull
;
91 | * @param encoding
92 | * the character encoding to use for the read input, cannot be
93 | * null
.
94 | */
95 | public InputStreamWorker( final InputStream inputStream, String encoding ) throws IOException
96 | {
97 | m_reader = new InputStreamReader( inputStream, encoding );
98 | }
99 |
100 | // METHODS
101 |
102 | @Override
103 | protected Void doInBackground() throws Exception
104 | {
105 | while ( !isCancelled() && !Thread.currentThread().isInterrupted() )
106 | {
107 | int r = m_reader.read();
108 | if ( r > 0 )
109 | {
110 | publish( Integer.valueOf( r ) );
111 | }
112 | }
113 | return null;
114 | }
115 |
116 | @Override
117 | protected void process( final Listnull
.
199 | * @return an array of length 2, containing the character width and height (in
200 | * that order).
201 | */
202 | private static CharacterDimensions getCharacterDimensions( Font aFont )
203 | {
204 | BufferedImage im = new BufferedImage( 1, 1, BufferedImage.TYPE_INT_ARGB );
205 |
206 | Graphics2D g2d = im.createGraphics();
207 | g2d.setFont( aFont );
208 | FontMetrics fm = g2d.getFontMetrics();
209 | g2d.dispose();
210 | im.flush();
211 |
212 | int w = fm.charWidth( '@' );
213 | int h = fm.getAscent() + fm.getDescent();
214 |
215 | return new CharacterDimensions( w, h, fm.getLeading() + 1 );
216 | }
217 |
218 | /**
219 | * Calculates the union of two rectangles, allowing null
values
220 | * to be passed in.
221 | *
222 | * @param rect1
223 | * the 1st rectangle to create the union, if null
, the
224 | * 2nd argument will be returned;
225 | * @param rect2
226 | * the 2nd rectangle to create the union, if null
, the
227 | * 1st argument will be returned.
228 | * @return the union of the two given rectangles.
229 | */
230 | private static Rectangle union( Rectangle rect1, Rectangle rect2 )
231 | {
232 | if ( rect2 == null )
233 | {
234 | return rect1;
235 | }
236 | if ( rect1 == null )
237 | {
238 | return rect2;
239 | }
240 |
241 | return rect1.union( rect2 );
242 | }
243 |
244 | /**
245 | * Connects this frontend to a given input and output stream.
246 | * 247 | * This method will start a background thread to read continuously from the 248 | * given input stream. 249 | *
250 | * 251 | * @param inputStream 252 | * the input stream to connect to, cannot benull
;
253 | * @param outputStream
254 | * the output stream to connect to, cannot be null
.
255 | * @throws IOException
256 | * in case of I/O problems.
257 | */
258 | @Override
259 | public void connect( InputStream inputStream, OutputStream outputStream ) throws IOException
260 | {
261 | if ( inputStream == null )
262 | {
263 | throw new IllegalArgumentException( "Input stream cannot be null!" );
264 | }
265 | if ( outputStream == null )
266 | {
267 | throw new IllegalArgumentException( "Output stream cannot be null!" );
268 | }
269 |
270 | disconnect();
271 |
272 | m_writer = new OutputStreamWriter( outputStream, m_encoding );
273 |
274 | m_inputStreamWorker = new InputStreamWorker( inputStream, m_encoding );
275 | m_inputStreamWorker.execute();
276 |
277 | setEnabled( true );
278 | }
279 |
280 | /**
281 | * Connects this frontend to a given output stream.
282 | * 283 | * NOTE: when using this method, you need to explicitly call 284 | * {@link #writeCharacters(Integer...)} yourself in order to let anything 285 | * appear on the terminal. 286 | *
287 | * 288 | * @param outputStream 289 | * the output stream to connect to, cannot benull
.
290 | * @throws IOException
291 | * in case of I/O problems.
292 | */
293 | @Override
294 | public void connect( OutputStream outputStream ) throws IOException
295 | {
296 | if ( outputStream == null )
297 | {
298 | throw new IllegalArgumentException( "Output stream cannot be null!" );
299 | }
300 |
301 | disconnect();
302 |
303 | m_writer = new OutputStreamWriter( outputStream, m_encoding );
304 |
305 | setEnabled( true );
306 | }
307 |
308 | /**
309 | * Disconnects this frontend from any input and output stream.
310 | *
311 | * @throws IOException
312 | * in case of I/O problems.
313 | */
314 | @Override
315 | public void disconnect() throws IOException
316 | {
317 | try
318 | {
319 | if ( m_inputStreamWorker != null )
320 | {
321 | m_inputStreamWorker.cancel( true /* mayInterruptIfRunning */);
322 | m_inputStreamWorker = null;
323 | }
324 | if ( m_writer != null )
325 | {
326 | m_writer.close();
327 | m_writer = null;
328 | }
329 | }
330 | finally
331 | {
332 | setEnabled( false );
333 | }
334 | }
335 |
336 | /**
337 | * {@inheritDoc}
338 | */
339 | @Override
340 | public Dimension getMaximumTerminalSize()
341 | {
342 | Rectangle bounds = getGraphicsConfiguration().getBounds();
343 | Insets insets = calculateTotalInsets();
344 |
345 | int width = bounds.width - insets.left - insets.right;
346 | int height = bounds.height - insets.top - insets.bottom;
347 |
348 | CharacterDimensions charDims = m_charDims;
349 |
350 | // Calculate the maximum number of columns & lines...
351 | int columns = width / charDims.m_width;
352 | int lines = height / ( charDims.m_height + charDims.m_lineSpacing );
353 |
354 | return new Dimension( columns, lines );
355 | }
356 |
357 | /**
358 | * Returns the current terminal.
359 | *
360 | * @return the terminal, can be null
.
361 | */
362 | public ITerminal getTerminal()
363 | {
364 | return m_terminal;
365 | }
366 |
367 | /**
368 | * {@inheritDoc}
369 | */
370 | @Override
371 | public Writer getWriter()
372 | {
373 | return m_writer;
374 | }
375 |
376 | /**
377 | * {@inheritDoc}
378 | */
379 | @Override
380 | public boolean isListening()
381 | {
382 | return m_listening;
383 | }
384 |
385 | /**
386 | * {@inheritDoc}
387 | */
388 | @Override
389 | public void setFont( Font font )
390 | {
391 | super.setFont( font );
392 |
393 | m_charDims = getCharacterDimensions( font );
394 | }
395 |
396 | /**
397 | * @see nl.lxtreme.jvt220.terminal.ITerminalFrontend#setReverse(boolean)
398 | */
399 | @Override
400 | public void setReverse( boolean reverse )
401 | {
402 | m_colorScheme.setInverted( reverse );
403 | }
404 |
405 | /**
406 | * Sets the size of this component in pixels. Overridden in order to redirect
407 | * this call to {@link #terminalSizeChanged(int, int)} with the correct number
408 | * of columns and lines.
409 | */
410 | @Override
411 | public void setSize( int width, int height )
412 | {
413 | Rectangle bounds = getGraphicsConfiguration().getBounds();
414 | Insets insets = calculateTotalInsets();
415 |
416 | if ( width == 0 )
417 | {
418 | width = bounds.width - insets.left - insets.right;
419 | }
420 | else if ( width < 0 )
421 | {
422 | width = getWidth();
423 | }
424 | if ( height == 0 )
425 | {
426 | height = bounds.height - insets.top - insets.bottom;
427 | }
428 | else if ( height < 0 )
429 | {
430 | height = getHeight();
431 | }
432 |
433 | CharacterDimensions charDims = m_charDims;
434 |
435 | int columns = width / charDims.m_width;
436 | int lines = height / ( charDims.m_height + charDims.m_lineSpacing );
437 |
438 | terminalSizeChanged( columns, lines );
439 | }
440 |
441 | /**
442 | * {@inheritDoc}
443 | */
444 | @Override
445 | public void setTerminal( ITerminal terminal )
446 | {
447 | if ( terminal == null )
448 | {
449 | throw new IllegalArgumentException( "Terminal cannot be null!" );
450 | }
451 | m_terminal = terminal;
452 | m_terminal.setFrontend( this );
453 | }
454 |
455 | /**
456 | * {@inheritDoc}
457 | */
458 | @Override
459 | public void terminalChanged( final ITextCell[] cells, final BitSet heatMap )
460 | {
461 | SwingUtilities.invokeLater( new Runnable()
462 | {
463 | @Override
464 | public void run()
465 | {
466 | updateTerminalImage( cells, heatMap );
467 | }
468 | } );
469 | }
470 |
471 | /**
472 | * {@inheritDoc}
473 | */
474 | @Override
475 | public void terminalSizeChanged( final int columns, final int lines )
476 | {
477 | SwingUtilities.invokeLater( new Runnable()
478 | {
479 | @Override
480 | public void run()
481 | {
482 | recreateTerminalImage( columns, lines, true /* forceRepaint */ );
483 | }
484 | } );
485 | }
486 |
487 | /**
488 | * Writes the given sequence of characters directly to the terminal, similar
489 | * as writing to the standard output.
490 | *
491 | * @param charSeq
492 | * the sequence of characters to write, cannot be null
.
493 | * @throws IOException
494 | * in case of I/O problems writing to the terminal.
495 | */
496 | @Override
497 | public void writeCharacters( CharSequence charSeq ) throws IOException
498 | {
499 | InputStream is = new ByteArrayInputStream( charSeq.toString().getBytes() );
500 | InputStreamReader isr = new InputStreamReader( is, m_encoding );
501 |
502 | int ch;
503 | while ( ( ch = isr.read() ) >= 0 )
504 | {
505 | writeCharacters( ch );
506 | }
507 | }
508 |
509 | /**
510 | * Writes the given array of characters directly to the terminal, similar as
511 | * writing to the standard output.
512 | *
513 | * @param chars
514 | * the characters to write, cannot be null
.
515 | * @throws IOException
516 | * in case of I/O problems writing to the terminal.
517 | */
518 | @Override
519 | public void writeCharacters( Integer... chars ) throws IOException
520 | {
521 | m_buffer.append( chars );
522 |
523 | int n = m_terminal.read( m_buffer );
524 |
525 | m_buffer.removeUntil( n );
526 | }
527 |
528 | /**
529 | * Recreates the terminal image.
530 | *
531 | * @param columns
532 | * the new number of columns, > 0;
533 | * @param lines
534 | * the new number of lines, > 0.
535 | */
536 | void recreateTerminalImage( int columns, int lines, boolean forceRepaint )
537 | {
538 | assert SwingUtilities.isEventDispatchThread() : "Should be called from EDT only!";
539 |
540 | final Dimension dims = calculateSizeInPixels( columns, lines );
541 |
542 | if ( ( m_image == null ) || ( m_image.getWidth() != dims.width ) || ( m_image.getHeight() != dims.height ) )
543 | {
544 | if ( m_image != null )
545 | {
546 | m_image.flush();
547 | }
548 | m_image = getGraphicsConfiguration().createCompatibleImage( dims.width, dims.height );
549 |
550 | Graphics2D canvas = m_image.createGraphics();
551 |
552 | try
553 | {
554 | canvas.setBackground( m_colorScheme.getBackgroundColor() );
555 | canvas.clearRect( 0, 0, m_image.getWidth(), m_image.getHeight() );
556 | }
557 | finally
558 | {
559 | canvas.dispose();
560 | canvas = null;
561 | }
562 |
563 | // Update the size of this component as well...
564 | Insets insets = getInsets();
565 | super.setSize( dims.width + insets.left + insets.right, dims.height + insets.top + insets.bottom );
566 |
567 | repaint( 50L );
568 | }
569 | }
570 |
571 | /**
572 | * Updates the image representing the terminal contents.
573 | *
574 | * @param cells
575 | * the text cells;
576 | * @param heatMap
577 | * the heat map denoting the changed cells.
578 | */
579 | void updateTerminalImage( ITextCell[] cells, BitSet heatMap )
580 | {
581 | assert SwingUtilities.isEventDispatchThread() : "Should be called from the EDT only!";
582 |
583 | final int columns = m_terminal.getWidth();
584 | final int lines = m_terminal.getHeight();
585 |
586 | // Create copies of these data items to ensure they remain constant for
587 | // the remainder of this method...
588 | CharacterDimensions charDims = m_charDims;
589 |
590 | int cw = charDims.m_width;
591 | int ch = charDims.m_height;
592 | int ls = charDims.m_lineSpacing;
593 |
594 | if ( m_image == null )
595 | {
596 | // Ensure there's a valid image to paint on...
597 | recreateTerminalImage( columns, lines, false /* forceRepaint */ );
598 | }
599 |
600 | final Graphics2D canvas = m_image.createGraphics();
601 | canvas.setFont( getFont() );
602 |
603 | final Font font = getFont();
604 | final FontMetrics fm = canvas.getFontMetrics();
605 | final FontRenderContext frc = new FontRenderContext( null, true /* aa */, true /* fractionalMetrics */);
606 |
607 | Color cursorColor = null;
608 | Rectangle repaintArea = null;
609 |
610 | if ( m_oldCursor != null )
611 | {
612 | repaintArea = drawCursor( canvas, m_oldCursor, m_colorScheme.getBackgroundColor() );
613 | }
614 |
615 | for ( int i = 0; i < cells.length; i++ )
616 | {
617 | boolean cellChanged = heatMap.get( i );
618 | if ( cellChanged )
619 | {
620 | // Cell is changed...
621 | final ITextCell cell = cells[i];
622 |
623 | final int x = ( i % columns ) * cw;
624 | final int y = ( i / columns ) * ( ch + ls );
625 |
626 | final Rectangle rect = new Rectangle( x, y, cw, ch + ls );
627 |
628 | canvas.setColor( convertToColor( cell.getBackground(), m_colorScheme.getBackgroundColor() ) );
629 | canvas.fillRect( rect.x, rect.y, rect.width, rect.height );
630 |
631 | final String txt = Character.toString( cell.getChar() );
632 |
633 | AttributedString attrStr = new AttributedString( txt );
634 | cursorColor = applyAttributes( cell, attrStr, font );
635 |
636 | AttributedCharacterIterator characterIterator = attrStr.getIterator();
637 | LineBreakMeasurer measurer = new LineBreakMeasurer( characterIterator, frc );
638 |
639 | while ( measurer.getPosition() < characterIterator.getEndIndex() )
640 | {
641 | TextLayout textLayout = measurer.nextLayout( getWidth() );
642 | textLayout.draw( canvas, x, y + fm.getAscent() );
643 | }
644 |
645 | repaintArea = union( repaintArea, rect );
646 | }
647 | }
648 |
649 | // Draw the cursor...
650 | m_oldCursor = m_terminal.getCursor().clone();
651 | if ( cursorColor == null )
652 | {
653 | cursorColor = m_colorScheme.getTextColor();
654 | }
655 |
656 | repaintArea = union( repaintArea, drawCursor( canvas, m_oldCursor, cursorColor ) );
657 |
658 | // Free the resources...
659 | canvas.dispose();
660 |
661 | if ( repaintArea != null )
662 | {
663 | repaintArea.grow( 5, 3 );
664 | repaint( repaintArea );
665 | }
666 | }
667 |
668 | /**
669 | * Maps the keyboard to respond to keys like 'up', 'down' and the function
670 | * keys.
671 | */
672 | protected void mapKeyboard()
673 | {
674 | mapKeystroke( KeyEvent.VK_UP );
675 | mapKeystroke( KeyEvent.VK_DOWN );
676 | mapKeystroke( KeyEvent.VK_RIGHT );
677 | mapKeystroke( KeyEvent.VK_LEFT );
678 |
679 | mapKeystroke( KeyEvent.VK_PAGE_DOWN );
680 | mapKeystroke( KeyEvent.VK_PAGE_UP );
681 | mapKeystroke( KeyEvent.VK_HOME );
682 | mapKeystroke( KeyEvent.VK_END );
683 |
684 | mapKeystroke( KeyEvent.VK_NUMPAD0 );
685 | mapKeystroke( KeyEvent.VK_NUMPAD1 );
686 | mapKeystroke( KeyEvent.VK_NUMPAD2 );
687 | mapKeystroke( KeyEvent.VK_NUMPAD3 );
688 | mapKeystroke( KeyEvent.VK_NUMPAD4 );
689 | mapKeystroke( KeyEvent.VK_NUMPAD5 );
690 | mapKeystroke( KeyEvent.VK_NUMPAD6 );
691 | mapKeystroke( KeyEvent.VK_NUMPAD7 );
692 | mapKeystroke( KeyEvent.VK_NUMPAD8 );
693 | mapKeystroke( KeyEvent.VK_NUMPAD9 );
694 | mapKeystroke( KeyEvent.VK_MINUS );
695 | mapKeystroke( KeyEvent.VK_PLUS );
696 | mapKeystroke( KeyEvent.VK_COMMA );
697 | mapKeystroke( KeyEvent.VK_PERIOD );
698 | mapKeystroke( KeyEvent.VK_ENTER );
699 | mapKeystroke( KeyEvent.VK_KP_DOWN );
700 | mapKeystroke( KeyEvent.VK_KP_LEFT );
701 | mapKeystroke( KeyEvent.VK_KP_RIGHT );
702 | mapKeystroke( KeyEvent.VK_KP_UP );
703 |
704 | mapKeystroke( KeyEvent.VK_F1 );
705 | mapKeystroke( KeyEvent.VK_F1, InputEvent.ALT_DOWN_MASK );
706 | mapKeystroke( KeyEvent.VK_F2 );
707 | mapKeystroke( KeyEvent.VK_F2, InputEvent.ALT_DOWN_MASK );
708 | mapKeystroke( KeyEvent.VK_F3 );
709 | mapKeystroke( KeyEvent.VK_F3, InputEvent.ALT_DOWN_MASK );
710 | mapKeystroke( KeyEvent.VK_F4 );
711 | mapKeystroke( KeyEvent.VK_F4, InputEvent.ALT_DOWN_MASK );
712 | mapKeystroke( KeyEvent.VK_F5 );
713 | mapKeystroke( KeyEvent.VK_F6 );
714 | mapKeystroke( KeyEvent.VK_F7 );
715 | mapKeystroke( KeyEvent.VK_F8 );
716 | mapKeystroke( KeyEvent.VK_F9 );
717 | mapKeystroke( KeyEvent.VK_F10 );
718 | mapKeystroke( KeyEvent.VK_F11 );
719 | mapKeystroke( KeyEvent.VK_F12 );
720 | }
721 |
722 | /**
723 | * Maps the given keycode and modifiers to a terminal specific sequence.
724 | *
725 | * @param keycode
726 | * the keycode to map;
727 | * @param modifiers
728 | * the modifiers to map.
729 | * @return a terminal-specific sequence for the given input, or
730 | * null
if no specific sequence exists for this terminal.
731 | */
732 | protected String mapKeyCode( int keycode, int modifiers )
733 | {
734 | return getTerminal().getKeyMapper().map( keycode, modifiers );
735 | }
736 |
737 | /**
738 | * Creates a key mapping for the given keystroke and the given action which is
739 | * send as literal text to the terminal.
740 | *
741 | * @param keycode
742 | * the keycode to map.
743 | */
744 | protected void mapKeystroke( int keycode )
745 | {
746 | mapKeystroke( keycode, 0 );
747 | }
748 |
749 | /**
750 | * Creates a key mapping for the given keystroke and the given action which is
751 | * send as literal text to the terminal.
752 | *
753 | * @param keycode
754 | * the keycode to map;
755 | * @param modifiers
756 | * the modifiers to map.
757 | */
758 | protected void mapKeystroke( int keycode, int modifiers )
759 | {
760 | final KeyStroke keystroke = KeyStroke.getKeyStroke( keycode, modifiers );
761 | final String key = keystroke.toString();
762 |
763 | getInputMap().put( keystroke, key );
764 | }
765 |
766 | /**
767 | * {@inheritDoc}
768 | */
769 | @Override
770 | protected void paintComponent( Graphics canvas )
771 | {
772 | m_listening = false;
773 |
774 | canvas.setColor( m_colorScheme.getBackgroundColor() );
775 |
776 | Rectangle clip = canvas.getClipBounds();
777 | canvas.fillRect( clip.x, clip.y, clip.width, clip.height );
778 |
779 | try
780 | {
781 | Insets insets = getInsets();
782 |
783 | canvas.drawImage( m_image, insets.left, insets.top, null /* observer */);
784 | }
785 | finally
786 | {
787 | m_listening = true;
788 | }
789 | }
790 |
791 | /**
792 | * {@inheritDoc}
793 | */
794 | @Override
795 | protected boolean processKeyBinding( KeyStroke keystroke, KeyEvent event, int condition, boolean pressed )
796 | {
797 | if ( !isEnabled() )
798 | {
799 | // Don't bother to process keys for a disabled component...
800 | return false;
801 | }
802 |
803 | InputMap inputMap = getInputMap( condition );
804 | ActionMap actionMap = getActionMap();
805 |
806 | try
807 | {
808 | if ( ( inputMap != null ) && ( actionMap != null ) && ( event.getID() == KeyEvent.KEY_PRESSED ) )
809 | {
810 | Object binding = inputMap.get( keystroke );
811 | if ( binding != null )
812 | {
813 | Action action = actionMap.get( binding );
814 | if ( action != null )
815 | {
816 | // Normal action; invoke it...
817 | return SwingUtilities.notifyAction( action, keystroke, event, this, event.getModifiers() );
818 | }
819 | else
820 | {
821 | // Keystroke we've mapped without an action, means we're going to
822 | // test whether there's a mapping for it. If so, use that as
823 | // response, otherwise respond with the 'regular' key...
824 | String mapping = mapKeyCode( keystroke.getKeyCode(), keystroke.getModifiers() );
825 | if ( mapping != null )
826 | {
827 | respond( mapping );
828 | return true;
829 | }
830 | }
831 | }
832 |
833 | if ( isRegularKey( keystroke ) )
834 | {
835 | respond( event.getKeyChar() );
836 | return true;
837 | }
838 | }
839 | }
840 | catch ( IOException exception )
841 | {
842 | exception.printStackTrace(); // XXX
843 | }
844 |
845 | return false;
846 | }
847 |
848 | /**
849 | * Writes a given number of characters to the terminal.
850 | *
851 | * @param chars
852 | * the characters to write, cannot be null
.
853 | * @throws IOException
854 | * in case of I/O problems responding.
855 | */
856 | protected void respond( char ch ) throws IOException
857 | {
858 | if ( m_writer != null )
859 | {
860 | m_writer.write( ch );
861 | m_writer.flush();
862 | }
863 | }
864 |
865 | /**
866 | * Writes a given number of characters to the terminal.
867 | *
868 | * @param chars
869 | * the characters to write, cannot be null
.
870 | * @throws IOException
871 | * in case of I/O problems responding.
872 | */
873 | protected void respond( String chars ) throws IOException
874 | {
875 | if ( m_writer != null )
876 | {
877 | m_writer.write( chars );
878 | m_writer.flush();
879 | }
880 | }
881 |
882 | /**
883 | * Applies the attributes from the given {@link TextCell} to the given
884 | * {@link AttributedString}.
885 | *
886 | * @param textCell
887 | * the text cell to get the attributes from;
888 | * @param attributedString
889 | * the {@link AttributedString} to apply the attributes to;
890 | * @param font
891 | * the font to use.
892 | * @return the primary foreground color, never null
.
893 | */
894 | private Color applyAttributes( ITextCell textCell, AttributedString attributedString, Font font )
895 | {
896 | Color fg = convertToColor( textCell.getForeground(), m_colorScheme.getTextColor() );
897 | Color bg = convertToColor( textCell.getBackground(), m_colorScheme.getBackgroundColor() );
898 |
899 | attributedString.addAttribute( TextAttribute.FAMILY, font.getFamily() );
900 | attributedString.addAttribute( TextAttribute.SIZE, font.getSize() );
901 | attributedString.addAttribute( TextAttribute.FOREGROUND, textCell.isReverse() ^ textCell.isHidden() ? bg : fg );
902 | attributedString.addAttribute( TextAttribute.BACKGROUND, textCell.isReverse() ? fg : bg );
903 |
904 | if ( textCell.isUnderline() )
905 | {
906 | attributedString.addAttribute( TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON );
907 | }
908 | if ( textCell.isBold() )
909 | {
910 | attributedString.addAttribute( TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD );
911 | }
912 | if ( textCell.isItalic() )
913 | {
914 | attributedString.addAttribute( TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE );
915 | }
916 | return textCell.isReverse() ^ textCell.isHidden() ? bg : fg;
917 | }
918 |
919 | /**
920 | * Calculates the size (in pixels) of the back buffer image.
921 | *
922 | * @param columns
923 | * the number of columns, > 0;
924 | * @param lines
925 | * the number of lines, > 0.
926 | * @return a dimension with the image width and height in pixels.
927 | */
928 | private Dimension calculateSizeInPixels( int columns, int lines )
929 | {
930 | CharacterDimensions charDims = m_charDims;
931 |
932 | int width = ( columns * charDims.m_width );
933 | int height = ( lines * ( charDims.m_height + charDims.m_lineSpacing ) );
934 | return new Dimension( width, height );
935 | }
936 |
937 | /**
938 | * Calculates the total insets of this container and all of its parents.
939 | *
940 | * @return the total insets, never null
.
941 | */
942 | private Insets calculateTotalInsets()
943 | {
944 | // Take the screen insets as starting point...
945 | Insets insets = Toolkit.getDefaultToolkit().getScreenInsets( getGraphicsConfiguration() );
946 |
947 | Container ptr = this;
948 | do
949 | {
950 | Insets compInsets = ptr.getInsets();
951 | insets.top += compInsets.top;
952 | insets.bottom += compInsets.bottom;
953 | insets.left += compInsets.left;
954 | insets.right += compInsets.right;
955 | ptr = ptr.getParent();
956 | }
957 | while ( ptr != null );
958 | return insets;
959 | }
960 |
961 | /**
962 | * Converts a given color index to a concrete color value.
963 | *
964 | * @param index
965 | * the numeric color index, >= 0;
966 | * @param defaultColor
967 | * the default color to use, cannot be null
.
968 | * @return a color value, never null
.
969 | */
970 | private Color convertToColor( int index, Color defaultColor )
971 | {
972 | if ( index < 1 )
973 | {
974 | return defaultColor;
975 | }
976 | return m_colorScheme.getColorByIndex( index - 1 );
977 | }
978 |
979 | /**
980 | * Draws the cursor on screen.
981 | *
982 | * @param canvas
983 | * the canvas to paint on;
984 | * @param cursor
985 | * the cursor information;
986 | * @param color
987 | * the color to paint the cursor in.
988 | */
989 | private Rectangle drawCursor( final Graphics2D canvas, final ICursor cursor, final Color color )
990 | {
991 | if ( !cursor.isVisible() )
992 | {
993 | return null;
994 | }
995 |
996 | CharacterDimensions charDims = m_charDims;
997 |
998 | int cw = charDims.m_width;
999 | int ch = charDims.m_height;
1000 | int ls = charDims.m_lineSpacing;
1001 |
1002 | int x = cursor.getX() * cw;
1003 | int y = cursor.getY() * ( ch + ls );
1004 |
1005 | Rectangle rect = new Rectangle( x, y, cw, ch - 2 * ls );
1006 |
1007 | canvas.setColor( color );
1008 | canvas.draw( rect );
1009 |
1010 | return rect;
1011 | }
1012 |
1013 | /**
1014 | * Returns whether the given keystroke represents a "regular" key, that is, it
1015 | * is defined and not a modifier.
1016 | *
1017 | * @param keystroke
1018 | * the keystroke to test, cannot be null
.
1019 | * @return false
if the given keystroke is either not defined or
1020 | * represents a modifier key (SHIFT, CTRL, etc.), true
1021 | * otherwise.
1022 | */
1023 | private boolean isRegularKey( KeyStroke keystroke )
1024 | {
1025 | int c = keystroke.getKeyCode();
1026 | return ( c != KeyEvent.VK_UNDEFINED ) && ( c != KeyEvent.VK_SHIFT ) && ( c != KeyEvent.VK_ALT )
1027 | && ( c != KeyEvent.VK_ALT_GRAPH ) && ( c != KeyEvent.VK_META ) && ( c != KeyEvent.VK_WINDOWS )
1028 | && ( c != KeyEvent.VK_CONTROL );
1029 | }
1030 | }
1031 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/swing/XtermColorScheme.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal.swing;
22 |
23 |
24 | import java.awt.*;
25 | import java.util.concurrent.atomic.*;
26 |
27 | import nl.lxtreme.jvt220.terminal.*;
28 |
29 |
30 | /**
31 | * Represents the default color scheme, derived from the one used by XTerm.
32 | */
33 | public final class XtermColorScheme implements ITerminalColorScheme
34 | {
35 | // CONSTANTS
36 |
37 | private static final Color PLAIN_TEXT_COLOR = new Color( 0xE6, 0xE6, 0xE6 );
38 | private static final Color BACKGROUND_COLOR = new Color( 0x1E, 0x21, 0x26 );
39 |
40 | private static final Color[] XTERM_COLORS = { BACKGROUND_COLOR, // Off-Black
41 | new Color( 205, 0, 0 ), // Red
42 | new Color( 0, 205, 0 ), // Green
43 | new Color( 205, 205, 0 ), // Yellow
44 | new Color( 0, 0, 238 ), // Blue
45 | new Color( 205, 0, 205 ), // Magenta
46 | new Color( 0, 205, 205 ), // Cyan
47 | new Color( 229, 229, 229 ), // White
48 | };
49 |
50 | // VARIABLES
51 |
52 | private final AtomicBoolean inverted;
53 |
54 | // CONSTRUCTORS
55 |
56 | /**
57 | * Creates a new {@link XtermColorScheme} instance.
58 | */
59 | public XtermColorScheme()
60 | {
61 | this.inverted = new AtomicBoolean( false );
62 | }
63 |
64 | // METHODS
65 |
66 | /**
67 | * {@inheritDoc}
68 | */
69 | @Override
70 | public Color getBackgroundColor()
71 | {
72 | return isInverted() ? PLAIN_TEXT_COLOR : BACKGROUND_COLOR;
73 | }
74 |
75 | /**
76 | * {@inheritDoc}
77 | */
78 | @Override
79 | public Color getColorByIndex( int aIndex )
80 | {
81 | return XTERM_COLORS[aIndex % XTERM_COLORS.length];
82 | }
83 |
84 | /**
85 | * {@inheritDoc}
86 | */
87 | @Override
88 | public Color getTextColor()
89 | {
90 | return isInverted() ? BACKGROUND_COLOR : PLAIN_TEXT_COLOR;
91 | }
92 |
93 | /**
94 | * @return true
if the foreground and background colors are to be
95 | * swapped, false
otherwise.
96 | */
97 | public boolean isInverted()
98 | {
99 | return this.inverted.get();
100 | }
101 |
102 | /**
103 | * {@inheritDoc}
104 | */
105 | @Override
106 | public void setInverted( boolean aInverted )
107 | {
108 | this.inverted.set( aInverted );
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/swing/packageinfo:
--------------------------------------------------------------------------------
1 | version 1.1.1
2 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/vt220/AbstractTerminal.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal.vt220;
22 |
23 |
24 | import java.io.*;
25 | import java.util.*;
26 |
27 | import nl.lxtreme.jvt220.terminal.*;
28 |
29 |
30 | /**
31 | * Provides an abstract base implementation of {@link ITerminal}.
32 | */
33 | public abstract class AbstractTerminal implements ITerminal
34 | {
35 | // INNER TYPES
36 |
37 | /**
38 | * Provides a default key mapper that does not map anything.
39 | */
40 | protected static class DefaultKeyMapper implements IKeyMapper
41 | {
42 | // METHODS
43 |
44 | /**
45 | * {@inheritDoc}
46 | */
47 | @Override
48 | public String map( int keyCode, int modifiers )
49 | {
50 | return null;
51 | }
52 | }
53 |
54 | /**
55 | * Provides a default tabulator implementation.
56 | */
57 | protected class DefaultTabulator implements ITabulator
58 | {
59 | // CONSTANTS
60 |
61 | private static final int DEFAULT_TABSTOP = 8;
62 |
63 | // VARIABLES
64 |
65 | private final SortedSetnull
.
137 | */
138 | public SortedSet405 | * When auto-newline mode is enabled, a LF, FF or VT will cause the cursor to 406 | * move to the first column of the next line. When disabled, it will 407 | * stay in the same column while moving the cursor to the next line. 408 | *
409 | * 410 | * @returntrue
if auto-wrap mode is enabled, false
411 | * otherwise.
412 | * @see #setAutoNewlineMode(boolean)
413 | */
414 | public final boolean isAutoNewlineMode()
415 | {
416 | return m_options.get( OPTION_NEWLINE );
417 | }
418 |
419 | /**
420 | * Returns whether or not the auto-wrap mode is enabled.
421 | *
422 | * @return true
if auto-wrap mode is enabled, false
423 | * otherwise.
424 | * @see #setAutoWrap(boolean)
425 | */
426 | public final boolean isAutoWrapMode()
427 | {
428 | return m_options.get( OPTION_AUTOWRAP );
429 | }
430 |
431 | /**
432 | * Returns whether or not insert mode is enabled.
433 | *
434 | * @return true
if insert mode is enabled, false
435 | * otherwise.
436 | * @see #setInsertMode(boolean)
437 | */
438 | public final boolean isInsertMode()
439 | {
440 | return m_options.get( OPTION_INSERT );
441 | }
442 |
443 | /**
444 | * Returns whether or not the origin mode is enabled.
445 | *
446 | * @return true
if origin mode is enabled, false
447 | * otherwise.
448 | * @see #setOriginMode(boolean)
449 | */
450 | public final boolean isOriginMode()
451 | {
452 | return m_options.get( OPTION_ORIGIN );
453 | }
454 |
455 | /**
456 | * Returns whether or not the reverse mode is enabled.
457 | *
458 | * @return true
if reverse mode is enabled, false
459 | * otherwise.
460 | * @see #setReverse(boolean)
461 | */
462 | public final boolean isReverseMode()
463 | {
464 | return m_options.get( OPTION_REVERSE );
465 | }
466 |
467 | /**
468 | * Moves the cursor to the given X,Y position.
469 | *
470 | * @param x
471 | * the absolute X-position, zero-based (zero meaning start of current
472 | * line). If -1, then the current X-position is unchanged.
473 | * @param y
474 | * the absolute Y-position, zero-based (zero meaning start of current
475 | * screen). If -1, then the current Y-position is unchanged.
476 | */
477 | public void moveCursorAbsolute( final int x, final int y )
478 | {
479 | int xPos = x;
480 | if ( xPos < 0 )
481 | {
482 | xPos = m_cursor.getX();
483 | }
484 | if ( xPos >= m_width )
485 | {
486 | xPos = m_width;
487 | }
488 |
489 | int yPos = y;
490 | if ( yPos < 0 )
491 | {
492 | yPos = m_cursor.getY();
493 | }
494 | if ( yPos >= m_height )
495 | {
496 | yPos = m_height - 1;
497 | }
498 |
499 | m_cursor.setPosition( xPos, yPos );
500 | }
501 |
502 | /**
503 | * Moves the cursor relatively to the given X,Y position.
504 | *
505 | * @param x
506 | * the relative X-position to move. If > 0, then move to the right;
507 | * if 0, then the X-position is unchanged; if < 0, then move to the
508 | * left;
509 | * @param y
510 | * the relative Y-position to move. If > 0, then move to the bottom;
511 | * if 0, then the Y-position is unchanged; if < 0, then move to the
512 | * top.
513 | */
514 | public void moveCursorRelative( final int x, final int y )
515 | {
516 | int xPos = Math.max( 0, m_cursor.getX() + x );
517 | int yPos = Math.max( 0, m_cursor.getY() + y );
518 |
519 | moveCursorAbsolute( xPos, yPos );
520 | }
521 |
522 | /**
523 | * {@inheritDoc}
524 | */
525 | @Override
526 | public final int read( CharSequence chars ) throws IOException
527 | {
528 | int r = doReadInput( chars );
529 |
530 | if ( m_frontend != null && m_frontend.isListening() )
531 | {
532 | TextCell[] b = m_buffer.clone();
533 | BitSet hm = ( BitSet )m_heatMap.clone();
534 |
535 | m_frontend.terminalChanged( b, hm );
536 |
537 | // Mark all "hot spots" as being processed...
538 | m_heatMap.clear();
539 | }
540 |
541 | return r;
542 | }
543 |
544 | /**
545 | * {@inheritDoc}
546 | */
547 | @Override
548 | public void reset()
549 | {
550 | // Clear the entire screen...
551 | clearScreen( 2 );
552 | // Move cursor to the first (upper left) position...
553 | updateCursorByAbsoluteIndex( getFirstAbsoluteIndex() );
554 | // Reset scroll region...
555 | m_firstScrollLine = 0;
556 | m_lastScrollLine = getHeight() - 1;
557 | }
558 |
559 | /**
560 | * Scrolls a given number of lines down, inserting empty lines at the top of
561 | * the scrolling region. The contents of the lines scrolled off the screen are
562 | * lost.
563 | *
564 | * @param lines
565 | * the number of lines to scroll down, > 0.
566 | * @see #setScrollRegion(int, int)
567 | */
568 | public void scrollDown( final int lines )
569 | {
570 | scrollDown( m_firstScrollLine, m_lastScrollLine, lines );
571 | }
572 |
573 | /**
574 | * Scrolls a given number of lines up, inserting empty lines at the bottom of
575 | * the scrolling region. The contents of the lines scrolled off the screen are
576 | * lost.
577 | *
578 | * @param lines
579 | * the number of lines to scroll up, > 0.
580 | * @see #setScrollRegion(int, int)
581 | */
582 | public void scrollUp( final int lines )
583 | {
584 | scrollUp( m_firstScrollLine, m_lastScrollLine, lines );
585 | }
586 |
587 | /**
588 | * Enables or disables the auto-newline mode.
589 | *
590 | * @param enable
591 | * true
to enable auto-newline mode, false
592 | * to disable it.
593 | */
594 | public void setAutoNewlineMode( boolean enable )
595 | {
596 | m_options.set( OPTION_NEWLINE, enable );
597 | }
598 |
599 | /**
600 | * Enables or disables the auto-wrap mode.
601 | *
602 | * @param enable
603 | * true
to enable auto-wrap mode, false
to
604 | * disable it.
605 | */
606 | public void setAutoWrap( boolean enable )
607 | {
608 | m_options.set( OPTION_AUTOWRAP, enable );
609 | }
610 |
611 | /**
612 | * Sets the dimensions of this terminal to the given width and height.
613 | *
614 | * @param newWidth
615 | * the new width of this terminal, in columns. If <= 0, then the
616 | * current width will be used;
617 | * @param newHeight
618 | * the new height of this terminal, in lines. If <= 0, then the
619 | * current height will be used.
620 | */
621 | public void setDimensions( int newWidth, int newHeight )
622 | {
623 | if ( newWidth <= 0 )
624 | {
625 | newWidth = m_width;
626 | }
627 | if ( newHeight <= 0 )
628 | {
629 | newHeight = m_height;
630 | }
631 |
632 | if ( ( newWidth == m_width ) && ( newHeight == m_height ) )
633 | {
634 | // Nothing to do...
635 | return;
636 | }
637 |
638 | internalSetDimensions( newWidth, newHeight );
639 | }
640 |
641 | /**
642 | * {@inheritDoc}
643 | */
644 | @Override
645 | public void setFrontend( ITerminalFrontend frontend )
646 | {
647 | if ( frontend == null )
648 | {
649 | throw new IllegalArgumentException( "Frontend cannot be null!" );
650 | }
651 | m_frontend = frontend;
652 | }
653 |
654 | /**
655 | * Enables or disables the insert mode.
656 | *
657 | * @param enable
658 | * true
to enable insert mode, false
to
659 | * disable it.
660 | */
661 | public void setInsertMode( boolean enable )
662 | {
663 | m_options.set( OPTION_INSERT, enable );
664 | }
665 |
666 | /**
667 | * @param logLevel
668 | * the logLevel to set
669 | */
670 | public void setLogLevel( int logLevel )
671 | {
672 | m_logLevel = logLevel;
673 | }
674 |
675 | /**
676 | * Enables or disables the origin mode.
677 | * 678 | * When the origin mode is set, cursor addressing is relative to the upper 679 | * left corner of the scrolling region. 680 | *
681 | * 682 | * @param enable 683 | *true
to enable origin mode, false
to
684 | * disable it.
685 | * @see #setScrollRegion(int, int)
686 | */
687 | public void setOriginMode( boolean enable )
688 | {
689 | m_options.set( OPTION_ORIGIN, enable );
690 | }
691 |
692 | /**
693 | * Enables or disables the reverse mode.
694 | *
695 | * @param enable
696 | * true
to enable reverse mode, false
to
697 | * disable it.
698 | */
699 | public void setReverse( boolean enable )
700 | {
701 | m_options.set( OPTION_REVERSE, enable );
702 | // The entire screen should be redrawn...
703 | m_heatMap.set( getFirstAbsoluteIndex(), getLastAbsoluteIndex() );
704 |
705 | if ( m_frontend != null )
706 | {
707 | m_frontend.setReverse( enable );
708 | }
709 | }
710 |
711 | /**
712 | * Sets the scrolling region. By default the entire screen is set as scrolling
713 | * region.
714 | *
715 | * @param topIndex
716 | * the top line that will be scrolled, >= 0;
717 | * @param bottomIndex
718 | * the bottom line that will be scrolled, >= aTopIndex.
719 | */
720 | public void setScrollRegion( final int topIndex, final int bottomIndex )
721 | {
722 | if ( topIndex < 0 )
723 | {
724 | throw new IllegalArgumentException( "TopIndex cannot be negative!" );
725 | }
726 | if ( bottomIndex <= topIndex )
727 | {
728 | throw new IllegalArgumentException( "BottomIndex cannot be equal or less than TopIndex!" );
729 | }
730 |
731 | m_firstScrollLine = Math.max( 0, topIndex );
732 | m_lastScrollLine = Math.min( getHeight() - 1, bottomIndex );
733 | }
734 |
735 | /**
736 | * {@inheritDoc}
737 | */
738 | @Override
739 | public String toString()
740 | {
741 | StringBuilder sb = new StringBuilder();
742 | for ( int idx = getFirstAbsoluteIndex(); idx <= getLastAbsoluteIndex(); idx++ )
743 | {
744 | ITextCell cell = getCellAt( idx );
745 | if ( ( idx > 0 ) && ( idx % getWidth() ) == 0 )
746 | {
747 | sb.append( "\n" );
748 | }
749 | sb.append( cell == null ? ' ' : cell.getChar() );
750 | }
751 | return sb.toString();
752 | }
753 |
754 | /**
755 | * {@inheritDoc}
756 | */
757 | @Override
758 | public int write( CharSequence response ) throws IOException
759 | {
760 | int length = 0;
761 |
762 | final Writer w = getWriter();
763 | if ( ( response != null ) && ( w != null ) )
764 | {
765 | w.write( response.toString() );
766 | w.flush();
767 |
768 | length = response.length();
769 | }
770 |
771 | return length;
772 | }
773 |
774 | /**
775 | * Clears all tab stops.
776 | */
777 | protected final void clearAllTabStops()
778 | {
779 | getTabulator().clearAll();
780 | }
781 |
782 | /**
783 | * Clears the line using the given absolute index as cursor position.
784 | *
785 | * @param mode
786 | * the clear modus: 0 = erase from cursor to right (default), 1 =
787 | * erase from cursor to left, 2 = erase entire line.
788 | * @param absoluteIndex
789 | * the absolute index of the cursor;
790 | * @param keepProtectedCells
791 | * true
to honor the 'protected' option in text cells
792 | * leaving those cells as-is, false
to disregard this
793 | * option and clear all text cells.
794 | */
795 | protected final void clearLine( final int mode, final int absoluteIndex, final boolean keepProtectedCells )
796 | {
797 | int width = getWidth();
798 | int yPos = ( int )Math.floor( absoluteIndex / width );
799 | int xPos = absoluteIndex - ( yPos * width );
800 |
801 | int idx;
802 | int length;
803 |
804 | switch ( mode )
805 | {
806 | case 0:
807 | // erase from cursor to end of line...
808 | idx = absoluteIndex;
809 | length = width - xPos;
810 | break;
811 | case 1:
812 | // erase from cursor to start of line...
813 | idx = absoluteIndex - xPos;
814 | length = xPos + 1;
815 | break;
816 | case 2:
817 | // erase entire line...
818 | idx = absoluteIndex - xPos;
819 | length = width;
820 | break;
821 |
822 | default:
823 | throw new IllegalArgumentException( "Invalid clear line mode!" );
824 | }
825 |
826 | for ( int i = 0; i < length; i++ )
827 | {
828 | removeChar( idx++, keepProtectedCells );
829 | }
830 | }
831 |
832 | /**
833 | * Clears the screen using the given absolute index as cursor position.
834 | *
835 | * @param mode
836 | * the clear modus: 0 = erase from cursor to below (default), 1 =
837 | * erase from cursor to top, 2 = erase entire screen.
838 | * @param absoluteIndex
839 | * the absolute index of the cursor;
840 | * @param keepProtectedCells
841 | * true
to honor the 'protected' option in text cells
842 | * leaving those cells as-is, false
to disregard this
843 | * option and clear all text cells.
844 | */
845 | protected final void clearScreen( final int mode, final int absoluteIndex, final boolean keepProtectedCells )
846 | {
847 | switch ( mode )
848 | {
849 | case 0:
850 | // erase from cursor to end of screen...
851 | int lastIdx = getLastAbsoluteIndex();
852 | for ( int i = absoluteIndex; i <= lastIdx; i++ )
853 | {
854 | removeChar( i, keepProtectedCells );
855 | }
856 | break;
857 | case 1:
858 | // erase from cursor to start of screen...
859 | int firstIdx = getFirstAbsoluteIndex();
860 | for ( int i = firstIdx; i <= absoluteIndex; i++ )
861 | {
862 | removeChar( i, keepProtectedCells );
863 | }
864 | break;
865 | case 2:
866 | // erase entire screen...
867 | if ( keepProtectedCells )
868 | {
869 | // Be selective in what we remove...
870 | for ( int i = getFirstAbsoluteIndex(), last = getLastAbsoluteIndex(); i <= last; i++ )
871 | {
872 | removeChar( i, keepProtectedCells );
873 | }
874 | }
875 | else
876 | {
877 | // Don't be selective in what we remove...
878 | Arrays.fill( m_buffer, getFirstAbsoluteIndex(), getLastAbsoluteIndex() + 1, new TextCell( ' ',
879 | getAttributes() ) );
880 | // Update the heat map...
881 | m_heatMap.set( getFirstAbsoluteIndex(), getLastAbsoluteIndex() );
882 | }
883 | break;
884 |
885 | default:
886 | throw new IllegalArgumentException( "Invalid clear screen mode!" );
887 | }
888 | }
889 |
890 | /**
891 | * Factory method for creating {@link IKeyMapper} instances.
892 | *
893 | * @return a new {@link IKeyMapper} instance, never null
.
894 | */
895 | protected IKeyMapper createKeyMapper()
896 | {
897 | return new DefaultKeyMapper();
898 | }
899 |
900 | /**
901 | * Deletes a given number of characters at the absolute index, first shifting
902 | * the remaining characters on that line to the left, inserting spaces at the
903 | * end of the line.
904 | *
905 | * @param absoluteIndex
906 | * the absolute index to delete the character at;
907 | * @param count
908 | * the number of times to insert the given character, > 0.
909 | * @return the next index.
910 | */
911 | protected final int deleteChars( final int absoluteIndex, final int count )
912 | {
913 | int col = ( absoluteIndex % getWidth() );
914 | int length = Math.max( 0, getWidth() - col - count );
915 |
916 | // Make room for the new characters at the end...
917 | System.arraycopy( m_buffer, absoluteIndex + count, m_buffer, absoluteIndex, length );
918 |
919 | // Fill the created room with the character to insert...
920 | int startIdx = absoluteIndex + length;
921 | int endIdx = absoluteIndex + getWidth() - col;
922 | Arrays.fill( m_buffer, startIdx, endIdx, new TextCell( ' ', getAttributes() ) );
923 |
924 | // Update the heat map for the *full* line...
925 | m_heatMap.set( absoluteIndex, absoluteIndex + getWidth() - col );
926 |
927 | return absoluteIndex;
928 | }
929 |
930 | /**
931 | * Provides the actual implementation for {@link #read(CharSequence)}.
932 | *
933 | * @see {@link #read(CharSequence)}
934 | */
935 | protected abstract int doReadInput( CharSequence chars ) throws IOException;
936 |
937 | /**
938 | * Returns the absolute index according to the current cursor position.
939 | *
940 | * @return an absolute index of the cursor position, >= 0.
941 | */
942 | protected final int getAbsoluteCursorIndex()
943 | {
944 | return getAbsoluteIndex( m_cursor.getX(), m_cursor.getY() );
945 | }
946 |
947 | /**
948 | * Returns the absolute index according to the given X,Y-position.
949 | *
950 | * @param x
951 | * the X-position;
952 | * @param y
953 | * the Y-position.
954 | * @return an absolute index of the cursor position, >= 0.
955 | */
956 | protected final int getAbsoluteIndex( final int x, final int y )
957 | {
958 | return ( y * getWidth() ) + x;
959 | }
960 |
961 | /**
962 | * @return the (encoded) text attributes.
963 | */
964 | protected final short getAttributes()
965 | {
966 | return m_textAttributes.getAttributes();
967 | }
968 |
969 | /**
970 | * Returns the cell at the given absolute index.
971 | *
972 | * @param absoluteIndex
973 | * the absolute of the cell to retrieve.
974 | * @return the text cell at the given index, can be null
if no
975 | * cell is defined.
976 | */
977 | protected final ITextCell getCellAt( final int absoluteIndex )
978 | {
979 | return m_buffer[absoluteIndex];
980 | }
981 |
982 | /**
983 | * Returns the cell at the given X,Y-position.
984 | *
985 | * @param x
986 | * the X-position of the cell to retrieve;
987 | * @param y
988 | * the Y-position of the cell to retrieve.
989 | * @return the text cell at the given X,Y-position, or null
if
990 | * there is no such cell.
991 | */
992 | protected final ITextCell getCellAt( final int x, final int y )
993 | {
994 | return getCellAt( getAbsoluteIndex( x, y ) );
995 | }
996 |
997 | /**
998 | * @return the first absolute index of this screen, >= 0.
999 | */
1000 | protected final int getFirstAbsoluteIndex()
1001 | {
1002 | return getAbsoluteIndex( 0, 0 );
1003 | }
1004 |
1005 | /**
1006 | * @return the last absolute index of this screen, >= 0.
1007 | */
1008 | protected final int getLastAbsoluteIndex()
1009 | {
1010 | return getAbsoluteIndex( getWidth() - 1, getHeight() - 1 );
1011 | }
1012 |
1013 | /**
1014 | * Inserts a given character at the absolute index, first shifting the
1015 | * remaining characters on that line to the right (possibly shifting text of
1016 | * the line).
1017 | *
1018 | * @param absoluteIndex
1019 | * the absolute index to insert the character at;
1020 | * @param ch
1021 | * the character to insert;
1022 | * @param count
1023 | * the number of times to insert the given character, > 0.
1024 | * @return the next index.
1025 | */
1026 | protected final int insertChars( final int absoluteIndex, final char ch, final int count )
1027 | {
1028 | int col = absoluteIndex % getWidth();
1029 | int length = getWidth() - col - count;
1030 |
1031 | // Make room for the new characters...
1032 | System.arraycopy( m_buffer, absoluteIndex, m_buffer, absoluteIndex + count, length );
1033 |
1034 | // Fill the created room with the character to insert...
1035 | Arrays.fill( m_buffer, absoluteIndex, absoluteIndex + count, new TextCell( ch, getAttributes() ) );
1036 |
1037 | // Update the heat map for the *full* line...
1038 | m_heatMap.set( absoluteIndex, absoluteIndex + getWidth() - col );
1039 |
1040 | return absoluteIndex;
1041 | }
1042 |
1043 | /**
1044 | * @return true
if the last written character caused a wrap to
1045 | * next line, false
otherwise.
1046 | */
1047 | protected final boolean isWrapped()
1048 | {
1049 | return m_wrapped;
1050 | }
1051 |
1052 | /**
1053 | * Logs the given text verbatimely at loglevel 0 or higher.
1054 | *
1055 | * @param text
1056 | * the text to log, cannot be null
.
1057 | */
1058 | protected final void log( String text )
1059 | {
1060 | if ( m_logLevel < 0 )
1061 | {
1062 | return;
1063 | }
1064 |
1065 | System.out.printf( "LOG> %s%n", text );
1066 | }
1067 |
1068 | /**
1069 | * Removes the character at the absolute index.
1070 | *
1071 | * @param absoluteIndex
1072 | * the index on which to remove the character, >= 0;
1073 | * @param keepProtectedCells
1074 | * true
to honor the 'protected' bit of text cells and
1075 | * leave the text cell unchanged, false
to ignore this
1076 | * bit and clear the text cell anyways.
1077 | * @return the absolute index on which the character was removed.
1078 | */
1079 | protected final int removeChar( final int absoluteIndex, final boolean keepProtectedCells )
1080 | {
1081 | int idx = absoluteIndex;
1082 | int firstIdx = getFirstAbsoluteIndex();
1083 | if ( idx < firstIdx )
1084 | {
1085 | return firstIdx;
1086 | }
1087 | if ( idx > getLastAbsoluteIndex() )
1088 | {
1089 | idx = getLastAbsoluteIndex();
1090 | }
1091 |
1092 | // Clear the character at the given position, using the most current
1093 | // attributes...
1094 | if ( !( keepProtectedCells && m_buffer[idx].isProtected() ) )
1095 | {
1096 | m_buffer[idx] = new TextCell( ' ', getAttributes() );
1097 | m_heatMap.set( idx );
1098 | }
1099 |
1100 | return idx;
1101 | }
1102 |
1103 | /**
1104 | * Resets the wrapped state.
1105 | */
1106 | protected final void resetWrapped()
1107 | {
1108 | m_wrapped = false;
1109 | }
1110 |
1111 | /**
1112 | * Scrolls all lines between [firstScrollLine, lastScrollLine] the given
1113 | * number of lines down.
1114 | *
1115 | * @param firstScrollLine
1116 | * the first line of the scroll region;
1117 | * @param lastScrollLine
1118 | * the last line of the scroll region;
1119 | * @param lines
1120 | * the number of lines to scroll, > 0.
1121 | */
1122 | protected void scrollDown( final int firstScrollLine, final int lastScrollLine, final int lines )
1123 | {
1124 | if ( firstScrollLine < 0 )
1125 | {
1126 | throw new IllegalArgumentException( "First scroll line cannot be less than zero!" );
1127 | }
1128 | if ( lastScrollLine >= getHeight() )
1129 | {
1130 | throw new IllegalArgumentException( "Last scroll line cannot be greater than terminal height!" );
1131 | }
1132 | if ( lastScrollLine < firstScrollLine )
1133 | {
1134 | throw new IllegalArgumentException( "Scroll region cannot be negative!" );
1135 | }
1136 | if ( lines < 1 )
1137 | {
1138 | throw new IllegalArgumentException( "Invalid number of lines!" );
1139 | }
1140 |
1141 | int region = ( lastScrollLine - firstScrollLine + 1 );
1142 | int n = Math.min( lines, region );
1143 | int width = getWidth();
1144 |
1145 | int srcPos = firstScrollLine * width;
1146 | int destPos = ( n + firstScrollLine ) * width;
1147 | int length = ( lastScrollLine + 1 ) * width - destPos;
1148 |
1149 | if ( length > 0 )
1150 | {
1151 | System.arraycopy( m_buffer, srcPos, m_buffer, destPos, length );
1152 | }
1153 |
1154 | Arrays.fill( m_buffer, srcPos, destPos, new TextCell( ' ', getAttributes() ) );
1155 | // Update the heat map...
1156 | m_heatMap.set( srcPos, destPos );
1157 | }
1158 |
1159 | /**
1160 | * Scrolls all lines between [firstScrollLine, lastScrollLine] the given
1161 | * number of lines up.
1162 | *
1163 | * @param firstScrollLine
1164 | * the first line of the scroll region;
1165 | * @param lastScrollLine
1166 | * the last line of the scroll region;
1167 | * @param lines
1168 | * the number of lines to scroll, > 0.
1169 | */
1170 | protected void scrollUp( final int firstScrollLine, final int lastScrollLine, final int lines )
1171 | {
1172 | if ( firstScrollLine < 0 )
1173 | {
1174 | throw new IllegalArgumentException( "First scroll line cannot be less than zero!" );
1175 | }
1176 | if ( lastScrollLine >= getHeight() )
1177 | {
1178 | throw new IllegalArgumentException( "Last scroll line cannot be greater than terminal height!" );
1179 | }
1180 | if ( lastScrollLine < firstScrollLine )
1181 | {
1182 | throw new IllegalArgumentException( "Scroll region cannot be negative!" );
1183 | }
1184 | if ( lines < 1 )
1185 | {
1186 | throw new IllegalArgumentException( "Invalid number of lines!" );
1187 | }
1188 |
1189 | int region = ( lastScrollLine - firstScrollLine + 1 );
1190 | int n = Math.min( lines, region );
1191 | int width = getWidth();
1192 |
1193 | int srcPos = ( n + firstScrollLine ) * width;
1194 | int destPos = firstScrollLine * width;
1195 | int lastPos = ( lastScrollLine + 1 ) * width;
1196 | int length = lastPos - srcPos;
1197 |
1198 | if ( length > 0 )
1199 | {
1200 | System.arraycopy( m_buffer, srcPos, m_buffer, destPos, length );
1201 | }
1202 | Arrays.fill( m_buffer, destPos + length, srcPos + length, new TextCell( ' ', getAttributes() ) );
1203 | // Update the heat map...
1204 | m_heatMap.set( destPos, lastPos );
1205 | }
1206 |
1207 | /**
1208 | * Updates the cursor according to the given absolute index.
1209 | *
1210 | * @param absoluteIndex
1211 | * the absolute index to convert back to a X,Y-position.
1212 | */
1213 | protected final void updateCursorByAbsoluteIndex( final int absoluteIndex )
1214 | {
1215 | int width = getWidth();
1216 | int yPos = ( int )Math.floor( absoluteIndex / width );
1217 | int xPos = absoluteIndex - ( yPos * width );
1218 | m_cursor.setPosition( xPos, yPos );
1219 | }
1220 |
1221 | /**
1222 | * Writes a given character at the absolute index, scrolling the screen up if
1223 | * beyond the last index is written.
1224 | *
1225 | * @param absoluteIndex
1226 | * the index on which to write the given char, >= 0;
1227 | * @param ch
1228 | * the character to write;
1229 | * @param aAttributes
1230 | * the attributes to use to write the character.
1231 | * @return the absolute index after which the character was written.
1232 | */
1233 | protected final int writeChar( final int absoluteIndex, final char ch )
1234 | {
1235 | int idx = absoluteIndex;
1236 | int lastIdx = getAbsoluteIndex( getWidth() - 1, getLastScrollLine() );
1237 | int width = getWidth();
1238 |
1239 | if ( idx > lastIdx )
1240 | {
1241 | idx -= width;
1242 | scrollUp( 1 );
1243 | }
1244 |
1245 | if ( idx <= lastIdx )
1246 | {
1247 | m_buffer[idx] = new TextCell( ch, getAttributes() );
1248 | m_heatMap.set( idx );
1249 | }
1250 |
1251 | // determine new absolute index...
1252 | boolean lastColumn = ( ( idx % width ) == ( width - 1 ) );
1253 | m_wrapped = ( isAutoWrapMode() && lastColumn );
1254 | if ( !( !isAutoWrapMode() && lastColumn ) )
1255 | {
1256 | idx++;
1257 | }
1258 |
1259 | return idx;
1260 | }
1261 |
1262 | /**
1263 | * @return the {@link Writer} to write the responses from this terminal to,
1264 | * can be null
.
1265 | */
1266 | private Writer getWriter()
1267 | {
1268 | Writer result = null;
1269 | if ( m_frontend != null )
1270 | {
1271 | result = m_frontend.getWriter();
1272 | }
1273 | return result;
1274 | }
1275 |
1276 | /**
1277 | * Sets the dimensions of this terminal to the given width and height.
1278 | *
1279 | * @param width
1280 | * the new width in columns, > 0;
1281 | * @param height
1282 | * the new height in lines, > 0.
1283 | */
1284 | private void internalSetDimensions( final int width, final int height )
1285 | {
1286 | TextCell[] newBuffer = new TextCell[width * height];
1287 | Arrays.fill( newBuffer, new TextCell() );
1288 | if ( m_buffer != null )
1289 | {
1290 | int oldWidth = m_width;
1291 |
1292 | for ( int oldIdx = 0; oldIdx < m_buffer.length; oldIdx++ )
1293 | {
1294 | int oldColumn = ( oldIdx % oldWidth );
1295 | int oldLine = ( oldIdx / oldWidth );
1296 | if ( ( oldColumn >= width ) || ( oldLine >= height ) )
1297 | {
1298 | continue;
1299 | }
1300 |
1301 | int newIdx = ( oldLine * width ) + oldColumn;
1302 | newBuffer[newIdx] = m_buffer[oldIdx];
1303 | }
1304 | }
1305 |
1306 | m_width = width;
1307 | m_height = height;
1308 |
1309 | m_firstScrollLine = 0;
1310 | m_lastScrollLine = height - 1;
1311 |
1312 | m_buffer = newBuffer;
1313 | m_heatMap = new BitSet( newBuffer.length );
1314 |
1315 | if ( m_frontend != null )
1316 | {
1317 | // Notify the frontend that we've changed...
1318 | m_frontend.terminalSizeChanged( width, height );
1319 | }
1320 | }
1321 | }
1322 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/vt220/CharacterSets.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal.vt220;
22 |
23 |
24 | /**
25 | * Provides the (graphical) character sets.
26 | */
27 | public final class CharacterSets
28 | {
29 | // INNER TYPES
30 |
31 | /**
32 | * Provides an enum with names for the supported character sets.
33 | */
34 | static enum CharacterSet
35 | {
36 | // CONSTANTS
37 |
38 | ASCII( 'B' )
39 | {
40 | @Override
41 | public int map( int index )
42 | {
43 | return -1;
44 | }
45 | },
46 | BRITISH( 'A' )
47 | {
48 | @Override
49 | public int map( int index )
50 | {
51 | if ( index == 3 )
52 | {
53 | // Pound sign...
54 | return '\u00a3';
55 | }
56 | return -1;
57 | }
58 | },
59 | DANISH( 'E', '6' )
60 | {
61 | @Override
62 | public int map( int index )
63 | {
64 | switch ( index )
65 | {
66 | case 32:
67 | return '\u00c4';
68 | case 59:
69 | return '\u00c6';
70 | case 60:
71 | return '\u00d8';
72 | case 61:
73 | return '\u00c5';
74 | case 62:
75 | return '\u00dc';
76 | case 64:
77 | return '\u00e4';
78 | case 91:
79 | return '\u00e6';
80 | case 92:
81 | return '\u00f8';
82 | case 93:
83 | return '\u00e5';
84 | case 94:
85 | return '\u00fc';
86 | default:
87 | return -1;
88 | }
89 | }
90 | },
91 | DEC_SPECIAL_GRAPHICS( '0', '2' )
92 | {
93 | @Override
94 | public int map( int index )
95 | {
96 | if ( index >= 64 && index < 96 )
97 | {
98 | return ( ( Character )DEC_SPECIAL_CHARS[index - 64][0] ).charValue();
99 | }
100 | return -1;
101 | }
102 | },
103 | DEC_SUPPLEMENTAL( 'U', '<' )
104 | {
105 | @Override
106 | public int map( int index )
107 | {
108 | if ( index >= 0 && index < 64 )
109 | {
110 | // Set the 8th bit...
111 | return index + 160;
112 | }
113 | return -1;
114 | }
115 | },
116 | DUTCH( '4' )
117 | {
118 | @Override
119 | public int map( int index )
120 | {
121 | switch ( index )
122 | {
123 | case 3:
124 | return '\u00a3';
125 | case 32:
126 | return '\u00be';
127 | case 59:
128 | return '\u0133';
129 | case 60:
130 | return '\u00bd';
131 | case 61:
132 | return '|';
133 | case 91:
134 | return '\u00a8';
135 | case 92:
136 | return '\u0192';
137 | case 93:
138 | return '\u00bc';
139 | case 94:
140 | return '\u00b4';
141 | default:
142 | return -1;
143 | }
144 | }
145 | },
146 | FINNISH( 'C', '5' )
147 | {
148 | @Override
149 | public int map( int index )
150 | {
151 | switch ( index )
152 | {
153 | case 59:
154 | return '\u00c4';
155 | case 60:
156 | return '\u00d4';
157 | case 61:
158 | return '\u00c5';
159 | case 62:
160 | return '\u00dc';
161 | case 64:
162 | return '\u00e9';
163 | case 91:
164 | return '\u00e4';
165 | case 92:
166 | return '\u00f6';
167 | case 93:
168 | return '\u00e5';
169 | case 94:
170 | return '\u00fc';
171 | default:
172 | return -1;
173 | }
174 | }
175 | },
176 | FRENCH( 'R' )
177 | {
178 | @Override
179 | public int map( int index )
180 | {
181 | switch ( index )
182 | {
183 | case 3:
184 | return '\u00a3';
185 | case 32:
186 | return '\u00e0';
187 | case 59:
188 | return '\u00b0';
189 | case 60:
190 | return '\u00e7';
191 | case 61:
192 | return '\u00a6';
193 | case 91:
194 | return '\u00e9';
195 | case 92:
196 | return '\u00f9';
197 | case 93:
198 | return '\u00e8';
199 | case 94:
200 | return '\u00a8';
201 | default:
202 | return -1;
203 | }
204 | }
205 | },
206 | FRENCH_CANADIAN( 'Q' )
207 | {
208 | @Override
209 | public int map( int index )
210 | {
211 | switch ( index )
212 | {
213 | case 32:
214 | return '\u00e0';
215 | case 59:
216 | return '\u00e2';
217 | case 60:
218 | return '\u00e7';
219 | case 61:
220 | return '\u00ea';
221 | case 62:
222 | return '\u00ee';
223 | case 91:
224 | return '\u00e9';
225 | case 92:
226 | return '\u00f9';
227 | case 93:
228 | return '\u00e8';
229 | case 94:
230 | return '\u00fb';
231 | default:
232 | return -1;
233 | }
234 | }
235 | },
236 | GERMAN( 'K' )
237 | {
238 | @Override
239 | public int map( int index )
240 | {
241 | switch ( index )
242 | {
243 | case 32:
244 | return '\u00a7';
245 | case 59:
246 | return '\u00c4';
247 | case 60:
248 | return '\u00d6';
249 | case 61:
250 | return '\u00dc';
251 | case 91:
252 | return '\u00e4';
253 | case 92:
254 | return '\u00f6';
255 | case 93:
256 | return '\u00fc';
257 | case 94:
258 | return '\u00df';
259 | default:
260 | return -1;
261 | }
262 | }
263 | },
264 | ITALIAN( 'Y' )
265 | {
266 | @Override
267 | public int map( int index )
268 | {
269 | switch ( index )
270 | {
271 | case 3:
272 | return '\u00a3';
273 | case 32:
274 | return '\u00a7';
275 | case 59:
276 | return '\u00ba';
277 | case 60:
278 | return '\u00e7';
279 | case 61:
280 | return '\u00e9';
281 | case 91:
282 | return '\u00e0';
283 | case 92:
284 | return '\u00f2';
285 | case 93:
286 | return '\u00e8';
287 | case 94:
288 | return '\u00ec';
289 | default:
290 | return -1;
291 | }
292 | }
293 | },
294 | SPANISH( 'Z' )
295 | {
296 | @Override
297 | public int map( int index )
298 | {
299 | switch ( index )
300 | {
301 | case 3:
302 | return '\u00a3';
303 | case 32:
304 | return '\u00a7';
305 | case 59:
306 | return '\u00a1';
307 | case 60:
308 | return '\u00d1';
309 | case 61:
310 | return '\u00bf';
311 | case 91:
312 | return '\u00b0';
313 | case 92:
314 | return '\u00f1';
315 | case 93:
316 | return '\u00e7';
317 | default:
318 | return -1;
319 | }
320 | }
321 | },
322 | SWEDISH( 'H', '7' )
323 | {
324 | @Override
325 | public int map( int index )
326 | {
327 | switch ( index )
328 | {
329 | case 32:
330 | return '\u00c9';
331 | case 59:
332 | return '\u00c4';
333 | case 60:
334 | return '\u00d6';
335 | case 61:
336 | return '\u00c5';
337 | case 62:
338 | return '\u00dc';
339 | case 64:
340 | return '\u00e9';
341 | case 91:
342 | return '\u00e4';
343 | case 92:
344 | return '\u00f6';
345 | case 93:
346 | return '\u00e5';
347 | case 94:
348 | return '\u00fc';
349 | default:
350 | return -1;
351 | }
352 | }
353 | },
354 | SWISS( '=' )
355 | {
356 | @Override
357 | public int map( int index )
358 | {
359 | switch ( index )
360 | {
361 | case 3:
362 | return '\u00f9';
363 | case 32:
364 | return '\u00e0';
365 | case 59:
366 | return '\u00e9';
367 | case 60:
368 | return '\u00e7';
369 | case 61:
370 | return '\u00ea';
371 | case 62:
372 | return '\u00ee';
373 | case 63:
374 | return '\u00e8';
375 | case 64:
376 | return '\u00f4';
377 | case 91:
378 | return '\u00e4';
379 | case 92:
380 | return '\u00f6';
381 | case 93:
382 | return '\u00fc';
383 | case 94:
384 | return '\u00fb';
385 | default:
386 | return -1;
387 | }
388 | }
389 | };
390 |
391 | // VARIABLES
392 |
393 | private final int[] m_designations;
394 |
395 | // CONSTRUCTORS
396 |
397 | /**
398 | * Creates a new {@link CharacterSet} instance.
399 | *
400 | * @param designations
401 | * the characters that designate this character set, cannot be
402 | * null
.
403 | */
404 | private CharacterSet( int... designations )
405 | {
406 | m_designations = designations;
407 | }
408 |
409 | // METHODS
410 |
411 | /**
412 | * Returns the {@link CharacterSet} for the given character.
413 | *
414 | * @param designation
415 | * the character to translate to a {@link CharacterSet}.
416 | * @return a character set name corresponding to the given character,
417 | * defaulting to ASCII if no mapping could be made.
418 | */
419 | public static CharacterSet valueOf( char designation )
420 | {
421 | for ( CharacterSet csn : values() )
422 | {
423 | if ( csn.isDesignation( designation ) )
424 | {
425 | return csn;
426 | }
427 | }
428 | return ASCII;
429 | }
430 |
431 | /**
432 | * Maps the character with the given index to a character in this character
433 | * set.
434 | *
435 | * @param index
436 | * the index of the character set, >= 0 && < 128.
437 | * @return a mapped character, or -1 if no mapping could be made and the
438 | * ASCII value should be used.
439 | */
440 | public abstract int map( int index );
441 |
442 | /**
443 | * Returns whether or not the given designation character belongs to this
444 | * character set's set of designations.
445 | *
446 | * @param designation
447 | * the designation to test for.
448 | * @return true
if the given designation character maps to this
449 | * character set, false
otherwise.
450 | */
451 | private boolean isDesignation( char designation )
452 | {
453 | for ( int i = 0; i < m_designations.length; i++ )
454 | {
455 | if ( m_designations[i] == designation )
456 | {
457 | return true;
458 | }
459 | }
460 | return false;
461 | }
462 | }
463 |
464 | /**
465 | * Denotes how a graphic set is designated.
466 | */
467 | static class GraphicSet
468 | {
469 | // VARIABLES
470 |
471 | private final int m_index; // 0..3
472 | private CharacterSet m_designation;
473 |
474 | // CONSTRUCTORS
475 |
476 | /**
477 | * Creates a new {@link GraphicSet} instance.
478 | */
479 | public GraphicSet( int index )
480 | {
481 | if ( index < 0 || index > 3 )
482 | {
483 | throw new IllegalArgumentException( "Invalid index!" );
484 | }
485 | m_index = index;
486 | // The default mapping, based on XTerm...
487 | m_designation = CharacterSet.valueOf( ( index == 1 ) ? '0' : 'B' );
488 | }
489 |
490 | // METHODS
491 |
492 | /**
493 | * @return the designation of this graphic set.
494 | */
495 | public CharacterSet getDesignation()
496 | {
497 | return m_designation;
498 | }
499 |
500 | /**
501 | * @return the index of this graphics set.
502 | */
503 | public int getIndex()
504 | {
505 | return m_index;
506 | }
507 |
508 | /**
509 | * Maps a given character index to a concrete character.
510 | *
511 | * @param original
512 | * the original character to map;
513 | * @param index
514 | * the index of the character to map.
515 | * @return the mapped character, or the given original if no mapping could
516 | * be made.
517 | */
518 | public int map( char original, int index )
519 | {
520 | int result = m_designation.map( index );
521 | if ( result < 0 )
522 | {
523 | // No mapping, simply return the given original one...
524 | result = original;
525 | }
526 | return result;
527 | }
528 |
529 | /**
530 | * Sets the designation of this graphic set.
531 | *
532 | * @param designation
533 | * the designation to set, cannot be null
.
534 | * @throws IllegalArgumentException
535 | * in case the given designation was null
.
536 | */
537 | public void setDesignation( CharacterSet designation )
538 | {
539 | if ( designation == null )
540 | {
541 | throw new IllegalArgumentException( "Designation cannot be null!" );
542 | }
543 | m_designation = designation;
544 | }
545 | }
546 |
547 | // CONSTANTS
548 |
549 | private static final int C0_START = 0;
550 | private static final int C0_END = 31;
551 | private static final int C1_START = 128;
552 | private static final int C1_END = 159;
553 | private static final int GL_START = 32;
554 | private static final int GL_END = 127;
555 | private static final int GR_START = 160;
556 | private static final int GR_END = 255;
557 |
558 | public static final String[] ASCII_NAMES = { "null
;
696 | * @param gr
697 | * the GR graphic set, cannot be null
.
698 | * @return the mapped character.
699 | */
700 | public static char getChar( char original, GraphicSet gl, GraphicSet gr )
701 | {
702 | Object[] mapping = getMapping( original, gl, gr );
703 |
704 | int ch = ( ( Integer )mapping[0] ).intValue();
705 | if ( ch > 0 )
706 | {
707 | return ( char )ch;
708 | }
709 |
710 | return ' ';
711 | }
712 |
713 | /**
714 | * Returns the name for the given character using the given graphic sets GL
715 | * and GR.
716 | *
717 | * @param original
718 | * the original character to return the name for;
719 | * @param gl
720 | * the GL graphic set, cannot be null
;
721 | * @param gr
722 | * the GR graphic set, cannot be null
.
723 | * @return the character name.
724 | */
725 | public static String getCharName( char original, GraphicSet gl, GraphicSet gr )
726 | {
727 | Object[] mapping = getMapping( original, gl, gr );
728 |
729 | String name = ( String )mapping[1];
730 | if ( name == null )
731 | {
732 | name = String.format( "<%d>", ( int )original );
733 | }
734 |
735 | return name;
736 | }
737 |
738 | /**
739 | * Returns the mapping for a given character using the given graphic sets GL
740 | * and GR.
741 | *
742 | * @param original
743 | * the original character to map;
744 | * @param gl
745 | * the GL graphic set, cannot be null
;
746 | * @param gr
747 | * the GR graphic set, cannot be null
.
748 | * @return the mapped character.
749 | */
750 | private static Object[] getMapping( char original, GraphicSet gl, GraphicSet gr )
751 | {
752 | int mappedChar = original;
753 | if ( original >= C0_START && original <= C0_END )
754 | {
755 | int idx = original - C0_START;
756 | return C0_CHARS[idx];
757 | }
758 | else if ( original >= C1_START && original <= C1_END )
759 | {
760 | int idx = original - C1_START;
761 | return C1_CHARS[idx];
762 | }
763 | else if ( original >= GL_START && original <= GL_END )
764 | {
765 | int idx = original - GL_START;
766 | mappedChar = gl.map( original, idx );
767 | }
768 | else if ( original >= GR_START && original <= GR_END )
769 | {
770 | int idx = original - GR_START;
771 | mappedChar = gr.map( original, idx );
772 | }
773 |
774 | return new Object[] { mappedChar, null };
775 | }
776 | }
777 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/vt220/CursorImpl.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal.vt220;
22 |
23 |
24 | import nl.lxtreme.jvt220.terminal.*;
25 |
26 |
27 | /**
28 | * Provides an implementation of {@link ICursor}.
29 | */
30 | public class CursorImpl implements ICursor
31 | {
32 | // VARIABLES
33 |
34 | private int m_blinkRate;
35 | private boolean m_visible;
36 | private int m_x;
37 | private int m_y;
38 |
39 | // CONSTRUCTORS
40 |
41 | /**
42 | * Creates a new {@link CursorImpl} instance.
43 | */
44 | public CursorImpl()
45 | {
46 | m_blinkRate = 500;
47 | m_visible = true;
48 | m_x = 0;
49 | m_y = 0;
50 | }
51 |
52 | // METHODS
53 |
54 | /**
55 | * {@inheritDoc}
56 | */
57 | @Override
58 | public ICursor clone()
59 | {
60 | try
61 | {
62 | CursorImpl clone = ( CursorImpl )super.clone();
63 | clone.m_blinkRate = m_blinkRate;
64 | clone.m_visible = m_visible;
65 | clone.m_x = m_x;
66 | clone.m_y = m_y;
67 | return clone;
68 | }
69 | catch ( CloneNotSupportedException e )
70 | {
71 | throw new RuntimeException( "Cloning not supported!?!" );
72 | }
73 | }
74 |
75 | /**
76 | * {@inheritDoc}
77 | */
78 | @Override
79 | public int getBlinkRate()
80 | {
81 | return m_blinkRate;
82 | }
83 |
84 | /**
85 | * {@inheritDoc}
86 | */
87 | @Override
88 | public int getX()
89 | {
90 | return m_x;
91 | }
92 |
93 | /**
94 | * {@inheritDoc}
95 | */
96 | @Override
97 | public int getY()
98 | {
99 | return m_y;
100 | }
101 |
102 | /**
103 | * {@inheritDoc}
104 | */
105 | @Override
106 | public boolean isVisible()
107 | {
108 | return m_visible;
109 | }
110 |
111 | /**
112 | * {@inheritDoc}
113 | */
114 | @Override
115 | public void setBlinkRate( final int rate )
116 | {
117 | if ( rate < 0 )
118 | {
119 | throw new IllegalArgumentException( "Invalid blink rate!" );
120 | }
121 | m_blinkRate = rate;
122 | }
123 |
124 | /**
125 | * {@inheritDoc}
126 | */
127 | @Override
128 | public void setVisible( final boolean visible )
129 | {
130 | m_visible = visible;
131 | }
132 |
133 | /**
134 | * {@inheritDoc}
135 | */
136 | final void setPosition( final int x, final int y )
137 | {
138 | m_x = x;
139 | m_y = y;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/vt220/PlainTerminal.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal.vt220;
22 |
23 | import static nl.lxtreme.jvt220.terminal.vt220.CharacterSets.*;
24 |
25 | import java.awt.*;
26 | import java.util.concurrent.atomic.*;
27 |
28 |
29 | /**
30 | * Provides a plain terminal, that escapes any non-printable ASCII characters.
31 | */
32 | public class PlainTerminal extends AbstractTerminal
33 | {
34 | // VARIABLES
35 |
36 | private final AtomicBoolean m_rawMode;
37 |
38 | // CONSTRUCTORS
39 |
40 | /**
41 | * Creates a new {@link PlainTerminal} instance.
42 | *
43 | * @param aColumns
44 | * the initial number of columns in this terminal, > 0;
45 | * @param aLines
46 | * the initial number of lines in this terminal, > 0.
47 | */
48 | public PlainTerminal( final int aColumns, final int aLines )
49 | {
50 | super( aColumns, aLines );
51 |
52 | m_rawMode = new AtomicBoolean();
53 | }
54 |
55 | // METHODS
56 |
57 | /**
58 | * Returns whether or not this terminal is in "raw" mode. In non-raw mode, the
59 | * non-printable ASCII characters are represented by their name.
60 | *
61 | * @return true
if this terminal is in 'raw' mode,
62 | * false
(the default) otherwise.
63 | */
64 | public boolean isRawMode()
65 | {
66 | return m_rawMode.get();
67 | }
68 |
69 | /**
70 | * Sets whether or not this terminal should display non-printable ASCII
71 | * characters by their name.
72 | *
73 | * @param aRawMode
74 | * false
if the names for non-printable ASCII characters
75 | * should be displayed, true
otherwise.
76 | */
77 | public void setRawMode( boolean aRawMode )
78 | {
79 | boolean old;
80 | do
81 | {
82 | old = m_rawMode.get();
83 | }
84 | while ( !m_rawMode.compareAndSet( old, aRawMode ) );
85 | }
86 |
87 | /**
88 | * {@inheritDoc}
89 | */
90 | @Override
91 | protected int doReadInput( final CharSequence aChars )
92 | {
93 | if ( aChars == null )
94 | {
95 | throw new IllegalArgumentException( "Chars cannot be null!" );
96 | }
97 |
98 | int idx = getAbsoluteCursorIndex();
99 |
100 | for ( int i = 0; i < aChars.length(); i++ )
101 | {
102 | char c = aChars.charAt( i );
103 |
104 | switch ( c )
105 | {
106 | case '\010':
107 | // Backspace
108 | idx = removeChar( --idx, false /* aKeepProtectedCells */);
109 | break;
110 |
111 | case '\007':
112 | // Bell
113 | Toolkit.getDefaultToolkit().beep();
114 | break;
115 |
116 | case '\012':
117 | // Newline
118 | idx += getWidth();
119 | break;
120 |
121 | case '\015':
122 | // Carriage return
123 | idx -= ( idx % getWidth() );
124 | break;
125 |
126 | default:
127 | if ( !isRawMode() && ( c < ASCII_NAMES.length ) )
128 | {
129 | String name = ASCII_NAMES[c];
130 | for ( int j = 0; j < name.length(); j++ )
131 | {
132 | idx = writeChar( idx, name.charAt( j ) );
133 | }
134 | }
135 | else
136 | {
137 | idx = writeChar( idx, c );
138 | }
139 | break;
140 | }
141 | }
142 |
143 | updateCursorByAbsoluteIndex( idx );
144 | return aChars.length();
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/vt220/TextAttributes.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal.vt220;
22 |
23 |
24 | /**
25 | * Denotes a container for text attributes.
26 | */
27 | class TextAttributes
28 | {
29 | // CONSTANTS
30 |
31 | static final int COLOR_MASK = ( 1 << 5 ) - 1;
32 | static final int BOLD_MASK = 1 << 10;
33 | static final int ITALIC_MASK = 1 << 11;
34 | static final int UNDERLINE_MASK = 1 << 12;
35 | static final int REVERSE_MASK = 1 << 13;
36 | static final int HIDDEN_MASK = 1 << 14;
37 | static final int PROTECTED_MASK = 1 << 15;
38 |
39 | // VARIABLES
40 |
41 | private short m_attr;
42 |
43 | // METHODS
44 |
45 | /**
46 | * {@inheritDoc}
47 | */
48 | @Override
49 | public boolean equals( Object object )
50 | {
51 | if ( this == object )
52 | {
53 | return true;
54 | }
55 |
56 | if ( ( object == null ) || getClass() != object.getClass() )
57 | {
58 | return false;
59 | }
60 |
61 | final TextAttributes other = ( TextAttributes )object;
62 | return ( m_attr == other.m_attr );
63 | }
64 |
65 | /**
66 | * Returns the encoded attributes.
67 | *
68 | * @return the encoded attributes as short value.
69 | */
70 | public short getAttributes()
71 | {
72 | return m_attr;
73 | }
74 |
75 | /**
76 | * {@inheritDoc}
77 | */
78 | public int getBackground()
79 | {
80 | int bg = ( ( m_attr >> 5 ) & COLOR_MASK );
81 | return bg;
82 | }
83 |
84 | /**
85 | * {@inheritDoc}
86 | */
87 | public int getForeground()
88 | {
89 | int fg = ( m_attr & COLOR_MASK );
90 | return fg;
91 | }
92 |
93 | /**
94 | * {@inheritDoc}
95 | */
96 | @Override
97 | public int hashCode()
98 | {
99 | final int prime = 31;
100 | int result = 1;
101 | result = prime * result + m_attr;
102 | return result;
103 | }
104 |
105 | /**
106 | * {@inheritDoc}
107 | */
108 | public boolean isBold()
109 | {
110 | return ( m_attr & BOLD_MASK ) != 0;
111 | }
112 |
113 | /**
114 | * @return
115 | */
116 | public boolean isHidden()
117 | {
118 | return ( m_attr & HIDDEN_MASK ) != 0;
119 | }
120 |
121 | /**
122 | * {@inheritDoc}
123 | */
124 | public boolean isItalic()
125 | {
126 | return ( m_attr & ITALIC_MASK ) != 0;
127 | }
128 |
129 | /**
130 | * {@inheritDoc}
131 | */
132 | public boolean isProtected()
133 | {
134 | return ( m_attr & PROTECTED_MASK ) != 0;
135 | }
136 |
137 | /**
138 | * @return
139 | */
140 | public boolean isReverse()
141 | {
142 | return ( m_attr & REVERSE_MASK ) != 0;
143 | }
144 |
145 | /**
146 | * {@inheritDoc}
147 | */
148 | public boolean isUnderline()
149 | {
150 | return ( m_attr & UNDERLINE_MASK ) != 0;
151 | }
152 |
153 | /**
154 | * Resets all attributes to their default values, except for the foreground
155 | * and background color.
156 | */
157 | public void reset()
158 | {
159 | m_attr &= 0x3FF; // keep lower 10 bits...
160 | }
161 |
162 | /**
163 | * Resets all attributes to their default values.
164 | */
165 | public void resetAll()
166 | {
167 | m_attr = 0;
168 | }
169 |
170 | /**
171 | * Directly sets the attributes as encoded value.
172 | *
173 | * @param attributes
174 | * the attributes to set.
175 | */
176 | public void setAttributes( short attributes )
177 | {
178 | m_attr = attributes;
179 | }
180 |
181 | /**
182 | * Sets the background color.
183 | *
184 | * @param index
185 | * the index of the background color, >= 0 && < 32.
186 | */
187 | public void setBackground( int index )
188 | {
189 | int bg = ( index & COLOR_MASK ) << 5;
190 | m_attr &= 0xFE1F; // clear bg color bits...
191 | m_attr |= bg;
192 | }
193 |
194 | /**
195 | * @param enable
196 | * true
to enable the bold representation,
197 | * false
to disable it.
198 | */
199 | public void setBold( boolean enable )
200 | {
201 | setAttrBit( enable, BOLD_MASK );
202 | }
203 |
204 | /**
205 | * Sets the foreground color.
206 | *
207 | * @param index
208 | * the index of the foreground color, >= 0 && < 32.
209 | */
210 | public void setForeground( int index )
211 | {
212 | int fg = index & COLOR_MASK;
213 | m_attr &= 0xFFE0; // clear fg color bits...
214 | m_attr |= fg;
215 | }
216 |
217 | /**
218 | * @param enable
219 | * true
to enable the hidden property,
220 | * false
to disable it.
221 | */
222 | public void setHidden( boolean enable )
223 | {
224 | setAttrBit( enable, HIDDEN_MASK );
225 | }
226 |
227 | /**
228 | * @param enable
229 | * true
to enable the italic property,
230 | * false
to disable it.
231 | */
232 | public void setItalic( boolean enable )
233 | {
234 | setAttrBit( enable, ITALIC_MASK );
235 | }
236 |
237 | /**
238 | * @param enable
239 | * true
to enable the protected property,
240 | * false
to disable it.
241 | */
242 | public void setProtected( boolean enable )
243 | {
244 | setAttrBit( enable, PROTECTED_MASK );
245 | }
246 |
247 | /**
248 | * @param enable
249 | * true
to enable the reverse property,
250 | * false
to disable it.
251 | */
252 | public void setReverse( boolean enable )
253 | {
254 | setAttrBit( enable, REVERSE_MASK );
255 | }
256 |
257 | /**
258 | * @param enable
259 | * true
to enable the underline property,
260 | * false
to disable it.
261 | */
262 | public void setUnderline( boolean enable )
263 | {
264 | setAttrBit( enable, UNDERLINE_MASK );
265 | }
266 |
267 | /**
268 | * Sets or resets the bit in the attributes denoted by the given mask.
269 | *
270 | * @param enable
271 | * true
to set the bit, false
to reset it;
272 | * @param mask
273 | * the mask of the bit to set or reset.
274 | */
275 | private void setAttrBit( boolean enable, int mask )
276 | {
277 | if ( enable )
278 | {
279 | m_attr |= mask;
280 | }
281 | else
282 | {
283 | m_attr &= ~mask;
284 | }
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/vt220/TextCell.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * Licensed to the Apache Software Foundation (ASF) under one
5 | * or more contributor license agreements. See the NOTICE file
6 | * distributed with this work for additional information
7 | * regarding copyright ownership. The ASF licenses this file
8 | * to you under the Apache License, Version 2.0 (the
9 | * "License"); you may not use this file except in compliance
10 | * with the License. You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing,
15 | * software distributed under the License is distributed on an
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 | * KIND, either express or implied. See the License for the
18 | * specific language governing permissions and limitations
19 | * under the License.
20 | */
21 | package nl.lxtreme.jvt220.terminal.vt220;
22 |
23 |
24 | import nl.lxtreme.jvt220.terminal.ITerminal.ITextCell;
25 |
26 |
27 | /**
28 | * Provides an implementation of {@link ITextCell} that packs all its
29 | * information in a character and short value.
30 | */
31 | final class TextCell extends TextAttributes implements ITextCell
32 | {
33 | // VARIABLES
34 |
35 | private char m_ch;
36 |
37 | // CONSTRUCTORS
38 |
39 | /**
40 | * Creates a new, empty {@link TextCell} instance.
41 | */
42 | public TextCell()
43 | {
44 | this( ' ', ( short )0 );
45 | }
46 |
47 | /**
48 | * Creates a new {@link TextCell} instance with the given contents and
49 | * attributes.
50 | *
51 | * @param ch
52 | * the contents of this cell;
53 | * @param attributes
54 | * the attributes of this cell.
55 | */
56 | public TextCell( char ch, short attributes )
57 | {
58 | m_ch = ch;
59 | setAttributes( attributes );
60 | }
61 |
62 | /**
63 | * Creates a new {@link TextCell} instance as copy of the given text cell.
64 | *
65 | * @param cell
66 | * the cell to copy the content + attributes from, cannot be
67 | * null
.
68 | */
69 | public TextCell( TextCell cell )
70 | {
71 | this( cell.getChar(), cell.getAttributes() );
72 | }
73 |
74 | // METHODS
75 |
76 | /**
77 | * {@inheritDoc}
78 | */
79 | @Override
80 | public char getChar()
81 | {
82 | return m_ch;
83 | }
84 |
85 | /**
86 | * {@inheritDoc}
87 | */
88 | @Override
89 | public String toString()
90 | {
91 | return "[" + m_ch + "]";
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/nl/lxtreme/jvt220/terminal/vt220/packageinfo:
--------------------------------------------------------------------------------
1 | version 1.1.0
2 |
--------------------------------------------------------------------------------
/test/nl/lxtreme/jvt220/terminal/swing/CharBufferTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * (C) Copyright 2012 - J.W. Janssen, null
.
270 | */
271 | private PlainTerminal createTerminal() throws IOException
272 | {
273 | return createTerminal( "12345\r\n12345\r\n12345" );
274 | }
275 |
276 | /**
277 | * @param aColumns
278 | * @param aLines
279 | * @return
280 | */
281 | private PlainTerminal createTerminal( int aColumns, int aLines )
282 | {
283 | return new PlainTerminal( aColumns, aLines );
284 | }
285 |
286 | /**
287 | * @return a new {@link AbstractTerminal} instance, never null
.
288 | */
289 | private PlainTerminal createTerminal( String aText ) throws IOException
290 | {
291 | PlainTerminal term = createTerminal( 5, 3 );
292 | term.setAutoWrap( false );
293 | term.read( aText );
294 | term.setAutoWrap( true );
295 | return term;
296 | }
297 |
298 | /**
299 | * @param aTerm
300 | * @return
301 | */
302 | private String getTermText( final AbstractTerminal aTerm )
303 | {
304 | StringBuilder sb = new StringBuilder();
305 | for ( int idx = aTerm.getFirstAbsoluteIndex(); idx <= aTerm.getLastAbsoluteIndex(); idx++ )
306 | {
307 | ITextCell cell = aTerm.getCellAt( idx );
308 | sb.append( cell == null ? ' ' : cell.getChar() );
309 | }
310 | return sb.toString();
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/test/nl/lxtreme/jvt220/terminal/vt220/VT220ParserTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * jVT220 - Java VT220 terminal emulator.
3 | *
4 | * (C) Copyright 2012 - J.W. Janssen,