author | zecke <zecke> | 2002-10-14 17:46:45 (UTC) |
---|---|---|
committer | zecke <zecke> | 2002-10-14 17:46:45 (UTC) |
commit | 2c5e8939ba073a42c032f5a9660ed0dd4580bf88 (patch) (unidiff) | |
tree | 163d660eb057ba0f78c119465d85413d3524ed1b | |
parent | 51e18b363eb37621479a059af58da3040db1be7e (diff) | |
download | opie-2c5e8939ba073a42c032f5a9660ed0dd4580bf88.zip opie-2c5e8939ba073a42c032f5a9660ed0dd4580bf88.tar.gz opie-2c5e8939ba073a42c032f5a9660ed0dd4580bf88.tar.bz2 |
The new old TerminalWidget I'm anything but happy about it
21 files changed, 5023 insertions, 48 deletions
diff --git a/noncore/apps/opie-console/TECommon.h b/noncore/apps/opie-console/TECommon.h new file mode 100644 index 0000000..5db41ad --- a/dev/null +++ b/noncore/apps/opie-console/TECommon.h | |||
@@ -0,0 +1,114 @@ | |||
1 | /* -------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [TECommon.h] Common Definitions */ | ||
4 | /* */ | ||
5 | /* -------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* -------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | /*! \file TECommon.h | ||
20 | \brief Definitions shared between TEScreen and TEWidget. | ||
21 | */ | ||
22 | |||
23 | #ifndef TECOMMON_H | ||
24 | #define TECOMMON_H | ||
25 | |||
26 | #include <qcolor.h> | ||
27 | |||
28 | #ifndef BOOL | ||
29 | typedef int BOOL; | ||
30 | #endif | ||
31 | |||
32 | #ifndef FALSE | ||
33 | #define FALSE 0 | ||
34 | #endif | ||
35 | |||
36 | #ifndef TRUE | ||
37 | #define TRUE 1 | ||
38 | #endif | ||
39 | |||
40 | #ifndef UINT8 | ||
41 | typedef unsigned char UINT8; | ||
42 | #endif | ||
43 | |||
44 | #ifndef UINT16 | ||
45 | typedef unsigned short UINT16; | ||
46 | #endif | ||
47 | |||
48 | // Attributed Character Representations /////////////////////////////// | ||
49 | |||
50 | // Colors | ||
51 | |||
52 | #define BASE_COLORS (2+8) | ||
53 | #define INTENSITIES 2 | ||
54 | #define TABLE_COLORS (INTENSITIES*BASE_COLORS) | ||
55 | |||
56 | #define DEFAULT_FORE_COLOR 0 | ||
57 | #define DEFAULT_BACK_COLOR 1 | ||
58 | |||
59 | #define DEFAULT_RENDITION 0 | ||
60 | #define RE_BOLD (1 << 0) | ||
61 | #define RE_BLINK (1 << 1) | ||
62 | #define RE_UNDERLINE (1 << 2) | ||
63 | #define RE_REVERSE (1 << 3) // Screen only | ||
64 | #define RE_INTENSIVE (1 << 3) // Widget only | ||
65 | |||
66 | /*! \class ca | ||
67 | * \brief a character with rendition attributes. | ||
68 | */ | ||
69 | |||
70 | class ca | ||
71 | { | ||
72 | public: | ||
73 | inline ca(UINT16 _c = ' ', | ||
74 | UINT8 _f = DEFAULT_FORE_COLOR, | ||
75 | UINT8 _b = DEFAULT_BACK_COLOR, | ||
76 | UINT8 _r = DEFAULT_RENDITION) | ||
77 | : c(_c), f(_f), b(_b), r(_r) {} | ||
78 | public: | ||
79 | UINT16 c; // character | ||
80 | UINT8 f; // foreground color | ||
81 | UINT8 b; // background color | ||
82 | UINT8 r; // rendition | ||
83 | public: | ||
84 | friend BOOL operator == (ca a, ca b); | ||
85 | friend BOOL operator != (ca a, ca b); | ||
86 | }; | ||
87 | |||
88 | inline BOOL operator == (ca a, ca b) | ||
89 | { | ||
90 | return a.c == b.c && a.f == b.f && a.b == b.b && a.r == b.r; | ||
91 | } | ||
92 | |||
93 | inline BOOL operator != (ca a, ca b) | ||
94 | { | ||
95 | return a.c != b.c || a.f != b.f || a.b != b.b || a.r != b.r; | ||
96 | } | ||
97 | |||
98 | /*! | ||
99 | */ | ||
100 | struct ColorEntry | ||
101 | { | ||
102 | ColorEntry(QColor c, bool tr, bool b) : color(c), transparent(tr), bold(b) {} | ||
103 | ColorEntry() : transparent(false), bold(false) {} // default constructors | ||
104 | void operator=(const ColorEntry& rhs) { | ||
105 | color = rhs.color; | ||
106 | transparent = rhs.transparent; | ||
107 | bold = rhs.bold; | ||
108 | } | ||
109 | QColor color; | ||
110 | bool transparent; // if used on bg | ||
111 | bool bold; // if used on fg | ||
112 | }; | ||
113 | |||
114 | #endif // TECOMMON_H | ||
diff --git a/noncore/apps/opie-console/TEHistory.cpp b/noncore/apps/opie-console/TEHistory.cpp new file mode 100644 index 0000000..317ce57 --- a/dev/null +++ b/noncore/apps/opie-console/TEHistory.cpp | |||
@@ -0,0 +1,212 @@ | |||
1 | /* -------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [TEHistory.C] History Buffer */ | ||
4 | /* */ | ||
5 | /* -------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* -------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | #include "TEHistory.h" | ||
20 | #include <stdlib.h> | ||
21 | #include <assert.h> | ||
22 | #include <stdio.h> | ||
23 | #include <sys/types.h> | ||
24 | #include <unistd.h> | ||
25 | #include <errno.h> | ||
26 | |||
27 | #define HERE printf("%s(%d): here\n",__FILE__,__LINE__) | ||
28 | |||
29 | /* | ||
30 | An arbitrary long scroll. | ||
31 | |||
32 | One can modify the scroll only by adding either cells | ||
33 | or newlines, but access it randomly. | ||
34 | |||
35 | The model is that of an arbitrary wide typewriter scroll | ||
36 | in that the scroll is a serie of lines and each line is | ||
37 | a serie of cells with no overwriting permitted. | ||
38 | |||
39 | The implementation provides arbitrary length and numbers | ||
40 | of cells and line/column indexed read access to the scroll | ||
41 | at constant costs. | ||
42 | |||
43 | FIXME: some complain about the history buffer comsuming the | ||
44 | memory of their machines. This problem is critical | ||
45 | since the history does not behave gracefully in cases | ||
46 | where the memory is used up completely. | ||
47 | |||
48 | I put in a workaround that should handle it problem | ||
49 | now gracefully. I'm not satisfied with the solution. | ||
50 | |||
51 | FIXME: Terminating the history is not properly indicated | ||
52 | in the menu. We should throw a signal. | ||
53 | |||
54 | FIXME: There is noticable decrease in speed, also. Perhaps, | ||
55 | there whole feature needs to be revisited therefore. | ||
56 | Disadvantage of a more elaborated, say block-oriented | ||
57 | scheme with wrap around would be it's complexity. | ||
58 | */ | ||
59 | |||
60 | //FIXME: tempory replacement for tmpfile | ||
61 | // this is here one for debugging purpose. | ||
62 | |||
63 | //#define tmpfile xTmpFile | ||
64 | |||
65 | FILE* xTmpFile() | ||
66 | { | ||
67 | static int fid = 0; | ||
68 | char fname[80]; | ||
69 | sprintf(fname,"TmpFile.%d",fid++); | ||
70 | return fopen(fname,"w"); | ||
71 | } | ||
72 | |||
73 | |||
74 | // History Buffer /////////////////////////////////////////// | ||
75 | |||
76 | /* | ||
77 | A Row(X) data type which allows adding elements to the end. | ||
78 | */ | ||
79 | |||
80 | HistoryBuffer::HistoryBuffer() | ||
81 | { | ||
82 | ion = -1; | ||
83 | length = 0; | ||
84 | } | ||
85 | |||
86 | HistoryBuffer::~HistoryBuffer() | ||
87 | { | ||
88 | setScroll(FALSE); | ||
89 | } | ||
90 | |||
91 | void HistoryBuffer::setScroll(bool on) | ||
92 | { | ||
93 | if (on == hasScroll()) return; | ||
94 | |||
95 | if (on) | ||
96 | { | ||
97 | assert( ion < 0 ); | ||
98 | assert( length == 0); | ||
99 | FILE* tmp = tmpfile(); if (!tmp) { perror("konsole: cannot open temp file.\n"); return; } | ||
100 | ion = dup(fileno(tmp)); if (ion<0) perror("konsole: cannot dup temp file.\n"); | ||
101 | fclose(tmp); | ||
102 | } | ||
103 | else | ||
104 | { | ||
105 | assert( ion >= 0 ); | ||
106 | close(ion); | ||
107 | ion = -1; | ||
108 | length = 0; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | bool HistoryBuffer::hasScroll() | ||
113 | { | ||
114 | return ion >= 0; | ||
115 | } | ||
116 | |||
117 | void HistoryBuffer::add(const unsigned char* bytes, int len) | ||
118 | { int rc; | ||
119 | assert(hasScroll()); | ||
120 | rc = lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setScroll(FALSE); return; } | ||
121 | rc = write(ion,bytes,len); if (rc < 0) { perror("HistoryBuffer::add.write"); setScroll(FALSE); return; } | ||
122 | length += rc; | ||
123 | } | ||
124 | |||
125 | void HistoryBuffer::get(unsigned char* bytes, int len, int loc) | ||
126 | { int rc; | ||
127 | assert(hasScroll()); | ||
128 | if (loc < 0 || len < 0 || loc + len > length) | ||
129 | fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc); | ||
130 | rc = lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryBuffer::get.seek"); setScroll(FALSE); return; } | ||
131 | rc = read(ion,bytes,len); if (rc < 0) { perror("HistoryBuffer::get.read"); setScroll(FALSE); return; } | ||
132 | } | ||
133 | |||
134 | int HistoryBuffer::len() | ||
135 | { | ||
136 | return length; | ||
137 | } | ||
138 | |||
139 | // History Scroll ////////////////////////////////////// | ||
140 | |||
141 | /* | ||
142 | The history scroll makes a Row(Row(Cell)) from | ||
143 | two history buffers. The index buffer contains | ||
144 | start of line positions which refere to the cells | ||
145 | buffer. | ||
146 | |||
147 | Note that index[0] addresses the second line | ||
148 | (line #1), while the first line (line #0) starts | ||
149 | at 0 in cells. | ||
150 | */ | ||
151 | |||
152 | HistoryScroll::HistoryScroll() | ||
153 | { | ||
154 | } | ||
155 | |||
156 | HistoryScroll::~HistoryScroll() | ||
157 | { | ||
158 | } | ||
159 | |||
160 | void HistoryScroll::setScroll(bool on) | ||
161 | { | ||
162 | index.setScroll(on); | ||
163 | cells.setScroll(on); | ||
164 | } | ||
165 | |||
166 | bool HistoryScroll::hasScroll() | ||
167 | { | ||
168 | return index.hasScroll() && cells.hasScroll(); | ||
169 | } | ||
170 | |||
171 | int HistoryScroll::getLines() | ||
172 | { | ||
173 | if (!hasScroll()) return 0; | ||
174 | return index.len() / sizeof(int); | ||
175 | } | ||
176 | |||
177 | int HistoryScroll::getLineLen(int lineno) | ||
178 | { | ||
179 | if (!hasScroll()) return 0; | ||
180 | return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(ca); | ||
181 | } | ||
182 | |||
183 | int HistoryScroll::startOfLine(int lineno) | ||
184 | { | ||
185 | if (lineno <= 0) return 0; | ||
186 | if (!hasScroll()) return 0; | ||
187 | if (lineno <= getLines()) | ||
188 | { int res; | ||
189 | index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int)); | ||
190 | return res; | ||
191 | } | ||
192 | return cells.len(); | ||
193 | } | ||
194 | |||
195 | void HistoryScroll::getCells(int lineno, int colno, int count, ca res[]) | ||
196 | { | ||
197 | assert(hasScroll()); | ||
198 | cells.get((unsigned char*)res,count*sizeof(ca),startOfLine(lineno)+colno*sizeof(ca)); | ||
199 | } | ||
200 | |||
201 | void HistoryScroll::addCells(ca text[], int count) | ||
202 | { | ||
203 | if (!hasScroll()) return; | ||
204 | cells.add((unsigned char*)text,count*sizeof(ca)); | ||
205 | } | ||
206 | |||
207 | void HistoryScroll::addLine() | ||
208 | { | ||
209 | if (!hasScroll()) return; | ||
210 | int locn = cells.len(); | ||
211 | index.add((unsigned char*)&locn,sizeof(int)); | ||
212 | } | ||
diff --git a/noncore/apps/opie-console/TEHistory.h b/noncore/apps/opie-console/TEHistory.h new file mode 100644 index 0000000..8339ec6 --- a/dev/null +++ b/noncore/apps/opie-console/TEHistory.h | |||
@@ -0,0 +1,75 @@ | |||
1 | /* -------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [TEHistory.H] History Buffer */ | ||
4 | /* */ | ||
5 | /* -------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* -------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | #ifndef TEHISTORY_H | ||
20 | #define TEHISTORY_H | ||
21 | |||
22 | #include "TECommon.h" | ||
23 | |||
24 | /* | ||
25 | An extendable tmpfile(1) based buffer. | ||
26 | */ | ||
27 | class HistoryBuffer | ||
28 | { | ||
29 | public: | ||
30 | HistoryBuffer(); | ||
31 | ~HistoryBuffer(); | ||
32 | |||
33 | public: | ||
34 | void setScroll(bool on); | ||
35 | bool hasScroll(); | ||
36 | |||
37 | public: | ||
38 | void add(const unsigned char* bytes, int len); | ||
39 | void get(unsigned char* bytes, int len, int loc); | ||
40 | int len(); | ||
41 | |||
42 | private: | ||
43 | int ion; | ||
44 | int length; | ||
45 | }; | ||
46 | |||
47 | class HistoryScroll | ||
48 | { | ||
49 | public: | ||
50 | HistoryScroll(); | ||
51 | ~HistoryScroll(); | ||
52 | |||
53 | public: | ||
54 | void setScroll(bool on); | ||
55 | bool hasScroll(); | ||
56 | |||
57 | public: // access to history | ||
58 | int getLines(); | ||
59 | int getLineLen(int lineno); | ||
60 | void getCells(int lineno, int colno, int count, ca res[]); | ||
61 | |||
62 | public: // backward compatibility (obsolete) | ||
63 | ca getCell(int lineno, int colno) { ca res; getCells(lineno,colno,1,&res); return res; } | ||
64 | |||
65 | public: // adding lines. | ||
66 | void addCells(ca a[], int count); | ||
67 | void addLine(); | ||
68 | |||
69 | private: | ||
70 | int startOfLine(int lineno); | ||
71 | HistoryBuffer index; // lines Row(int) | ||
72 | HistoryBuffer cells; // text Row(ca) | ||
73 | }; | ||
74 | |||
75 | #endif // TEHISTORY_H | ||
diff --git a/noncore/apps/opie-console/TEScreen.cpp b/noncore/apps/opie-console/TEScreen.cpp new file mode 100644 index 0000000..a3d115d --- a/dev/null +++ b/noncore/apps/opie-console/TEScreen.cpp | |||
@@ -0,0 +1,1197 @@ | |||
1 | /* -------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [TEScreen.C] Screen Data Type */ | ||
4 | /* */ | ||
5 | /* -------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* -------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | /*! \file | ||
20 | */ | ||
21 | |||
22 | /*! \class TEScreen | ||
23 | |||
24 | \brief The image manipulated by the emulation. | ||
25 | |||
26 | This class implements the operations of the terminal emulation framework. | ||
27 | It is a complete passive device, driven by the emulation decoder | ||
28 | (TEmuVT102). By this it forms in fact an ADT, that defines operations | ||
29 | on a rectangular image. | ||
30 | |||
31 | It does neither know how to display its image nor about escape sequences. | ||
32 | It is further independent of the underlying toolkit. By this, one can even | ||
33 | use this module for an ordinary text surface. | ||
34 | |||
35 | Since the operations are called by a specific emulation decoder, one may | ||
36 | collect their different operations here. | ||
37 | |||
38 | The state manipulated by the operations is mainly kept in `image', though | ||
39 | it is a little more complex bejond this. See the header file of the class. | ||
40 | |||
41 | \sa TEWidget \sa VT102Emulation | ||
42 | */ | ||
43 | |||
44 | #include <stdio.h> | ||
45 | #include <stdlib.h> | ||
46 | #include <unistd.h> | ||
47 | // #include <kdebug.h> | ||
48 | |||
49 | #include <assert.h> | ||
50 | #include <string.h> | ||
51 | #include <ctype.h> | ||
52 | |||
53 | #include "TEScreen.h" | ||
54 | |||
55 | #define HERE printf("%s(%d): here\n",__FILE__,__LINE__) | ||
56 | |||
57 | //FIXME: this is emulation specific. Use FALSE for xterm, TRUE for ANSI. | ||
58 | //FIXME: see if we can get this from terminfo. | ||
59 | #define BS_CLEARS FALSE | ||
60 | |||
61 | #define loc(X,Y) ((Y)*columns+(X)) | ||
62 | |||
63 | /*! creates a `TEScreen' of `lines' lines and `columns' columns. | ||
64 | */ | ||
65 | |||
66 | TEScreen::TEScreen(int lines, int columns) | ||
67 | { | ||
68 | this->lines = lines; | ||
69 | this->columns = columns; | ||
70 | |||
71 | image = (ca*) malloc(lines*columns*sizeof(ca)); | ||
72 | tabstops = NULL; initTabStops(); | ||
73 | |||
74 | histCursor = 0; | ||
75 | |||
76 | clearSelection(); | ||
77 | reset(); | ||
78 | } | ||
79 | |||
80 | /*! Destructor | ||
81 | */ | ||
82 | |||
83 | TEScreen::~TEScreen() | ||
84 | { | ||
85 | free(image); | ||
86 | if (tabstops) free(tabstops); | ||
87 | } | ||
88 | |||
89 | /* ------------------------------------------------------------------------- */ | ||
90 | /* */ | ||
91 | /* Normalized Screen Operations */ | ||
92 | /* */ | ||
93 | /* ------------------------------------------------------------------------- */ | ||
94 | |||
95 | // Cursor Setting -------------------------------------------------------------- | ||
96 | |||
97 | /*! \section Cursor | ||
98 | |||
99 | The `cursor' is a location within the screen that is implicitely used in | ||
100 | many operations. The operations within this section allow to manipulate | ||
101 | the cursor explicitly and to obtain it's value. | ||
102 | |||
103 | The position of the cursor is guarantied to be between (including) 0 and | ||
104 | `columns-1' and `lines-1'. | ||
105 | */ | ||
106 | |||
107 | /*! | ||
108 | Move the cursor up. | ||
109 | |||
110 | The cursor will not be moved beyond the top margin. | ||
111 | */ | ||
112 | |||
113 | void TEScreen::cursorUp(int n) | ||
114 | //=CUU | ||
115 | { | ||
116 | if (n == 0) n = 1; // Default | ||
117 | int stop = cuY < tmargin ? 0 : tmargin; | ||
118 | cuX = QMIN(columns-1,cuX); // nowrap! | ||
119 | cuY = QMAX(stop,cuY-n); | ||
120 | } | ||
121 | |||
122 | /*! | ||
123 | Move the cursor down. | ||
124 | |||
125 | The cursor will not be moved beyond the bottom margin. | ||
126 | */ | ||
127 | |||
128 | void TEScreen::cursorDown(int n) | ||
129 | //=CUD | ||
130 | { | ||
131 | if (n == 0) n = 1; // Default | ||
132 | int stop = cuY > bmargin ? lines-1 : bmargin; | ||
133 | cuX = QMIN(columns-1,cuX); // nowrap! | ||
134 | cuY = QMIN(stop,cuY+n); | ||
135 | } | ||
136 | |||
137 | /*! | ||
138 | Move the cursor left. | ||
139 | |||
140 | The cursor will not move beyond the first column. | ||
141 | */ | ||
142 | |||
143 | void TEScreen::cursorLeft(int n) | ||
144 | //=CUB | ||
145 | { | ||
146 | if (n == 0) n = 1; // Default | ||
147 | cuX = QMIN(columns-1,cuX); // nowrap! | ||
148 | cuX = QMAX(0,cuX-n); | ||
149 | } | ||
150 | |||
151 | /*! | ||
152 | Move the cursor left. | ||
153 | |||
154 | The cursor will not move beyond the rightmost column. | ||
155 | */ | ||
156 | |||
157 | void TEScreen::cursorRight(int n) | ||
158 | //=CUF | ||
159 | { | ||
160 | if (n == 0) n = 1; // Default | ||
161 | cuX = QMIN(columns-1,cuX+n); | ||
162 | } | ||
163 | |||
164 | /*! | ||
165 | Set top and bottom margin. | ||
166 | */ | ||
167 | |||
168 | void TEScreen::setMargins(int top, int bot) | ||
169 | //=STBM | ||
170 | { | ||
171 | if (top == 0) top = 1; // Default | ||
172 | if (bot == 0) bot = lines; // Default | ||
173 | top = top - 1; // Adjust to internal lineno | ||
174 | bot = bot - 1; // Adjust to internal lineno | ||
175 | if ( !( 0 <= top && top < bot && bot < lines ) ) | ||
176 | { fprintf(stderr,"%s(%d) : setRegion(%d,%d) : bad range.\n", | ||
177 | __FILE__,__LINE__,top,bot); | ||
178 | return; // Default error action: ignore | ||
179 | } | ||
180 | tmargin = top; | ||
181 | bmargin = bot; | ||
182 | cuX = 0; | ||
183 | cuY = getMode(MODE_Origin) ? top : 0; | ||
184 | } | ||
185 | |||
186 | /*! | ||
187 | Move the cursor down one line. | ||
188 | |||
189 | If cursor is on bottom margin, the region between the | ||
190 | actual top and bottom margin is scrolled up instead. | ||
191 | */ | ||
192 | |||
193 | void TEScreen::index() | ||
194 | //=IND | ||
195 | { | ||
196 | if (cuY == bmargin) | ||
197 | { | ||
198 | if (tmargin == 0 && bmargin == lines-1) addHistLine(); // hist.history | ||
199 | scrollUp(tmargin,1); | ||
200 | } | ||
201 | else if (cuY < lines-1) | ||
202 | cuY += 1; | ||
203 | } | ||
204 | |||
205 | /*! | ||
206 | Move the cursor up one line. | ||
207 | |||
208 | If cursor is on the top margin, the region between the | ||
209 | actual top and bottom margin is scrolled down instead. | ||
210 | */ | ||
211 | |||
212 | void TEScreen::reverseIndex() | ||
213 | //=RI | ||
214 | { | ||
215 | if (cuY == tmargin) | ||
216 | scrollDown(tmargin,1); | ||
217 | else if (cuY > 0) | ||
218 | cuY -= 1; | ||
219 | } | ||
220 | |||
221 | /*! | ||
222 | Move the cursor to the begin of the next line. | ||
223 | |||
224 | If cursor is on bottom margin, the region between the | ||
225 | actual top and bottom margin is scrolled up. | ||
226 | */ | ||
227 | |||
228 | void TEScreen::NextLine() | ||
229 | //=NEL | ||
230 | { | ||
231 | Return(); index(); | ||
232 | } | ||
233 | |||
234 | // Line Editing ---------------------------------------------------------------- | ||
235 | |||
236 | /*! \section inserting / deleting characters | ||
237 | */ | ||
238 | |||
239 | /*! erase `n' characters starting from (including) the cursor position. | ||
240 | |||
241 | The line is filled in from the right with spaces. | ||
242 | */ | ||
243 | |||
244 | void TEScreen::eraseChars(int n) | ||
245 | { | ||
246 | if (n == 0) n = 1; // Default | ||
247 | int p = QMAX(0,QMIN(cuX+n-1,columns-1)); | ||
248 | clearImage(loc(cuX,cuY),loc(p,cuY),' '); | ||
249 | } | ||
250 | |||
251 | /*! delete `n' characters starting from (including) the cursor position. | ||
252 | |||
253 | The line is filled in from the right with spaces. | ||
254 | */ | ||
255 | |||
256 | void TEScreen::deleteChars(int n) | ||
257 | { | ||
258 | if (n == 0) n = 1; // Default | ||
259 | int p = QMAX(0,QMIN(cuX+n,columns-1)); | ||
260 | moveImage(loc(cuX,cuY),loc(p,cuY),loc(columns-1,cuY)); | ||
261 | clearImage(loc(columns-n,cuY),loc(columns-1,cuY),' '); | ||
262 | } | ||
263 | |||
264 | /*! insert `n' spaces at the cursor position. | ||
265 | |||
266 | The cursor is not moved by the operation. | ||
267 | */ | ||
268 | |||
269 | void TEScreen::insertChars(int n) | ||
270 | { | ||
271 | if (n == 0) n = 1; // Default | ||
272 | int p = QMAX(0,QMIN(columns-1-n,columns-1)); | ||
273 | int q = QMAX(0,QMIN(cuX+n,columns-1)); | ||
274 | moveImage(loc(q,cuY),loc(cuX,cuY),loc(p,cuY)); | ||
275 | clearImage(loc(cuX,cuY),loc(q-1,cuY),' '); | ||
276 | } | ||
277 | |||
278 | /*! delete `n' lines starting from (including) the cursor position. | ||
279 | |||
280 | The cursor is not moved by the operation. | ||
281 | */ | ||
282 | |||
283 | void TEScreen::deleteLines(int n) | ||
284 | { | ||
285 | if (n == 0) n = 1; // Default | ||
286 | scrollUp(cuY,n); | ||
287 | } | ||
288 | |||
289 | /*! insert `n' lines at the cursor position. | ||
290 | |||
291 | The cursor is not moved by the operation. | ||
292 | */ | ||
293 | |||
294 | void TEScreen::insertLines(int n) | ||
295 | { | ||
296 | if (n == 0) n = 1; // Default | ||
297 | scrollDown(cuY,n); | ||
298 | } | ||
299 | |||
300 | // Mode Operations ----------------------------------------------------------- | ||
301 | |||
302 | /*! Set a specific mode. */ | ||
303 | |||
304 | void TEScreen::setMode(int m) | ||
305 | { | ||
306 | currParm.mode[m] = TRUE; | ||
307 | switch(m) | ||
308 | { | ||
309 | case MODE_Origin : cuX = 0; cuY = tmargin; break; //FIXME: home | ||
310 | } | ||
311 | } | ||
312 | |||
313 | /*! Reset a specific mode. */ | ||
314 | |||
315 | void TEScreen::resetMode(int m) | ||
316 | { | ||
317 | currParm.mode[m] = FALSE; | ||
318 | switch(m) | ||
319 | { | ||
320 | case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home | ||
321 | } | ||
322 | } | ||
323 | |||
324 | /*! Save a specific mode. */ | ||
325 | |||
326 | void TEScreen::saveMode(int m) | ||
327 | { | ||
328 | saveParm.mode[m] = currParm.mode[m]; | ||
329 | } | ||
330 | |||
331 | /*! Restore a specific mode. */ | ||
332 | |||
333 | void TEScreen::restoreMode(int m) | ||
334 | { | ||
335 | currParm.mode[m] = saveParm.mode[m]; | ||
336 | } | ||
337 | |||
338 | //NOTE: this is a helper function | ||
339 | /*! Return the setting a specific mode. */ | ||
340 | BOOL TEScreen::getMode(int m) | ||
341 | { | ||
342 | return currParm.mode[m]; | ||
343 | } | ||
344 | |||
345 | /*! Save the cursor position and the rendition attribute settings. */ | ||
346 | |||
347 | void TEScreen::saveCursor() | ||
348 | { | ||
349 | sa_cuX = cuX; | ||
350 | sa_cuY = cuY; | ||
351 | sa_cu_re = cu_re; | ||
352 | sa_cu_fg = cu_fg; | ||
353 | sa_cu_bg = cu_bg; | ||
354 | } | ||
355 | |||
356 | /*! Restore the cursor position and the rendition attribute settings. */ | ||
357 | |||
358 | void TEScreen::restoreCursor() | ||
359 | { | ||
360 | cuX = QMIN(sa_cuX,columns-1); | ||
361 | cuY = QMIN(sa_cuY,lines-1); | ||
362 | cu_re = sa_cu_re; | ||
363 | cu_fg = sa_cu_fg; | ||
364 | cu_bg = sa_cu_bg; | ||
365 | effectiveRendition(); | ||
366 | } | ||
367 | |||
368 | /* ------------------------------------------------------------------------- */ | ||
369 | /* */ | ||
370 | /* Screen Operations */ | ||
371 | /* */ | ||
372 | /* ------------------------------------------------------------------------- */ | ||
373 | |||
374 | /*! Assing a new size to the screen. | ||
375 | |||
376 | The topmost left position is maintained, while lower lines | ||
377 | or right hand side columns might be removed or filled with | ||
378 | spaces to fit the new size. | ||
379 | |||
380 | The region setting is reset to the whole screen and the | ||
381 | tab positions reinitialized. | ||
382 | */ | ||
383 | |||
384 | void TEScreen::resizeImage(int new_lines, int new_columns) | ||
385 | { | ||
386 | |||
387 | if (cuY > new_lines-1) | ||
388 | { // attempt to preserve focus and lines | ||
389 | bmargin = lines-1; //FIXME: margin lost | ||
390 | for (int i = 0; i < cuY-(new_lines-1); i++) | ||
391 | { | ||
392 | addHistLine(); scrollUp(0,1); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | // make new image | ||
397 | ca* newimg = (ca*)malloc(new_lines*new_columns*sizeof(ca)); | ||
398 | |||
399 | clearSelection(); | ||
400 | |||
401 | // clear new image | ||
402 | for (int y = 0; y < new_lines; y++) | ||
403 | for (int x = 0; x < new_columns; x++) | ||
404 | { | ||
405 | newimg[y*new_columns+x].c = ' '; | ||
406 | newimg[y*new_columns+x].f = DEFAULT_FORE_COLOR; | ||
407 | newimg[y*new_columns+x].b = DEFAULT_BACK_COLOR; | ||
408 | newimg[y*new_columns+x].r = DEFAULT_RENDITION; | ||
409 | } | ||
410 | int cpy_lines = QMIN(new_lines, lines); | ||
411 | int cpy_columns = QMIN(new_columns,columns); | ||
412 | // copy to new image | ||
413 | for (int y = 0; y < cpy_lines; y++) | ||
414 | for (int x = 0; x < cpy_columns; x++) | ||
415 | { | ||
416 | newimg[y*new_columns+x].c = image[loc(x,y)].c; | ||
417 | newimg[y*new_columns+x].f = image[loc(x,y)].f; | ||
418 | newimg[y*new_columns+x].b = image[loc(x,y)].b; | ||
419 | newimg[y*new_columns+x].r = image[loc(x,y)].r; | ||
420 | } | ||
421 | free(image); | ||
422 | image = newimg; | ||
423 | lines = new_lines; | ||
424 | columns = new_columns; | ||
425 | cuX = QMIN(cuX,columns-1); | ||
426 | cuY = QMIN(cuY,lines-1); | ||
427 | |||
428 | // FIXME: try to keep values, evtl. | ||
429 | tmargin=0; | ||
430 | bmargin=lines-1; | ||
431 | initTabStops(); | ||
432 | clearSelection(); | ||
433 | } | ||
434 | |||
435 | /* | ||
436 | Clarifying rendition here and in TEWidget. | ||
437 | |||
438 | currently, TEWidget's color table is | ||
439 | 0 1 2 .. 9 10 .. 17 | ||
440 | dft_fg, dft_bg, dim 0..7, intensive 0..7 | ||
441 | |||
442 | cu_fg, cu_bg contain values 0..8; | ||
443 | - 0 = default color | ||
444 | - 1..8 = ansi specified color | ||
445 | |||
446 | re_fg, re_bg contain values 0..17 | ||
447 | due to the TEWidget's color table | ||
448 | |||
449 | rendition attributes are | ||
450 | |||
451 | attr widget screen | ||
452 | -------------- ------ ------ | ||
453 | RE_UNDERLINE XX XX affects foreground only | ||
454 | RE_BLINK XX XX affects foreground only | ||
455 | RE_BOLD XX XX affects foreground only | ||
456 | RE_REVERSE -- XX | ||
457 | RE_TRANSPARENT XX -- affects background only | ||
458 | RE_INTENSIVE XX -- affects foreground only | ||
459 | |||
460 | Note that RE_BOLD is used in both widget | ||
461 | and screen rendition. Since xterm/vt102 | ||
462 | is to poor to distinguish between bold | ||
463 | (which is a font attribute) and intensive | ||
464 | (which is a color attribute), we translate | ||
465 | this and RE_BOLD in falls eventually appart | ||
466 | into RE_BOLD and RE_INTENSIVE. | ||
467 | */ | ||
468 | |||
469 | void TEScreen::reverseRendition(ca* p) | ||
470 | { UINT8 f = p->f; UINT8 b = p->b; | ||
471 | p->f = b; p->b = f; //p->r &= ~RE_TRANSPARENT; | ||
472 | } | ||
473 | |||
474 | void TEScreen::effectiveRendition() | ||
475 | // calculate rendition | ||
476 | { | ||
477 | ef_re = cu_re & (RE_UNDERLINE | RE_BLINK); | ||
478 | if (cu_re & RE_REVERSE) | ||
479 | { | ||
480 | ef_fg = cu_bg; | ||
481 | ef_bg = cu_fg; | ||
482 | } | ||
483 | else | ||
484 | { | ||
485 | ef_fg = cu_fg; | ||
486 | ef_bg = cu_bg; | ||
487 | } | ||
488 | if (cu_re & RE_BOLD) | ||
489 | { | ||
490 | if (ef_fg < BASE_COLORS) | ||
491 | ef_fg += BASE_COLORS; | ||
492 | else | ||
493 | ef_fg -= BASE_COLORS; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | /*! | ||
498 | returns the image. | ||
499 | |||
500 | Get the size of the image by \sa getLines and \sa getColumns. | ||
501 | |||
502 | NOTE that the image returned by this function must later be | ||
503 | freed. | ||
504 | |||
505 | */ | ||
506 | |||
507 | ca* TEScreen::getCookedImage() | ||
508 | { int x,y; | ||
509 | ca* merged = (ca*)malloc(lines*columns*sizeof(ca)); | ||
510 | ca dft(' ',DEFAULT_FORE_COLOR,DEFAULT_BACK_COLOR,DEFAULT_RENDITION); | ||
511 | |||
512 | for (y = 0; (y < lines) && (y < (hist.getLines()-histCursor)); y++) | ||
513 | { | ||
514 | int len = QMIN(columns,hist.getLineLen(y+histCursor)); | ||
515 | int yp = y*columns; | ||
516 | int yq = (y+histCursor)*columns; | ||
517 | |||
518 | hist.getCells(y+histCursor,0,len,merged+yp); | ||
519 | for (x = len; x < columns; x++) merged[yp+x] = dft; | ||
520 | for (x = 0; x < columns; x++) | ||
521 | { int p=x + yp; int q=x + yq; | ||
522 | if ( ( q >= sel_TL ) && ( q <= sel_BR ) ) | ||
523 | reverseRendition(&merged[p]); // for selection | ||
524 | } | ||
525 | } | ||
526 | if (lines >= hist.getLines()-histCursor) | ||
527 | { | ||
528 | for (y = (hist.getLines()-histCursor); y < lines ; y++) | ||
529 | { | ||
530 | int yp = y*columns; | ||
531 | int yq = (y+histCursor)*columns; | ||
532 | int yr = (y-hist.getLines()+histCursor)*columns; | ||
533 | for (x = 0; x < columns; x++) | ||
534 | { int p = x + yp; int q = x + yq; int r = x + yr; | ||
535 | merged[p] = image[r]; | ||
536 | if ( q >= sel_TL && q <= sel_BR ) | ||
537 | reverseRendition(&merged[p]); // for selection | ||
538 | } | ||
539 | |||
540 | } | ||
541 | } | ||
542 | // evtl. inverse display | ||
543 | if (getMode(MODE_Screen)) | ||
544 | { int i,n = lines*columns; | ||
545 | for (i = 0; i < n; i++) | ||
546 | reverseRendition(&merged[i]); // for reverse display | ||
547 | } | ||
548 | if (getMode(MODE_Cursor) && (cuY+(hist.getLines()-histCursor) < lines)) // cursor visible | ||
549 | reverseRendition(&merged[loc(cuX,cuY+(hist.getLines()-histCursor))]); | ||
550 | return merged; | ||
551 | } | ||
552 | |||
553 | |||
554 | /*! | ||
555 | */ | ||
556 | |||
557 | void TEScreen::reset() | ||
558 | { | ||
559 | setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin | ||
560 | resetMode(MODE_Origin); saveMode(MODE_Origin); // position refere to [1,1] | ||
561 | resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke | ||
562 | setMode(MODE_Cursor); // cursor visible | ||
563 | resetMode(MODE_Screen); // screen not inverse | ||
564 | resetMode(MODE_NewLine); | ||
565 | |||
566 | tmargin=0; | ||
567 | bmargin=lines-1; | ||
568 | |||
569 | setDefaultRendition(); | ||
570 | saveCursor(); | ||
571 | |||
572 | clear(); | ||
573 | } | ||
574 | |||
575 | /*! Clear the entire screen and home the cursor. | ||
576 | */ | ||
577 | |||
578 | void TEScreen::clear() | ||
579 | { | ||
580 | clearEntireScreen(); | ||
581 | home(); | ||
582 | } | ||
583 | |||
584 | /*! Moves the cursor left one column. | ||
585 | */ | ||
586 | |||
587 | void TEScreen::BackSpace() | ||
588 | { | ||
589 | cuX = QMAX(0,cuX-1); | ||
590 | if (BS_CLEARS) image[loc(cuX,cuY)].c = ' '; | ||
591 | } | ||
592 | |||
593 | /*! | ||
594 | */ | ||
595 | |||
596 | void TEScreen::Tabulate() | ||
597 | { | ||
598 | // note that TAB is a format effector (does not write ' '); | ||
599 | cursorRight(1); while(cuX < columns-1 && !tabstops[cuX]) cursorRight(1); | ||
600 | } | ||
601 | |||
602 | void TEScreen::clearTabStops() | ||
603 | { | ||
604 | for (int i = 0; i < columns; i++) tabstops[i-1] = FALSE; | ||
605 | } | ||
606 | |||
607 | void TEScreen::changeTabStop(bool set) | ||
608 | { | ||
609 | if (cuX >= columns) return; | ||
610 | tabstops[cuX] = set; | ||
611 | } | ||
612 | |||
613 | void TEScreen::initTabStops() | ||
614 | { | ||
615 | if (tabstops) free(tabstops); | ||
616 | tabstops = (bool*)malloc(columns*sizeof(bool)); | ||
617 | // Arrg! The 1st tabstop has to be one longer than the other. | ||
618 | // i.e. the kids start counting from 0 instead of 1. | ||
619 | // Other programs might behave correctly. Be aware. | ||
620 | for (int i = 0; i < columns; i++) tabstops[i] = (i%8 == 0 && i != 0); | ||
621 | } | ||
622 | |||
623 | /*! | ||
624 | This behaves either as IND (Screen::Index) or as NEL (Screen::NextLine) | ||
625 | depending on the NewLine Mode (LNM). This mode also | ||
626 | affects the key sequence returned for newline ([CR]LF). | ||
627 | */ | ||
628 | |||
629 | void TEScreen::NewLine() | ||
630 | { | ||
631 | if (getMode(MODE_NewLine)) Return(); | ||
632 | index(); | ||
633 | } | ||
634 | |||
635 | /*! put `c' literally onto the screen at the current cursor position. | ||
636 | |||
637 | VT100 uses the convention to produce an automatic newline (am) | ||
638 | with the *first* character that would fall onto the next line (xenl). | ||
639 | */ | ||
640 | |||
641 | void TEScreen::checkSelection(int from, int to) | ||
642 | { | ||
643 | if (sel_begin == -1) return; | ||
644 | int scr_TL = loc(0, hist.getLines()); | ||
645 | //Clear entire selection if it overlaps region [from, to] | ||
646 | if ( (sel_BR > (from+scr_TL) )&&(sel_TL < (to+scr_TL)) ) | ||
647 | { | ||
648 | clearSelection(); | ||
649 | } | ||
650 | } | ||
651 | |||
652 | void TEScreen::ShowCharacter(unsigned short c) | ||
653 | { | ||
654 | // Note that VT100 does wrapping BEFORE putting the character. | ||
655 | // This has impact on the assumption of valid cursor positions. | ||
656 | // We indicate the fact that a newline has to be triggered by | ||
657 | // putting the cursor one right to the last column of the screen. | ||
658 | |||
659 | if (cuX >= columns) | ||
660 | { | ||
661 | if (getMode(MODE_Wrap)) NextLine(); else cuX = columns-1; | ||
662 | } | ||
663 | |||
664 | if (getMode(MODE_Insert)) insertChars(1); | ||
665 | |||
666 | int i = loc(cuX,cuY); | ||
667 | |||
668 | checkSelection(i, i); // check if selection is still valid. | ||
669 | |||
670 | image[i].c = c; | ||
671 | image[i].f = ef_fg; | ||
672 | image[i].b = ef_bg; | ||
673 | image[i].r = ef_re; | ||
674 | |||
675 | cuX += 1; | ||
676 | } | ||
677 | |||
678 | // Region commands ------------------------------------------------------------- | ||
679 | |||
680 | |||
681 | /*! scroll up `n' lines within current region. | ||
682 | The `n' new lines are cleared. | ||
683 | \sa setRegion \sa scrollDown | ||
684 | */ | ||
685 | |||
686 | void TEScreen::scrollUp(int from, int n) | ||
687 | { | ||
688 | if (n <= 0 || from + n > bmargin) return; | ||
689 | //FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds. | ||
690 | moveImage(loc(0,from),loc(0,from+n),loc(columns-1,bmargin)); | ||
691 | clearImage(loc(0,bmargin-n+1),loc(columns-1,bmargin),' '); | ||
692 | } | ||
693 | |||
694 | /*! scroll down `n' lines within current region. | ||
695 | The `n' new lines are cleared. | ||
696 | \sa setRegion \sa scrollUp | ||
697 | */ | ||
698 | |||
699 | void TEScreen::scrollDown(int from, int n) | ||
700 | { | ||
701 | //FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds. | ||
702 | if (n <= 0) return; | ||
703 | if (from > bmargin) return; | ||
704 | if (from + n > bmargin) n = bmargin - from; | ||
705 | moveImage(loc(0,from+n),loc(0,from),loc(columns-1,bmargin-n)); | ||
706 | clearImage(loc(0,from),loc(columns-1,from+n-1),' '); | ||
707 | } | ||
708 | |||
709 | /*! position the cursor to a specific line and column. */ | ||
710 | void TEScreen::setCursorYX(int y, int x) | ||
711 | { | ||
712 | setCursorY(y); setCursorX(x); | ||
713 | } | ||
714 | |||
715 | /*! Set the cursor to x-th line. */ | ||
716 | |||
717 | void TEScreen::setCursorX(int x) | ||
718 | { | ||
719 | if (x == 0) x = 1; // Default | ||
720 | x -= 1; // Adjust | ||
721 | cuX = QMAX(0,QMIN(columns-1, x)); | ||
722 | } | ||
723 | |||
724 | /*! Set the cursor to y-th line. */ | ||
725 | |||
726 | void TEScreen::setCursorY(int y) | ||
727 | { | ||
728 | if (y == 0) y = 1; // Default | ||
729 | y -= 1; // Adjust | ||
730 | cuY = QMAX(0,QMIN(lines -1, y + (getMode(MODE_Origin) ? tmargin : 0) )); | ||
731 | } | ||
732 | |||
733 | /*! set cursor to the `left upper' corner of the screen (1,1). | ||
734 | */ | ||
735 | |||
736 | void TEScreen::home() | ||
737 | { | ||
738 | cuX = 0; | ||
739 | cuY = 0; | ||
740 | } | ||
741 | |||
742 | /*! set cursor to the begin of the current line. | ||
743 | */ | ||
744 | |||
745 | void TEScreen::Return() | ||
746 | { | ||
747 | cuX = 0; | ||
748 | } | ||
749 | |||
750 | /*! returns the current cursor columns. | ||
751 | */ | ||
752 | |||
753 | int TEScreen::getCursorX() | ||
754 | { | ||
755 | return cuX; | ||
756 | } | ||
757 | |||
758 | /*! returns the current cursor line. | ||
759 | */ | ||
760 | |||
761 | int TEScreen::getCursorY() | ||
762 | { | ||
763 | return cuY; | ||
764 | } | ||
765 | |||
766 | // Erasing --------------------------------------------------------------------- | ||
767 | |||
768 | /*! \section Erasing | ||
769 | |||
770 | This group of operations erase parts of the screen contents by filling | ||
771 | it with spaces colored due to the current rendition settings. | ||
772 | |||
773 | Althought the cursor position is involved in most of these operations, | ||
774 | it is never modified by them. | ||
775 | */ | ||
776 | |||
777 | /*! fill screen between (including) `loca' and `loce' with spaces. | ||
778 | |||
779 | This is an internal helper functions. The parameter types are internal | ||
780 | addresses of within the screen image and make use of the way how the | ||
781 | screen matrix is mapped to the image vector. | ||
782 | */ | ||
783 | |||
784 | void TEScreen::clearImage(int loca, int loce, char c) | ||
785 | { int i; | ||
786 | int scr_TL=loc(0,hist.getLines()); | ||
787 | //FIXME: check positions | ||
788 | |||
789 | //Clear entire selection if it overlaps region to be moved... | ||
790 | if ( (sel_BR > (loca+scr_TL) )&&(sel_TL < (loce+scr_TL)) ) | ||
791 | { | ||
792 | clearSelection(); | ||
793 | } | ||
794 | for (i = loca; i <= loce; i++) | ||
795 | { | ||
796 | image[i].c = c; | ||
797 | image[i].f = ef_fg; //DEFAULT_FORE_COLOR; //FIXME: xterm and linux/ansi | ||
798 | image[i].b = ef_bg; //DEFAULT_BACK_COLOR; // many have different | ||
799 | image[i].r = ef_re; //DEFAULT_RENDITION; // ideas here. | ||
800 | } | ||
801 | } | ||
802 | |||
803 | /*! move image between (including) `loca' and `loce' to 'dst'. | ||
804 | |||
805 | This is an internal helper functions. The parameter types are internal | ||
806 | addresses of within the screen image and make use of the way how the | ||
807 | screen matrix is mapped to the image vector. | ||
808 | */ | ||
809 | |||
810 | void TEScreen::moveImage(int dst, int loca, int loce) | ||
811 | { | ||
812 | //FIXME: check positions | ||
813 | if (loce < loca) { | ||
814 | // kdDebug() << "WARNING!!! call to TEScreen:moveImage with loce < loca!" << endl; | ||
815 | return; | ||
816 | } | ||
817 | memmove(&image[dst],&image[loca],(loce-loca+1)*sizeof(ca)); | ||
818 | } | ||
819 | |||
820 | /*! clear from (including) current cursor position to end of screen. | ||
821 | */ | ||
822 | |||
823 | void TEScreen::clearToEndOfScreen() | ||
824 | { | ||
825 | clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' '); | ||
826 | } | ||
827 | |||
828 | /*! clear from begin of screen to (including) current cursor position. | ||
829 | */ | ||
830 | |||
831 | void TEScreen::clearToBeginOfScreen() | ||
832 | { | ||
833 | clearImage(loc(0,0),loc(cuX,cuY),' '); | ||
834 | } | ||
835 | |||
836 | /*! clear the entire screen. | ||
837 | */ | ||
838 | |||
839 | void TEScreen::clearEntireScreen() | ||
840 | { | ||
841 | clearImage(loc(0,0),loc(columns-1,lines-1),' '); | ||
842 | } | ||
843 | |||
844 | /*! fill screen with 'E' | ||
845 | This is to aid screen alignment | ||
846 | */ | ||
847 | |||
848 | void TEScreen::helpAlign() | ||
849 | { | ||
850 | clearImage(loc(0,0),loc(columns-1,lines-1),'E'); | ||
851 | } | ||
852 | |||
853 | /*! clear from (including) current cursor position to end of current cursor line. | ||
854 | */ | ||
855 | |||
856 | void TEScreen::clearToEndOfLine() | ||
857 | { | ||
858 | clearImage(loc(cuX,cuY),loc(columns-1,cuY),' '); | ||
859 | } | ||
860 | |||
861 | /*! clear from begin of current cursor line to (including) current cursor position. | ||
862 | */ | ||
863 | |||
864 | void TEScreen::clearToBeginOfLine() | ||
865 | { | ||
866 | clearImage(loc(0,cuY),loc(cuX,cuY),' '); | ||
867 | } | ||
868 | |||
869 | /*! clears entire current cursor line | ||
870 | */ | ||
871 | |||
872 | void TEScreen::clearEntireLine() | ||
873 | { | ||
874 | clearImage(loc(0,cuY),loc(columns-1,cuY),' '); | ||
875 | } | ||
876 | |||
877 | // Rendition ------------------------------------------------------------------ | ||
878 | |||
879 | /*! | ||
880 | set rendition mode | ||
881 | */ | ||
882 | |||
883 | void TEScreen::setRendition(int re) | ||
884 | { | ||
885 | cu_re |= re; | ||
886 | effectiveRendition(); | ||
887 | } | ||
888 | |||
889 | /*! | ||
890 | reset rendition mode | ||
891 | */ | ||
892 | |||
893 | void TEScreen::resetRendition(int re) | ||
894 | { | ||
895 | cu_re &= ~re; | ||
896 | effectiveRendition(); | ||
897 | } | ||
898 | |||
899 | /*! | ||
900 | */ | ||
901 | |||
902 | void TEScreen::setDefaultRendition() | ||
903 | { | ||
904 | setForeColorToDefault(); | ||
905 | setBackColorToDefault(); | ||
906 | cu_re = DEFAULT_RENDITION; | ||
907 | effectiveRendition(); | ||
908 | } | ||
909 | |||
910 | /*! | ||
911 | */ | ||
912 | |||
913 | void TEScreen::setForeColor(int fgcolor) | ||
914 | { | ||
915 | cu_fg = (fgcolor&7)+((fgcolor&8) ? 4+8 : 2); | ||
916 | effectiveRendition(); | ||
917 | } | ||
918 | |||
919 | /*! | ||
920 | */ | ||
921 | |||
922 | void TEScreen::setBackColor(int bgcolor) | ||
923 | { | ||
924 | cu_bg = (bgcolor&7)+((bgcolor&8) ? 4+8 : 2); | ||
925 | effectiveRendition(); | ||
926 | } | ||
927 | |||
928 | /*! | ||
929 | */ | ||
930 | |||
931 | void TEScreen::setBackColorToDefault() | ||
932 | { | ||
933 | cu_bg = DEFAULT_BACK_COLOR; | ||
934 | effectiveRendition(); | ||
935 | } | ||
936 | |||
937 | /*! | ||
938 | */ | ||
939 | |||
940 | void TEScreen::setForeColorToDefault() | ||
941 | { | ||
942 | cu_fg = DEFAULT_FORE_COLOR; | ||
943 | effectiveRendition(); | ||
944 | } | ||
945 | |||
946 | /* ------------------------------------------------------------------------- */ | ||
947 | /* */ | ||
948 | /* Marking & Selection */ | ||
949 | /* */ | ||
950 | /* ------------------------------------------------------------------------- */ | ||
951 | |||
952 | void TEScreen::clearSelection() | ||
953 | { | ||
954 | sel_BR = -1; | ||
955 | sel_TL = -1; | ||
956 | sel_begin = -1; | ||
957 | } | ||
958 | |||
959 | void TEScreen::setSelBeginXY(const int x, const int y) | ||
960 | { | ||
961 | sel_begin = loc(x,y+histCursor) ; | ||
962 | sel_BR = sel_begin; | ||
963 | sel_TL = sel_begin; | ||
964 | } | ||
965 | |||
966 | void TEScreen::setSelExtentXY(const int x, const int y) | ||
967 | { | ||
968 | if (sel_begin == -1) return; | ||
969 | int l = loc(x,y + histCursor); | ||
970 | |||
971 | if (l < sel_begin) | ||
972 | { | ||
973 | sel_TL = l; | ||
974 | sel_BR = sel_begin; | ||
975 | } | ||
976 | else | ||
977 | { | ||
978 | /* FIXME, HACK to correct for x too far to the right... */ | ||
979 | if (( x == columns )|| (x == 0)) l--; | ||
980 | |||
981 | sel_TL = sel_begin; | ||
982 | sel_BR = l; | ||
983 | } | ||
984 | } | ||
985 | |||
986 | QString TEScreen::getSelText(const BOOL preserve_line_breaks) | ||
987 | { | ||
988 | if (sel_begin == -1) | ||
989 | return QString::null; // Selection got clear while selecting. | ||
990 | |||
991 | int *m; // buffer to fill. | ||
992 | int s, d; // source index, dest. index. | ||
993 | int hist_BR = loc(0, hist.getLines()); | ||
994 | int hY = sel_TL / columns; | ||
995 | int hX = sel_TL % columns; | ||
996 | int eol; // end of line | ||
997 | |||
998 | s = sel_TL; // tracks copy in source. | ||
999 | |||
1000 | // allocate buffer for maximum | ||
1001 | // possible size... | ||
1002 | d = (sel_BR - sel_TL) / columns + 1; | ||
1003 | m = new int[d * (columns + 1) + 2]; | ||
1004 | d = 0; | ||
1005 | |||
1006 | while (s <= sel_BR) | ||
1007 | { | ||
1008 | if (s < hist_BR) | ||
1009 | { // get lines from hist.history | ||
1010 | // buffer. | ||
1011 | eol = hist.getLineLen(hY); | ||
1012 | |||
1013 | if ((hY == (sel_BR / columns)) && | ||
1014 | (eol >= (sel_BR % columns))) | ||
1015 | { | ||
1016 | eol = sel_BR % columns + 1; | ||
1017 | } | ||
1018 | |||
1019 | while (hX < eol) | ||
1020 | { | ||
1021 | m[d++] = hist.getCell(hY, hX++).c; | ||
1022 | s++; | ||
1023 | } | ||
1024 | |||
1025 | if (s <= sel_BR) | ||
1026 | { | ||
1027 | // The line break handling | ||
1028 | // It's different from the screen | ||
1029 | // image case! | ||
1030 | if (eol % columns == 0) | ||
1031 | { | ||
1032 | // That's either a completely filled | ||
1033 | // line or an empty line | ||
1034 | if (eol == 0) | ||
1035 | { | ||
1036 | m[d++] = '\n'; | ||
1037 | } | ||
1038 | else | ||
1039 | { | ||
1040 | // We have a full line. | ||
1041 | // FIXME: How can we handle newlines | ||
1042 | // at this position?! | ||
1043 | } | ||
1044 | } | ||
1045 | else if ((eol + 1) % columns == 0) | ||
1046 | { | ||
1047 | // FIXME: We don't know if this was a | ||
1048 | // space at the last position or a | ||
1049 | // short line!! | ||
1050 | m[d++] = ' '; | ||
1051 | } | ||
1052 | else | ||
1053 | { | ||
1054 | // We have a short line here. Put a | ||
1055 | // newline or a space into the | ||
1056 | // buffer. | ||
1057 | m[d++] = preserve_line_breaks ? '\n' : ' '; | ||
1058 | } | ||
1059 | } | ||
1060 | |||
1061 | hY++; | ||
1062 | hX = 0; | ||
1063 | s = hY * columns; | ||
1064 | } | ||
1065 | else | ||
1066 | { // or from screen image. | ||
1067 | eol = (s / columns + 1) * columns - 1; | ||
1068 | |||
1069 | if (eol < sel_BR) | ||
1070 | { | ||
1071 | while ((eol > s) && | ||
1072 | isspace(image[eol - hist_BR].c)) | ||
1073 | { | ||
1074 | eol--; | ||
1075 | } | ||
1076 | } | ||
1077 | else | ||
1078 | { | ||
1079 | eol = sel_BR; | ||
1080 | } | ||
1081 | |||
1082 | while (s <= eol) | ||
1083 | { | ||
1084 | m[d++] = image[s++ - hist_BR].c; | ||
1085 | } | ||
1086 | |||
1087 | if (eol < sel_BR) | ||
1088 | { | ||
1089 | // eol processing see below ... | ||
1090 | if ((eol + 1) % columns == 0) | ||
1091 | { | ||
1092 | if (image[eol - hist_BR].c == ' ') | ||
1093 | { | ||
1094 | m[d++] = ' '; | ||
1095 | } | ||
1096 | } | ||
1097 | else | ||
1098 | { | ||
1099 | m[d++] = ((preserve_line_breaks || | ||
1100 | ((eol % columns) == 0)) ? | ||
1101 | '\n' : ' '); | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | s = (eol / columns + 1) * columns; | ||
1106 | } | ||
1107 | } | ||
1108 | |||
1109 | QChar* qc = new QChar[d]; | ||
1110 | |||
1111 | for (int i = 0; i < d; i++) | ||
1112 | { | ||
1113 | qc[i] = m[i]; | ||
1114 | } | ||
1115 | |||
1116 | QString res(qc, d); | ||
1117 | |||
1118 | delete m; | ||
1119 | delete qc; | ||
1120 | |||
1121 | return res; | ||
1122 | } | ||
1123 | /* above ... end of line processing for selection -- psilva | ||
1124 | cases: | ||
1125 | |||
1126 | 1) (eol+1)%columns == 0 --> the whole line is filled. | ||
1127 | If the last char is a space, insert (preserve) space. otherwise | ||
1128 | leave the text alone, so that words that are broken by linewrap | ||
1129 | are preserved. | ||
1130 | |||
1131 | FIXME: | ||
1132 | * this suppresses \n for command output that is | ||
1133 | sized to the exact column width of the screen. | ||
1134 | |||
1135 | 2) eol%columns == 0 --> blank line. | ||
1136 | insert a \n unconditionally. | ||
1137 | Do it either you would because you are in preserve_line_break mode, | ||
1138 | or because it's an ASCII paragraph delimiter, so even when | ||
1139 | not preserving line_breaks, you want to preserve paragraph breaks. | ||
1140 | |||
1141 | 3) else --> partially filled line | ||
1142 | insert a \n in preserve line break mode, else a space | ||
1143 | The space prevents concatenation of the last word of one | ||
1144 | line with the first of the next. | ||
1145 | |||
1146 | */ | ||
1147 | |||
1148 | void TEScreen::addHistLine() | ||
1149 | { | ||
1150 | assert(hasScroll() || histCursor == 0); | ||
1151 | |||
1152 | // add to hist buffer | ||
1153 | // we have to take care about scrolling, too... | ||
1154 | |||
1155 | if (hasScroll()) | ||
1156 | { ca dft; | ||
1157 | |||
1158 | int end = columns-1; | ||
1159 | while (end >= 0 && image[end] == dft) | ||
1160 | end -= 1; | ||
1161 | |||
1162 | hist.addCells(image,end+1); | ||
1163 | hist.addLine(); | ||
1164 | |||
1165 | // adjust history cursor | ||
1166 | histCursor += (hist.getLines()-1 == histCursor); | ||
1167 | } | ||
1168 | |||
1169 | if (!hasScroll()) histCursor = 0; //FIXME: a poor workaround | ||
1170 | } | ||
1171 | |||
1172 | void TEScreen::setHistCursor(int cursor) | ||
1173 | { | ||
1174 | histCursor = cursor; //FIXME:rangecheck | ||
1175 | } | ||
1176 | |||
1177 | int TEScreen::getHistCursor() | ||
1178 | { | ||
1179 | return histCursor; | ||
1180 | } | ||
1181 | |||
1182 | int TEScreen::getHistLines() | ||
1183 | { | ||
1184 | return hist.getLines(); | ||
1185 | } | ||
1186 | |||
1187 | void TEScreen::setScroll(bool on) | ||
1188 | { | ||
1189 | histCursor = 0; | ||
1190 | clearSelection(); | ||
1191 | hist.setScroll(on); | ||
1192 | } | ||
1193 | |||
1194 | bool TEScreen::hasScroll() | ||
1195 | { | ||
1196 | return hist.hasScroll(); | ||
1197 | } | ||
diff --git a/noncore/apps/opie-console/TEScreen.h b/noncore/apps/opie-console/TEScreen.h new file mode 100644 index 0000000..ba47ee5 --- a/dev/null +++ b/noncore/apps/opie-console/TEScreen.h | |||
@@ -0,0 +1,259 @@ | |||
1 | /* -------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [te_screen.h] Screen Data Type */ | ||
4 | /* */ | ||
5 | /* -------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* -------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | #ifndef TESCREEN_H | ||
20 | #define TESCREEN_H | ||
21 | |||
22 | /*! \file | ||
23 | */ | ||
24 | |||
25 | #include "TECommon.h" | ||
26 | #include "TEHistory.h" | ||
27 | |||
28 | #define MODE_Origin 0 | ||
29 | #define MODE_Wrap 1 | ||
30 | #define MODE_Insert 2 | ||
31 | #define MODE_Screen 3 | ||
32 | #define MODE_Cursor 4 | ||
33 | #define MODE_NewLine 5 | ||
34 | #define MODES_SCREEN 6 | ||
35 | |||
36 | /*! | ||
37 | */ | ||
38 | struct ScreenParm | ||
39 | { | ||
40 | int mode[MODES_SCREEN]; | ||
41 | }; | ||
42 | |||
43 | |||
44 | class TEScreen | ||
45 | { | ||
46 | public: | ||
47 | TEScreen(int lines, int columns); | ||
48 | ~TEScreen(); | ||
49 | |||
50 | public: // these are all `Screen' operations | ||
51 | // | ||
52 | // VT100/2 Operations ------------------ | ||
53 | // | ||
54 | // Cursor Movement | ||
55 | // | ||
56 | void cursorUp (int n); | ||
57 | void cursorDown (int n); | ||
58 | void cursorLeft (int n); | ||
59 | void cursorRight (int n); | ||
60 | void setCursorY (int y); | ||
61 | void setCursorX (int x); | ||
62 | void setCursorYX (int y, int x); | ||
63 | void setMargins (int t, int b); | ||
64 | // | ||
65 | // Cursor Movement with Scrolling | ||
66 | // | ||
67 | void NewLine (); | ||
68 | void NextLine (); | ||
69 | void index (); | ||
70 | void reverseIndex(); | ||
71 | // | ||
72 | void Return (); | ||
73 | void BackSpace (); | ||
74 | void Tabulate (); | ||
75 | // | ||
76 | // Editing | ||
77 | // | ||
78 | void eraseChars (int n); | ||
79 | void deleteChars (int n); | ||
80 | void insertChars (int n); | ||
81 | void deleteLines (int n); | ||
82 | void insertLines (int n); | ||
83 | // | ||
84 | // ------------------------------------- | ||
85 | // | ||
86 | void clearTabStops(); | ||
87 | void changeTabStop(bool set); | ||
88 | // | ||
89 | void resetMode (int n); | ||
90 | void setMode (int n); | ||
91 | void saveMode (int n); | ||
92 | void restoreMode (int n); | ||
93 | // | ||
94 | void saveCursor (); | ||
95 | void restoreCursor(); | ||
96 | // | ||
97 | // ------------------------------------- | ||
98 | // | ||
99 | void clearEntireScreen(); | ||
100 | void clearToEndOfScreen(); | ||
101 | void clearToBeginOfScreen(); | ||
102 | // | ||
103 | void clearEntireLine(); | ||
104 | void clearToEndOfLine(); | ||
105 | void clearToBeginOfLine(); | ||
106 | // | ||
107 | void helpAlign (); | ||
108 | // | ||
109 | // ------------------------------------- | ||
110 | // | ||
111 | void setRendition (int rendition); | ||
112 | void resetRendition(int rendition); | ||
113 | void setForeColor (int fgcolor); | ||
114 | void setBackColor (int bgcolor); | ||
115 | // | ||
116 | void setDefaultRendition(); | ||
117 | void setForeColorToDefault(); | ||
118 | void setBackColorToDefault(); | ||
119 | // | ||
120 | // ------------------------------------- | ||
121 | // | ||
122 | BOOL getMode (int n); | ||
123 | // | ||
124 | // only for report cursor position | ||
125 | // | ||
126 | int getCursorX(); | ||
127 | int getCursorY(); | ||
128 | // | ||
129 | // ------------------------------------- | ||
130 | // | ||
131 | void clear(); | ||
132 | void home(); | ||
133 | void reset(); | ||
134 | // | ||
135 | void ShowCharacter(unsigned short c); | ||
136 | // | ||
137 | void resizeImage(int new_lines, int new_columns); | ||
138 | // | ||
139 | ca* getCookedImage(); | ||
140 | |||
141 | /*! return the number of lines. */ | ||
142 | int getLines() { return lines; } | ||
143 | /*! return the number of columns. */ | ||
144 | int getColumns() { return columns; } | ||
145 | |||
146 | /*! set the position of the history cursor. */ | ||
147 | void setHistCursor(int cursor); | ||
148 | /*! return the position of the history cursor. */ | ||
149 | int getHistCursor(); | ||
150 | |||
151 | int getHistLines (); | ||
152 | void setScroll(bool on); | ||
153 | bool hasScroll(); | ||
154 | |||
155 | // | ||
156 | // Selection | ||
157 | // | ||
158 | void setSelBeginXY(const int x, const int y); | ||
159 | void setSelExtentXY(const int x, const int y); | ||
160 | void clearSelection(); | ||
161 | QString getSelText(const BOOL preserve_line_breaks); | ||
162 | |||
163 | void checkSelection(int from, int to); | ||
164 | |||
165 | private: // helper | ||
166 | |||
167 | void clearImage(int loca, int loce, char c); | ||
168 | void moveImage(int dst, int loca, int loce); | ||
169 | |||
170 | void scrollUp(int from, int i); | ||
171 | void scrollDown(int from, int i); | ||
172 | |||
173 | void addHistLine(); | ||
174 | |||
175 | void initTabStops(); | ||
176 | |||
177 | void effectiveRendition(); | ||
178 | void reverseRendition(ca* p); | ||
179 | |||
180 | private: | ||
181 | |||
182 | /* | ||
183 | The state of the screen is more complex as one would | ||
184 | expect first. The screem does really do part of the | ||
185 | emulation providing state informations in form of modes, | ||
186 | margins, tabulators, cursor etc. | ||
187 | |||
188 | Even more unexpected are variables to save and restore | ||
189 | parts of the state. | ||
190 | */ | ||
191 | |||
192 | // screen image ---------------- | ||
193 | |||
194 | int lines; | ||
195 | int columns; | ||
196 | ca *image; // [lines][columns] | ||
197 | |||
198 | // history buffer --------------- | ||
199 | |||
200 | int histCursor; // display position relative to start of the history buffer | ||
201 | HistoryScroll hist; | ||
202 | |||
203 | // cursor location | ||
204 | |||
205 | int cuX; | ||
206 | int cuY; | ||
207 | |||
208 | // cursor color and rendition info | ||
209 | |||
210 | UINT8 cu_fg; // foreground | ||
211 | UINT8 cu_bg; // background | ||
212 | UINT8 cu_re; // rendition | ||
213 | |||
214 | // margins ---------------- | ||
215 | |||
216 | int tmargin; // top margin | ||
217 | int bmargin; // bottom margin | ||
218 | |||
219 | // states ---------------- | ||
220 | |||
221 | ScreenParm currParm; | ||
222 | |||
223 | // ---------------------------- | ||
224 | |||
225 | bool* tabstops; | ||
226 | |||
227 | // selection ------------------- | ||
228 | |||
229 | int sel_begin; // The first location selected. | ||
230 | int sel_TL; // TopLeft Location. | ||
231 | int sel_BR; // Bottom Right Location. | ||
232 | |||
233 | // effective colors and rendition ------------ | ||
234 | |||
235 | UINT8 ef_fg; // These are derived from | ||
236 | UINT8 ef_bg; // the cu_* variables above | ||
237 | UINT8 ef_re; // to speed up operation | ||
238 | |||
239 | // | ||
240 | // save cursor, rendition & states ------------ | ||
241 | // | ||
242 | |||
243 | // cursor location | ||
244 | |||
245 | int sa_cuX; | ||
246 | int sa_cuY; | ||
247 | |||
248 | // rendition info | ||
249 | |||
250 | UINT8 sa_cu_re; | ||
251 | UINT8 sa_cu_fg; | ||
252 | UINT8 sa_cu_bg; | ||
253 | |||
254 | // modes | ||
255 | |||
256 | ScreenParm saveParm; | ||
257 | }; | ||
258 | |||
259 | #endif // TESCREEN_H | ||
diff --git a/noncore/apps/opie-console/TEWidget.cpp b/noncore/apps/opie-console/TEWidget.cpp new file mode 100644 index 0000000..75c438c --- a/dev/null +++ b/noncore/apps/opie-console/TEWidget.cpp | |||
@@ -0,0 +1,1264 @@ | |||
1 | /* ------------------------------------------------------------------------ */ | ||
2 | /* */ | ||
3 | /* [TEWidget.C] Terminal Emulation Widget */ | ||
4 | /* */ | ||
5 | /* ------------------------------------------------------------------------ */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* ------------------------------------------------------------------------ */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | /*! \class TEWidget | ||
19 | |||
20 | \brief Visible screen contents | ||
21 | |||
22 | This class is responsible to map the `image' of a terminal emulation to the | ||
23 | display. All the dependency of the emulation to a specific GUI or toolkit is | ||
24 | localized here. Further, this widget has no knowledge about being part of an | ||
25 | emulation, it simply work within the terminal emulation framework by exposing | ||
26 | size and key events and by being ordered to show a new image. | ||
27 | |||
28 | <ul> | ||
29 | <li> The internal image has the size of the widget (evtl. rounded up) | ||
30 | <li> The external image used in setImage can have any size. | ||
31 | <li> (internally) the external image is simply copied to the internal | ||
32 | when a setImage happens. During a resizeEvent no painting is done | ||
33 | a paintEvent is expected to follow anyway. | ||
34 | </ul> | ||
35 | |||
36 | \sa TEScreen \sa Emulation | ||
37 | */ | ||
38 | |||
39 | /* FIXME: | ||
40 | - 'image' may also be used uninitialized (it isn't in fact) in resizeEvent | ||
41 | - 'font_a' not used in mouse events | ||
42 | - add destructor | ||
43 | */ | ||
44 | |||
45 | /* TODO | ||
46 | - evtl. be sensitive to `paletteChange' while using default colors. | ||
47 | - set different 'rounding' styles? I.e. have a mode to show clipped chars? | ||
48 | */ | ||
49 | |||
50 | // #include "config.h" | ||
51 | #include "TEWidget.h" | ||
52 | #include "session.h" | ||
53 | #include <qpe/config.h> | ||
54 | |||
55 | #include <qapplication.h> | ||
56 | #include <qcursor.h> | ||
57 | #include <qregexp.h> | ||
58 | #include <qpainter.h> | ||
59 | #include <qclipboard.h> | ||
60 | #include <qstyle.h> | ||
61 | #include <qfile.h> | ||
62 | #include <qdragobject.h> | ||
63 | |||
64 | #include <stdio.h> | ||
65 | #include <stdlib.h> | ||
66 | #include <unistd.h> | ||
67 | #include <ctype.h> | ||
68 | #include <sys/stat.h> | ||
69 | #include <sys/types.h> | ||
70 | #include <signal.h> | ||
71 | |||
72 | #include <assert.h> | ||
73 | |||
74 | // #include "TEWidget.moc" | ||
75 | //#include <kapp.h> | ||
76 | //#include <kcursor.h> | ||
77 | //#include <kurl.h> | ||
78 | //#include <kdebug.h> | ||
79 | //#include <klocale.h> | ||
80 | |||
81 | #define HERE printf("%s(%d): %s\n",__FILE__,__LINE__,__FUNCTION__) | ||
82 | #define HCNT(Name) // { static int cnt = 1; printf("%s(%d): %s %d\n",__FILE__,__LINE__,Name,cnt++); } | ||
83 | |||
84 | #define loc(X,Y) ((Y)*columns+(X)) | ||
85 | |||
86 | //FIXME: the rim should normally be 1, 0 only when running in full screen mode. | ||
87 | #define rimX 0 // left/right rim width | ||
88 | #define rimY 0 // top/bottom rim high | ||
89 | |||
90 | #define SCRWIDTH 16 // width of the scrollbar | ||
91 | |||
92 | #define yMouseScroll 1 | ||
93 | // scroll increment used when dragging selection at top/bottom of window. | ||
94 | |||
95 | /* ------------------------------------------------------------------------- */ | ||
96 | /* */ | ||
97 | /* Colors */ | ||
98 | /* */ | ||
99 | /* ------------------------------------------------------------------------- */ | ||
100 | |||
101 | //FIXME: the default color table is in session.C now. | ||
102 | // We need a way to get rid of this one, here. | ||
103 | static const ColorEntry base_color_table[TABLE_COLORS] = | ||
104 | // The following are almost IBM standard color codes, with some slight | ||
105 | // gamma correction for the dim colors to compensate for bright X screens. | ||
106 | // It contains the 8 ansiterm/xterm colors in 2 intensities. | ||
107 | { | ||
108 | // Fixme: could add faint colors here, also. | ||
109 | // normal | ||
110 | ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback | ||
111 | ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red | ||
112 | ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow | ||
113 | ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta | ||
114 | ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White | ||
115 | // intensiv | ||
116 | ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), | ||
117 | ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), | ||
118 | ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), | ||
119 | ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), | ||
120 | ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) | ||
121 | }; | ||
122 | |||
123 | /* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) | ||
124 | |||
125 | Code 0 1 2 3 4 5 6 7 | ||
126 | ----------- ------- ------- ------- ------- ------- ------- ------- ------- | ||
127 | ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White | ||
128 | IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White | ||
129 | */ | ||
130 | |||
131 | QColor TEWidget::getDefaultBackColor() | ||
132 | { | ||
133 | return color_table[DEFAULT_BACK_COLOR].color; | ||
134 | } | ||
135 | |||
136 | const ColorEntry* TEWidget::getColorTable() const | ||
137 | { | ||
138 | return color_table; | ||
139 | } | ||
140 | |||
141 | const ColorEntry* TEWidget::getdefaultColorTable() const | ||
142 | { | ||
143 | return base_color_table; | ||
144 | } | ||
145 | |||
146 | |||
147 | const QPixmap *TEWidget::backgroundPixmap() | ||
148 | { | ||
149 | static QPixmap *bg = new QPixmap("~/qpim/main/pics/faded_bg.xpm"); | ||
150 | const QPixmap *pm = bg; | ||
151 | return pm; | ||
152 | } | ||
153 | |||
154 | void TEWidget::setColorTable(const ColorEntry table[]) | ||
155 | { | ||
156 | for (int i = 0; i < TABLE_COLORS; i++) color_table[i] = table[i]; | ||
157 | |||
158 | const QPixmap* pm = backgroundPixmap(); | ||
159 | if (!pm) setBackgroundColor(color_table[DEFAULT_BACK_COLOR].color); | ||
160 | update(); | ||
161 | } | ||
162 | |||
163 | //FIXME: add backgroundPixmapChanged. | ||
164 | |||
165 | /* ------------------------------------------------------------------------- */ | ||
166 | /* */ | ||
167 | /* Font */ | ||
168 | /* */ | ||
169 | /* ------------------------------------------------------------------------- */ | ||
170 | |||
171 | /* | ||
172 | The VT100 has 32 special graphical characters. The usual vt100 extended | ||
173 | xterm fonts have these at 0x00..0x1f. | ||
174 | |||
175 | QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals | ||
176 | come in here as proper unicode characters. | ||
177 | |||
178 | We treat non-iso10646 fonts as VT100 extended and do the requiered mapping | ||
179 | from unicode to 0x00..0x1f. The remaining translation is then left to the | ||
180 | QCodec. | ||
181 | */ | ||
182 | |||
183 | // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. | ||
184 | |||
185 | unsigned short vt100_graphics[32] = | ||
186 | { // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 | ||
187 | 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, | ||
188 | 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, | ||
189 | 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, | ||
190 | 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 | ||
191 | }; | ||
192 | |||
193 | static QChar vt100extended(QChar c) | ||
194 | { | ||
195 | switch (c.unicode()) | ||
196 | { | ||
197 | case 0x25c6 : return 1; | ||
198 | case 0x2592 : return 2; | ||
199 | case 0x2409 : return 3; | ||
200 | case 0x240c : return 4; | ||
201 | case 0x240d : return 5; | ||
202 | case 0x240a : return 6; | ||
203 | case 0x00b0 : return 7; | ||
204 | case 0x00b1 : return 8; | ||
205 | case 0x2424 : return 9; | ||
206 | case 0x240b : return 10; | ||
207 | case 0x2518 : return 11; | ||
208 | case 0x2510 : return 12; | ||
209 | case 0x250c : return 13; | ||
210 | case 0x2514 : return 14; | ||
211 | case 0x253c : return 15; | ||
212 | case 0xf800 : return 16; | ||
213 | case 0xf801 : return 17; | ||
214 | case 0x2500 : return 18; | ||
215 | case 0xf803 : return 19; | ||
216 | case 0xf804 : return 20; | ||
217 | case 0x251c : return 21; | ||
218 | case 0x2524 : return 22; | ||
219 | case 0x2534 : return 23; | ||
220 | case 0x252c : return 24; | ||
221 | case 0x2502 : return 25; | ||
222 | case 0x2264 : return 26; | ||
223 | case 0x2265 : return 27; | ||
224 | case 0x03c0 : return 28; | ||
225 | case 0x2260 : return 29; | ||
226 | case 0x00a3 : return 30; | ||
227 | case 0x00b7 : return 31; | ||
228 | } | ||
229 | return c; | ||
230 | } | ||
231 | |||
232 | static QChar identicalMap(QChar c) | ||
233 | { | ||
234 | return c; | ||
235 | } | ||
236 | |||
237 | void TEWidget::fontChange(const QFont &) | ||
238 | { | ||
239 | QFontMetrics fm(font()); | ||
240 | font_h = fm.height(); | ||
241 | font_w = fm.maxWidth(); | ||
242 | font_a = fm.ascent(); | ||
243 | //printf("font_h: %d\n",font_h); | ||
244 | //printf("font_w: %d\n",font_w); | ||
245 | //printf("font_a: %d\n",font_a); | ||
246 | //printf("charset: %s\n",QFont::encodingName(font().charSet()).ascii()); | ||
247 | //printf("rawname: %s\n",font().rawName().ascii()); | ||
248 | fontMap = | ||
249 | #if QT_VERSION < 300 | ||
250 | strcmp(QFont::encodingName(font().charSet()).ascii(),"iso10646") | ||
251 | ? vt100extended | ||
252 | : | ||
253 | #endif | ||
254 | identicalMap; | ||
255 | propagateSize(); | ||
256 | update(); | ||
257 | } | ||
258 | |||
259 | void TEWidget::setVTFont(const QFont& f) | ||
260 | { | ||
261 | QFrame::setFont(f); | ||
262 | } | ||
263 | |||
264 | QFont TEWidget::getVTFont() { | ||
265 | return font(); | ||
266 | } | ||
267 | |||
268 | void TEWidget::setFont(const QFont &) | ||
269 | { | ||
270 | // ignore font change request if not coming from konsole itself | ||
271 | } | ||
272 | |||
273 | /* ------------------------------------------------------------------------- */ | ||
274 | /* */ | ||
275 | /* Constructor / Destructor */ | ||
276 | /* */ | ||
277 | /* ------------------------------------------------------------------------- */ | ||
278 | |||
279 | TEWidget::TEWidget(QWidget *parent, const char *name) : QFrame(parent,name) | ||
280 | { | ||
281 | #ifndef QT_NO_CLIPBOARD | ||
282 | cb = QApplication::clipboard(); | ||
283 | QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), | ||
284 | this, SLOT(onClearSelection()) ); | ||
285 | #endif | ||
286 | |||
287 | scrollbar = new QScrollBar(this); | ||
288 | scrollbar->setCursor( arrowCursor ); | ||
289 | connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); | ||
290 | |||
291 | Config cfg("Konsole"); | ||
292 | cfg.setGroup("ScrollBar"); | ||
293 | switch( cfg.readNumEntry("Position",2)){ | ||
294 | case 0: | ||
295 | scrollLoc = SCRNONE; | ||
296 | break; | ||
297 | case 1: | ||
298 | scrollLoc = SCRLEFT; | ||
299 | break; | ||
300 | case 2: | ||
301 | scrollLoc = SCRRIGHT; | ||
302 | break; | ||
303 | }; | ||
304 | |||
305 | blinkT = new QTimer(this); | ||
306 | connect(blinkT, SIGNAL(timeout()), this, SLOT(blinkEvent())); | ||
307 | // blinking = FALSE; | ||
308 | blinking = TRUE; | ||
309 | |||
310 | resizing = FALSE; | ||
311 | actSel = 0; | ||
312 | image = 0; | ||
313 | lines = 1; | ||
314 | columns = 1; | ||
315 | font_w = 1; | ||
316 | font_h = 1; | ||
317 | font_a = 1; | ||
318 | word_selection_mode = FALSE; | ||
319 | |||
320 | setMouseMarks(TRUE); | ||
321 | setVTFont( QFont("fixed") ); | ||
322 | setColorTable(base_color_table); // init color table | ||
323 | |||
324 | qApp->installEventFilter( this ); //FIXME: see below | ||
325 | // KCursor::setAutoHideCursor( this, true ); | ||
326 | |||
327 | // Init DnD //////////////////////////////////////////////////////////////// | ||
328 | currentSession = NULL; | ||
329 | // setAcceptDrops(true); // attempt | ||
330 | // m_drop = new QPopupMenu(this); | ||
331 | // m_drop->insertItem( QString("Paste"), 0); | ||
332 | // m_drop->insertItem( QString("cd"), 1); | ||
333 | // connect(m_drop, SIGNAL(activated(int)), SLOT(drop_menu_activated(int))); | ||
334 | |||
335 | // we need focus so that the auto-hide cursor feature works | ||
336 | setFocus(); | ||
337 | setFocusPolicy( WheelFocus ); | ||
338 | } | ||
339 | |||
340 | //FIXME: make proper destructor | ||
341 | // Here's a start (David) | ||
342 | TEWidget::~TEWidget() | ||
343 | { | ||
344 | qApp->removeEventFilter( this ); | ||
345 | if (image) free(image); | ||
346 | } | ||
347 | |||
348 | /* ------------------------------------------------------------------------- */ | ||
349 | /* */ | ||
350 | /* Display Operations */ | ||
351 | /* */ | ||
352 | /* ------------------------------------------------------------------------- */ | ||
353 | |||
354 | /*! | ||
355 | attributed string draw primitive | ||
356 | */ | ||
357 | |||
358 | void TEWidget::drawAttrStr(QPainter &paint, QRect rect, | ||
359 | QString& str, ca attr, BOOL pm, BOOL clear) | ||
360 | { | ||
361 | if (pm && color_table[attr.b].transparent) | ||
362 | { | ||
363 | paint.setBackgroundMode( TransparentMode ); | ||
364 | if (clear) erase(rect); | ||
365 | } | ||
366 | else | ||
367 | { | ||
368 | if (blinking) | ||
369 | paint.fillRect(rect, color_table[attr.b].color); | ||
370 | else | ||
371 | { | ||
372 | paint.setBackgroundMode( OpaqueMode ); | ||
373 | paint.setBackgroundColor( color_table[attr.b].color ); | ||
374 | } | ||
375 | } | ||
376 | |||
377 | if (color_table[attr.f].bold) | ||
378 | paint.setPen(QColor( 0x8F, 0x00, 0x00 )); | ||
379 | else | ||
380 | paint.setPen(color_table[attr.f].color); | ||
381 | |||
382 | paint.drawText(rect.x(),rect.y()+font_a, str); | ||
383 | |||
384 | if (attr.r & RE_UNDERLINE) | ||
385 | paint.drawLine(rect.left(), rect.y()+font_a+1, rect.right(),rect.y()+font_a+1 ); | ||
386 | } | ||
387 | |||
388 | /*! | ||
389 | The image can only be set completely. | ||
390 | |||
391 | The size of the new image may or may not match the size of the widget. | ||
392 | */ | ||
393 | |||
394 | void TEWidget::setImage(const ca* const newimg, int lines, int columns) | ||
395 | { int y,x,len; | ||
396 | const QPixmap* pm = backgroundPixmap(); | ||
397 | QPainter paint; | ||
398 | setUpdatesEnabled(FALSE); | ||
399 | paint.begin( this ); | ||
400 | HCNT("setImage"); | ||
401 | |||
402 | QPoint tL = contentsRect().topLeft(); | ||
403 | int tLx = tL.x(); | ||
404 | int tLy = tL.y(); | ||
405 | hasBlinker = FALSE; | ||
406 | |||
407 | int cf = -1; // undefined | ||
408 | int cb = -1; // undefined | ||
409 | int cr = -1; // undefined | ||
410 | |||
411 | int lins = QMIN(this->lines, QMAX(0,lines )); | ||
412 | int cols = QMIN(this->columns,QMAX(0,columns)); | ||
413 | QChar *disstrU = new QChar[cols]; | ||
414 | |||
415 | //{ static int cnt = 0; printf("setImage %d\n",cnt++); } | ||
416 | for (y = 0; y < lins; y++) | ||
417 | { | ||
418 | const ca* lcl = &image[y*this->columns]; | ||
419 | const ca* const ext = &newimg[y*columns]; | ||
420 | if (!resizing) // not while resizing, we're expecting a paintEvent | ||
421 | for (x = 0; x < cols; x++) | ||
422 | { | ||
423 | hasBlinker |= (ext[x].r & RE_BLINK); | ||
424 | if (ext[x] != lcl[x]) | ||
425 | { | ||
426 | cr = ext[x].r; | ||
427 | cb = ext[x].b; | ||
428 | if (ext[x].f != cf) cf = ext[x].f; | ||
429 | int lln = cols - x; | ||
430 | disstrU[0] = fontMap(ext[x+0].c); | ||
431 | for (len = 1; len < lln; len++) | ||
432 | { | ||
433 | if (ext[x+len].f != cf || ext[x+len].b != cb || ext[x+len].r != cr || | ||
434 | ext[x+len] == lcl[x+len] ) | ||
435 | break; | ||
436 | disstrU[len] = fontMap(ext[x+len].c); | ||
437 | } | ||
438 | QString unistr(disstrU,len); | ||
439 | drawAttrStr(paint, | ||
440 | QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), | ||
441 | unistr, ext[x], pm != NULL, true); | ||
442 | x += len - 1; | ||
443 | } | ||
444 | } | ||
445 | // finally, make `image' become `newimg'. | ||
446 | memcpy((void*)lcl,(const void*)ext,cols*sizeof(ca)); | ||
447 | } | ||
448 | drawFrame( &paint ); | ||
449 | paint.end(); | ||
450 | setUpdatesEnabled(TRUE); | ||
451 | if ( hasBlinker && !blinkT->isActive()) blinkT->start(1000); // 1000 ms | ||
452 | if (!hasBlinker && blinkT->isActive()) { blinkT->stop(); blinking = FALSE; } | ||
453 | delete [] disstrU; | ||
454 | } | ||
455 | |||
456 | // paint Event //////////////////////////////////////////////////// | ||
457 | |||
458 | /*! | ||
459 | The difference of this routine vs. the `setImage' is, | ||
460 | that the drawing does not include a difference analysis | ||
461 | between the old and the new image. Instead, the internal | ||
462 | image is used and the painting bound by the PaintEvent box. | ||
463 | */ | ||
464 | |||
465 | void TEWidget::paintEvent( QPaintEvent* pe ) | ||
466 | { | ||
467 | |||
468 | //{ static int cnt = 0; printf("paint %d\n",cnt++); } | ||
469 | const QPixmap* pm = backgroundPixmap(); | ||
470 | QPainter paint; | ||
471 | setUpdatesEnabled(FALSE); | ||
472 | paint.begin( this ); | ||
473 | paint.setBackgroundMode( TransparentMode ); | ||
474 | HCNT("paintEvent"); | ||
475 | |||
476 | // Note that the actual widget size can be slightly larger | ||
477 | // that the image (the size is truncated towards the smaller | ||
478 | // number of characters in `resizeEvent'. The paint rectangle | ||
479 | // can thus be larger than the image, but less then the size | ||
480 | // of one character. | ||
481 | |||
482 | QRect rect = pe->rect().intersect(contentsRect()); | ||
483 | |||
484 | QPoint tL = contentsRect().topLeft(); | ||
485 | int tLx = tL.x(); | ||
486 | int tLy = tL.y(); | ||
487 | |||
488 | int lux = QMIN(columns-1, QMAX(0,(rect.left() - tLx - blX ) / font_w)); | ||
489 | int luy = QMIN(lines-1, QMAX(0,(rect.top() - tLy - bY ) / font_h)); | ||
490 | int rlx = QMIN(columns-1, QMAX(0,(rect.right() - tLx - blX ) / font_w)); | ||
491 | int rly = QMIN(lines-1, QMAX(0,(rect.bottom() - tLy - bY ) / font_h)); | ||
492 | |||
493 | /* | ||
494 | printf("paintEvent: %d..%d, %d..%d (%d..%d, %d..%d)\n",lux,rlx,luy,rly, | ||
495 | rect.left(), rect.right(), rect.top(), rect.bottom()); | ||
496 | */ | ||
497 | |||
498 | // if (pm != NULL && color_table[image->b].transparent) | ||
499 | // erase(rect); | ||
500 | // BL: I have no idea why we need this, and it breaks the refresh. | ||
501 | |||
502 | QChar *disstrU = new QChar[columns]; | ||
503 | for (int y = luy; y <= rly; y++) | ||
504 | for (int x = lux; x <= rlx; x++) | ||
505 | { | ||
506 | int len = 1; | ||
507 | disstrU[0] = fontMap(image[loc(x,y)].c); | ||
508 | int cf = image[loc(x,y)].f; | ||
509 | int cb = image[loc(x,y)].b; | ||
510 | int cr = image[loc(x,y)].r; | ||
511 | while (x+len <= rlx && | ||
512 | image[loc(x+len,y)].f == cf && | ||
513 | image[loc(x+len,y)].b == cb && | ||
514 | image[loc(x+len,y)].r == cr ) | ||
515 | { | ||
516 | disstrU[len] = fontMap(image[loc(x+len,y)].c); | ||
517 | len += 1; | ||
518 | } | ||
519 | QString unistr(disstrU,len); | ||
520 | drawAttrStr(paint, | ||
521 | QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), | ||
522 | unistr, image[loc(x,y)], pm != NULL, false); | ||
523 | x += len - 1; | ||
524 | } | ||
525 | delete [] disstrU; | ||
526 | drawFrame( &paint ); | ||
527 | paint.end(); | ||
528 | setUpdatesEnabled(TRUE); | ||
529 | } | ||
530 | |||
531 | void TEWidget::blinkEvent() | ||
532 | { | ||
533 | blinking = !blinking; | ||
534 | repaint(FALSE); | ||
535 | } | ||
536 | |||
537 | /* ------------------------------------------------------------------------- */ | ||
538 | /* */ | ||
539 | /* Resizing */ | ||
540 | /* */ | ||
541 | /* ------------------------------------------------------------------------- */ | ||
542 | |||
543 | void TEWidget::resizeEvent(QResizeEvent* ev) | ||
544 | { | ||
545 | // printf("resize: %d,%d\n",ev->size().width(),ev->size().height()); | ||
546 | //printf("approx: %d,%d\n",ev->size().width()/font_w,ev->size().height()/font_h); | ||
547 | //printf("leaves: %d,%d\n",ev->size().width()%font_w,ev->size().height()%font_h); | ||
548 | //printf("curren: %d,%d\n",width(),height()); | ||
549 | HCNT("resizeEvent"); | ||
550 | |||
551 | // see comment in `paintEvent' concerning the rounding. | ||
552 | //FIXME: could make a routine here; check width(),height() | ||
553 | assert(ev->size().width() == width()); | ||
554 | assert(ev->size().height() == height()); | ||
555 | |||
556 | propagateSize(); | ||
557 | } | ||
558 | |||
559 | void TEWidget::propagateSize() | ||
560 | { | ||
561 | ca* oldimg = image; | ||
562 | int oldlin = lines; | ||
563 | int oldcol = columns; | ||
564 | makeImage(); | ||
565 | // we copy the old image to reduce flicker | ||
566 | int lins = QMIN(oldlin,lines); | ||
567 | int cols = QMIN(oldcol,columns); | ||
568 | if (oldimg) | ||
569 | { | ||
570 | for (int lin = 0; lin < lins; lin++) | ||
571 | memcpy((void*)&image[columns*lin], | ||
572 | (void*)&oldimg[oldcol*lin],cols*sizeof(ca)); | ||
573 | free(oldimg); //FIXME: try new,delete | ||
574 | } | ||
575 | else | ||
576 | clearImage(); | ||
577 | |||
578 | //NOTE: control flows from the back through the chest right into the eye. | ||
579 | // `emu' will call back via `setImage'. | ||
580 | |||
581 | resizing = TRUE; | ||
582 | emit changedImageSizeSignal(lines, columns); // expose resizeEvent | ||
583 | resizing = FALSE; | ||
584 | } | ||
585 | |||
586 | /* ------------------------------------------------------------------------- */ | ||
587 | /* */ | ||
588 | /* Scrollbar */ | ||
589 | /* */ | ||
590 | /* ------------------------------------------------------------------------- */ | ||
591 | |||
592 | void TEWidget::scrollChanged(int) | ||
593 | { | ||
594 | emit changedHistoryCursor(scrollbar->value()); //expose | ||
595 | } | ||
596 | |||
597 | void TEWidget::setScroll(int cursor, int slines) | ||
598 | { | ||
599 | disconnect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); | ||
600 | scrollbar->setRange(0,slines); | ||
601 | scrollbar->setSteps(1,lines); | ||
602 | scrollbar->setValue(cursor); | ||
603 | connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); | ||
604 | } | ||
605 | |||
606 | void TEWidget::setScrollbarLocation(int loc) | ||
607 | { | ||
608 | if (scrollLoc == loc) return; // quickly | ||
609 | scrollLoc = loc; | ||
610 | propagateSize(); | ||
611 | update(); | ||
612 | } | ||
613 | |||
614 | /* ------------------------------------------------------------------------- */ | ||
615 | /* */ | ||
616 | /* Mouse */ | ||
617 | /* */ | ||
618 | /* ------------------------------------------------------------------------- */ | ||
619 | |||
620 | /*! | ||
621 | Three different operations can be performed using the mouse, and the | ||
622 | routines in this section serve all of them: | ||
623 | |||
624 | 1) The press/release events are exposed to the application | ||
625 | 2) Marking (press and move left button) and Pasting (press middle button) | ||
626 | 3) The right mouse button is used from the configuration menu | ||
627 | |||
628 | NOTE: During the marking process we attempt to keep the cursor within | ||
629 | the bounds of the text as being displayed by setting the mouse position | ||
630 | whenever the mouse has left the text area. | ||
631 | |||
632 | Two reasons to do so: | ||
633 | 1) QT does not allow the `grabMouse' to confine-to the TEWidget. | ||
634 | Thus a `XGrapPointer' would have to be used instead. | ||
635 | 2) Even if so, this would not help too much, since the text area | ||
636 | of the TEWidget is normally not identical with it's bounds. | ||
637 | |||
638 | The disadvantage of the current handling is, that the mouse can visibly | ||
639 | leave the bounds of the widget and is then moved back. Because of the | ||
640 | current construction, and the reasons mentioned above, we cannot do better | ||
641 | without changing the overall construction. | ||
642 | */ | ||
643 | |||
644 | /*! | ||
645 | */ | ||
646 | |||
647 | void TEWidget::mousePressEvent(QMouseEvent* ev) | ||
648 | { | ||
649 | //printf("press [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); | ||
650 | if ( !contentsRect().contains(ev->pos()) ) return; | ||
651 | QPoint tL = contentsRect().topLeft(); | ||
652 | int tLx = tL.x(); | ||
653 | int tLy = tL.y(); | ||
654 | |||
655 | word_selection_mode = FALSE; | ||
656 | |||
657 | //printf("press top left [%d,%d] by=%d\n",tLx,tLy, bY); | ||
658 | if ( ev->button() == LeftButton) | ||
659 | { | ||
660 | QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); | ||
661 | |||
662 | if ( ev->state() & ControlButton ) preserve_line_breaks = FALSE ; | ||
663 | |||
664 | if (mouse_marks || (ev->state() & ShiftButton)) | ||
665 | { | ||
666 | emit clearSelectionSignal(); | ||
667 | iPntSel = pntSel = pos; | ||
668 | actSel = 1; // left mouse button pressed but nothing selected yet. | ||
669 | grabMouse( /*crossCursor*/ ); // handle with care! | ||
670 | } | ||
671 | else | ||
672 | { | ||
673 | emit mouseSignal( 0, pos.x() + 1, pos.y() + 1 ); // left button | ||
674 | } | ||
675 | } | ||
676 | if ( ev->button() == MidButton ) | ||
677 | { | ||
678 | emitSelection(); | ||
679 | } | ||
680 | if ( ev->button() == RightButton ) // Configure | ||
681 | { | ||
682 | emit configureRequest( this, ev->state()&(ShiftButton|ControlButton), ev->x(), ev->y() ); | ||
683 | } | ||
684 | } | ||
685 | |||
686 | void TEWidget::mouseMoveEvent(QMouseEvent* ev) | ||
687 | { | ||
688 | // for auto-hiding the cursor, we need mouseTracking | ||
689 | if (ev->state() == NoButton ) return; | ||
690 | |||
691 | if (actSel == 0) return; | ||
692 | |||
693 | // don't extend selection while pasting | ||
694 | if (ev->state() & MidButton) return; | ||
695 | |||
696 | //if ( !contentsRect().contains(ev->pos()) ) return; | ||
697 | QPoint tL = contentsRect().topLeft(); | ||
698 | int tLx = tL.x(); | ||
699 | int tLy = tL.y(); | ||
700 | int scroll = scrollbar->value(); | ||
701 | |||
702 | // we're in the process of moving the mouse with the left button pressed | ||
703 | // the mouse cursor will kept catched within the bounds of the text in | ||
704 | // this widget. | ||
705 | |||
706 | // Adjust position within text area bounds. See FIXME above. | ||
707 | QPoint pos = ev->pos(); | ||
708 | if ( pos.x() < tLx+blX ) pos.setX( tLx+blX ); | ||
709 | if ( pos.x() > tLx+blX+columns*font_w-1 ) pos.setX( tLx+blX+columns*font_w ); | ||
710 | if ( pos.y() < tLy+bY ) pos.setY( tLy+bY ); | ||
711 | if ( pos.y() > tLy+bY+lines*font_h-1 ) pos.setY( tLy+bY+lines*font_h-1 ); | ||
712 | // check if we produce a mouse move event by this | ||
713 | if ( pos != ev->pos() ) cursor().setPos(mapToGlobal(pos)); | ||
714 | |||
715 | if ( pos.y() == tLy+bY+lines*font_h-1 ) | ||
716 | { | ||
717 | scrollbar->setValue(scrollbar->value()+yMouseScroll); // scrollforward | ||
718 | } | ||
719 | if ( pos.y() == tLy+bY ) | ||
720 | { | ||
721 | scrollbar->setValue(scrollbar->value()-yMouseScroll); // scrollback | ||
722 | } | ||
723 | |||
724 | QPoint here = QPoint((pos.x()-tLx-blX)/font_w,(pos.y()-tLy-bY)/font_h); | ||
725 | QPoint ohere; | ||
726 | bool swapping = FALSE; | ||
727 | |||
728 | if ( word_selection_mode ) | ||
729 | { | ||
730 | // Extend to word boundaries | ||
731 | int i; | ||
732 | int selClass; | ||
733 | |||
734 | bool left_not_right = ( here.y() < iPntSel.y() || | ||
735 | here.y() == iPntSel.y() && here.x() < iPntSel.x() ); | ||
736 | bool old_left_not_right = ( pntSel.y() < iPntSel.y() || | ||
737 | pntSel.y() == iPntSel.y() && pntSel.x() < iPntSel.x() ); | ||
738 | swapping = left_not_right != old_left_not_right; | ||
739 | |||
740 | // Find left (left_not_right ? from here : from start) | ||
741 | QPoint left = left_not_right ? here : iPntSel; | ||
742 | i = loc(left.x(),left.y()); | ||
743 | selClass = charClass(image[i].c); | ||
744 | while ( left.x() > 0 && charClass(image[i-1].c) == selClass ) | ||
745 | { i--; left.rx()--; } | ||
746 | |||
747 | // Find left (left_not_right ? from start : from here) | ||
748 | QPoint right = left_not_right ? iPntSel : here; | ||
749 | i = loc(right.x(),right.y()); | ||
750 | selClass = charClass(image[i].c); | ||
751 | while ( right.x() < columns-1 && charClass(image[i+1].c) == selClass ) | ||
752 | { i++; right.rx()++; } | ||
753 | |||
754 | // Pick which is start (ohere) and which is extension (here) | ||
755 | if ( left_not_right ) | ||
756 | { | ||
757 | here = left; ohere = right; | ||
758 | } | ||
759 | else | ||
760 | { | ||
761 | here = right; ohere = left; | ||
762 | } | ||
763 | } | ||
764 | |||
765 | if (here == pntSel && scroll == scrollbar->value()) return; // not moved | ||
766 | |||
767 | if ( word_selection_mode ) { | ||
768 | if ( actSel < 2 || swapping ) { | ||
769 | emit beginSelectionSignal( ohere.x(), ohere.y() ); | ||
770 | } | ||
771 | } else if ( actSel < 2 ) { | ||
772 | emit beginSelectionSignal( pntSel.x(), pntSel.y() ); | ||
773 | } | ||
774 | |||
775 | actSel = 2; // within selection | ||
776 | pntSel = here; | ||
777 | emit extendSelectionSignal( here.x(), here.y() ); | ||
778 | } | ||
779 | |||
780 | void TEWidget::mouseReleaseEvent(QMouseEvent* ev) | ||
781 | { | ||
782 | //printf("release [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); | ||
783 | if ( ev->button() == LeftButton) | ||
784 | { | ||
785 | if ( actSel > 1 ) emit endSelectionSignal(preserve_line_breaks); | ||
786 | preserve_line_breaks = TRUE; | ||
787 | actSel = 0; | ||
788 | |||
789 | //FIXME: emits a release event even if the mouse is | ||
790 | // outside the range. The procedure used in `mouseMoveEvent' | ||
791 | // applies here, too. | ||
792 | |||
793 | QPoint tL = contentsRect().topLeft(); | ||
794 | int tLx = tL.x(); | ||
795 | int tLy = tL.y(); | ||
796 | |||
797 | if (!mouse_marks && !(ev->state() & ShiftButton)) | ||
798 | emit mouseSignal( 3, // release | ||
799 | (ev->x()-tLx-blX)/font_w + 1, | ||
800 | (ev->y()-tLy-bY)/font_h + 1 ); | ||
801 | releaseMouse(); | ||
802 | } | ||
803 | } | ||
804 | |||
805 | void TEWidget::mouseDoubleClickEvent(QMouseEvent* ev) | ||
806 | { | ||
807 | if ( ev->button() != LeftButton) return; | ||
808 | |||
809 | QPoint tL = contentsRect().topLeft(); | ||
810 | int tLx = tL.x(); | ||
811 | int tLy = tL.y(); | ||
812 | QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); | ||
813 | |||
814 | // pass on double click as two clicks. | ||
815 | if (!mouse_marks && !(ev->state() & ShiftButton)) | ||
816 | { | ||
817 | emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button | ||
818 | emit mouseSignal( 3, pos.x()+1, pos.y()+1 ); // release | ||
819 | emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button | ||
820 | return; | ||
821 | } | ||
822 | |||
823 | |||
824 | emit clearSelectionSignal(); | ||
825 | QPoint bgnSel = pos; | ||
826 | QPoint endSel = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); | ||
827 | int i = loc(bgnSel.x(),bgnSel.y()); | ||
828 | iPntSel = bgnSel; | ||
829 | |||
830 | word_selection_mode = TRUE; | ||
831 | |||
832 | // find word boundaries... | ||
833 | int selClass = charClass(image[i].c); | ||
834 | { | ||
835 | // set the start... | ||
836 | int x = bgnSel.x(); | ||
837 | while ( x > 0 && charClass(image[i-1].c) == selClass ) | ||
838 | { i--; x--; } | ||
839 | bgnSel.setX(x); | ||
840 | emit beginSelectionSignal( bgnSel.x(), bgnSel.y() ); | ||
841 | |||
842 | // set the end... | ||
843 | i = loc( endSel.x(), endSel.y() ); | ||
844 | x = endSel.x(); | ||
845 | while( x < columns-1 && charClass(image[i+1].c) == selClass ) | ||
846 | { i++; x++ ; } | ||
847 | endSel.setX(x); | ||
848 | actSel = 2; // within selection | ||
849 | emit extendSelectionSignal( endSel.x(), endSel.y() ); | ||
850 | emit endSelectionSignal(preserve_line_breaks); | ||
851 | preserve_line_breaks = TRUE; | ||
852 | } | ||
853 | } | ||
854 | |||
855 | void TEWidget::focusInEvent( QFocusEvent * ) | ||
856 | { | ||
857 | |||
858 | // do nothing, to prevent repainting | ||
859 | } | ||
860 | |||
861 | |||
862 | void TEWidget::focusOutEvent( QFocusEvent * ) | ||
863 | { | ||
864 | // do nothing, to prevent repainting | ||
865 | } | ||
866 | |||
867 | bool TEWidget::focusNextPrevChild( bool next ) | ||
868 | { | ||
869 | if (next) | ||
870 | return false; // This disables changing the active part in konqueror | ||
871 | // when pressing Tab | ||
872 | return QFrame::focusNextPrevChild( next ); | ||
873 | } | ||
874 | |||
875 | |||
876 | int TEWidget::charClass(char ch) const | ||
877 | { | ||
878 | // This might seem like overkill, but imagine if ch was a Unicode | ||
879 | // character (Qt 2.0 QChar) - it might then be sensible to separate | ||
880 | // the different language ranges, etc. | ||
881 | |||
882 | if ( isspace(ch) ) return ' '; | ||
883 | |||
884 | static const char *word_characters = ":@-./_~"; | ||
885 | if ( isalnum(ch) || strchr(word_characters, ch) ) | ||
886 | return 'a'; | ||
887 | |||
888 | // Everything else is weird | ||
889 | return 1; | ||
890 | } | ||
891 | |||
892 | void TEWidget::setMouseMarks(bool on) | ||
893 | { | ||
894 | mouse_marks = on; | ||
895 | setCursor( mouse_marks ? ibeamCursor : arrowCursor ); | ||
896 | } | ||
897 | |||
898 | /* ------------------------------------------------------------------------- */ | ||
899 | /* */ | ||
900 | /* Clipboard */ | ||
901 | /* */ | ||
902 | /* ------------------------------------------------------------------------- */ | ||
903 | |||
904 | #undef KeyPress | ||
905 | |||
906 | void TEWidget::emitSelection() | ||
907 | // Paste Clipboard by simulating keypress events | ||
908 | { | ||
909 | #ifndef QT_NO_CLIPBOARD | ||
910 | QString text = QApplication::clipboard()->text(); | ||
911 | if ( ! text.isNull() ) | ||
912 | { | ||
913 | text.replace(QRegExp("\n"), "\r"); | ||
914 | QKeyEvent e(QEvent::KeyPress, 0, -1, 0, text); | ||
915 | emit keyPressedSignal(&e); // expose as a big fat keypress event | ||
916 | emit clearSelectionSignal(); | ||
917 | } | ||
918 | #endif | ||
919 | } | ||
920 | |||
921 | void TEWidget::emitText(QString text) | ||
922 | { | ||
923 | QKeyEvent e(QEvent::KeyPress, 0, -1, 0, text); | ||
924 | emit keyPressedSignal(&e); // expose as a big fat keypress event | ||
925 | } | ||
926 | |||
927 | void TEWidget::pasteClipboard( ) | ||
928 | { | ||
929 | emitSelection(); | ||
930 | } | ||
931 | |||
932 | void TEWidget::setSelection(const QString& t) | ||
933 | { | ||
934 | #ifndef QT_NO_CLIPBOARD | ||
935 | // Disconnect signal while WE set the clipboard | ||
936 | QObject *cb = QApplication::clipboard(); | ||
937 | QObject::disconnect( cb, SIGNAL(dataChanged()), | ||
938 | this, SLOT(onClearSelection()) ); | ||
939 | |||
940 | QApplication::clipboard()->setText(t); | ||
941 | |||
942 | QObject::connect( cb, SIGNAL(dataChanged()), | ||
943 | this, SLOT(onClearSelection()) ); | ||
944 | #endif | ||
945 | } | ||
946 | |||
947 | void TEWidget::onClearSelection() | ||
948 | { | ||
949 | emit clearSelectionSignal(); | ||
950 | } | ||
951 | |||
952 | /* ------------------------------------------------------------------------- */ | ||
953 | /* */ | ||
954 | /* Keyboard */ | ||
955 | /* */ | ||
956 | /* ------------------------------------------------------------------------- */ | ||
957 | |||
958 | //FIXME: an `eventFilter' has been installed instead of a `keyPressEvent' | ||
959 | // due to a bug in `QT' or the ignorance of the author to prevent | ||
960 | // repaint events being emitted to the screen whenever one leaves | ||
961 | // or reenters the screen to/from another application. | ||
962 | // | ||
963 | // Troll says one needs to change focusInEvent() and focusOutEvent(), | ||
964 | // which would also let you have an in-focus cursor and an out-focus | ||
965 | // cursor like xterm does. | ||
966 | |||
967 | // for the auto-hide cursor feature, I added empty focusInEvent() and | ||
968 | // focusOutEvent() so that update() isn't called. | ||
969 | // For auto-hide, we need to get keypress-events, but we only get them when | ||
970 | // we have focus. | ||
971 | |||
972 | void TEWidget::doScroll(int lines) | ||
973 | { | ||
974 | scrollbar->setValue(scrollbar->value()+lines); | ||
975 | } | ||
976 | |||
977 | bool TEWidget::eventFilter( QObject *obj, QEvent *e ) | ||
978 | { | ||
979 | if ( (e->type() == QEvent::Accel || | ||
980 | e->type() == QEvent::AccelAvailable ) && qApp->focusWidget() == this ) { | ||
981 | static_cast<QKeyEvent *>( e )->ignore(); | ||
982 | return true; | ||
983 | } | ||
984 | if ( obj != this /* when embedded */ && obj != parent() /* when standalone */ ) | ||
985 | return FALSE; // not us | ||
986 | if ( e->type() == QEvent::Wheel) { | ||
987 | QApplication::sendEvent(scrollbar, e); | ||
988 | } | ||
989 | |||
990 | #ifdef FAKE_CTRL_AND_ALT | ||
991 | static bool control = FALSE; | ||
992 | static bool alt = FALSE; | ||
993 | // qDebug(" Has a keyboard with no CTRL and ALT keys, but we fake it:"); | ||
994 | bool dele=FALSE; | ||
995 | if ( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease ) { | ||
996 | QKeyEvent* ke = (QKeyEvent*)e; | ||
997 | bool keydown = e->type() == QEvent::KeyPress || ke->isAutoRepeat(); | ||
998 | switch (ke->key()) { | ||
999 | case Key_F9: // let this be "Control" | ||
1000 | control = keydown; | ||
1001 | e = new QKeyEvent(QEvent::KeyPress, Key_Control, 0, ke->state()); | ||
1002 | dele=TRUE; | ||
1003 | break; | ||
1004 | case Key_F13: // let this be "Alt" | ||
1005 | alt = keydown; | ||
1006 | e = new QKeyEvent(QEvent::KeyPress, Key_Alt, 0, ke->state()); | ||
1007 | dele=TRUE; | ||
1008 | break; | ||
1009 | default: | ||
1010 | if ( control ) { | ||
1011 | int a = toupper(ke->ascii())-64; | ||
1012 | if ( a >= 0 && a < ' ' ) { | ||
1013 | e = new QKeyEvent(e->type(), ke->key(), | ||
1014 | a, ke->state()|ControlButton, QChar(a,0)); | ||
1015 | dele=TRUE; | ||
1016 | } | ||
1017 | } | ||
1018 | if ( alt ) { | ||
1019 | e = new QKeyEvent(e->type(), ke->key(), | ||
1020 | ke->ascii(), ke->state()|AltButton, ke->text()); | ||
1021 | dele=TRUE; | ||
1022 | } | ||
1023 | } | ||
1024 | } | ||
1025 | #endif | ||
1026 | |||
1027 | if ( e->type() == QEvent::KeyPress ) { | ||
1028 | QKeyEvent* ke = (QKeyEvent*)e; | ||
1029 | actSel=0; // Key stroke implies a screen update, so TEWidget won't | ||
1030 | // know where the current selection is. | ||
1031 | |||
1032 | // qDebug("key pressed is 0x%x",ke->key()); | ||
1033 | |||
1034 | if( ke->state() == ShiftButton && ke->key() == Key_Tab) { //lets hardcode this sucker | ||
1035 | |||
1036 | // qDebug("key pressed 2 is 0x%x",ke->key()); | ||
1037 | emitText("\\"); // expose | ||
1038 | } else | ||
1039 | emit keyPressedSignal(ke); // expose | ||
1040 | ke->accept(); | ||
1041 | #ifdef FAKE_CTRL_AND_ALT | ||
1042 | if ( dele ) delete e; | ||
1043 | #endif | ||
1044 | return true; // stop the event | ||
1045 | } | ||
1046 | if ( e->type() == QEvent::Enter ) { | ||
1047 | QObject::disconnect( (QObject*)cb, SIGNAL(dataChanged()), | ||
1048 | this, SLOT(onClearSelection()) ); | ||
1049 | } | ||
1050 | if ( e->type() == QEvent::Leave ) { | ||
1051 | QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), | ||
1052 | this, SLOT(onClearSelection()) ); | ||
1053 | } | ||
1054 | return QFrame::eventFilter( obj, e ); | ||
1055 | } | ||
1056 | |||
1057 | /* ------------------------------------------------------------------------- */ | ||
1058 | /* */ | ||
1059 | /* Frame */ | ||
1060 | /* */ | ||
1061 | /* ------------------------------------------------------------------------- */ | ||
1062 | |||
1063 | void TEWidget::frameChanged() | ||
1064 | { | ||
1065 | propagateSize(); | ||
1066 | update(); | ||
1067 | } | ||
1068 | |||
1069 | /* ------------------------------------------------------------------------- */ | ||
1070 | /* */ | ||
1071 | /* Sound */ | ||
1072 | /* */ | ||
1073 | /* ------------------------------------------------------------------------- */ | ||
1074 | |||
1075 | void TEWidget::Bell() | ||
1076 | { | ||
1077 | QApplication::beep(); | ||
1078 | } | ||
1079 | |||
1080 | /* ------------------------------------------------------------------------- */ | ||
1081 | /* */ | ||
1082 | /* Auxiluary */ | ||
1083 | /* */ | ||
1084 | /* ------------------------------------------------------------------------- */ | ||
1085 | |||
1086 | void TEWidget::clearImage() | ||
1087 | // initialize the image | ||
1088 | // for internal use only | ||
1089 | { | ||
1090 | for (int y = 0; y < lines; y++) | ||
1091 | for (int x = 0; x < columns; x++) | ||
1092 | { | ||
1093 | image[loc(x,y)].c = 0xff; //' '; | ||
1094 | image[loc(x,y)].f = 0xff; //DEFAULT_FORE_COLOR; | ||
1095 | image[loc(x,y)].b = 0xff; //DEFAULT_BACK_COLOR; | ||
1096 | image[loc(x,y)].r = 0xff; //DEFAULT_RENDITION; | ||
1097 | } | ||
1098 | } | ||
1099 | |||
1100 | // Create Image /////////////////////////////////////////////////////// | ||
1101 | |||
1102 | void TEWidget::calcGeometry() | ||
1103 | { | ||
1104 | //FIXME: set rimX == rimY == 0 when running in full screen mode. | ||
1105 | |||
1106 | scrollbar->resize(QApplication::style().scrollBarExtent().width(), | ||
1107 | contentsRect().height()); | ||
1108 | switch(scrollLoc) | ||
1109 | { | ||
1110 | case SCRNONE : | ||
1111 | columns = ( contentsRect().width() - 2 * rimX ) / font_w; | ||
1112 | blX = (contentsRect().width() - (columns*font_w) ) / 2; | ||
1113 | brX = blX; | ||
1114 | scrollbar->hide(); | ||
1115 | break; | ||
1116 | case SCRLEFT : | ||
1117 | columns = ( contentsRect().width() - 2 * rimX - scrollbar->width()) / font_w; | ||
1118 | brX = (contentsRect().width() - (columns*font_w) - scrollbar->width() ) / 2; | ||
1119 | blX = brX + scrollbar->width(); | ||
1120 | scrollbar->move(contentsRect().topLeft()); | ||
1121 | scrollbar->show(); | ||
1122 | break; | ||
1123 | case SCRRIGHT: | ||
1124 | columns = ( contentsRect().width() - 2 * rimX - scrollbar->width()) / font_w; | ||
1125 | blX = (contentsRect().width() - (columns*font_w) - scrollbar->width() ) / 2; | ||
1126 | brX = blX; | ||
1127 | scrollbar->move(contentsRect().topRight() - QPoint(scrollbar->width()-1,0)); | ||
1128 | scrollbar->show(); | ||
1129 | break; | ||
1130 | } | ||
1131 | //FIXME: support 'rounding' styles | ||
1132 | lines = ( contentsRect().height() - 2 * rimY ) / font_h; | ||
1133 | bY = (contentsRect().height() - (lines *font_h)) / 2; | ||
1134 | } | ||
1135 | |||
1136 | void TEWidget::makeImage() | ||
1137 | //FIXME: rename 'calcGeometry? | ||
1138 | { | ||
1139 | calcGeometry(); | ||
1140 | image = (ca*) malloc(lines*columns*sizeof(ca)); | ||
1141 | clearImage(); | ||
1142 | } | ||
1143 | |||
1144 | // calculate the needed size | ||
1145 | QSize TEWidget::calcSize(int cols, int lins) const | ||
1146 | { | ||
1147 | int frw = width() - contentsRect().width(); | ||
1148 | int frh = height() - contentsRect().height(); | ||
1149 | int scw = (scrollLoc==SCRNONE?0:scrollbar->width()); | ||
1150 | return QSize( font_w*cols + 2*rimX + frw + scw, font_h*lins + 2*rimY + frh ); | ||
1151 | } | ||
1152 | |||
1153 | QSize TEWidget::sizeHint() const | ||
1154 | { | ||
1155 | return size(); | ||
1156 | } | ||
1157 | |||
1158 | void TEWidget::styleChange(QStyle &) | ||
1159 | { | ||
1160 | propagateSize(); | ||
1161 | } | ||
1162 | |||
1163 | #ifndef QT_NO_DRAGANDDROP | ||
1164 | |||
1165 | /* --------------------------------------------------------------------- */ | ||
1166 | /* */ | ||
1167 | /* Drag & Drop */ | ||
1168 | /* */ | ||
1169 | /* --------------------------------------------------------------------- */ | ||
1170 | |||
1171 | |||
1172 | void TEWidget::dragEnterEvent(QDragEnterEvent* e) | ||
1173 | { | ||
1174 | e->accept(QTextDrag::canDecode(e) || | ||
1175 | QUriDrag::canDecode(e)); | ||
1176 | } | ||
1177 | |||
1178 | void TEWidget::dropEvent(QDropEvent* event) | ||
1179 | { | ||
1180 | // The current behaviour when url(s) are dropped is | ||
1181 | // * if there is only ONE url and if it's a LOCAL one, ask for paste or cd | ||
1182 | // * in all other cases, just paste | ||
1183 | // (for non-local ones, or for a list of URLs, 'cd' is nonsense) | ||
1184 | QStrList strlist; | ||
1185 | int file_count = 0; | ||
1186 | dropText = ""; | ||
1187 | bool bPopup = true; | ||
1188 | |||
1189 | if(QUriDrag::decode(event, strlist)) { | ||
1190 | if (strlist.count()) { | ||
1191 | for(const char* p = strlist.first(); p; p = strlist.next()) { | ||
1192 | if(file_count++ > 0) { | ||
1193 | dropText += " "; | ||
1194 | bPopup = false; // more than one file, don't popup | ||
1195 | } | ||
1196 | |||
1197 | /* | ||
1198 | KURL url(p); | ||
1199 | if (url.isLocalFile()) { | ||
1200 | dropText += url.path(); // local URL : remove protocol | ||
1201 | } | ||
1202 | else { | ||
1203 | dropText += url.prettyURL(); | ||
1204 | bPopup = false; // a non-local file, don't popup | ||
1205 | } | ||
1206 | */ | ||
1207 | |||
1208 | } | ||
1209 | |||
1210 | if (bPopup) | ||
1211 | // m_drop->popup(pos() + event->pos()); | ||
1212 | m_drop->popup(mapToGlobal(event->pos())); | ||
1213 | else | ||
1214 | { | ||
1215 | if (currentSession) { | ||
1216 | currentSession->getEmulation()->sendString(dropText.local8Bit()); | ||
1217 | } | ||
1218 | // kdDebug() << "Drop:" << dropText.local8Bit() << "\n"; | ||
1219 | } | ||
1220 | } | ||
1221 | } | ||
1222 | else if(QTextDrag::decode(event, dropText)) { | ||
1223 | // kdDebug() << "Drop:" << dropText.local8Bit() << "\n"; | ||
1224 | if (currentSession) { | ||
1225 | currentSession->getEmulation()->sendString(dropText.local8Bit()); | ||
1226 | } | ||
1227 | // Paste it | ||
1228 | } | ||
1229 | } | ||
1230 | #endif | ||
1231 | |||
1232 | |||
1233 | void TEWidget::drop_menu_activated(int item) | ||
1234 | { | ||
1235 | #ifndef QT_NO_DRAGANDDROP | ||
1236 | switch (item) | ||
1237 | { | ||
1238 | case 0: // paste | ||
1239 | currentSession->getEmulation()->sendString(dropText.local8Bit()); | ||
1240 | // KWM::activate((Window)this->winId()); | ||
1241 | break; | ||
1242 | case 1: // cd ... | ||
1243 | currentSession->getEmulation()->sendString("cd "); | ||
1244 | struct stat statbuf; | ||
1245 | if ( ::stat( QFile::encodeName( dropText ), &statbuf ) == 0 ) | ||
1246 | { | ||
1247 | if ( !S_ISDIR(statbuf.st_mode) ) | ||
1248 | { | ||
1249 | /* | ||
1250 | KURL url; | ||
1251 | url.setPath( dropText ); | ||
1252 | dropText = url.directory( true, false ); // remove filename | ||
1253 | */ | ||
1254 | } | ||
1255 | } | ||
1256 | dropText.replace(QRegExp(" "), "\\ "); // escape spaces | ||
1257 | currentSession->getEmulation()->sendString(dropText.local8Bit()); | ||
1258 | currentSession->getEmulation()->sendString("\n"); | ||
1259 | // KWM::activate((Window)this->winId()); | ||
1260 | break; | ||
1261 | } | ||
1262 | #endif | ||
1263 | } | ||
1264 | |||
diff --git a/noncore/apps/opie-console/TEWidget.h b/noncore/apps/opie-console/TEWidget.h new file mode 100644 index 0000000..40e1aea --- a/dev/null +++ b/noncore/apps/opie-console/TEWidget.h | |||
@@ -0,0 +1,202 @@ | |||
1 | /* ----------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [te_widget.h] Terminal Emulation Widget */ | ||
4 | /* */ | ||
5 | /* ----------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* ----------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | #ifndef TE_WIDGET_H | ||
19 | #define TE_WIDGET_H | ||
20 | |||
21 | #include <qwidget.h> | ||
22 | #include <qlabel.h> | ||
23 | #include <qtimer.h> | ||
24 | #include <qcolor.h> | ||
25 | #include <qkeycode.h> | ||
26 | #include <qscrollbar.h> | ||
27 | |||
28 | #include <qpopupmenu.h> | ||
29 | |||
30 | #include "TECommon.h" | ||
31 | |||
32 | extern unsigned short vt100_graphics[32]; | ||
33 | |||
34 | class TESession; | ||
35 | |||
36 | // class Konsole; | ||
37 | |||
38 | class TEWidget : public QFrame | ||
39 | // a widget representing attributed text | ||
40 | { Q_OBJECT | ||
41 | |||
42 | // friend class Konsole; | ||
43 | |||
44 | public: | ||
45 | |||
46 | TEWidget(QWidget *parent=0, const char *name=0); | ||
47 | virtual ~TEWidget(); | ||
48 | |||
49 | public: | ||
50 | |||
51 | QColor getDefaultBackColor(); | ||
52 | |||
53 | const ColorEntry* getColorTable() const; | ||
54 | const ColorEntry* getdefaultColorTable() const; | ||
55 | void setColorTable(const ColorEntry table[]); | ||
56 | |||
57 | void setScrollbarLocation(int loc); | ||
58 | enum { SCRNONE=0, SCRLEFT=1, SCRRIGHT=2 }; | ||
59 | |||
60 | void setScroll(int cursor, int lines); | ||
61 | void doScroll(int lines); | ||
62 | |||
63 | void emitSelection(); | ||
64 | |||
65 | public: | ||
66 | |||
67 | void setImage(const ca* const newimg, int lines, int columns); | ||
68 | |||
69 | int Lines() { return lines; } | ||
70 | int Columns() { return columns; } | ||
71 | |||
72 | void calcGeometry(); | ||
73 | void propagateSize(); | ||
74 | QSize calcSize(int cols, int lins) const; | ||
75 | |||
76 | QSize sizeHint() const; | ||
77 | |||
78 | public: | ||
79 | |||
80 | void Bell(); | ||
81 | void emitText(QString text); | ||
82 | void pasteClipboard(); | ||
83 | |||
84 | signals: | ||
85 | |||
86 | void keyPressedSignal(QKeyEvent *e); | ||
87 | void mouseSignal(int cb, int cx, int cy); | ||
88 | void changedImageSizeSignal(int lines, int columns); | ||
89 | void changedHistoryCursor(int value); | ||
90 | void configureRequest( TEWidget*, int state, int x, int y ); | ||
91 | |||
92 | void clearSelectionSignal(); | ||
93 | void beginSelectionSignal( const int x, const int y ); | ||
94 | void extendSelectionSignal( const int x, const int y ); | ||
95 | void endSelectionSignal(const BOOL preserve_line_breaks); | ||
96 | |||
97 | |||
98 | protected: | ||
99 | |||
100 | virtual void styleChange( QStyle& ); | ||
101 | |||
102 | bool eventFilter( QObject *, QEvent * ); | ||
103 | |||
104 | void drawAttrStr(QPainter &paint, QRect rect, | ||
105 | QString& str, ca attr, BOOL pm, BOOL clear); | ||
106 | void paintEvent( QPaintEvent * ); | ||
107 | |||
108 | void resizeEvent(QResizeEvent*); | ||
109 | |||
110 | void fontChange(const QFont &font); | ||
111 | void frameChanged(); | ||
112 | |||
113 | void mouseDoubleClickEvent(QMouseEvent* ev); | ||
114 | void mousePressEvent( QMouseEvent* ); | ||
115 | void mouseReleaseEvent( QMouseEvent* ); | ||
116 | void mouseMoveEvent( QMouseEvent* ); | ||
117 | |||
118 | void focusInEvent( QFocusEvent * ); | ||
119 | void focusOutEvent( QFocusEvent * ); | ||
120 | bool focusNextPrevChild( bool next ); | ||
121 | |||
122 | #ifndef QT_NO_DRAGANDDROP | ||
123 | // Dnd | ||
124 | void dragEnterEvent(QDragEnterEvent* event); | ||
125 | void dropEvent(QDropEvent* event); | ||
126 | #endif | ||
127 | |||
128 | virtual int charClass(char) const; | ||
129 | |||
130 | void clearImage(); | ||
131 | |||
132 | public: | ||
133 | const QPixmap *backgroundPixmap(); | ||
134 | |||
135 | void setSelection(const QString &t); | ||
136 | |||
137 | virtual void setFont(const QFont &); | ||
138 | void setVTFont(const QFont &); | ||
139 | QFont getVTFont(); | ||
140 | |||
141 | void setMouseMarks(bool on); | ||
142 | |||
143 | public slots: | ||
144 | |||
145 | void onClearSelection(); | ||
146 | |||
147 | protected slots: | ||
148 | |||
149 | void scrollChanged(int value); | ||
150 | void blinkEvent(); | ||
151 | |||
152 | private: | ||
153 | |||
154 | QChar (*fontMap)(QChar); // possible vt100 font extention | ||
155 | |||
156 | bool fixed_font; // has fixed pitch | ||
157 | int font_h; // height | ||
158 | int font_w; // width | ||
159 | int font_a; // ascend | ||
160 | |||
161 | int blX; // actual offset (left) | ||
162 | int brX; // actual offset (right) | ||
163 | int bY; // actual offset | ||
164 | |||
165 | int lines; | ||
166 | int columns; | ||
167 | ca *image; // [lines][columns] | ||
168 | |||
169 | ColorEntry color_table[TABLE_COLORS]; | ||
170 | |||
171 | BOOL resizing; | ||
172 | bool mouse_marks; | ||
173 | |||
174 | void makeImage(); | ||
175 | |||
176 | QPoint iPntSel; // initial selection point | ||
177 | QPoint pntSel; // current selection point | ||
178 | int actSel; // selection state | ||
179 | BOOL word_selection_mode; | ||
180 | BOOL preserve_line_breaks; | ||
181 | |||
182 | QClipboard* cb; | ||
183 | QScrollBar* scrollbar; | ||
184 | int scrollLoc; | ||
185 | |||
186 | //#define SCRNONE 0 | ||
187 | //#define SCRLEFT 1 | ||
188 | //#define SCRRIGHT 2 | ||
189 | |||
190 | BOOL blinking; // hide text in paintEvent | ||
191 | BOOL hasBlinker; // has characters to blink | ||
192 | QTimer* blinkT; // active when hasBlinker | ||
193 | QPopupMenu* m_drop; | ||
194 | QString dropText; | ||
195 | public: | ||
196 | // current session in this widget | ||
197 | TESession *currentSession; | ||
198 | private slots: | ||
199 | void drop_menu_activated(int item); | ||
200 | }; | ||
201 | |||
202 | #endif // TE_WIDGET_H | ||
diff --git a/noncore/apps/opie-console/TEmuVt102.cpp b/noncore/apps/opie-console/TEmuVt102.cpp new file mode 100644 index 0000000..275c18d --- a/dev/null +++ b/noncore/apps/opie-console/TEmuVt102.cpp | |||
@@ -0,0 +1,1020 @@ | |||
1 | /* ------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [TEmuVt102.C] VT102 Terminal Emulation */ | ||
4 | /* */ | ||
5 | /* ------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* ------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | /*! \class TEmuVt102 | ||
20 | |||
21 | \brief Actual Emulation for Konsole | ||
22 | |||
23 | \sa TEWidget \sa TEScreen | ||
24 | */ | ||
25 | |||
26 | #include "TEmuVt102.h" | ||
27 | #include "TEWidget.h" | ||
28 | #include "TEScreen.h" | ||
29 | #include "keytrans.h" | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <unistd.h> | ||
33 | #include <qkeycode.h> | ||
34 | #include <qtextcodec.h> | ||
35 | |||
36 | |||
37 | /* VT102 Terminal Emulation | ||
38 | |||
39 | This class puts together the screens, the pty and the widget to a | ||
40 | complete terminal emulation. Beside combining it's componentes, it | ||
41 | handles the emulations's protocol. | ||
42 | |||
43 | This module consists of the following sections: | ||
44 | |||
45 | - Constructor/Destructor | ||
46 | - Incoming Bytes Event pipeline | ||
47 | - Outgoing Bytes | ||
48 | - Mouse Events | ||
49 | - Keyboard Events | ||
50 | - Modes and Charset State | ||
51 | - Diagnostics | ||
52 | */ | ||
53 | |||
54 | |||
55 | /* ------------------------------------------------------------------------- */ | ||
56 | /* */ | ||
57 | /* Constructor / Destructor */ | ||
58 | /* */ | ||
59 | /* ------------------------------------------------------------------------- */ | ||
60 | |||
61 | /* | ||
62 | Nothing really intesting happens here. | ||
63 | */ | ||
64 | |||
65 | /*! | ||
66 | */ | ||
67 | |||
68 | TEmuVt102::TEmuVt102(TEWidget* gui) : TEmulation(gui) | ||
69 | { | ||
70 | QObject::connect(gui,SIGNAL(mouseSignal(int,int,int)), | ||
71 | this,SLOT(onMouse(int,int,int))); | ||
72 | initTokenizer(); | ||
73 | reset(); | ||
74 | } | ||
75 | |||
76 | /*! | ||
77 | */ | ||
78 | |||
79 | TEmuVt102::~TEmuVt102() | ||
80 | { | ||
81 | } | ||
82 | |||
83 | /*! | ||
84 | */ | ||
85 | |||
86 | void TEmuVt102::reset() | ||
87 | { | ||
88 | resetToken(); | ||
89 | resetModes(); | ||
90 | resetCharset(0); screen[0]->reset(); | ||
91 | resetCharset(1); screen[0]->reset(); | ||
92 | setCodec(0); | ||
93 | setKeytrans("linux.keytab"); | ||
94 | } | ||
95 | |||
96 | /* ------------------------------------------------------------------------- */ | ||
97 | /* */ | ||
98 | /* Processing the incoming byte stream */ | ||
99 | /* */ | ||
100 | /* ------------------------------------------------------------------------- */ | ||
101 | |||
102 | /* Incoming Bytes Event pipeline | ||
103 | |||
104 | This section deals with decoding the incoming character stream. | ||
105 | Decoding means here, that the stream is first seperated into `tokens' | ||
106 | which are then mapped to a `meaning' provided as operations by the | ||
107 | `TEScreen' class or by the emulation class itself. | ||
108 | |||
109 | The pipeline proceeds as follows: | ||
110 | |||
111 | - Tokenizing the ESC codes (onRcvChar) | ||
112 | - VT100 code page translation of plain characters (applyCharset) | ||
113 | - Interpretation of ESC codes (tau) | ||
114 | |||
115 | The escape codes and their meaning are described in the | ||
116 | technical reference of this program. | ||
117 | */ | ||
118 | |||
119 | // Tokens ------------------------------------------------------------------ -- | ||
120 | |||
121 | /* | ||
122 | Since the tokens are the central notion if this section, we've put them | ||
123 | in front. They provide the syntactical elements used to represent the | ||
124 | terminals operations as byte sequences. | ||
125 | |||
126 | They are encodes here into a single machine word, so that we can later | ||
127 | switch over them easily. Depending on the token itself, additional | ||
128 | argument variables are filled with parameter values. | ||
129 | |||
130 | The tokens are defined below: | ||
131 | |||
132 | - CHR - Printable characters (32..255 but DEL (=127)) | ||
133 | - CTL - Control characters (0..31 but ESC (= 27), DEL) | ||
134 | - ESC - Escape codes of the form <ESC><CHR but `[]()+*#'> | ||
135 | - ESC_DE - Escape codes of the form <ESC><any of `()+*#%'> C | ||
136 | - CSI_PN - Escape codes of the form <ESC>'[' {Pn} ';' {Pn} C | ||
137 | - CSI_PS - Escape codes of the form <ESC>'[' {Pn} ';' ... C | ||
138 | - CSI_PR - Escape codes of the form <ESC>'[' '?' {Pn} ';' ... C | ||
139 | - VT52 - VT52 escape codes | ||
140 | - <ESC><Chr> | ||
141 | - <ESC>'Y'{Pc}{Pc} | ||
142 | - XTE_HA - Xterm hacks <ESC>`]' {Pn} `;' {Text} <BEL> | ||
143 | note that this is handled differently | ||
144 | |||
145 | The last two forms allow list of arguments. Since the elements of | ||
146 | the lists are treated individually the same way, they are passed | ||
147 | as individual tokens to the interpretation. Further, because the | ||
148 | meaning of the parameters are names (althought represented as numbers), | ||
149 | they are includes within the token ('N'). | ||
150 | |||
151 | */ | ||
152 | |||
153 | #define TY_CONSTR(T,A,N) ( ((((int)N) & 0xffff) << 16) | ((((int)A) & 0xff) << 8) | (((int)T) & 0xff) ) | ||
154 | |||
155 | #define TY_CHR___( ) TY_CONSTR(0,0,0) | ||
156 | #define TY_CTL___(A ) TY_CONSTR(1,A,0) | ||
157 | #define TY_ESC___(A ) TY_CONSTR(2,A,0) | ||
158 | #define TY_ESC_CS(A,B) TY_CONSTR(3,A,B) | ||
159 | #define TY_ESC_DE(A ) TY_CONSTR(4,A,0) | ||
160 | #define TY_CSI_PS(A,N) TY_CONSTR(5,A,N) | ||
161 | #define TY_CSI_PN(A ) TY_CONSTR(6,A,0) | ||
162 | #define TY_CSI_PR(A,N) TY_CONSTR(7,A,N) | ||
163 | |||
164 | #define TY_VT52__(A ) TY_CONSTR(8,A,0) | ||
165 | |||
166 | // Tokenizer --------------------------------------------------------------- -- | ||
167 | |||
168 | /* The tokenizers state | ||
169 | |||
170 | The state is represented by the buffer (pbuf, ppos), | ||
171 | and accompanied by decoded arguments kept in (argv,argc). | ||
172 | Note that they are kept internal in the tokenizer. | ||
173 | */ | ||
174 | |||
175 | void TEmuVt102::resetToken() | ||
176 | { | ||
177 | ppos = 0; argc = 0; argv[0] = 0; argv[1] = 0; | ||
178 | } | ||
179 | |||
180 | void TEmuVt102::addDigit(int dig) | ||
181 | { | ||
182 | argv[argc] = 10*argv[argc] + dig; | ||
183 | } | ||
184 | |||
185 | void TEmuVt102::addArgument() | ||
186 | { | ||
187 | argc = QMIN(argc+1,MAXARGS-1); | ||
188 | argv[argc] = 0; | ||
189 | } | ||
190 | |||
191 | void TEmuVt102::pushToToken(int cc) | ||
192 | { | ||
193 | pbuf[ppos] = cc; | ||
194 | ppos = QMIN(ppos+1,MAXPBUF-1); | ||
195 | } | ||
196 | |||
197 | // Character Classes used while decoding | ||
198 | |||
199 | #define CTL 1 | ||
200 | #define CHR 2 | ||
201 | #define CPN 4 | ||
202 | #define DIG 8 | ||
203 | #define SCS 16 | ||
204 | #define GRP 32 | ||
205 | |||
206 | void TEmuVt102::initTokenizer() | ||
207 | { int i; UINT8* s; | ||
208 | for(i = 0; i < 256; i++) tbl[ i] = 0; | ||
209 | for(i = 0; i < 32; i++) tbl[ i] |= CTL; | ||
210 | for(i = 32; i < 256; i++) tbl[ i] |= CHR; | ||
211 | for(s = (UINT8*)"@ABCDGHLMPXcdfry"; *s; s++) tbl[*s] |= CPN; | ||
212 | for(s = (UINT8*)"0123456789" ; *s; s++) tbl[*s] |= DIG; | ||
213 | for(s = (UINT8*)"()+*%" ; *s; s++) tbl[*s] |= SCS; | ||
214 | for(s = (UINT8*)"()+*#[]%" ; *s; s++) tbl[*s] |= GRP; | ||
215 | resetToken(); | ||
216 | } | ||
217 | |||
218 | /* Ok, here comes the nasty part of the decoder. | ||
219 | |||
220 | Instead of keeping an explicit state, we deduce it from the | ||
221 | token scanned so far. It is then immediately combined with | ||
222 | the current character to form a scanning decision. | ||
223 | |||
224 | This is done by the following defines. | ||
225 | |||
226 | - P is the length of the token scanned so far. | ||
227 | - L (often P-1) is the position on which contents we base a decision. | ||
228 | - C is a character or a group of characters (taken from 'tbl'). | ||
229 | |||
230 | Note that they need to applied in proper order. | ||
231 | */ | ||
232 | |||
233 | #define lec(P,L,C) (p == (P) && s[(L)] == (C)) | ||
234 | #define lun( ) (p == 1 && cc >= 32 ) | ||
235 | #define les(P,L,C) (p == (P) && s[L] < 256 && (tbl[s[(L)]] & (C)) == (C)) | ||
236 | #define eec(C) (p >= 3 && cc == (C)) | ||
237 | #define ees(C) (p >= 3 && cc < 256 && (tbl[ cc ] & (C)) == (C)) | ||
238 | #define eps(C) (p >= 3 && s[2] != '?' && cc < 256 && (tbl[ cc ] & (C)) == (C)) | ||
239 | #define epp( ) (p >= 3 && s[2] == '?' ) | ||
240 | #define egt( ) (p == 3 && s[2] == '>' ) | ||
241 | #define Xpe (ppos>=2 && pbuf[1] == ']' ) | ||
242 | #define Xte (Xpe && cc == 7 ) | ||
243 | #define ces(C) ( cc < 256 && (tbl[ cc ] & (C)) == (C) && !Xte) | ||
244 | |||
245 | #define ESC 27 | ||
246 | #define CNTL(c) ((c)-'@') | ||
247 | |||
248 | // process an incoming unicode character | ||
249 | |||
250 | void TEmuVt102::onRcvChar(int cc) | ||
251 | { int i; | ||
252 | |||
253 | if (cc == 127) return; //VT100: ignore. | ||
254 | |||
255 | if (ces( CTL)) | ||
256 | { // DEC HACK ALERT! Control Characters are allowed *within* esc sequences in VT100 | ||
257 | // This means, they do neither a resetToken nor a pushToToken. Some of them, do | ||
258 | // of course. Guess this originates from a weakly layered handling of the X-on | ||
259 | // X-off protocol, which comes really below this level. | ||
260 | if (cc == CNTL('X') || cc == CNTL('Z') || cc == ESC) resetToken(); //VT100: CAN or SUB | ||
261 | if (cc != ESC) { tau( TY_CTL___(cc+'@' ), 0, 0); return; } | ||
262 | } | ||
263 | |||
264 | pushToToken(cc); // advance the state | ||
265 | |||
266 | int* s = pbuf; | ||
267 | int p = ppos; | ||
268 | |||
269 | if (getMode(MODE_Ansi)) // decide on proper action | ||
270 | { | ||
271 | if (lec(1,0,ESC)) { return; } | ||
272 | if (les(2,1,GRP)) { return; } | ||
273 | if (Xte ) { XtermHack(); resetToken(); return; } | ||
274 | if (Xpe ) { return; } | ||
275 | if (lec(3,2,'?')) { return; } | ||
276 | if (lec(3,2,'>')) { return; } | ||
277 | if (lun( )) { tau( TY_CHR___(), applyCharset(cc), 0); resetToken(); return; } | ||
278 | if (lec(2,0,ESC)) { tau( TY_ESC___(s[1]), 0, 0); resetToken(); return; } | ||
279 | if (les(3,1,SCS)) { tau( TY_ESC_CS(s[1],s[2]), 0, 0); resetToken(); return; } | ||
280 | if (lec(3,1,'#')) { tau( TY_ESC_DE(s[2]), 0, 0); resetToken(); return; } | ||
281 | // if (egt( )) { tau( TY_CSI_PG(cc ), '>', 0); resetToken(); return; } | ||
282 | if (eps( CPN)) { tau( TY_CSI_PN(cc), argv[0],argv[1]); resetToken(); return; } | ||
283 | if (ees( DIG)) { addDigit(cc-'0'); return; } | ||
284 | if (eec( ';')) { addArgument(); return; } | ||
285 | for (i=0;i<=argc;i++) | ||
286 | if (epp( )) tau( TY_CSI_PR(cc,argv[i]), 0, 0); else | ||
287 | tau( TY_CSI_PS(cc,argv[i]), 0, 0); | ||
288 | resetToken(); | ||
289 | } | ||
290 | else // mode VT52 | ||
291 | { | ||
292 | if (lec(1,0,ESC)) return; | ||
293 | if (les(1,0,CHR)) { tau( TY_CHR___( ), s[0], 0); resetToken(); return; } | ||
294 | if (lec(2,1,'Y')) return; | ||
295 | if (lec(3,1,'Y')) return; | ||
296 | if (p < 4) { tau( TY_VT52__(s[1] ), 0, 0); resetToken(); return; } | ||
297 | tau( TY_VT52__(s[1] ), s[2],s[3]); resetToken(); return; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | void TEmuVt102::XtermHack() | ||
302 | { int i,arg = 0; | ||
303 | for (i = 2; i < ppos && '0'<=pbuf[i] && pbuf[i]<'9' ; i++) | ||
304 | arg = 10*arg + (pbuf[i]-'0'); | ||
305 | if (pbuf[i] != ';') { ReportErrorToken(); return; } | ||
306 | QChar *str = new QChar[ppos-i-2]; | ||
307 | for (int j = 0; j < ppos-i-2; j++) str[j] = pbuf[i+1+j]; | ||
308 | QString unistr(str,ppos-i-2); | ||
309 | // arg == 1 doesn't change the title. In XTerm it only changes the icon name | ||
310 | // (btw: arg=0 changes title and icon, arg=1 only icon, arg=2 only title | ||
311 | if (arg == 0 || arg == 2) emit changeTitle(arg,unistr); | ||
312 | delete [] str; | ||
313 | } | ||
314 | |||
315 | // Interpreting Codes --------------------------------------------------------- | ||
316 | |||
317 | /* | ||
318 | Now that the incoming character stream is properly tokenized, | ||
319 | meaning is assigned to them. These are either operations of | ||
320 | the current screen, or of the emulation class itself. | ||
321 | |||
322 | The token to be interpreteted comes in as a machine word | ||
323 | possibly accompanied by two parameters. | ||
324 | |||
325 | Likewise, the operations assigned to, come with up to two | ||
326 | arguments. One could consider to make up a proper table | ||
327 | from the function below. | ||
328 | |||
329 | The technical reference manual provides more informations | ||
330 | about this mapping. | ||
331 | */ | ||
332 | |||
333 | void TEmuVt102::tau( int token, int p, int q ) | ||
334 | { | ||
335 | //scan_buffer_report(); | ||
336 | //if (token == TY_CHR___()) printf("%c",p); else | ||
337 | //printf("tau(%d,%d,%d, %d,%d)\n",(token>>0)&0xff,(token>>8)&0xff,(token>>16)&0xffff,p,q); | ||
338 | switch (token) | ||
339 | { | ||
340 | |||
341 | case TY_CHR___( ) : scr->ShowCharacter (p ); break; //UTF16 | ||
342 | |||
343 | // 127 DEL : ignored on input | ||
344 | |||
345 | case TY_CTL___('@' ) : /* NUL: ignored */ break; | ||
346 | case TY_CTL___('A' ) : /* SOH: ignored */ break; | ||
347 | case TY_CTL___('B' ) : /* STX: ignored */ break; | ||
348 | case TY_CTL___('C' ) : /* ETX: ignored */ break; | ||
349 | case TY_CTL___('D' ) : /* EOT: ignored */ break; | ||
350 | case TY_CTL___('E' ) : reportAnswerBack ( ); break; //VT100 | ||
351 | case TY_CTL___('F' ) : /* ACK: ignored */ break; | ||
352 | case TY_CTL___('G' ) : gui->Bell ( ); break; //VT100 | ||
353 | case TY_CTL___('H' ) : scr->BackSpace ( ); break; //VT100 | ||
354 | case TY_CTL___('I' ) : scr->Tabulate ( ); break; //VT100 | ||
355 | case TY_CTL___('J' ) : scr->NewLine ( ); break; //VT100 | ||
356 | case TY_CTL___('K' ) : scr->NewLine ( ); break; //VT100 | ||
357 | case TY_CTL___('L' ) : scr->NewLine ( ); break; //VT100 | ||
358 | case TY_CTL___('M' ) : scr->Return ( ); break; //VT100 | ||
359 | |||
360 | case TY_CTL___('N' ) : useCharset ( 1); break; //VT100 | ||
361 | case TY_CTL___('O' ) : useCharset ( 0); break; //VT100 | ||
362 | |||
363 | case TY_CTL___('P' ) : /* DLE: ignored */ break; | ||
364 | case TY_CTL___('Q' ) : /* DC1: XON continue */ break; //VT100 | ||
365 | case TY_CTL___('R' ) : /* DC2: ignored */ break; | ||
366 | case TY_CTL___('S' ) : /* DC3: XOFF halt */ break; //VT100 | ||
367 | case TY_CTL___('T' ) : /* DC4: ignored */ break; | ||
368 | case TY_CTL___('U' ) : /* NAK: ignored */ break; | ||
369 | case TY_CTL___('V' ) : /* SYN: ignored */ break; | ||
370 | case TY_CTL___('W' ) : /* ETB: ignored */ break; | ||
371 | case TY_CTL___('X' ) : scr->ShowCharacter ( 0x2592); break; //VT100 | ||
372 | case TY_CTL___('Y' ) : /* EM : ignored */ break; | ||
373 | case TY_CTL___('Z' ) : scr->ShowCharacter ( 0x2592); break; //VT100 | ||
374 | case TY_CTL___('[' ) : /* ESC: cannot be seen here. */ break; | ||
375 | case TY_CTL___('\\' ) : /* FS : ignored */ break; | ||
376 | case TY_CTL___(']' ) : /* GS : ignored */ break; | ||
377 | case TY_CTL___('^' ) : /* RS : ignored */ break; | ||
378 | case TY_CTL___('_' ) : /* US : ignored */ break; | ||
379 | |||
380 | case TY_ESC___('D' ) : scr->index ( ); break; //VT100 | ||
381 | case TY_ESC___('E' ) : scr->NextLine ( ); break; //VT100 | ||
382 | case TY_ESC___('H' ) : scr->changeTabStop (TRUE ); break; //VT100 | ||
383 | case TY_ESC___('M' ) : scr->reverseIndex ( ); break; //VT100 | ||
384 | case TY_ESC___('Z' ) : reportTerminalType ( ); break; | ||
385 | case TY_ESC___('c' ) : reset ( ); break; | ||
386 | |||
387 | case TY_ESC___('n' ) : useCharset ( 2); break; | ||
388 | case TY_ESC___('o' ) : useCharset ( 3); break; | ||
389 | case TY_ESC___('7' ) : saveCursor ( ); break; | ||
390 | case TY_ESC___('8' ) : restoreCursor ( ); break; | ||
391 | |||
392 | case TY_ESC___('=' ) : setMode (MODE_AppKeyPad); break; | ||
393 | case TY_ESC___('>' ) : resetMode (MODE_AppKeyPad); break; | ||
394 | case TY_ESC___('<' ) : setMode (MODE_Ansi ); break; //VT100 | ||
395 | |||
396 | case TY_ESC_CS('(', '0') : setCharset (0, '0'); break; //VT100 | ||
397 | case TY_ESC_CS('(', 'A') : setCharset (0, 'A'); break; //VT100 | ||
398 | case TY_ESC_CS('(', 'B') : setCharset (0, 'B'); break; //VT100 | ||
399 | |||
400 | case TY_ESC_CS(')', '0') : setCharset (1, '0'); break; //VT100 | ||
401 | case TY_ESC_CS(')', 'A') : setCharset (1, 'A'); break; //VT100 | ||
402 | case TY_ESC_CS(')', 'B') : setCharset (1, 'B'); break; //VT100 | ||
403 | |||
404 | case TY_ESC_CS('*', '0') : setCharset (2, '0'); break; //VT100 | ||
405 | case TY_ESC_CS('*', 'A') : setCharset (2, 'A'); break; //VT100 | ||
406 | case TY_ESC_CS('*', 'B') : setCharset (2, 'B'); break; //VT100 | ||
407 | |||
408 | case TY_ESC_CS('+', '0') : setCharset (3, '0'); break; //VT100 | ||
409 | case TY_ESC_CS('+', 'A') : setCharset (3, 'A'); break; //VT100 | ||
410 | case TY_ESC_CS('+', 'B') : setCharset (3, 'B'); break; //VT100 | ||
411 | |||
412 | case TY_ESC_CS('%', 'G') : setCodec (1 ); break; //LINUX | ||
413 | case TY_ESC_CS('%', '@') : setCodec (0 ); break; //LINUX | ||
414 | |||
415 | case TY_ESC_DE('3' ) : /* IGNORED: double high, top half */ break; | ||
416 | case TY_ESC_DE('4' ) : /* IGNORED: double high, bottom half */ break; | ||
417 | case TY_ESC_DE('5' ) : /* IGNORED: single width, single high*/ break; | ||
418 | case TY_ESC_DE('6' ) : /* IGNORED: double width, single high*/ break; | ||
419 | case TY_ESC_DE('8' ) : scr->helpAlign ( ); break; | ||
420 | |||
421 | case TY_CSI_PS('K', 0) : scr->clearToEndOfLine ( ); break; | ||
422 | case TY_CSI_PS('K', 1) : scr->clearToBeginOfLine ( ); break; | ||
423 | case TY_CSI_PS('K', 2) : scr->clearEntireLine ( ); break; | ||
424 | case TY_CSI_PS('J', 0) : scr->clearToEndOfScreen ( ); break; | ||
425 | case TY_CSI_PS('J', 1) : scr->clearToBeginOfScreen ( ); break; | ||
426 | case TY_CSI_PS('J', 2) : scr->clearEntireScreen ( ); break; | ||
427 | case TY_CSI_PS('g', 0) : scr->changeTabStop (FALSE ); break; //VT100 | ||
428 | case TY_CSI_PS('g', 3) : scr->clearTabStops ( ); break; //VT100 | ||
429 | case TY_CSI_PS('h', 4) : scr-> setMode (MODE_Insert ); break; | ||
430 | case TY_CSI_PS('h', 20) : setMode (MODE_NewLine ); break; | ||
431 | case TY_CSI_PS('i', 0) : /* IGNORE: attached printer */ break; //VT100 | ||
432 | case TY_CSI_PS('l', 4) : scr-> resetMode (MODE_Insert ); break; | ||
433 | case TY_CSI_PS('l', 20) : resetMode (MODE_NewLine ); break; | ||
434 | |||
435 | case TY_CSI_PS('m', 0) : scr->setDefaultRendition ( ); break; | ||
436 | case TY_CSI_PS('m', 1) : scr-> setRendition (RE_BOLD ); break; //VT100 | ||
437 | case TY_CSI_PS('m', 4) : scr-> setRendition (RE_UNDERLINE); break; //VT100 | ||
438 | case TY_CSI_PS('m', 5) : scr-> setRendition (RE_BLINK ); break; //VT100 | ||
439 | case TY_CSI_PS('m', 7) : scr-> setRendition (RE_REVERSE ); break; | ||
440 | case TY_CSI_PS('m', 10) : /* IGNORED: mapping related */ break; //LINUX | ||
441 | case TY_CSI_PS('m', 11) : /* IGNORED: mapping related */ break; //LINUX | ||
442 | case TY_CSI_PS('m', 12) : /* IGNORED: mapping related */ break; //LINUX | ||
443 | case TY_CSI_PS('m', 22) : scr->resetRendition (RE_BOLD ); break; | ||
444 | case TY_CSI_PS('m', 24) : scr->resetRendition (RE_UNDERLINE); break; | ||
445 | case TY_CSI_PS('m', 25) : scr->resetRendition (RE_BLINK ); break; | ||
446 | case TY_CSI_PS('m', 27) : scr->resetRendition (RE_REVERSE ); break; | ||
447 | |||
448 | case TY_CSI_PS('m', 30) : scr->setForeColor ( 0); break; | ||
449 | case TY_CSI_PS('m', 31) : scr->setForeColor ( 1); break; | ||
450 | case TY_CSI_PS('m', 32) : scr->setForeColor ( 2); break; | ||
451 | case TY_CSI_PS('m', 33) : scr->setForeColor ( 3); break; | ||
452 | case TY_CSI_PS('m', 34) : scr->setForeColor ( 4); break; | ||
453 | case TY_CSI_PS('m', 35) : scr->setForeColor ( 5); break; | ||
454 | case TY_CSI_PS('m', 36) : scr->setForeColor ( 6); break; | ||
455 | case TY_CSI_PS('m', 37) : scr->setForeColor ( 7); break; | ||
456 | case TY_CSI_PS('m', 39) : scr->setForeColorToDefault( ); break; | ||
457 | |||
458 | case TY_CSI_PS('m', 40) : scr->setBackColor ( 0); break; | ||
459 | case TY_CSI_PS('m', 41) : scr->setBackColor ( 1); break; | ||
460 | case TY_CSI_PS('m', 42) : scr->setBackColor ( 2); break; | ||
461 | case TY_CSI_PS('m', 43) : scr->setBackColor ( 3); break; | ||
462 | case TY_CSI_PS('m', 44) : scr->setBackColor ( 4); break; | ||
463 | case TY_CSI_PS('m', 45) : scr->setBackColor ( 5); break; | ||
464 | case TY_CSI_PS('m', 46) : scr->setBackColor ( 6); break; | ||
465 | case TY_CSI_PS('m', 47) : scr->setBackColor ( 7); break; | ||
466 | case TY_CSI_PS('m', 49) : scr->setBackColorToDefault( ); break; | ||
467 | |||
468 | case TY_CSI_PS('m', 90) : scr->setForeColor ( 8); break; | ||
469 | case TY_CSI_PS('m', 91) : scr->setForeColor ( 9); break; | ||
470 | case TY_CSI_PS('m', 92) : scr->setForeColor ( 10); break; | ||
471 | case TY_CSI_PS('m', 93) : scr->setForeColor ( 11); break; | ||
472 | case TY_CSI_PS('m', 94) : scr->setForeColor ( 12); break; | ||
473 | case TY_CSI_PS('m', 95) : scr->setForeColor ( 13); break; | ||
474 | case TY_CSI_PS('m', 96) : scr->setForeColor ( 14); break; | ||
475 | case TY_CSI_PS('m', 97) : scr->setForeColor ( 15); break; | ||
476 | |||
477 | case TY_CSI_PS('m', 100) : scr->setBackColor ( 8); break; | ||
478 | case TY_CSI_PS('m', 101) : scr->setBackColor ( 9); break; | ||
479 | case TY_CSI_PS('m', 102) : scr->setBackColor ( 10); break; | ||
480 | case TY_CSI_PS('m', 103) : scr->setBackColor ( 11); break; | ||
481 | case TY_CSI_PS('m', 104) : scr->setBackColor ( 12); break; | ||
482 | case TY_CSI_PS('m', 105) : scr->setBackColor ( 13); break; | ||
483 | case TY_CSI_PS('m', 106) : scr->setBackColor ( 14); break; | ||
484 | case TY_CSI_PS('m', 107) : scr->setBackColor ( 15); break; | ||
485 | |||
486 | case TY_CSI_PS('n', 5) : reportStatus ( ); break; | ||
487 | case TY_CSI_PS('n', 6) : reportCursorPosition ( ); break; | ||
488 | case TY_CSI_PS('q', 0) : /* IGNORED: LEDs off */ break; //VT100 | ||
489 | case TY_CSI_PS('q', 1) : /* IGNORED: LED1 on */ break; //VT100 | ||
490 | case TY_CSI_PS('q', 2) : /* IGNORED: LED2 on */ break; //VT100 | ||
491 | case TY_CSI_PS('q', 3) : /* IGNORED: LED3 on */ break; //VT100 | ||
492 | case TY_CSI_PS('q', 4) : /* IGNORED: LED4 on */ break; //VT100 | ||
493 | case TY_CSI_PS('x', 0) : reportTerminalParms ( 2); break; //VT100 | ||
494 | case TY_CSI_PS('x', 1) : reportTerminalParms ( 3); break; //VT100 | ||
495 | |||
496 | case TY_CSI_PN('@' ) : scr->insertChars (p ); break; | ||
497 | case TY_CSI_PN('A' ) : scr->cursorUp (p ); break; //VT100 | ||
498 | case TY_CSI_PN('B' ) : scr->cursorDown (p ); break; //VT100 | ||
499 | case TY_CSI_PN('C' ) : scr->cursorRight (p ); break; //VT100 | ||
500 | case TY_CSI_PN('D' ) : scr->cursorLeft (p ); break; //VT100 | ||
501 | case TY_CSI_PN('G' ) : scr->setCursorX (p ); break; //LINUX | ||
502 | case TY_CSI_PN('H' ) : scr->setCursorYX (p, q); break; //VT100 | ||
503 | case TY_CSI_PN('L' ) : scr->insertLines (p ); break; | ||
504 | case TY_CSI_PN('M' ) : scr->deleteLines (p ); break; | ||
505 | case TY_CSI_PN('P' ) : scr->deleteChars (p ); break; | ||
506 | case TY_CSI_PN('X' ) : scr->eraseChars (p ); break; | ||
507 | case TY_CSI_PN('c' ) : reportTerminalType ( ); break; //VT100 | ||
508 | case TY_CSI_PN('d' ) : scr->setCursorY (p ); break; //LINUX | ||
509 | case TY_CSI_PN('f' ) : scr->setCursorYX (p, q); break; //VT100 | ||
510 | case TY_CSI_PN('r' ) : scr->setMargins (p, q); break; //VT100 | ||
511 | case TY_CSI_PN('y' ) : /* IGNORED: Confidence test */ break; //VT100 | ||
512 | |||
513 | case TY_CSI_PR('h', 1) : setMode (MODE_AppCuKeys); break; //VT100 | ||
514 | case TY_CSI_PR('l', 1) : resetMode (MODE_AppCuKeys); break; //VT100 | ||
515 | case TY_CSI_PR('s', 1) : saveMode (MODE_AppCuKeys); break; //FIXME | ||
516 | case TY_CSI_PR('r', 1) : restoreMode (MODE_AppCuKeys); break; //FIXME | ||
517 | |||
518 | case TY_CSI_PR('l', 2) : resetMode (MODE_Ansi ); break; //VT100 | ||
519 | |||
520 | case TY_CSI_PR('h', 3) : setColumns ( 132); break; //VT100 | ||
521 | case TY_CSI_PR('l', 3) : setColumns ( 80); break; //VT100 | ||
522 | |||
523 | case TY_CSI_PR('h', 4) : /* IGNORED: soft scrolling */ break; //VT100 | ||
524 | case TY_CSI_PR('l', 4) : /* IGNORED: soft scrolling */ break; //VT100 | ||
525 | |||
526 | case TY_CSI_PR('h', 5) : scr-> setMode (MODE_Screen ); break; //VT100 | ||
527 | case TY_CSI_PR('l', 5) : scr-> resetMode (MODE_Screen ); break; //VT100 | ||
528 | |||
529 | case TY_CSI_PR('h', 6) : scr-> setMode (MODE_Origin ); break; //VT100 | ||
530 | case TY_CSI_PR('l', 6) : scr-> resetMode (MODE_Origin ); break; //VT100 | ||
531 | case TY_CSI_PR('s', 6) : scr-> saveMode (MODE_Origin ); break; //FIXME | ||
532 | case TY_CSI_PR('r', 6) : scr->restoreMode (MODE_Origin ); break; //FIXME | ||
533 | |||
534 | case TY_CSI_PR('h', 7) : scr-> setMode (MODE_Wrap ); break; //VT100 | ||
535 | case TY_CSI_PR('l', 7) : scr-> resetMode (MODE_Wrap ); break; //VT100 | ||
536 | case TY_CSI_PR('s', 7) : scr-> saveMode (MODE_Wrap ); break; //FIXME | ||
537 | case TY_CSI_PR('r', 7) : scr->restoreMode (MODE_Wrap ); break; //FIXME | ||
538 | |||
539 | case TY_CSI_PR('h', 8) : /* IGNORED: autorepeat on */ break; //VT100 | ||
540 | case TY_CSI_PR('l', 8) : /* IGNORED: autorepeat off */ break; //VT100 | ||
541 | |||
542 | case TY_CSI_PR('h', 9) : /* IGNORED: interlace */ break; //VT100 | ||
543 | case TY_CSI_PR('l', 9) : /* IGNORED: interlace */ break; //VT100 | ||
544 | |||
545 | case TY_CSI_PR('h', 25) : setMode (MODE_Cursor ); break; //VT100 | ||
546 | case TY_CSI_PR('l', 25) : resetMode (MODE_Cursor ); break; //VT100 | ||
547 | |||
548 | case TY_CSI_PR('h', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM | ||
549 | case TY_CSI_PR('l', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM | ||
550 | case TY_CSI_PR('s', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM | ||
551 | case TY_CSI_PR('r', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM | ||
552 | |||
553 | case TY_CSI_PR('h', 47) : setMode (MODE_AppScreen); break; //VT100 | ||
554 | case TY_CSI_PR('l', 47) : resetMode (MODE_AppScreen); break; //VT100 | ||
555 | |||
556 | case TY_CSI_PR('h', 1000) : setMode (MODE_Mouse1000); break; //XTERM | ||
557 | case TY_CSI_PR('l', 1000) : resetMode (MODE_Mouse1000); break; //XTERM | ||
558 | case TY_CSI_PR('s', 1000) : saveMode (MODE_Mouse1000); break; //XTERM | ||
559 | case TY_CSI_PR('r', 1000) : restoreMode (MODE_Mouse1000); break; //XTERM | ||
560 | |||
561 | case TY_CSI_PR('h', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM | ||
562 | case TY_CSI_PR('l', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM | ||
563 | case TY_CSI_PR('s', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM | ||
564 | case TY_CSI_PR('r', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM | ||
565 | |||
566 | case TY_CSI_PR('h', 1047) : setMode (MODE_AppScreen); break; //XTERM | ||
567 | case TY_CSI_PR('l', 1047) : resetMode (MODE_AppScreen); break; //XTERM | ||
568 | |||
569 | //FIXME: Unitoken: save translations | ||
570 | case TY_CSI_PR('h', 1048) : saveCursor ( ); break; //XTERM | ||
571 | case TY_CSI_PR('l', 1048) : restoreCursor ( ); break; //XTERM | ||
572 | |||
573 | //FIXME: every once new sequences like this pop up in xterm. | ||
574 | // Here's a guess of what they could mean. | ||
575 | case TY_CSI_PR('h', 1049) : setMode (MODE_AppScreen); break; //XTERM | ||
576 | case TY_CSI_PR('l', 1049) : resetMode (MODE_AppScreen); break; //XTERM | ||
577 | |||
578 | //FIXME: when changing between vt52 and ansi mode evtl do some resetting. | ||
579 | case TY_VT52__('A' ) : scr->cursorUp ( 1); break; //VT52 | ||
580 | case TY_VT52__('B' ) : scr->cursorDown ( 1); break; //VT52 | ||
581 | case TY_VT52__('C' ) : scr->cursorRight ( 1); break; //VT52 | ||
582 | case TY_VT52__('D' ) : scr->cursorLeft ( 1); break; //VT52 | ||
583 | |||
584 | case TY_VT52__('F' ) : setAndUseCharset (0, '0'); break; //VT52 | ||
585 | case TY_VT52__('G' ) : setAndUseCharset (0, 'B'); break; //VT52 | ||
586 | |||
587 | case TY_VT52__('H' ) : scr->setCursorYX (1,1 ); break; //VT52 | ||
588 | case TY_VT52__('I' ) : scr->reverseIndex ( ); break; //VT52 | ||
589 | case TY_VT52__('J' ) : scr->clearToEndOfScreen ( ); break; //VT52 | ||
590 | case TY_VT52__('K' ) : scr->clearToEndOfLine ( ); break; //VT52 | ||
591 | case TY_VT52__('Y' ) : scr->setCursorYX (p-31,q-31 ); break; //VT52 | ||
592 | case TY_VT52__('Z' ) : reportTerminalType ( ); break; //VT52 | ||
593 | case TY_VT52__('<' ) : setMode (MODE_Ansi ); break; //VT52 | ||
594 | case TY_VT52__('=' ) : setMode (MODE_AppKeyPad); break; //VT52 | ||
595 | case TY_VT52__('>' ) : resetMode (MODE_AppKeyPad); break; //VT52 | ||
596 | |||
597 | default : ReportErrorToken(); break; | ||
598 | }; | ||
599 | } | ||
600 | |||
601 | /* ------------------------------------------------------------------------- */ | ||
602 | /* */ | ||
603 | /* Terminal to Host protocol */ | ||
604 | /* */ | ||
605 | /* ------------------------------------------------------------------------- */ | ||
606 | |||
607 | /* | ||
608 | Outgoing bytes originate from several sources: | ||
609 | |||
610 | - Replies to Enquieries. | ||
611 | - Mouse Events | ||
612 | - Keyboard Events | ||
613 | */ | ||
614 | |||
615 | /*! | ||
616 | */ | ||
617 | |||
618 | void TEmuVt102::sendString(const char* s) | ||
619 | { | ||
620 | emit sndBlock(s,strlen(s)); | ||
621 | } | ||
622 | |||
623 | // Replies ----------------------------------------------------------------- -- | ||
624 | |||
625 | // This section copes with replies send as response to an enquiery control code. | ||
626 | |||
627 | /*! | ||
628 | */ | ||
629 | |||
630 | void TEmuVt102::reportCursorPosition() | ||
631 | { char tmp[20]; | ||
632 | sprintf(tmp,"\033[%d;%dR",scr->getCursorY()+1,scr->getCursorX()+1); | ||
633 | sendString(tmp); | ||
634 | } | ||
635 | |||
636 | /* | ||
637 | What follows here is rather obsolete and faked stuff. | ||
638 | The correspondent enquieries are neverthenless issued. | ||
639 | */ | ||
640 | |||
641 | /*! | ||
642 | */ | ||
643 | |||
644 | void TEmuVt102::reportTerminalType() | ||
645 | { | ||
646 | //FIXME: should change? | ||
647 | if (getMode(MODE_Ansi)) | ||
648 | // sendString("\033[?1;2c"); // I'm a VT100 with AP0 //FIXME: send only in response to ^[[0c | ||
649 | sendString("\033[>0;115;0c"); // I'm a VT220 //FIXME: send only in response to ^[[>c | ||
650 | else | ||
651 | sendString("\033/Z"); // I'm a VT52 | ||
652 | } | ||
653 | |||
654 | void TEmuVt102::reportTerminalParms(int p) | ||
655 | // DECREPTPARM | ||
656 | { char tmp[100]; | ||
657 | sprintf(tmp,"\033[%d;1;1;112;112;1;0x",p); // not really true. | ||
658 | sendString(tmp); | ||
659 | } | ||
660 | |||
661 | /*! | ||
662 | */ | ||
663 | |||
664 | void TEmuVt102::reportStatus() | ||
665 | { | ||
666 | sendString("\033[0n"); //VT100. Device status report. 0 = Ready. | ||
667 | } | ||
668 | |||
669 | /*! | ||
670 | */ | ||
671 | |||
672 | #define ANSWER_BACK "" // This is really obsolete VT100 stuff. | ||
673 | |||
674 | void TEmuVt102::reportAnswerBack() | ||
675 | { | ||
676 | sendString(ANSWER_BACK); | ||
677 | } | ||
678 | |||
679 | // Mouse Handling ---------------------------------------------------------- -- | ||
680 | |||
681 | /*! | ||
682 | Mouse clicks are possibly reported to the client | ||
683 | application if it has issued interest in them. | ||
684 | They are normally consumed by the widget for copy | ||
685 | and paste, but may be propagated from the widget | ||
686 | when gui->setMouseMarks is set via setMode(MODE_Mouse1000). | ||
687 | |||
688 | `x',`y' are 1-based. | ||
689 | `ev' (event) indicates the button pressed (0-2) | ||
690 | or a general mouse release (3). | ||
691 | */ | ||
692 | |||
693 | void TEmuVt102::onMouse( int cb, int cx, int cy ) | ||
694 | { char tmp[20]; | ||
695 | if (!connected) return; | ||
696 | sprintf(tmp,"\033[M%c%c%c",cb+040,cx+040,cy+040); | ||
697 | sendString(tmp); | ||
698 | } | ||
699 | |||
700 | // Keyboard Handling ------------------------------------------------------- -- | ||
701 | |||
702 | #define encodeMode(M,B) BITS(B,getMode(M)) | ||
703 | #define encodeStat(M,B) BITS(B,((ev->state() & (M)) == (M))) | ||
704 | |||
705 | /* | ||
706 | Keyboard event handling has been simplified somewhat by pushing | ||
707 | the complications towards a configuration file [see KeyTrans class]. | ||
708 | */ | ||
709 | |||
710 | void TEmuVt102::onKeyPress( QKeyEvent* ev ) | ||
711 | { | ||
712 | if (!connected) return; // someone else gets the keys | ||
713 | |||
714 | //printf("State/Key: 0x%04x 0x%04x (%d,%d)\n",ev->state(),ev->key(),ev->text().length(),ev->text().length()?ev->text().ascii()[0]:0); | ||
715 | |||
716 | // revert to non-history when typing | ||
717 | if (scr->getHistCursor() != scr->getHistLines()); | ||
718 | scr->setHistCursor(scr->getHistLines()); | ||
719 | |||
720 | // lookup in keyboard translation table ... | ||
721 | int cmd; const char* txt; int len; | ||
722 | if (keytrans->findEntry(ev->key(), encodeMode(MODE_NewLine , BITS_NewLine ) + // OLD, | ||
723 | encodeMode(MODE_Ansi , BITS_Ansi ) + // OBSOLETE, | ||
724 | encodeMode(MODE_AppCuKeys, BITS_AppCuKeys ) + // VT100 stuff | ||
725 | encodeStat(ControlButton , BITS_Control ) + | ||
726 | encodeStat(ShiftButton , BITS_Shift ) + | ||
727 | encodeStat(AltButton , BITS_Alt ), | ||
728 | &cmd, &txt, &len )) | ||
729 | //printf("cmd: %d, %s, %d\n",cmd,txt,len); | ||
730 | switch(cmd) // ... and execute if found. | ||
731 | { | ||
732 | case CMD_emitSelection : gui->emitSelection(); return; | ||
733 | case CMD_scrollPageUp : gui->doScroll(-gui->Lines()/2); return; | ||
734 | case CMD_scrollPageDown : gui->doScroll(+gui->Lines()/2); return; | ||
735 | case CMD_scrollLineUp : gui->doScroll(-1 ); return; | ||
736 | case CMD_scrollLineDown : gui->doScroll(+1 ); return; | ||
737 | case CMD_send : emit sndBlock(txt,len); return; | ||
738 | case CMD_prevSession : emit prevSession(); return; | ||
739 | case CMD_nextSession : emit nextSession(); return; | ||
740 | } | ||
741 | |||
742 | // fall back handling | ||
743 | if (!ev->text().isEmpty()) | ||
744 | { | ||
745 | if (ev->state() & AltButton) sendString("\033"); // ESC, this is the ALT prefix | ||
746 | /// very hacky | ||
747 | if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='A')) sendString("\01"); | ||
748 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='B')) sendString("\02"); | ||
749 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='C')) sendString("\03"); | ||
750 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='D')) sendString("\04"); | ||
751 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='E')) sendString("\05"); | ||
752 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='F')) sendString("\06"); | ||
753 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='G')) sendString("\07"); | ||
754 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='H')) sendString("\010"); | ||
755 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='I')) sendString("\011"); | ||
756 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='J')) sendString("\012"); | ||
757 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='K')) sendString("\013"); | ||
758 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='L')) sendString("\014"); | ||
759 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='M')) sendString("\015"); | ||
760 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='N')) sendString("\016"); | ||
761 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='O')) sendString("\017"); | ||
762 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='P')) sendString("\020"); | ||
763 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='Q')) sendString("\021"); | ||
764 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='R')) sendString("\022"); | ||
765 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='S')) sendString("\023"); | ||
766 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='T')) sendString("\024"); | ||
767 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='U')) sendString("\025"); | ||
768 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='V')) sendString("\026"); | ||
769 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='W')) sendString("\027"); | ||
770 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='X')) sendString("\030"); | ||
771 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='Y')) sendString("\031"); | ||
772 | else if ((ev->state() & ControlButton) && (ev->text().upper().ascii()[0]=='Z')) sendString("\032"); | ||
773 | else { | ||
774 | QCString s = codec->fromUnicode(ev->text()); // encode for application | ||
775 | emit sndBlock(s.data(),s.length()); // we may well have s.length() > 1 | ||
776 | } | ||
777 | return; | ||
778 | } | ||
779 | } | ||
780 | |||
781 | /* ------------------------------------------------------------------------- */ | ||
782 | /* */ | ||
783 | /* VT100 Charsets */ | ||
784 | /* */ | ||
785 | /* ------------------------------------------------------------------------- */ | ||
786 | |||
787 | // Character Set Conversion ------------------------------------------------ -- | ||
788 | |||
789 | /* | ||
790 | The processing contains a VT100 specific code translation layer. | ||
791 | It's still in use and mainly responsible for the line drawing graphics. | ||
792 | |||
793 | These and some other glyphs are assigned to codes (0x5f-0xfe) | ||
794 | normally occupied by the latin letters. Since this codes also | ||
795 | appear within control sequences, the extra code conversion | ||
796 | does not permute with the tokenizer and is placed behind it | ||
797 | in the pipeline. It only applies to tokens, which represent | ||
798 | plain characters. | ||
799 | |||
800 | This conversion it eventually continued in TEWidget.C, since | ||
801 | it might involve VT100 enhanced fonts, which have these | ||
802 | particular glyphs allocated in (0x00-0x1f) in their code page. | ||
803 | */ | ||
804 | |||
805 | #define CHARSET charset[scr==screen[1]] | ||
806 | |||
807 | // Apply current character map. | ||
808 | |||
809 | unsigned short TEmuVt102::applyCharset(unsigned short c) | ||
810 | { | ||
811 | if (CHARSET.graphic && 0x5f <= c && c <= 0x7e) return vt100_graphics[c-0x5f]; | ||
812 | if (CHARSET.pound && c == '#' ) return 0xa3; //This mode is obsolete | ||
813 | return c; | ||
814 | } | ||
815 | |||
816 | /* | ||
817 | "Charset" related part of the emulation state. | ||
818 | This configures the VT100 charset filter. | ||
819 | |||
820 | While most operation work on the current screen, | ||
821 | the following two are different. | ||
822 | */ | ||
823 | |||
824 | void TEmuVt102::resetCharset(int scrno) | ||
825 | { | ||
826 | charset[scrno].cu_cs = 0; | ||
827 | strncpy(charset[scrno].charset,"BBBB",4); | ||
828 | charset[scrno].sa_graphic = FALSE; | ||
829 | charset[scrno].sa_pound = FALSE; | ||
830 | charset[scrno].graphic = FALSE; | ||
831 | charset[scrno].pound = FALSE; | ||
832 | } | ||
833 | |||
834 | /*! | ||
835 | */ | ||
836 | |||
837 | void TEmuVt102::setCharset(int n, int cs) // on both screens. | ||
838 | { | ||
839 | charset[0].charset[n&3] = cs; useCharset(charset[0].cu_cs); | ||
840 | charset[1].charset[n&3] = cs; useCharset(charset[1].cu_cs); | ||
841 | } | ||
842 | |||
843 | /*! | ||
844 | */ | ||
845 | |||
846 | void TEmuVt102::setAndUseCharset(int n, int cs) | ||
847 | { | ||
848 | CHARSET.charset[n&3] = cs; | ||
849 | useCharset(n&3); | ||
850 | } | ||
851 | |||
852 | /*! | ||
853 | */ | ||
854 | |||
855 | void TEmuVt102::useCharset(int n) | ||
856 | { | ||
857 | CHARSET.cu_cs = n&3; | ||
858 | CHARSET.graphic = (CHARSET.charset[n&3] == '0'); | ||
859 | CHARSET.pound = (CHARSET.charset[n&3] == 'A'); //This mode is obsolete | ||
860 | } | ||
861 | |||
862 | /*! Save the cursor position and the rendition attribute settings. */ | ||
863 | |||
864 | void TEmuVt102::saveCursor() | ||
865 | { | ||
866 | CHARSET.sa_graphic = CHARSET.graphic; | ||
867 | CHARSET.sa_pound = CHARSET.pound; //This mode is obsolete | ||
868 | // we are not clear about these | ||
869 | //sa_charset = charsets[cScreen->charset]; | ||
870 | //sa_charset_num = cScreen->charset; | ||
871 | scr->saveCursor(); | ||
872 | } | ||
873 | |||
874 | /*! Restore the cursor position and the rendition attribute settings. */ | ||
875 | |||
876 | void TEmuVt102::restoreCursor() | ||
877 | { | ||
878 | CHARSET.graphic = CHARSET.sa_graphic; | ||
879 | CHARSET.pound = CHARSET.sa_pound; //This mode is obsolete | ||
880 | scr->restoreCursor(); | ||
881 | } | ||
882 | |||
883 | /* ------------------------------------------------------------------------- */ | ||
884 | /* */ | ||
885 | /* Mode Operations */ | ||
886 | /* */ | ||
887 | /* ------------------------------------------------------------------------- */ | ||
888 | |||
889 | /* | ||
890 | Some of the emulations state is either added to the state of the screens. | ||
891 | |||
892 | This causes some scoping problems, since different emulations choose to | ||
893 | located the mode either to the current screen or to both. | ||
894 | |||
895 | For strange reasons, the extend of the rendition attributes ranges over | ||
896 | all screens and not over the actual screen. | ||
897 | |||
898 | We decided on the precise precise extend, somehow. | ||
899 | */ | ||
900 | |||
901 | // "Mode" related part of the state. These are all booleans. | ||
902 | |||
903 | void TEmuVt102::resetModes() | ||
904 | { | ||
905 | resetMode(MODE_Mouse1000); saveMode(MODE_Mouse1000); | ||
906 | resetMode(MODE_AppScreen); saveMode(MODE_AppScreen); | ||
907 | // here come obsolete modes | ||
908 | resetMode(MODE_AppCuKeys); saveMode(MODE_AppCuKeys); | ||
909 | resetMode(MODE_NewLine ); | ||
910 | setMode(MODE_Ansi ); | ||
911 | } | ||
912 | |||
913 | void TEmuVt102::setMode(int m) | ||
914 | { | ||
915 | currParm.mode[m] = TRUE; | ||
916 | switch (m) | ||
917 | { | ||
918 | case MODE_Mouse1000 : gui->setMouseMarks(FALSE); | ||
919 | break; | ||
920 | case MODE_AppScreen : screen[1]->clearSelection(); | ||
921 | screen[1]->clearEntireScreen(); | ||
922 | setScreen(1); | ||
923 | break; | ||
924 | } | ||
925 | if (m < MODES_SCREEN || m == MODE_NewLine) | ||
926 | { | ||
927 | screen[0]->setMode(m); | ||
928 | screen[1]->setMode(m); | ||
929 | } | ||
930 | } | ||
931 | |||
932 | void TEmuVt102::resetMode(int m) | ||
933 | { | ||
934 | currParm.mode[m] = FALSE; | ||
935 | switch (m) | ||
936 | { | ||
937 | case MODE_Mouse1000 : gui->setMouseMarks(TRUE); | ||
938 | break; | ||
939 | case MODE_AppScreen : screen[0]->clearSelection(); | ||
940 | setScreen(0); | ||
941 | break; | ||
942 | } | ||
943 | if (m < MODES_SCREEN || m == MODE_NewLine) | ||
944 | { | ||
945 | screen[0]->resetMode(m); | ||
946 | screen[1]->resetMode(m); | ||
947 | } | ||
948 | } | ||
949 | |||
950 | void TEmuVt102::saveMode(int m) | ||
951 | { | ||
952 | saveParm.mode[m] = currParm.mode[m]; | ||
953 | } | ||
954 | |||
955 | void TEmuVt102::restoreMode(int m) | ||
956 | { | ||
957 | if(saveParm.mode[m]) setMode(m); else resetMode(m); | ||
958 | } | ||
959 | |||
960 | BOOL TEmuVt102::getMode(int m) | ||
961 | { | ||
962 | return currParm.mode[m]; | ||
963 | } | ||
964 | |||
965 | void TEmuVt102::setConnect(bool c) | ||
966 | { | ||
967 | TEmulation::setConnect(c); | ||
968 | if (c) | ||
969 | { // refresh mouse mode | ||
970 | if (getMode(MODE_Mouse1000)) | ||
971 | setMode(MODE_Mouse1000); | ||
972 | else | ||
973 | resetMode(MODE_Mouse1000); | ||
974 | } | ||
975 | } | ||
976 | |||
977 | /* ------------------------------------------------------------------------- */ | ||
978 | /* */ | ||
979 | /* Diagnostic */ | ||
980 | /* */ | ||
981 | /* ------------------------------------------------------------------------- */ | ||
982 | |||
983 | /*! shows the contents of the scan buffer. | ||
984 | |||
985 | This functions is used for diagnostics. It is called by \e ReportErrorToken | ||
986 | to inform about strings that cannot be decoded or handled by the emulation. | ||
987 | |||
988 | \sa ReportErrorToken | ||
989 | */ | ||
990 | |||
991 | /*! | ||
992 | */ | ||
993 | |||
994 | static void hexdump(int* s, int len) | ||
995 | { int i; | ||
996 | for (i = 0; i < len; i++) | ||
997 | { | ||
998 | if (s[i] == '\\') | ||
999 | printf("\\\\"); | ||
1000 | else | ||
1001 | if ((s[i]) > 32 && s[i] < 127) | ||
1002 | printf("%c",s[i]); | ||
1003 | else | ||
1004 | printf("\\%04x(hex)",s[i]); | ||
1005 | } | ||
1006 | } | ||
1007 | |||
1008 | void TEmuVt102::scan_buffer_report() | ||
1009 | { | ||
1010 | if (ppos == 0 || ppos == 1 && (pbuf[0] & 0xff) >= 32) return; | ||
1011 | printf("token: "); hexdump(pbuf,ppos); printf("\n"); | ||
1012 | } | ||
1013 | |||
1014 | /*! | ||
1015 | */ | ||
1016 | |||
1017 | void TEmuVt102::ReportErrorToken() | ||
1018 | { | ||
1019 | printf("undecodable "); scan_buffer_report(); | ||
1020 | } | ||
diff --git a/noncore/apps/opie-console/TEmuVt102.h b/noncore/apps/opie-console/TEmuVt102.h new file mode 100644 index 0000000..a448a71 --- a/dev/null +++ b/noncore/apps/opie-console/TEmuVt102.h | |||
@@ -0,0 +1,135 @@ | |||
1 | /* -------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [TEmuVt102.h] X Terminal Emulation */ | ||
4 | /* */ | ||
5 | /* -------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* -------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | #ifndef VT102EMU_H | ||
20 | #define VT102EMU_H | ||
21 | |||
22 | #include "TEWidget.h" | ||
23 | #include "TEScreen.h" | ||
24 | #include "TEmulation.h" | ||
25 | #include <qtimer.h> | ||
26 | #include <stdio.h> | ||
27 | |||
28 | // | ||
29 | |||
30 | #define MODE_AppScreen (MODES_SCREEN+0) | ||
31 | #define MODE_AppCuKeys (MODES_SCREEN+1) | ||
32 | #define MODE_AppKeyPad (MODES_SCREEN+2) | ||
33 | #define MODE_Mouse1000 (MODES_SCREEN+3) | ||
34 | #define MODE_Ansi (MODES_SCREEN+4) | ||
35 | #define MODE_total (MODES_SCREEN+5) | ||
36 | |||
37 | struct DECpar | ||
38 | { | ||
39 | BOOL mode[MODE_total]; | ||
40 | }; | ||
41 | |||
42 | struct CharCodes | ||
43 | { | ||
44 | // coding info | ||
45 | char charset[4]; // | ||
46 | int cu_cs; // actual charset. | ||
47 | bool graphic; // Some VT100 tricks | ||
48 | bool pound ; // Some VT100 tricks | ||
49 | bool sa_graphic; // saved graphic | ||
50 | bool sa_pound; // saved pound | ||
51 | }; | ||
52 | |||
53 | class TEmuVt102 : public TEmulation | ||
54 | { Q_OBJECT | ||
55 | |||
56 | public: | ||
57 | |||
58 | TEmuVt102(TEWidget* gui); | ||
59 | ~TEmuVt102(); | ||
60 | |||
61 | public slots: // signals incoming from TEWidget | ||
62 | |||
63 | void onKeyPress(QKeyEvent*); | ||
64 | void onMouse(int cb, int cx, int cy); | ||
65 | |||
66 | signals: | ||
67 | |||
68 | void changeTitle(int,const QString&); | ||
69 | void prevSession(); | ||
70 | void nextSession(); | ||
71 | |||
72 | public: | ||
73 | |||
74 | void reset(); | ||
75 | |||
76 | void onRcvChar(int cc); | ||
77 | void sendString(const char *); | ||
78 | |||
79 | public: | ||
80 | |||
81 | BOOL getMode (int m); | ||
82 | |||
83 | void setMode (int m); | ||
84 | void resetMode (int m); | ||
85 | void saveMode (int m); | ||
86 | void restoreMode(int m); | ||
87 | void resetModes(); | ||
88 | |||
89 | void setConnect(bool r); | ||
90 | |||
91 | private: | ||
92 | |||
93 | void resetToken(); | ||
94 | #define MAXPBUF 80 | ||
95 | void pushToToken(int cc); | ||
96 | int pbuf[MAXPBUF]; //FIXME: overflow? | ||
97 | int ppos; | ||
98 | #define MAXARGS 15 | ||
99 | void addDigit(int dig); | ||
100 | void addArgument(); | ||
101 | int argv[MAXARGS]; | ||
102 | int argc; | ||
103 | void initTokenizer(); | ||
104 | int tbl[256]; | ||
105 | |||
106 | void scan_buffer_report(); //FIXME: rename | ||
107 | void ReportErrorToken(); //FIXME: rename | ||
108 | |||
109 | void tau(int code, int p, int q); | ||
110 | void XtermHack(); | ||
111 | |||
112 | // | ||
113 | |||
114 | void reportTerminalType(); | ||
115 | void reportStatus(); | ||
116 | void reportAnswerBack(); | ||
117 | void reportCursorPosition(); | ||
118 | void reportTerminalParms(int p); | ||
119 | |||
120 | protected: | ||
121 | |||
122 | unsigned short applyCharset(unsigned short c); | ||
123 | void setCharset(int n, int cs); | ||
124 | void useCharset(int n); | ||
125 | void setAndUseCharset(int n, int cs); | ||
126 | void saveCursor(); | ||
127 | void restoreCursor(); | ||
128 | void resetCharset(int scrno); | ||
129 | CharCodes charset[2]; | ||
130 | |||
131 | DECpar currParm; | ||
132 | DECpar saveParm; | ||
133 | }; | ||
134 | |||
135 | #endif // ifndef ANSIEMU_H | ||
diff --git a/noncore/apps/opie-console/TEmulation.cpp b/noncore/apps/opie-console/TEmulation.cpp new file mode 100644 index 0000000..6f3ad32 --- a/dev/null +++ b/noncore/apps/opie-console/TEmulation.cpp | |||
@@ -0,0 +1,363 @@ | |||
1 | /* -------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [TEmulation.cpp] Terminal Emulation Decoder */ | ||
4 | /* */ | ||
5 | /* -------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* -------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | /*! \class TEmulation | ||
20 | |||
21 | \brief Mediator between TEWidget and TEScreen. | ||
22 | |||
23 | This class is responsible to scan the escapes sequences of the terminal | ||
24 | emulation and to map it to their corresponding semantic complements. | ||
25 | Thus this module knows mainly about decoding escapes sequences and | ||
26 | is a stateless device w.r.t. the semantics. | ||
27 | |||
28 | It is also responsible to refresh the TEWidget by certain rules. | ||
29 | |||
30 | \sa TEWidget \sa TEScreen | ||
31 | |||
32 | \par A note on refreshing | ||
33 | |||
34 | Although the modifications to the current screen image could immediately | ||
35 | be propagated via `TEWidget' to the graphical surface, we have chosen | ||
36 | another way here. | ||
37 | |||
38 | The reason for doing so is twofold. | ||
39 | |||
40 | First, experiments show that directly displaying the operation results | ||
41 | in slowing down the overall performance of emulations. Displaying | ||
42 | individual characters using X11 creates a lot of overhead. | ||
43 | |||
44 | Second, by using the following refreshing method, the screen operations | ||
45 | can be completely separated from the displaying. This greatly simplifies | ||
46 | the programmer's task of coding and maintaining the screen operations, | ||
47 | since one need not worry about differential modifications on the | ||
48 | display affecting the operation of concern. | ||
49 | |||
50 | We use a refreshing algorithm here that has been adoped from rxvt/kvt. | ||
51 | |||
52 | By this, refreshing is driven by a timer, which is (re)started whenever | ||
53 | a new bunch of data to be interpreted by the emulation arives at `onRcvBlock'. | ||
54 | As soon as no more data arrive for `BULK_TIMEOUT' milliseconds, we trigger | ||
55 | refresh. This rule suits both bulk display operation as done by curses as | ||
56 | well as individual characters typed. | ||
57 | (BULK_TIMEOUT < 1000 / max characters received from keyboard per second). | ||
58 | |||
59 | Additionally, we trigger refreshing by newlines comming in to make visual | ||
60 | snapshots of lists as produced by `cat', `ls' and likely programs, thereby | ||
61 | producing the illusion of a permanent and immediate display operation. | ||
62 | |||
63 | As a sort of catch-all needed for cases where none of the above | ||
64 | conditions catch, the screen refresh is also triggered by a count | ||
65 | of incoming bulks (`bulk_incnt'). | ||
66 | */ | ||
67 | |||
68 | /* FIXME | ||
69 | - evtl. the bulk operations could be made more transparent. | ||
70 | */ | ||
71 | |||
72 | #include "TEmulation.h" | ||
73 | #include "TEWidget.h" | ||
74 | #include "TEScreen.h" | ||
75 | #include <stdio.h> | ||
76 | #include <stdlib.h> | ||
77 | #include <unistd.h> | ||
78 | #include <qkeycode.h> | ||
79 | |||
80 | |||
81 | /* ------------------------------------------------------------------------- */ | ||
82 | /* */ | ||
83 | /* TEmulation */ | ||
84 | /* */ | ||
85 | /* ------------------------------------------------------------------------- */ | ||
86 | |||
87 | #define CNTL(c) ((c)-'@') | ||
88 | |||
89 | /*! | ||
90 | */ | ||
91 | |||
92 | TEmulation::TEmulation(TEWidget* gui) | ||
93 | : decoder((QTextDecoder*)NULL) | ||
94 | { | ||
95 | this->gui = gui; | ||
96 | |||
97 | screen[0] = new TEScreen(gui->Lines(),gui->Columns()); | ||
98 | screen[1] = new TEScreen(gui->Lines(),gui->Columns()); | ||
99 | scr = screen[0]; | ||
100 | |||
101 | bulk_nlcnt = 0; // reset bulk newline counter | ||
102 | bulk_incnt = 0; // reset bulk counter | ||
103 | connected = FALSE; | ||
104 | |||
105 | QObject::connect(&bulk_timer, SIGNAL(timeout()), this, SLOT(showBulk()) ); | ||
106 | QObject::connect(gui,SIGNAL(changedImageSizeSignal(int,int)), | ||
107 | this,SLOT(onImageSizeChange(int,int))); | ||
108 | QObject::connect(gui,SIGNAL(changedHistoryCursor(int)), | ||
109 | this,SLOT(onHistoryCursorChange(int))); | ||
110 | QObject::connect(gui,SIGNAL(keyPressedSignal(QKeyEvent*)), | ||
111 | this,SLOT(onKeyPress(QKeyEvent*))); | ||
112 | QObject::connect(gui,SIGNAL(beginSelectionSignal(const int,const int)), | ||
113 | this,SLOT(onSelectionBegin(const int,const int)) ); | ||
114 | QObject::connect(gui,SIGNAL(extendSelectionSignal(const int,const int)), | ||
115 | this,SLOT(onSelectionExtend(const int,const int)) ); | ||
116 | QObject::connect(gui,SIGNAL(endSelectionSignal(const BOOL)), | ||
117 | this,SLOT(setSelection(const BOOL)) ); | ||
118 | QObject::connect(gui,SIGNAL(clearSelectionSignal()), | ||
119 | this,SLOT(clearSelection()) ); | ||
120 | } | ||
121 | |||
122 | /*! | ||
123 | */ | ||
124 | |||
125 | TEmulation::~TEmulation() | ||
126 | { | ||
127 | delete screen[0]; | ||
128 | delete screen[1]; | ||
129 | bulk_timer.stop(); | ||
130 | } | ||
131 | |||
132 | /*! change between primary and alternate screen | ||
133 | */ | ||
134 | |||
135 | void TEmulation::setScreen(int n) | ||
136 | { | ||
137 | scr = screen[n&1]; | ||
138 | } | ||
139 | |||
140 | void TEmulation::setHistory(bool on) | ||
141 | { | ||
142 | screen[0]->setScroll(on); | ||
143 | if (!connected) return; | ||
144 | showBulk(); | ||
145 | } | ||
146 | |||
147 | bool TEmulation::history() | ||
148 | { | ||
149 | return screen[0]->hasScroll(); | ||
150 | } | ||
151 | |||
152 | void TEmulation::setCodec(int c) | ||
153 | { | ||
154 | //FIXME: check whether we have to free codec | ||
155 | codec = c ? QTextCodec::codecForName("utf8") | ||
156 | : QTextCodec::codecForLocale(); | ||
157 | if (decoder) delete decoder; | ||
158 | decoder = codec->makeDecoder(); | ||
159 | } | ||
160 | |||
161 | void TEmulation::setKeytrans(int no) | ||
162 | { | ||
163 | keytrans = KeyTrans::find(no); | ||
164 | } | ||
165 | |||
166 | void TEmulation::setKeytrans(const char * no) | ||
167 | { | ||
168 | keytrans = KeyTrans::find(no); | ||
169 | } | ||
170 | |||
171 | // Interpreting Codes --------------------------------------------------------- | ||
172 | |||
173 | /* | ||
174 | This section deals with decoding the incoming character stream. | ||
175 | Decoding means here, that the stream is first seperated into `tokens' | ||
176 | which are then mapped to a `meaning' provided as operations by the | ||
177 | `Screen' class. | ||
178 | */ | ||
179 | |||
180 | /*! | ||
181 | */ | ||
182 | |||
183 | void TEmulation::onRcvChar(int c) | ||
184 | // process application unicode input to terminal | ||
185 | // this is a trivial scanner | ||
186 | { | ||
187 | c &= 0xff; | ||
188 | switch (c) | ||
189 | { | ||
190 | case '\b' : scr->BackSpace(); break; | ||
191 | case '\t' : scr->Tabulate(); break; | ||
192 | case '\n' : scr->NewLine(); break; | ||
193 | case '\r' : scr->Return(); break; | ||
194 | case 0x07 : gui->Bell(); break; | ||
195 | default : scr->ShowCharacter(c); break; | ||
196 | }; | ||
197 | } | ||
198 | |||
199 | /* ------------------------------------------------------------------------- */ | ||
200 | /* */ | ||
201 | /* Keyboard Handling */ | ||
202 | /* */ | ||
203 | /* ------------------------------------------------------------------------- */ | ||
204 | |||
205 | /*! | ||
206 | */ | ||
207 | |||
208 | void TEmulation::onKeyPress( QKeyEvent* ev ) | ||
209 | { | ||
210 | if (!connected) return; // someone else gets the keys | ||
211 | if (scr->getHistCursor() != scr->getHistLines()); | ||
212 | scr->setHistCursor(scr->getHistLines()); | ||
213 | if (!ev->text().isEmpty()) | ||
214 | { // A block of text | ||
215 | // Note that the text is proper unicode. | ||
216 | // We should do a conversion here, but since this | ||
217 | // routine will never be used, we simply emit plain ascii. | ||
218 | emit sndBlock(ev->text().ascii(),ev->text().length()); | ||
219 | } | ||
220 | else if (ev->ascii()>0) | ||
221 | { unsigned char c[1]; | ||
222 | c[0] = ev->ascii(); | ||
223 | emit sndBlock((char*)c,1); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | // Unblocking, Byte to Unicode translation --------------------------------- -- | ||
228 | |||
229 | /* | ||
230 | We are doing code conversion from locale to unicode first. | ||
231 | */ | ||
232 | |||
233 | void TEmulation::onRcvBlock(const char *s, int len) | ||
234 | { | ||
235 | bulkStart(); | ||
236 | bulk_incnt += 1; | ||
237 | for (int i = 0; i < len; i++) | ||
238 | { | ||
239 | QString result = decoder->toUnicode(&s[i],1); | ||
240 | int reslen = result.length(); | ||
241 | for (int j = 0; j < reslen; j++) | ||
242 | onRcvChar(result[j].unicode()); | ||
243 | if (s[i] == '\n') bulkNewline(); | ||
244 | } | ||
245 | bulkEnd(); | ||
246 | } | ||
247 | |||
248 | // Selection --------------------------------------------------------------- -- | ||
249 | |||
250 | void TEmulation::onSelectionBegin(const int x, const int y) { | ||
251 | if (!connected) return; | ||
252 | scr->setSelBeginXY(x,y); | ||
253 | showBulk(); | ||
254 | } | ||
255 | |||
256 | void TEmulation::onSelectionExtend(const int x, const int y) { | ||
257 | if (!connected) return; | ||
258 | scr->setSelExtentXY(x,y); | ||
259 | showBulk(); | ||
260 | } | ||
261 | |||
262 | void TEmulation::setSelection(const BOOL preserve_line_breaks) { | ||
263 | if (!connected) return; | ||
264 | QString t = scr->getSelText(preserve_line_breaks); | ||
265 | if (!t.isNull()) gui->setSelection(t); | ||
266 | } | ||
267 | |||
268 | void TEmulation::clearSelection() { | ||
269 | if (!connected) return; | ||
270 | scr->clearSelection(); | ||
271 | showBulk(); | ||
272 | } | ||
273 | |||
274 | // Refreshing -------------------------------------------------------------- -- | ||
275 | |||
276 | #define BULK_TIMEOUT 20 | ||
277 | |||
278 | /*! | ||
279 | called when \n comes in. Evtl. triggers showBulk at endBulk | ||
280 | */ | ||
281 | |||
282 | void TEmulation::bulkNewline() | ||
283 | { | ||
284 | bulk_nlcnt += 1; | ||
285 | bulk_incnt = 0; // reset bulk counter since `nl' rule applies | ||
286 | } | ||
287 | |||
288 | /*! | ||
289 | */ | ||
290 | |||
291 | void TEmulation::showBulk() | ||
292 | { | ||
293 | bulk_nlcnt = 0; // reset bulk newline counter | ||
294 | bulk_incnt = 0; // reset bulk counter | ||
295 | if (connected) | ||
296 | { | ||
297 | ca* image = scr->getCookedImage(); // get the image | ||
298 | gui->setImage(image, | ||
299 | scr->getLines(), | ||
300 | scr->getColumns()); // actual refresh | ||
301 | free(image); | ||
302 | //FIXME: check that we do not trigger other draw event here. | ||
303 | gui->setScroll(scr->getHistCursor(),scr->getHistLines()); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | void TEmulation::bulkStart() | ||
308 | { | ||
309 | if (bulk_timer.isActive()) bulk_timer.stop(); | ||
310 | } | ||
311 | |||
312 | void TEmulation::bulkEnd() | ||
313 | { | ||
314 | if ( bulk_nlcnt > gui->Lines() || bulk_incnt > 20 ) | ||
315 | showBulk(); // resets bulk_??cnt to 0, too. | ||
316 | else | ||
317 | bulk_timer.start(BULK_TIMEOUT,TRUE); | ||
318 | } | ||
319 | |||
320 | void TEmulation::setConnect(bool c) | ||
321 | { | ||
322 | connected = c; | ||
323 | if ( connected) | ||
324 | { | ||
325 | onImageSizeChange(gui->Lines(), gui->Columns()); | ||
326 | showBulk(); | ||
327 | } | ||
328 | else | ||
329 | { | ||
330 | scr->clearSelection(); | ||
331 | } | ||
332 | } | ||
333 | |||
334 | // --------------------------------------------------------------------------- | ||
335 | |||
336 | /*! triggered by image size change of the TEWidget `gui'. | ||
337 | |||
338 | This event is simply propagated to the attached screens | ||
339 | and to the related serial line. | ||
340 | */ | ||
341 | |||
342 | void TEmulation::onImageSizeChange(int lines, int columns) | ||
343 | { | ||
344 | if (!connected) return; | ||
345 | screen[0]->resizeImage(lines,columns); | ||
346 | screen[1]->resizeImage(lines,columns); | ||
347 | showBulk(); | ||
348 | emit ImageSizeChanged(lines,columns); // propagate event to serial line | ||
349 | } | ||
350 | |||
351 | void TEmulation::onHistoryCursorChange(int cursor) | ||
352 | { | ||
353 | if (!connected) return; | ||
354 | scr->setHistCursor(cursor); | ||
355 | showBulk(); | ||
356 | } | ||
357 | |||
358 | void TEmulation::setColumns(int columns) | ||
359 | { | ||
360 | //FIXME: this goes strange ways. | ||
361 | // Can we put this straight or explain it at least? | ||
362 | emit changeColumns(columns); | ||
363 | } | ||
diff --git a/noncore/apps/opie-console/TEmulation.h b/noncore/apps/opie-console/TEmulation.h new file mode 100644 index 0000000..ec15e7a --- a/dev/null +++ b/noncore/apps/opie-console/TEmulation.h | |||
@@ -0,0 +1,117 @@ | |||
1 | /* -------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [emulation.h] Fundamental Terminal Emulation */ | ||
4 | /* */ | ||
5 | /* -------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* -------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | #ifndef EMULATION_H | ||
20 | #define EMULATION_H | ||
21 | |||
22 | #include "TEWidget.h" | ||
23 | #include "TEScreen.h" | ||
24 | #include <qtimer.h> | ||
25 | #include <stdio.h> | ||
26 | #include <qtextcodec.h> | ||
27 | #include "keytrans.h" | ||
28 | |||
29 | class TEmulation : public QObject | ||
30 | { Q_OBJECT | ||
31 | |||
32 | public: | ||
33 | |||
34 | TEmulation(TEWidget* gui); | ||
35 | ~TEmulation(); | ||
36 | |||
37 | public: | ||
38 | virtual void setHistory(bool on); | ||
39 | virtual bool history(); | ||
40 | |||
41 | public slots: // signals incoming from TEWidget | ||
42 | |||
43 | virtual void onImageSizeChange(int lines, int columns); | ||
44 | virtual void onHistoryCursorChange(int cursor); | ||
45 | virtual void onKeyPress(QKeyEvent*); | ||
46 | |||
47 | virtual void clearSelection(); | ||
48 | virtual void onSelectionBegin(const int x, const int y); | ||
49 | virtual void onSelectionExtend(const int x, const int y); | ||
50 | virtual void setSelection(const BOOL preserve_line_breaks); | ||
51 | |||
52 | public slots: // signals incoming from data source | ||
53 | |||
54 | void onRcvBlock(const char* txt,int len); | ||
55 | |||
56 | signals: | ||
57 | |||
58 | void sndBlock(const char* txt,int len); | ||
59 | void ImageSizeChanged(int lines, int columns); | ||
60 | void changeColumns(int columns); | ||
61 | void changeTitle(int arg, const char* str); | ||
62 | |||
63 | public: | ||
64 | |||
65 | virtual void onRcvChar(int); | ||
66 | |||
67 | virtual void setMode (int) = 0; | ||
68 | virtual void resetMode(int) = 0; | ||
69 | |||
70 | virtual void sendString(const char*) = 0; | ||
71 | |||
72 | virtual void setConnect(bool r); | ||
73 | void setColumns(int columns); | ||
74 | |||
75 | void setKeytrans(int no); | ||
76 | void setKeytrans(const char * no); | ||
77 | |||
78 | protected: | ||
79 | |||
80 | TEWidget* gui; | ||
81 | TEScreen* scr; // referes to one `screen' | ||
82 | TEScreen* screen[2]; // 0 = primary, 1 = alternate | ||
83 | void setScreen(int n); // set `scr' to `screen[n]' | ||
84 | |||
85 | bool connected; // communicate with widget | ||
86 | |||
87 | void setCodec(int c); // codec number, 0 = locale, 1=utf8 | ||
88 | |||
89 | QTextCodec* codec; | ||
90 | QTextCodec* localeCodec; | ||
91 | QTextDecoder* decoder; | ||
92 | |||
93 | KeyTrans* keytrans; | ||
94 | |||
95 | // refreshing related material. | ||
96 | // this is localized in the class. | ||
97 | private slots: // triggered by timer | ||
98 | |||
99 | void showBulk(); | ||
100 | |||
101 | private: | ||
102 | |||
103 | void bulkNewline(); | ||
104 | void bulkStart(); | ||
105 | void bulkEnd(); | ||
106 | |||
107 | private: | ||
108 | |||
109 | QTimer bulk_timer; | ||
110 | int bulk_nlcnt; // bulk newline counter | ||
111 | char* SelectedText; | ||
112 | int bulk_incnt; // bulk counter | ||
113 | |||
114 | |||
115 | }; | ||
116 | |||
117 | #endif // ifndef EMULATION_H | ||
diff --git a/noncore/apps/opie-console/default.cpp b/noncore/apps/opie-console/default.cpp index dd9681d..ce5c870 100644 --- a/noncore/apps/opie-console/default.cpp +++ b/noncore/apps/opie-console/default.cpp | |||
@@ -63,13 +63,13 @@ extern "C" { | |||
63 | 63 | ||
64 | // Terminal Widget(s) | 64 | // Terminal Widget(s) |
65 | ProfileDialogWidget* newTerminalWidget(const QString& na, QWidget* wid) { | 65 | /* ProfileDialogWidget* newTerminalWidget(const QString& na, QWidget* wid) { |
66 | return new TerminalWidget(na, wid,0 ); | 66 | return new TerminalWidget(na, wid,0 ); |
67 | } | 67 | } |
68 | 68 | */ | |
69 | // VT Emulations | 69 | /* // VT Emulations |
70 | EmulationLayer* newVT102( WidgetLayer* wid ) { | 70 | EmulationLayer* newVT102( WidgetLayer* wid ) { |
71 | return new Vt102Emulation( wid ); | 71 | return new Vt102Emulation( wid ); |
72 | } | 72 | } |
73 | 73 | */ | |
74 | }; | 74 | }; |
75 | 75 | ||
@@ -92,7 +92,7 @@ Default::Default( MetaFactory* fact ) { | |||
92 | fact->addConnectionWidgetFactory( "bt", QObject::tr("Bluetooth"), newBTWidget ); | 92 | fact->addConnectionWidgetFactory( "bt", QObject::tr("Bluetooth"), newBTWidget ); |
93 | 93 | ||
94 | fact->addTerminalWidgetFactory( "default", QObject::tr("Default Terminal"), newTerminalWidget ); | 94 | // fact->addTerminalWidgetFactory( "default", QObject::tr("Default Terminal"), newTerminalWidget ); |
95 | 95 | ||
96 | fact->addEmulationLayer( "default", QObject::tr("Default Terminal"), newVT102 ); | 96 | // fact->addEmulationLayer( "default", QObject::tr("Default Terminal"), newVT102 ); |
97 | } | 97 | } |
98 | Default::~Default() { | 98 | Default::~Default() { |
diff --git a/noncore/apps/opie-console/default.h b/noncore/apps/opie-console/default.h index 03616f3..288f370 100644 --- a/noncore/apps/opie-console/default.h +++ b/noncore/apps/opie-console/default.h | |||
@@ -23,7 +23,7 @@ extern "C" { | |||
23 | ProfileDialogWidget* newBTWidget (const QString&, QWidget* ); | 23 | ProfileDialogWidget* newBTWidget (const QString&, QWidget* ); |
24 | 24 | ||
25 | ProfileDialogWidget* newTerminalWidget(const QString&, QWidget* ); | 25 | // ProfileDialogWidget* newTerminalWidget(const QString&, QWidget* ); |
26 | 26 | ||
27 | EmulationLayer* newVT102( WidgetLayer* ); | 27 | // EmulationLayer* newVT102( WidgetLayer* ); |
28 | }; | 28 | }; |
29 | 29 | ||
diff --git a/noncore/apps/opie-console/history.cpp b/noncore/apps/opie-console/history.cpp index 3b82d16..226a0a2 100644 --- a/noncore/apps/opie-console/history.cpp +++ b/noncore/apps/opie-console/history.cpp | |||
@@ -68,5 +68,5 @@ FIXME: There is noticable decrease in speed, also. Perhaps, | |||
68 | 68 | ||
69 | //#define tmpfile xTmpFile | 69 | //#define tmpfile xTmpFile |
70 | 70 | /* | |
71 | FILE* xTmpFile() | 71 | FILE* xTmpFile() |
72 | { | 72 | { |
@@ -76,5 +76,5 @@ FILE* xTmpFile() | |||
76 | return fopen(fname,"w"); | 76 | return fopen(fname,"w"); |
77 | } | 77 | } |
78 | 78 | */ | |
79 | 79 | ||
80 | // History Buffer /////////////////////////////////////////// | 80 | // History Buffer /////////////////////////////////////////// |
diff --git a/noncore/apps/opie-console/io_layer.cpp b/noncore/apps/opie-console/io_layer.cpp index 9ba2f70..975ee60 100644 --- a/noncore/apps/opie-console/io_layer.cpp +++ b/noncore/apps/opie-console/io_layer.cpp | |||
@@ -19,2 +19,4 @@ void IOLayer::closeRawIO(int) { | |||
19 | 19 | ||
20 | } | 20 | } |
21 | void IOLayer::setSize(int, int ) { | ||
22 | } | ||
diff --git a/noncore/apps/opie-console/io_layer.h b/noncore/apps/opie-console/io_layer.h index bf5a893..9c99222 100644 --- a/noncore/apps/opie-console/io_layer.h +++ b/noncore/apps/opie-console/io_layer.h | |||
@@ -65,4 +65,6 @@ public: | |||
65 | */ | 65 | */ |
66 | virtual void closeRawIO(int); | 66 | virtual void closeRawIO(int); |
67 | |||
68 | |||
67 | signals: | 69 | signals: |
68 | /** | 70 | /** |
@@ -98,4 +100,10 @@ public slots: | |||
98 | */ | 100 | */ |
99 | virtual void reload( const Profile& ) = 0; | 101 | virtual void reload( const Profile& ) = 0; |
102 | |||
103 | /** | ||
104 | * set the size | ||
105 | * needed for pty | ||
106 | */ | ||
107 | virtual void setSize(int rows, int cols ); | ||
100 | }; | 108 | }; |
101 | 109 | ||
diff --git a/noncore/apps/opie-console/mainwindow.cpp b/noncore/apps/opie-console/mainwindow.cpp index 1adb43d..1d7a4be 100644 --- a/noncore/apps/opie-console/mainwindow.cpp +++ b/noncore/apps/opie-console/mainwindow.cpp | |||
@@ -215,11 +215,12 @@ void MainWindow::slotNew() { | |||
215 | 215 | ||
216 | void MainWindow::slotRecordScript() { | 216 | void MainWindow::slotRecordScript() { |
217 | if (currentSession()) { | 217 | /* if (currentSession()) { |
218 | currentSession()->emulationLayer()->startRecording(); | 218 | currentSession()->emulationLayer()->startRecording(); |
219 | } | 219 | } |
220 | */ | ||
220 | } | 221 | } |
221 | 222 | ||
222 | void MainWindow::slotSaveScript() { | 223 | void MainWindow::slotSaveScript() { |
223 | if (currentSession() && currentSession()->emulationLayer()->isRecording()) { | 224 | /* if (currentSession() && currentSession()->emulationLayer()->isRecording()) { |
224 | MimeTypes types; | 225 | MimeTypes types; |
225 | QStringList script; | 226 | QStringList script; |
@@ -232,7 +233,9 @@ void MainWindow::slotSaveScript() { | |||
232 | } | 233 | } |
233 | } | 234 | } |
235 | */ | ||
234 | } | 236 | } |
235 | 237 | ||
236 | void MainWindow::slotRunScript() { | 238 | void MainWindow::slotRunScript() { |
239 | /* | ||
237 | if (currentSession()) { | 240 | if (currentSession()) { |
238 | MimeTypes types; | 241 | MimeTypes types; |
@@ -246,4 +249,5 @@ void MainWindow::slotRunScript() { | |||
246 | } | 249 | } |
247 | } | 250 | } |
251 | */ | ||
248 | } | 252 | } |
249 | 253 | ||
diff --git a/noncore/apps/opie-console/opie-console.pro b/noncore/apps/opie-console/opie-console.pro index 26bce83..7990a64 100644 --- a/noncore/apps/opie-console/opie-console.pro +++ b/noncore/apps/opie-console/opie-console.pro | |||
@@ -14,25 +14,21 @@ HEADERS = io_layer.h io_serial.h io_irda.h io_bt.h io_modem.h \ | |||
14 | tabwidget.h \ | 14 | tabwidget.h \ |
15 | configdialog.h \ | 15 | configdialog.h \ |
16 | emulation_layer.h \ | ||
17 | vt102emulation.h \ | ||
18 | common.h \ | ||
19 | history.h \ | ||
20 | screen.h \ | ||
21 | keytrans.h \ | 16 | keytrans.h \ |
22 | widget_layer.h \ | ||
23 | transferdialog.h \ | 17 | transferdialog.h \ |
24 | profiledialogwidget.h \ | 18 | profiledialogwidget.h \ |
25 | profileeditordialog.h \ | 19 | profileeditordialog.h \ |
26 | default.h \ | 20 | default.h \ |
27 | terminalwidget.h \ | ||
28 | iolayerbase.h \ | 21 | iolayerbase.h \ |
29 | serialconfigwidget.h irdaconfigwidget.h \ | 22 | serialconfigwidget.h irdaconfigwidget.h \ |
30 | btconfigwidget.h modemconfigwidget.h \ | 23 | btconfigwidget.h modemconfigwidget.h \ |
31 | atconfigdialog.h dialdialog.h \ | 24 | atconfigdialog.h dialdialog.h \ |
32 | emulation_widget.h procctl.h \ | 25 | procctl.h \ |
33 | function_keyboard.h \ | 26 | function_keyboard.h \ |
34 | receive_layer.h filereceive.h \ | 27 | receive_layer.h filereceive.h \ |
35 | script.h \ | 28 | script.h \ |
36 | dialer.h | 29 | dialer.h \ |
30 | emulation_handler.h TECommon.h \ | ||
31 | TEHistroy.h TEScreen.h TEWidget.h \ | ||
32 | TEmuVt102.h TEmulation.h | ||
37 | 33 | ||
38 | SOURCES = io_layer.cpp io_serial.cpp io_irda.cpp io_bt.cpp io_modem.cpp \ | 34 | SOURCES = io_layer.cpp io_serial.cpp io_irda.cpp io_bt.cpp io_modem.cpp \ |
@@ -47,23 +43,21 @@ SOURCES = io_layer.cpp io_serial.cpp io_irda.cpp io_bt.cpp io_modem.cpp \ | |||
47 | tabwidget.cpp \ | 43 | tabwidget.cpp \ |
48 | configdialog.cpp \ | 44 | configdialog.cpp \ |
49 | emulation_layer.cpp \ | ||
50 | vt102emulation.cpp \ | ||
51 | history.cpp \ | ||
52 | screen.cpp \ | ||
53 | keytrans.cpp \ | 45 | keytrans.cpp \ |
54 | widget_layer.cpp \ | ||
55 | transferdialog.cpp \ | 46 | transferdialog.cpp \ |
56 | profiledialogwidget.cpp \ | 47 | profiledialogwidget.cpp \ |
57 | profileeditordialog.cpp \ | 48 | profileeditordialog.cpp \ |
58 | terminalwidget.cpp \ | ||
59 | iolayerbase.cpp \ | 49 | iolayerbase.cpp \ |
60 | serialconfigwidget.cpp irdaconfigwidget.cpp \ | 50 | serialconfigwidget.cpp irdaconfigwidget.cpp \ |
61 | btconfigwidget.cpp modemconfigwidget.cpp \ | 51 | btconfigwidget.cpp modemconfigwidget.cpp \ |
62 | atconfigdialog.cpp dialdialog.cpp \ | 52 | atconfigdialog.cpp dialdialog.cpp \ |
63 | emulation_widget.cpp default.cpp procctl.cpp \ | 53 | default.cpp procctl.cpp \ |
64 | function_keyboard.cpp \ | 54 | function_keyboard.cpp \ |
65 | receive_layer.cpp filereceive.cpp \ | 55 | receive_layer.cpp filereceive.cpp \ |
66 | script.cpp \ | 56 | script.cpp \ |
67 | dialer.cpp | 57 | dialer.cpp \ |
58 | emulation_handler.cpp TEHistory.cpp \ | ||
59 | TEScreen.cpp TEWidget.cpp \ | ||
60 | TEmuVt102.cpp TEmulation.cpp | ||
61 | |||
68 | 62 | ||
69 | INTERFACES = configurebase.ui editbase.ui | 63 | INTERFACES = configurebase.ui editbase.ui |
diff --git a/noncore/apps/opie-console/profilemanager.cpp b/noncore/apps/opie-console/profilemanager.cpp index 4b88dec..113327c 100644 --- a/noncore/apps/opie-console/profilemanager.cpp +++ b/noncore/apps/opie-console/profilemanager.cpp | |||
@@ -8,4 +8,5 @@ | |||
8 | #include <qpe/config.h> | 8 | #include <qpe/config.h> |
9 | 9 | ||
10 | #include "emulation_handler.h" | ||
10 | #include "widget_layer.h" | 11 | #include "widget_layer.h" |
11 | #include "emulation_widget.h" | 12 | #include "emulation_widget.h" |
@@ -84,10 +85,12 @@ Session* ProfileManager::fromProfile( const Profile& prof, QWidget* parent) { | |||
84 | stack->addWidget( dummy, 0 ); | 85 | stack->addWidget( dummy, 0 ); |
85 | stack->raiseWidget( 0 ); | 86 | stack->raiseWidget( 0 ); |
86 | WidgetLayer* wid = new EmulationWidget( prof, dummy ); | 87 | EmulationHandler* handler = new EmulationHandler(prof,dummy ); |
87 | lay->addWidget( wid ); | 88 | lay->addWidget( handler->widget() ); |
88 | 89 | // WidgetLayer* wid = new EmulationWidget( prof, dummy ); | |
89 | session->setEmulationWidget( wid ); | 90 | // lay->addWidget( wid ); |
90 | session->setEmulationLayer( m_fact->newEmulationLayer( m_fact->external( prof.terminalName() ), | 91 | |
91 | wid ) ); | 92 | // session->setEmulationWidget( wid ); |
93 | // session->setEmulationLayer( m_fact->newEmulationLayer( m_fact->external( prof.terminalName() ), | ||
94 | // wid ) ); | ||
92 | session->connect(); | 95 | session->connect(); |
93 | 96 | ||
diff --git a/noncore/apps/opie-console/session.cpp b/noncore/apps/opie-console/session.cpp index ff2c3e2..7cae0df 100644 --- a/noncore/apps/opie-console/session.cpp +++ b/noncore/apps/opie-console/session.cpp | |||
@@ -11,16 +11,16 @@ Session::Session() { | |||
11 | m_widget = 0l; | 11 | m_widget = 0l; |
12 | m_layer = 0l; | 12 | m_layer = 0l; |
13 | m_widLay = 0l; | 13 | // m_widLay = 0l; |
14 | m_emLay = 0l; | 14 | // m_emLay = 0l; |
15 | } | 15 | } |
16 | Session::Session( const QString& na, QWidgetStack* widget, IOLayer* lay) | 16 | Session::Session( const QString& na, QWidgetStack* widget, IOLayer* lay) |
17 | : m_name( na ), m_widget( widget ), m_layer( lay ) | 17 | : m_name( na ), m_widget( widget ), m_layer( lay ) |
18 | { | 18 | { |
19 | m_widLay = 0l; | 19 | // m_widLay = 0l; |
20 | m_emLay = 0l; | 20 | // m_emLay = 0l; |
21 | } | 21 | } |
22 | Session::~Session() { | 22 | Session::~Session() { |
23 | delete m_layer; | 23 | delete m_layer; |
24 | delete m_emLay; | 24 | // delete m_emLay; |
25 | delete m_widget; | 25 | delete m_widget; |
26 | /* the widget layer should be deleted by the m_widget */ | 26 | /* the widget layer should be deleted by the m_widget */ |
@@ -35,5 +35,5 @@ IOLayer* Session::layer() { | |||
35 | return m_layer; | 35 | return m_layer; |
36 | } | 36 | } |
37 | EmulationLayer* Session::emulationLayer() { | 37 | /*EmulationLayer* Session::emulationLayer() { |
38 | return m_emLay; | 38 | return m_emLay; |
39 | } | 39 | } |
@@ -41,6 +41,7 @@ WidgetLayer* Session::emulationWidget() { | |||
41 | return m_widLay; | 41 | return m_widLay; |
42 | } | 42 | } |
43 | */ | ||
43 | void Session::connect() { | 44 | void Session::connect() { |
44 | if ( !m_layer || !m_emLay ) | 45 | /* if ( !m_layer || !m_emLay ) |
45 | return; | 46 | return; |
46 | 47 | ||
@@ -49,6 +50,8 @@ void Session::connect() { | |||
49 | QObject::connect(m_emLay, SIGNAL(sndBlock(const QByteArray&) ), | 50 | QObject::connect(m_emLay, SIGNAL(sndBlock(const QByteArray&) ), |
50 | m_layer, SLOT(send(const QByteArray&) ) ); | 51 | m_layer, SLOT(send(const QByteArray&) ) ); |
52 | */ | ||
51 | } | 53 | } |
52 | void Session::disconnect() { | 54 | void Session::disconnect() { |
55 | /* | ||
53 | if ( !m_layer || !m_emLay ) | 56 | if ( !m_layer || !m_emLay ) |
54 | return; | 57 | return; |
@@ -58,4 +61,5 @@ void Session::disconnect() { | |||
58 | QObject::disconnect(m_emLay, SIGNAL(sndBlock(const QByteArray&) ), | 61 | QObject::disconnect(m_emLay, SIGNAL(sndBlock(const QByteArray&) ), |
59 | m_layer, SLOT(send(const QByteArray&) ) ); | 62 | m_layer, SLOT(send(const QByteArray&) ) ); |
63 | */ | ||
60 | } | 64 | } |
61 | void Session::setName( const QString& na){ | 65 | void Session::setName( const QString& na){ |
@@ -65,5 +69,5 @@ void Session::setWidgetStack( QWidgetStack* wid ) { | |||
65 | delete m_widget; | 69 | delete m_widget; |
66 | /* the EmulationLayer was destroyed... */ | 70 | /* the EmulationLayer was destroyed... */ |
67 | delete m_emLay; | 71 | //delete m_emLay; |
68 | m_widget = wid; | 72 | m_widget = wid; |
69 | } | 73 | } |
@@ -72,4 +76,5 @@ void Session::setIOLayer( IOLayer* lay ) { | |||
72 | m_layer = lay; | 76 | m_layer = lay; |
73 | } | 77 | } |
78 | /* | ||
74 | void Session::setEmulationLayer( EmulationLayer* lay ) { | 79 | void Session::setEmulationLayer( EmulationLayer* lay ) { |
75 | delete m_emLay; | 80 | delete m_emLay; |
@@ -80,2 +85,3 @@ void Session::setEmulationWidget( WidgetLayer* lay ) { | |||
80 | m_widLay = lay; | 85 | m_widLay = lay; |
81 | } | 86 | } |
87 | */ \ No newline at end of file | ||
diff --git a/noncore/apps/opie-console/session.h b/noncore/apps/opie-console/session.h index 64c2cdb..04bf257 100644 --- a/noncore/apps/opie-console/session.h +++ b/noncore/apps/opie-console/session.h | |||
@@ -42,5 +42,5 @@ public: | |||
42 | IOLayer* layer(); | 42 | IOLayer* layer(); |
43 | 43 | ||
44 | EmulationLayer* emulationLayer(); | 44 | // EmulationLayer* emulationLayer(); |
45 | WidgetLayer* emulationWidget(); | 45 | WidgetLayer* emulationWidget(); |
46 | 46 | ||
@@ -58,6 +58,6 @@ public: | |||
58 | 58 | ||
59 | void setWidgetStack( QWidgetStack* widget ); | 59 | void setWidgetStack( QWidgetStack* widget ); |
60 | void setEmulationLayer( EmulationLayer* lay ); | 60 | // void setEmulationLayer( EmulationLayer* lay ); |
61 | void setEmulationWidget( WidgetLayer* lay ); | 61 | // void setEmulationWidget( WidgetLayer* lay ); |
62 | void setIOLayer( IOLayer* ); | 62 | void setIOLayer( IOLayer* ); |
63 | void setName( const QString& ); | 63 | void setName( const QString& ); |
@@ -67,6 +67,6 @@ private: | |||
67 | QWidgetStack* m_widget; | 67 | QWidgetStack* m_widget; |
68 | IOLayer* m_layer; | 68 | IOLayer* m_layer; |
69 | EmulationLayer* m_emLay; | 69 | // EmulationLayer* m_emLay; |
70 | WidgetLayer* m_widLay; | 70 | // WidgetLayer* m_widLay; |
71 | 71 | ||
72 | }; | 72 | }; |